- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / app_mode / startup_app_launcher.cc
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 "chrome/browser/chromeos/app_mode/startup_app_launcher.h"
6
7 #include "base/command_line.h"
8 #include "base/files/file_path.h"
9 #include "base/json/json_file_value_serializer.h"
10 #include "base/path_service.h"
11 #include "base/time/time.h"
12 #include "base/values.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/chromeos/app_mode/app_session_lifetime.h"
15 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
16 #include "chrome/browser/chromeos/login/user_manager.h"
17 #include "chrome/browser/extensions/extension_service.h"
18 #include "chrome/browser/extensions/extension_system.h"
19 #include "chrome/browser/extensions/webstore_startup_installer.h"
20 #include "chrome/browser/lifetime/application_lifetime.h"
21 #include "chrome/browser/signin/profile_oauth2_token_service.h"
22 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
23 #include "chrome/browser/signin/token_service.h"
24 #include "chrome/browser/signin/token_service_factory.h"
25 #include "chrome/browser/ui/extensions/application_launch.h"
26 #include "chrome/common/chrome_paths.h"
27 #include "chrome/common/chrome_switches.h"
28 #include "chrome/common/extensions/extension.h"
29 #include "chrome/common/extensions/manifest_handlers/kiosk_mode_info.h"
30 #include "content/public/browser/browser_thread.h"
31 #include "content/public/browser/notification_service.h"
32 #include "google_apis/gaia/gaia_auth_consumer.h"
33 #include "google_apis/gaia/gaia_constants.h"
34
35 using content::BrowserThread;
36 using extensions::Extension;
37 using extensions::WebstoreStartupInstaller;
38
39 namespace chromeos {
40
41 namespace {
42
43 const char kOAuthRefreshToken[] = "refresh_token";
44 const char kOAuthClientId[] = "client_id";
45 const char kOAuthClientSecret[] = "client_secret";
46
47 const base::FilePath::CharType kOAuthFileName[] =
48     FILE_PATH_LITERAL("kiosk_auth");
49
50 bool IsAppInstalled(Profile* profile, const std::string& app_id) {
51   return extensions::ExtensionSystem::Get(profile)->extension_service()->
52       GetInstalledExtension(app_id);
53 }
54
55 }  // namespace
56
57
58 StartupAppLauncher::StartupAppLauncher(Profile* profile,
59                                        const std::string& app_id)
60     : profile_(profile),
61       app_id_(app_id),
62       ready_to_launch_(false) {
63   DCHECK(profile_);
64   DCHECK(Extension::IdIsValid(app_id_));
65 }
66
67 StartupAppLauncher::~StartupAppLauncher() {
68   // StartupAppLauncher can be deleted at anytime during the launch process
69   // through a user bailout shortcut.
70   ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
71       ->RemoveObserver(this);
72   net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
73 }
74
75 void StartupAppLauncher::Initialize() {
76   DVLOG(1) << "Starting... connection = "
77            <<  net::NetworkChangeNotifier::GetConnectionType();
78   StartLoadingOAuthFile();
79 }
80
81 void StartupAppLauncher::AddObserver(Observer* observer) {
82   observer_list_.AddObserver(observer);
83 }
84
85 void StartupAppLauncher::RemoveObserver(Observer* observer) {
86   observer_list_.RemoveObserver(observer);
87 }
88
89 void StartupAppLauncher::StartLoadingOAuthFile() {
90   FOR_EACH_OBSERVER(Observer, observer_list_, OnLoadingOAuthFile());
91
92   KioskOAuthParams* auth_params = new KioskOAuthParams();
93   BrowserThread::PostBlockingPoolTaskAndReply(
94       FROM_HERE,
95       base::Bind(&StartupAppLauncher::LoadOAuthFileOnBlockingPool,
96                  auth_params),
97       base::Bind(&StartupAppLauncher::OnOAuthFileLoaded,
98                  AsWeakPtr(),
99                  base::Owned(auth_params)));
100 }
101
102 // static.
103 void StartupAppLauncher::LoadOAuthFileOnBlockingPool(
104     KioskOAuthParams* auth_params) {
105   int error_code = JSONFileValueSerializer::JSON_NO_ERROR;
106   std::string error_msg;
107   base::FilePath user_data_dir;
108   CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
109   base::FilePath auth_file = user_data_dir.Append(kOAuthFileName);
110   scoped_ptr<JSONFileValueSerializer> serializer(
111       new JSONFileValueSerializer(user_data_dir.Append(kOAuthFileName)));
112   scoped_ptr<base::Value> value(
113       serializer->Deserialize(&error_code, &error_msg));
114   base::DictionaryValue* dict = NULL;
115   if (error_code != JSONFileValueSerializer::JSON_NO_ERROR ||
116       !value.get() || !value->GetAsDictionary(&dict)) {
117     LOG(WARNING) << "Can't find auth file at " << auth_file.value();
118     return;
119   }
120
121   dict->GetString(kOAuthRefreshToken, &auth_params->refresh_token);
122   dict->GetString(kOAuthClientId, &auth_params->client_id);
123   dict->GetString(kOAuthClientSecret, &auth_params->client_secret);
124 }
125
126 void StartupAppLauncher::OnOAuthFileLoaded(KioskOAuthParams* auth_params) {
127   auth_params_ = *auth_params;
128   // Override chrome client_id and secret that will be used for identity
129   // API token minting.
130   if (!auth_params_.client_id.empty() && !auth_params_.client_secret.empty()) {
131     UserManager::Get()->SetAppModeChromeClientOAuthInfo(
132             auth_params_.client_id,
133             auth_params_.client_secret);
134   }
135
136   // If we are restarting chrome (i.e. on crash), we need to initialize
137   // TokenService as well.
138   InitializeTokenService();
139 }
140
141 void StartupAppLauncher::InitializeNetwork() {
142   FOR_EACH_OBSERVER(Observer, observer_list_, OnInitializingNetwork());
143
144   // TODO(tengs): Use NetworkStateInformer instead because it can handle
145   // portal and proxy detection. We will need to do some refactoring to
146   // make NetworkStateInformer more independent from the WebUI handlers.
147   net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
148   OnNetworkChanged(net::NetworkChangeNotifier::GetConnectionType());
149 }
150
151 void StartupAppLauncher::InitializeTokenService() {
152   FOR_EACH_OBSERVER(Observer, observer_list_, OnInitializingTokenService());
153
154   ProfileOAuth2TokenService* profile_token_service =
155       ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
156   if (profile_token_service->RefreshTokenIsAvailable(
157           profile_token_service->GetPrimaryAccountId())) {
158     InitializeNetwork();
159     return;
160   }
161
162   // At the end of this method, the execution will be put on hold until
163   // ProfileOAuth2TokenService triggers either OnRefreshTokenAvailable or
164   // OnRefreshTokensLoaded. Given that we want to handle exactly one event,
165   // whichever comes first, both handlers call RemoveObserver on PO2TS. Handling
166   // any of the two events is the only way to resume the execution and enable
167   // Cleanup method to be called, self-invoking a destructor. In destructor
168   // StartupAppLauncher is no longer an observer of PO2TS and there is no need
169   // to call RemoveObserver again.
170   profile_token_service->AddObserver(this);
171
172   TokenService* token_service = TokenServiceFactory::GetForProfile(profile_);
173   token_service->Initialize(GaiaConstants::kChromeSource, profile_);
174
175   // Pass oauth2 refresh token from the auth file.
176   // TODO(zelidrag): We should probably remove this option after M27.
177   // TODO(fgorski): This can go when we have persistence implemented on PO2TS.
178   // Unless the code is no longer needed.
179   if (!auth_params_.refresh_token.empty()) {
180     token_service->UpdateCredentialsWithOAuth2(
181         GaiaAuthConsumer::ClientOAuthResult(
182             auth_params_.refresh_token,
183             std::string(),  // access_token
184             0));            // new_expires_in_secs
185   } else {
186     // Load whatever tokens we have stored there last time around.
187     token_service->LoadTokensFromDB();
188   }
189 }
190
191 void StartupAppLauncher::OnRefreshTokenAvailable(
192     const std::string& account_id) {
193   ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
194       ->RemoveObserver(this);
195   InitializeNetwork();
196 }
197
198 void StartupAppLauncher::OnRefreshTokensLoaded() {
199   ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
200       ->RemoveObserver(this);
201   InitializeNetwork();
202 }
203
204 void StartupAppLauncher::OnLaunchSuccess() {
205   FOR_EACH_OBSERVER(Observer, observer_list_, OnLaunchSucceeded());
206 }
207
208 void StartupAppLauncher::OnLaunchFailure(KioskAppLaunchError::Error error) {
209   LOG(ERROR) << "App launch failed, error: " << error;
210   DCHECK_NE(KioskAppLaunchError::NONE, error);
211
212   FOR_EACH_OBSERVER(Observer, observer_list_, OnLaunchFailed(error));
213 }
214
215 void StartupAppLauncher::LaunchApp() {
216   if (!ready_to_launch_) {
217     NOTREACHED();
218     LOG(ERROR) << "LaunchApp() called but launcher is not initialized.";
219   }
220
221   const Extension* extension = extensions::ExtensionSystem::Get(profile_)->
222       extension_service()->GetInstalledExtension(app_id_);
223   CHECK(extension);
224
225   if (!extensions::KioskModeInfo::IsKioskEnabled(extension)) {
226     OnLaunchFailure(KioskAppLaunchError::NOT_KIOSK_ENABLED);
227     return;
228   }
229
230   // Always open the app in a window.
231   OpenApplication(AppLaunchParams(profile_, extension,
232                                   extension_misc::LAUNCH_WINDOW, NEW_WINDOW));
233   InitAppSession(profile_, app_id_);
234
235   UserManager::Get()->SessionStarted();
236
237   content::NotificationService::current()->Notify(
238       chrome::NOTIFICATION_KIOSK_APP_LAUNCHED,
239       content::NotificationService::AllSources(),
240       content::NotificationService::NoDetails());
241
242   OnLaunchSuccess();
243 }
244
245 void StartupAppLauncher::BeginInstall() {
246   FOR_EACH_OBSERVER(Observer, observer_list_, OnInstallingApp());
247
248   DVLOG(1) << "BeginInstall... connection = "
249            <<  net::NetworkChangeNotifier::GetConnectionType();
250
251   if (IsAppInstalled(profile_, app_id_)) {
252     OnReadyToLaunch();
253     return;
254   }
255
256   installer_ = new WebstoreStartupInstaller(
257       app_id_,
258       profile_,
259       false,
260       base::Bind(&StartupAppLauncher::InstallCallback, AsWeakPtr()));
261   installer_->BeginInstall();
262 }
263
264 void StartupAppLauncher::InstallCallback(bool success,
265                                          const std::string& error) {
266   installer_ = NULL;
267   if (success) {
268     // Finish initialization after the callback returns.
269     // So that the app finishes its installation.
270     BrowserThread::PostTask(
271         BrowserThread::UI,
272         FROM_HERE,
273         base::Bind(&StartupAppLauncher::OnReadyToLaunch,
274                    AsWeakPtr()));
275     return;
276   }
277
278   LOG(ERROR) << "App install failed: " << error;
279   OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
280 }
281
282 void StartupAppLauncher::OnReadyToLaunch() {
283   ready_to_launch_ = true;
284   FOR_EACH_OBSERVER(Observer, observer_list_, OnReadyToLaunch());
285 }
286
287 void StartupAppLauncher::OnNetworkChanged(
288     net::NetworkChangeNotifier::ConnectionType type) {
289   DVLOG(1) << "OnNetworkChanged... connection = "
290            <<  net::NetworkChangeNotifier::GetConnectionType();
291   if (!net::NetworkChangeNotifier::IsOffline()) {
292     DVLOG(1) << "Network up and running!";
293     net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
294
295     BeginInstall();
296   } else {
297     DVLOG(1) << "Network not running yet!";
298   }
299 }
300
301 }   // namespace chromeos