OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/chromeos/app_mode/startup_app_launcher.h" | 5 #include "chrome/browser/chromeos/app_mode/startup_app_launcher.h" |
6 | 6 |
7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
8 #include "base/files/file_path.h" | 8 #include "base/files/file_path.h" |
9 #include "base/json/json_file_value_serializer.h" | 9 #include "base/json/json_file_value_serializer.h" |
10 #include "base/path_service.h" | 10 #include "base/path_service.h" |
11 #include "base/time/time.h" | 11 #include "base/time/time.h" |
12 #include "base/values.h" | 12 #include "base/values.h" |
13 #include "chrome/browser/chrome_notification_types.h" | 13 #include "chrome/browser/chrome_notification_types.h" |
14 #include "chrome/browser/chromeos/app_mode/app_session_lifetime.h" | 14 #include "chrome/browser/chromeos/app_mode/app_session_lifetime.h" |
15 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" | 15 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" |
16 #include "chrome/browser/chromeos/login/user_manager.h" | 16 #include "chrome/browser/chromeos/login/user_manager.h" |
17 #include "chrome/browser/extensions/extension_service.h" | 17 #include "chrome/browser/extensions/extension_service.h" |
18 #include "chrome/browser/extensions/extension_system.h" | 18 #include "chrome/browser/extensions/extension_system.h" |
| 19 #include "chrome/browser/extensions/updater/manifest_fetch_data.h" |
| 20 #include "chrome/browser/extensions/updater/safe_manifest_parser.h" |
19 #include "chrome/browser/extensions/webstore_startup_installer.h" | 21 #include "chrome/browser/extensions/webstore_startup_installer.h" |
20 #include "chrome/browser/lifetime/application_lifetime.h" | 22 #include "chrome/browser/lifetime/application_lifetime.h" |
21 #include "chrome/browser/signin/profile_oauth2_token_service.h" | 23 #include "chrome/browser/signin/profile_oauth2_token_service.h" |
22 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" | 24 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" |
23 #include "chrome/browser/ui/extensions/application_launch.h" | 25 #include "chrome/browser/ui/extensions/application_launch.h" |
24 #include "chrome/common/chrome_paths.h" | 26 #include "chrome/common/chrome_paths.h" |
25 #include "chrome/common/chrome_switches.h" | 27 #include "chrome/common/chrome_switches.h" |
| 28 #include "chrome/common/chrome_version_info.h" |
| 29 #include "chrome/common/extensions/manifest_url_handler.h" |
26 #include "content/public/browser/browser_thread.h" | 30 #include "content/public/browser/browser_thread.h" |
27 #include "content/public/browser/notification_service.h" | 31 #include "content/public/browser/notification_service.h" |
28 #include "extensions/common/extension.h" | 32 #include "extensions/common/extension.h" |
29 #include "extensions/common/manifest_handlers/kiosk_mode_info.h" | 33 #include "extensions/common/manifest_handlers/kiosk_mode_info.h" |
30 #include "google_apis/gaia/gaia_auth_consumer.h" | 34 #include "google_apis/gaia/gaia_auth_consumer.h" |
31 #include "google_apis/gaia/gaia_constants.h" | 35 #include "google_apis/gaia/gaia_constants.h" |
| 36 #include "net/base/load_flags.h" |
| 37 #include "net/url_request/url_fetcher.h" |
| 38 #include "net/url_request/url_fetcher_delegate.h" |
| 39 #include "net/url_request/url_request_context_getter.h" |
| 40 #include "net/url_request/url_request_status.h" |
| 41 #include "url/gurl.h" |
32 | 42 |
33 using content::BrowserThread; | 43 using content::BrowserThread; |
34 using extensions::Extension; | 44 using extensions::Extension; |
35 using extensions::WebstoreStartupInstaller; | 45 using extensions::WebstoreStartupInstaller; |
36 | 46 |
37 namespace chromeos { | 47 namespace chromeos { |
38 | 48 |
39 namespace { | 49 namespace { |
40 | 50 |
41 const char kOAuthRefreshToken[] = "refresh_token"; | 51 const char kOAuthRefreshToken[] = "refresh_token"; |
42 const char kOAuthClientId[] = "client_id"; | 52 const char kOAuthClientId[] = "client_id"; |
43 const char kOAuthClientSecret[] = "client_secret"; | 53 const char kOAuthClientSecret[] = "client_secret"; |
44 | 54 |
45 const base::FilePath::CharType kOAuthFileName[] = | 55 const base::FilePath::CharType kOAuthFileName[] = |
46 FILE_PATH_LITERAL("kiosk_auth"); | 56 FILE_PATH_LITERAL("kiosk_auth"); |
47 | 57 |
48 bool IsAppInstalled(Profile* profile, const std::string& app_id) { | |
49 return extensions::ExtensionSystem::Get(profile)->extension_service()-> | |
50 GetInstalledExtension(app_id); | |
51 } | |
52 | |
53 } // namespace | 58 } // namespace |
54 | 59 |
| 60 class StartupAppLauncher::AppUpdateChecker |
| 61 : public base::SupportsWeakPtr<AppUpdateChecker>, |
| 62 public net::URLFetcherDelegate { |
| 63 public: |
| 64 explicit AppUpdateChecker(StartupAppLauncher* launcher) |
| 65 : launcher_(launcher), |
| 66 profile_(launcher->profile_), |
| 67 app_id_(launcher->app_id_) {} |
| 68 virtual ~AppUpdateChecker() {} |
| 69 |
| 70 void Start() { |
| 71 const Extension* app = GetInstalledApp(); |
| 72 if (!app) { |
| 73 launcher_->OnUpdateCheckNotInstalled(); |
| 74 return; |
| 75 } |
| 76 |
| 77 GURL update_url = extensions::ManifestURL::GetUpdateURL(app); |
| 78 if (update_url.is_empty()) |
| 79 update_url = extension_urls::GetWebstoreUpdateUrl(); |
| 80 if (!update_url.is_valid()) { |
| 81 launcher_->OnUpdateCheckNoUpdate(); |
| 82 return; |
| 83 } |
| 84 |
| 85 manifest_fetch_data_.reset( |
| 86 new extensions::ManifestFetchData(update_url, 0)); |
| 87 manifest_fetch_data_->AddExtension( |
| 88 app_id_, app->version()->GetString(), NULL, "", ""); |
| 89 |
| 90 manifest_fetcher_.reset(net::URLFetcher::Create( |
| 91 manifest_fetch_data_->full_url(), net::URLFetcher::GET, this)); |
| 92 manifest_fetcher_->SetRequestContext(profile_->GetRequestContext()); |
| 93 manifest_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | |
| 94 net::LOAD_DO_NOT_SAVE_COOKIES | |
| 95 net::LOAD_DISABLE_CACHE); |
| 96 manifest_fetcher_->SetAutomaticallyRetryOnNetworkChanges(3); |
| 97 manifest_fetcher_->Start(); |
| 98 } |
| 99 |
| 100 private: |
| 101 const Extension* GetInstalledApp() { |
| 102 ExtensionService* extension_service = |
| 103 extensions::ExtensionSystem::Get(profile_)->extension_service(); |
| 104 return extension_service->GetInstalledExtension(app_id_); |
| 105 } |
| 106 |
| 107 void HandleManifestResults(const extensions::ManifestFetchData& fetch_data, |
| 108 const UpdateManifest::Results* results) { |
| 109 if (!results || results->list.empty()) { |
| 110 launcher_->OnUpdateCheckNoUpdate(); |
| 111 return; |
| 112 } |
| 113 |
| 114 DCHECK_EQ(1u, results->list.size()); |
| 115 |
| 116 const UpdateManifest::Result& update = results->list[0]; |
| 117 |
| 118 if (update.browser_min_version.length() > 0) { |
| 119 Version browser_version; |
| 120 chrome::VersionInfo version_info; |
| 121 if (version_info.is_valid()) |
| 122 browser_version = Version(version_info.Version()); |
| 123 |
| 124 Version browser_min_version(update.browser_min_version); |
| 125 if (browser_version.IsValid() && |
| 126 browser_min_version.IsValid() && |
| 127 browser_min_version.CompareTo(browser_version) > 0) { |
| 128 launcher_->OnUpdateCheckNoUpdate(); |
| 129 return; |
| 130 } |
| 131 } |
| 132 |
| 133 const Version& existing_version = *GetInstalledApp()->version(); |
| 134 Version update_version(update.version); |
| 135 if (existing_version.IsValid() && |
| 136 update_version.IsValid() && |
| 137 update_version.CompareTo(existing_version) <= 0) { |
| 138 launcher_->OnUpdateCheckNoUpdate(); |
| 139 return; |
| 140 } |
| 141 |
| 142 launcher_->OnUpdateCheckUpdateAvailable(); |
| 143 } |
| 144 |
| 145 // net::URLFetcherDelegate implementation. |
| 146 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE { |
| 147 DCHECK_EQ(source, manifest_fetcher_.get()); |
| 148 |
| 149 if (source->GetStatus().status() != net::URLRequestStatus::SUCCESS || |
| 150 source->GetResponseCode() != 200) { |
| 151 launcher_->OnUpdateCheckNoUpdate(); |
| 152 return; |
| 153 } |
| 154 |
| 155 std::string data; |
| 156 source->GetResponseAsString(&data); |
| 157 scoped_refptr<extensions::SafeManifestParser> safe_parser( |
| 158 new extensions::SafeManifestParser( |
| 159 data, |
| 160 manifest_fetch_data_.release(), |
| 161 base::Bind(&AppUpdateChecker::HandleManifestResults, |
| 162 AsWeakPtr()))); |
| 163 safe_parser->Start(); |
| 164 } |
| 165 |
| 166 StartupAppLauncher* launcher_; |
| 167 Profile* profile_; |
| 168 const std::string app_id_; |
| 169 |
| 170 scoped_ptr<extensions::ManifestFetchData> manifest_fetch_data_; |
| 171 scoped_ptr<net::URLFetcher> manifest_fetcher_; |
| 172 |
| 173 DISALLOW_COPY_AND_ASSIGN(AppUpdateChecker); |
| 174 }; |
55 | 175 |
56 StartupAppLauncher::StartupAppLauncher(Profile* profile, | 176 StartupAppLauncher::StartupAppLauncher(Profile* profile, |
57 const std::string& app_id, | 177 const std::string& app_id, |
58 StartupAppLauncher::Delegate* delegate) | 178 StartupAppLauncher::Delegate* delegate) |
59 : profile_(profile), | 179 : profile_(profile), |
60 app_id_(app_id), | 180 app_id_(app_id), |
61 delegate_(delegate), | 181 delegate_(delegate), |
62 install_attempted_(false), | 182 install_attempted_(false), |
63 ready_to_launch_(false) { | 183 ready_to_launch_(false) { |
64 DCHECK(profile_); | 184 DCHECK(profile_); |
65 DCHECK(Extension::IdIsValid(app_id_)); | 185 DCHECK(Extension::IdIsValid(app_id_)); |
66 } | 186 } |
67 | 187 |
68 StartupAppLauncher::~StartupAppLauncher() { | 188 StartupAppLauncher::~StartupAppLauncher() { |
69 // StartupAppLauncher can be deleted at anytime during the launch process | 189 // StartupAppLauncher can be deleted at anytime during the launch process |
70 // through a user bailout shortcut. | 190 // through a user bailout shortcut. |
71 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_) | 191 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_) |
72 ->RemoveObserver(this); | 192 ->RemoveObserver(this); |
73 } | 193 } |
74 | 194 |
75 void StartupAppLauncher::Initialize() { | 195 void StartupAppLauncher::Initialize() { |
76 StartLoadingOAuthFile(); | 196 StartLoadingOAuthFile(); |
77 } | 197 } |
78 | 198 |
79 void StartupAppLauncher::ContinueWithNetworkReady() { | 199 void StartupAppLauncher::ContinueWithNetworkReady() { |
80 // Starts install if it is not started. | 200 // Starts install if it is not started. |
81 if (!install_attempted_) { | 201 if (!install_attempted_) { |
82 install_attempted_ = true; | 202 install_attempted_ = true; |
83 BeginInstall(); | 203 MaybeInstall(); |
84 } | 204 } |
85 } | 205 } |
86 | 206 |
87 void StartupAppLauncher::StartLoadingOAuthFile() { | 207 void StartupAppLauncher::StartLoadingOAuthFile() { |
88 delegate_->OnLoadingOAuthFile(); | 208 delegate_->OnLoadingOAuthFile(); |
89 | 209 |
90 KioskOAuthParams* auth_params = new KioskOAuthParams(); | 210 KioskOAuthParams* auth_params = new KioskOAuthParams(); |
91 BrowserThread::PostBlockingPoolTaskAndReply( | 211 BrowserThread::PostBlockingPoolTaskAndReply( |
92 FROM_HERE, | 212 FROM_HERE, |
93 base::Bind(&StartupAppLauncher::LoadOAuthFileOnBlockingPool, | 213 base::Bind(&StartupAppLauncher::LoadOAuthFileOnBlockingPool, |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
176 ->RemoveObserver(this); | 296 ->RemoveObserver(this); |
177 InitializeNetwork(); | 297 InitializeNetwork(); |
178 } | 298 } |
179 | 299 |
180 void StartupAppLauncher::OnRefreshTokensLoaded() { | 300 void StartupAppLauncher::OnRefreshTokensLoaded() { |
181 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_) | 301 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_) |
182 ->RemoveObserver(this); | 302 ->RemoveObserver(this); |
183 InitializeNetwork(); | 303 InitializeNetwork(); |
184 } | 304 } |
185 | 305 |
186 void StartupAppLauncher::OnLaunchSuccess() { | |
187 delegate_->OnLaunchSucceeded(); | |
188 } | |
189 | |
190 void StartupAppLauncher::OnLaunchFailure(KioskAppLaunchError::Error error) { | |
191 LOG(ERROR) << "App launch failed, error: " << error; | |
192 DCHECK_NE(KioskAppLaunchError::NONE, error); | |
193 | |
194 delegate_->OnLaunchFailed(error); | |
195 } | |
196 | |
197 void StartupAppLauncher::LaunchApp() { | 306 void StartupAppLauncher::LaunchApp() { |
198 if (!ready_to_launch_) { | 307 if (!ready_to_launch_) { |
199 NOTREACHED(); | 308 NOTREACHED(); |
200 LOG(ERROR) << "LaunchApp() called but launcher is not initialized."; | 309 LOG(ERROR) << "LaunchApp() called but launcher is not initialized."; |
201 } | 310 } |
202 | 311 |
203 const Extension* extension = extensions::ExtensionSystem::Get(profile_)-> | 312 const Extension* extension = extensions::ExtensionSystem::Get(profile_)-> |
204 extension_service()->GetInstalledExtension(app_id_); | 313 extension_service()->GetInstalledExtension(app_id_); |
205 CHECK(extension); | 314 CHECK(extension); |
206 | 315 |
(...skipping 11 matching lines...) Expand all Loading... |
218 UserManager::Get()->SessionStarted(); | 327 UserManager::Get()->SessionStarted(); |
219 | 328 |
220 content::NotificationService::current()->Notify( | 329 content::NotificationService::current()->Notify( |
221 chrome::NOTIFICATION_KIOSK_APP_LAUNCHED, | 330 chrome::NOTIFICATION_KIOSK_APP_LAUNCHED, |
222 content::NotificationService::AllSources(), | 331 content::NotificationService::AllSources(), |
223 content::NotificationService::NoDetails()); | 332 content::NotificationService::NoDetails()); |
224 | 333 |
225 OnLaunchSuccess(); | 334 OnLaunchSuccess(); |
226 } | 335 } |
227 | 336 |
228 void StartupAppLauncher::BeginInstall() { | 337 void StartupAppLauncher::OnLaunchSuccess() { |
| 338 delegate_->OnLaunchSucceeded(); |
| 339 } |
| 340 |
| 341 void StartupAppLauncher::OnLaunchFailure(KioskAppLaunchError::Error error) { |
| 342 LOG(ERROR) << "App launch failed, error: " << error; |
| 343 DCHECK_NE(KioskAppLaunchError::NONE, error); |
| 344 |
| 345 delegate_->OnLaunchFailed(error); |
| 346 } |
| 347 |
| 348 void StartupAppLauncher::MaybeInstall() { |
229 delegate_->OnInstallingApp(); | 349 delegate_->OnInstallingApp(); |
230 | 350 |
231 if (IsAppInstalled(profile_, app_id_)) { | 351 update_checker_.reset(new AppUpdateChecker(this)); |
232 OnReadyToLaunch(); | 352 update_checker_->Start(); |
233 return; | 353 } |
234 } | |
235 | 354 |
| 355 void StartupAppLauncher::OnUpdateCheckNotInstalled() { |
| 356 BeginInstall(); |
| 357 } |
| 358 |
| 359 void StartupAppLauncher::OnUpdateCheckUpdateAvailable() { |
| 360 // Uninstall to force a re-install. |
| 361 // TODO(xiyuan): Find a better way. Either download CRX and install it |
| 362 // directly or integrate with ExtensionUpdater in someway. |
| 363 ExtensionService* extension_service = |
| 364 extensions::ExtensionSystem::Get(profile_)->extension_service(); |
| 365 extension_service->UninstallExtension(app_id_, false, NULL); |
| 366 |
| 367 OnUpdateCheckNotInstalled(); |
| 368 } |
| 369 |
| 370 void StartupAppLauncher::OnUpdateCheckNoUpdate() { |
| 371 OnReadyToLaunch(); |
| 372 } |
| 373 |
| 374 void StartupAppLauncher::BeginInstall() { |
236 installer_ = new WebstoreStartupInstaller( | 375 installer_ = new WebstoreStartupInstaller( |
237 app_id_, | 376 app_id_, |
238 profile_, | 377 profile_, |
239 false, | 378 false, |
240 base::Bind(&StartupAppLauncher::InstallCallback, AsWeakPtr())); | 379 base::Bind(&StartupAppLauncher::InstallCallback, AsWeakPtr())); |
241 installer_->BeginInstall(); | 380 installer_->BeginInstall(); |
242 } | 381 } |
243 | 382 |
244 void StartupAppLauncher::InstallCallback(bool success, | 383 void StartupAppLauncher::InstallCallback(bool success, |
245 const std::string& error) { | 384 const std::string& error) { |
246 installer_ = NULL; | 385 installer_ = NULL; |
247 if (success) { | 386 if (success) { |
248 // Finish initialization after the callback returns. | 387 // Finish initialization after the callback returns. |
249 // So that the app finishes its installation. | 388 // So that the app finishes its installation. |
250 BrowserThread::PostTask( | 389 BrowserThread::PostTask( |
251 BrowserThread::UI, | 390 BrowserThread::UI, |
252 FROM_HERE, | 391 FROM_HERE, |
253 base::Bind(&StartupAppLauncher::OnReadyToLaunch, | 392 base::Bind(&StartupAppLauncher::OnReadyToLaunch, |
254 AsWeakPtr())); | 393 AsWeakPtr())); |
| 394 |
| 395 // Schedule app data update after installation. |
| 396 BrowserThread::PostTask( |
| 397 BrowserThread::UI, |
| 398 FROM_HERE, |
| 399 base::Bind(&StartupAppLauncher::UpdateAppData, |
| 400 AsWeakPtr())); |
255 return; | 401 return; |
256 } | 402 } |
257 | 403 |
258 LOG(ERROR) << "App install failed: " << error; | 404 LOG(ERROR) << "App install failed: " << error; |
259 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL); | 405 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL); |
260 } | 406 } |
261 | 407 |
262 void StartupAppLauncher::OnReadyToLaunch() { | 408 void StartupAppLauncher::OnReadyToLaunch() { |
263 ready_to_launch_ = true; | 409 ready_to_launch_ = true; |
264 delegate_->OnReadyToLaunch(); | 410 delegate_->OnReadyToLaunch(); |
265 } | 411 } |
266 | 412 |
| 413 void StartupAppLauncher::UpdateAppData() { |
| 414 KioskAppManager::Get()->ClearAppData(app_id_); |
| 415 KioskAppManager::Get()->UpdateAppDataFromProfile(app_id_, profile_, NULL); |
| 416 } |
| 417 |
267 } // namespace chromeos | 418 } // namespace chromeos |
OLD | NEW |