Chromium Code Reviews| 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 manifest_fetch_data_.reset( | |
| 79 new extensions::ManifestFetchData(update_url, 0)); | |
| 80 manifest_fetch_data_->AddExtension( | |
| 81 app_id_, app->version()->GetString(), NULL, "", ""); | |
| 82 | |
| 83 manifest_fetcher_.reset(net::URLFetcher::Create( | |
| 84 manifest_fetch_data_->full_url(), net::URLFetcher::GET, this)); | |
| 85 manifest_fetcher_->SetRequestContext(profile_->GetRequestContext()); | |
| 86 manifest_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | | |
| 87 net::LOAD_DO_NOT_SAVE_COOKIES | | |
| 88 net::LOAD_DISABLE_CACHE); | |
| 89 manifest_fetcher_->SetAutomaticallyRetryOnNetworkChanges(3); | |
| 90 manifest_fetcher_->Start(); | |
| 91 } | |
| 92 | |
| 93 private: | |
| 94 const Extension* GetInstalledApp() { | |
| 95 ExtensionService* extension_service = | |
| 96 extensions::ExtensionSystem::Get(profile_)->extension_service(); | |
| 97 return extension_service->GetInstalledExtension(app_id_); | |
| 98 } | |
| 99 | |
| 100 void HandleManifestResults(const extensions::ManifestFetchData& fetch_data, | |
| 101 const UpdateManifest::Results* results) { | |
| 102 if (!results || results->list.empty()) { | |
| 103 launcher_->OnUpdateCheckNoUpdate(); | |
| 104 return; | |
| 105 } | |
| 106 | |
| 107 DCHECK_EQ(1u, results->list.size()); | |
| 108 | |
| 109 const UpdateManifest::Result& update = results->list[0]; | |
| 110 | |
| 111 if (update.browser_min_version.length() > 0) { | |
| 112 Version browser_version; | |
| 113 chrome::VersionInfo version_info; | |
| 114 if (version_info.is_valid()) | |
| 115 browser_version = Version(version_info.Version()); | |
| 116 | |
| 117 Version browser_min_version(update.browser_min_version); | |
| 118 if (browser_version.IsValid() && | |
| 119 browser_min_version.IsValid() && | |
| 120 browser_min_version.CompareTo(browser_version) > 0) { | |
| 121 launcher_->OnUpdateCheckNoUpdate(); | |
| 122 return; | |
| 123 } | |
| 124 } | |
| 125 | |
| 126 const Version& existing_version = *GetInstalledApp()->version(); | |
| 127 Version update_version(update.version); | |
| 128 if (existing_version.IsValid() && | |
| 129 update_version.IsValid() && | |
| 130 update_version.CompareTo(existing_version) <= 0) { | |
| 131 launcher_->OnUpdateCheckNoUpdate(); | |
| 132 return; | |
| 133 } | |
| 134 | |
| 135 launcher_->OnUpdateCheckUpdateAvailable(); | |
| 136 } | |
| 137 | |
| 138 // net::URLFetcherDelegate implementation. | |
| 139 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE { | |
| 140 DCHECK_EQ(source, manifest_fetcher_.get()); | |
| 141 | |
| 142 if (source->GetStatus().status() != net::URLRequestStatus::SUCCESS || | |
| 143 source->GetResponseCode() != 200) { | |
| 144 launcher_->OnUpdateCheckNoUpdate(); | |
| 145 return; | |
| 146 } | |
| 147 | |
| 148 std::string data; | |
| 149 source->GetResponseAsString(&data); | |
| 150 scoped_refptr<extensions::SafeManifestParser> safe_parser( | |
| 151 new extensions::SafeManifestParser( | |
| 152 data, | |
| 153 manifest_fetch_data_.release(), | |
| 154 base::Bind(&AppUpdateChecker::HandleManifestResults, | |
| 155 AsWeakPtr()))); | |
| 156 safe_parser->Start(); | |
| 157 } | |
| 158 | |
| 159 StartupAppLauncher* launcher_; | |
| 160 Profile* profile_; | |
| 161 const std::string app_id_; | |
| 162 | |
| 163 scoped_ptr<extensions::ManifestFetchData> manifest_fetch_data_; | |
| 164 scoped_ptr<net::URLFetcher> manifest_fetcher_; | |
| 165 | |
| 166 DISALLOW_COPY_AND_ASSIGN(AppUpdateChecker); | |
| 167 }; | |
| 55 | 168 |
| 56 StartupAppLauncher::StartupAppLauncher(Profile* profile, | 169 StartupAppLauncher::StartupAppLauncher(Profile* profile, |
| 57 const std::string& app_id, | 170 const std::string& app_id, |
| 58 StartupAppLauncher::Delegate* delegate) | 171 StartupAppLauncher::Delegate* delegate) |
| 59 : profile_(profile), | 172 : profile_(profile), |
| 60 app_id_(app_id), | 173 app_id_(app_id), |
| 61 delegate_(delegate), | 174 delegate_(delegate), |
| 62 install_attempted_(false), | 175 install_attempted_(false), |
| 63 ready_to_launch_(false) { | 176 ready_to_launch_(false) { |
| 64 DCHECK(profile_); | 177 DCHECK(profile_); |
| 65 DCHECK(Extension::IdIsValid(app_id_)); | 178 DCHECK(Extension::IdIsValid(app_id_)); |
| 66 } | 179 } |
| 67 | 180 |
| 68 StartupAppLauncher::~StartupAppLauncher() { | 181 StartupAppLauncher::~StartupAppLauncher() { |
| 69 // StartupAppLauncher can be deleted at anytime during the launch process | 182 // StartupAppLauncher can be deleted at anytime during the launch process |
| 70 // through a user bailout shortcut. | 183 // through a user bailout shortcut. |
| 71 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_) | 184 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_) |
| 72 ->RemoveObserver(this); | 185 ->RemoveObserver(this); |
| 73 } | 186 } |
| 74 | 187 |
| 75 void StartupAppLauncher::Initialize() { | 188 void StartupAppLauncher::Initialize() { |
| 76 StartLoadingOAuthFile(); | 189 StartLoadingOAuthFile(); |
| 77 } | 190 } |
| 78 | 191 |
| 79 void StartupAppLauncher::ContinueWithNetworkReady() { | 192 void StartupAppLauncher::ContinueWithNetworkReady() { |
| 80 // Starts install if it is not started. | 193 // Starts install if it is not started. |
| 81 if (!install_attempted_) { | 194 if (!install_attempted_) { |
| 82 install_attempted_ = true; | 195 install_attempted_ = true; |
| 83 BeginInstall(); | 196 MaybeInstall(); |
| 84 } | 197 } |
| 85 } | 198 } |
| 86 | 199 |
| 87 void StartupAppLauncher::StartLoadingOAuthFile() { | 200 void StartupAppLauncher::StartLoadingOAuthFile() { |
| 88 delegate_->OnLoadingOAuthFile(); | 201 delegate_->OnLoadingOAuthFile(); |
| 89 | 202 |
| 90 KioskOAuthParams* auth_params = new KioskOAuthParams(); | 203 KioskOAuthParams* auth_params = new KioskOAuthParams(); |
| 91 BrowserThread::PostBlockingPoolTaskAndReply( | 204 BrowserThread::PostBlockingPoolTaskAndReply( |
| 92 FROM_HERE, | 205 FROM_HERE, |
| 93 base::Bind(&StartupAppLauncher::LoadOAuthFileOnBlockingPool, | 206 base::Bind(&StartupAppLauncher::LoadOAuthFileOnBlockingPool, |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 176 ->RemoveObserver(this); | 289 ->RemoveObserver(this); |
| 177 InitializeNetwork(); | 290 InitializeNetwork(); |
| 178 } | 291 } |
| 179 | 292 |
| 180 void StartupAppLauncher::OnRefreshTokensLoaded() { | 293 void StartupAppLauncher::OnRefreshTokensLoaded() { |
| 181 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_) | 294 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_) |
| 182 ->RemoveObserver(this); | 295 ->RemoveObserver(this); |
| 183 InitializeNetwork(); | 296 InitializeNetwork(); |
| 184 } | 297 } |
| 185 | 298 |
| 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() { | 299 void StartupAppLauncher::LaunchApp() { |
| 198 if (!ready_to_launch_) { | 300 if (!ready_to_launch_) { |
| 199 NOTREACHED(); | 301 NOTREACHED(); |
| 200 LOG(ERROR) << "LaunchApp() called but launcher is not initialized."; | 302 LOG(ERROR) << "LaunchApp() called but launcher is not initialized."; |
| 201 } | 303 } |
| 202 | 304 |
| 203 const Extension* extension = extensions::ExtensionSystem::Get(profile_)-> | 305 const Extension* extension = extensions::ExtensionSystem::Get(profile_)-> |
| 204 extension_service()->GetInstalledExtension(app_id_); | 306 extension_service()->GetInstalledExtension(app_id_); |
| 205 CHECK(extension); | 307 CHECK(extension); |
| 206 | 308 |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 218 UserManager::Get()->SessionStarted(); | 320 UserManager::Get()->SessionStarted(); |
| 219 | 321 |
| 220 content::NotificationService::current()->Notify( | 322 content::NotificationService::current()->Notify( |
| 221 chrome::NOTIFICATION_KIOSK_APP_LAUNCHED, | 323 chrome::NOTIFICATION_KIOSK_APP_LAUNCHED, |
| 222 content::NotificationService::AllSources(), | 324 content::NotificationService::AllSources(), |
| 223 content::NotificationService::NoDetails()); | 325 content::NotificationService::NoDetails()); |
| 224 | 326 |
| 225 OnLaunchSuccess(); | 327 OnLaunchSuccess(); |
| 226 } | 328 } |
| 227 | 329 |
| 228 void StartupAppLauncher::BeginInstall() { | 330 void StartupAppLauncher::OnLaunchSuccess() { |
| 331 delegate_->OnLaunchSucceeded(); | |
| 332 } | |
| 333 | |
| 334 void StartupAppLauncher::OnLaunchFailure(KioskAppLaunchError::Error error) { | |
| 335 LOG(ERROR) << "App launch failed, error: " << error; | |
| 336 DCHECK_NE(KioskAppLaunchError::NONE, error); | |
| 337 | |
| 338 delegate_->OnLaunchFailed(error); | |
| 339 } | |
| 340 | |
| 341 void StartupAppLauncher::MaybeInstall() { | |
| 229 delegate_->OnInstallingApp(); | 342 delegate_->OnInstallingApp(); |
| 230 | 343 |
| 231 if (IsAppInstalled(profile_, app_id_)) { | 344 update_checker_.reset(new AppUpdateChecker(this)); |
| 232 OnReadyToLaunch(); | 345 update_checker_->Start(); |
| 233 return; | 346 } |
| 234 } | |
| 235 | 347 |
| 348 void StartupAppLauncher::OnUpdateCheckNotInstalled() { | |
| 349 BeginInstall(); | |
| 350 } | |
| 351 | |
| 352 void StartupAppLauncher::OnUpdateCheckUpdateAvailable() { | |
| 353 // Uninstall to force a re-install. | |
| 354 // TODO(xiyuan): Find a better way. Either download CRX and install it | |
| 355 // directly or integrate with ExtensionUpdater in someway. | |
| 356 ExtensionService* extension_service = | |
| 357 extensions::ExtensionSystem::Get(profile_)->extension_service(); | |
| 358 extension_service->UninstallExtension(app_id_, false, NULL); | |
| 359 | |
| 360 OnUpdateCheckNotInstalled(); | |
| 361 } | |
| 362 | |
| 363 void StartupAppLauncher::OnUpdateCheckNoUpdate() { | |
| 364 OnReadyToLaunch(); | |
| 365 } | |
| 366 | |
| 367 void StartupAppLauncher::BeginInstall() { | |
| 236 installer_ = new WebstoreStartupInstaller( | 368 installer_ = new WebstoreStartupInstaller( |
| 237 app_id_, | 369 app_id_, |
| 238 profile_, | 370 profile_, |
| 239 false, | 371 false, |
| 240 base::Bind(&StartupAppLauncher::InstallCallback, AsWeakPtr())); | 372 base::Bind(&StartupAppLauncher::InstallCallback, AsWeakPtr())); |
| 241 installer_->BeginInstall(); | 373 installer_->BeginInstall(); |
| 242 } | 374 } |
| 243 | 375 |
| 244 void StartupAppLauncher::InstallCallback(bool success, | 376 void StartupAppLauncher::InstallCallback(bool success, |
| 245 const std::string& error) { | 377 const std::string& error) { |
| 246 installer_ = NULL; | 378 installer_ = NULL; |
| 247 if (success) { | 379 if (success) { |
| 248 // Finish initialization after the callback returns. | 380 // Finish initialization after the callback returns. |
| 249 // So that the app finishes its installation. | 381 // So that the app finishes its installation. |
| 250 BrowserThread::PostTask( | 382 BrowserThread::PostTask( |
| 251 BrowserThread::UI, | 383 BrowserThread::UI, |
| 252 FROM_HERE, | 384 FROM_HERE, |
| 253 base::Bind(&StartupAppLauncher::OnReadyToLaunch, | 385 base::Bind(&StartupAppLauncher::OnReadyToLaunch, |
| 254 AsWeakPtr())); | 386 AsWeakPtr())); |
| 387 | |
| 388 // Schedule app data update after installation. | |
| 389 BrowserThread::PostTask( | |
| 390 BrowserThread::UI, | |
| 391 FROM_HERE, | |
| 392 base::Bind(&StartupAppLauncher::UpdateAppData, | |
| 393 AsWeakPtr())); | |
| 255 return; | 394 return; |
| 256 } | 395 } |
| 257 | 396 |
| 258 LOG(ERROR) << "App install failed: " << error; | 397 LOG(ERROR) << "App install failed: " << error; |
| 259 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL); | 398 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL); |
| 260 } | 399 } |
| 261 | 400 |
| 262 void StartupAppLauncher::OnReadyToLaunch() { | 401 void StartupAppLauncher::OnReadyToLaunch() { |
| 263 ready_to_launch_ = true; | 402 ready_to_launch_ = true; |
| 264 delegate_->OnReadyToLaunch(); | 403 delegate_->OnReadyToLaunch(); |
| 265 } | 404 } |
| 266 | 405 |
| 406 void StartupAppLauncher::UpdateAppData() { | |
|
Tim Song
2014/01/13 23:41:08
Can't hurt to call ClearAppData here as well.
xiyuan
2014/01/14 00:15:07
Done.
| |
| 407 KioskAppManager::Get()->UpdateAppDataFromProfile(app_id_, profile_, NULL); | |
| 408 } | |
| 409 | |
| 267 } // namespace chromeos | 410 } // namespace chromeos |
| OLD | NEW |