Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / app_mode / startup_app_launcher.cc
index 6418cff..6a2451e 100644 (file)
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/app_mode/app_session_lifetime.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
-#include "chrome/browser/chromeos/login/user_manager.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_diagnosis_runner.h"
+#include "chrome/browser/chromeos/login/session/user_session_manager.h"
 #include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_system.h"
-#include "chrome/browser/extensions/webstore_startup_installer.h"
+#include "chrome/browser/extensions/install_tracker.h"
+#include "chrome/browser/extensions/install_tracker_factory.h"
+#include "chrome/browser/extensions/updater/extension_updater.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
-#include "chrome/browser/signin/profile_oauth2_token_service.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
-#include "chrome/browser/signin/token_service.h"
-#include "chrome/browser/signin/token_service_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/extensions/extension.h"
-#include "chrome/common/extensions/manifest_handlers/kiosk_mode_info.h"
+#include "chrome/common/chrome_version_info.h"
+#include "components/crx_file/id_util.h"
+#include "components/signin/core/browser/profile_oauth2_token_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/user_manager/user_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/manifest_handlers/kiosk_mode_info.h"
+#include "extensions/common/manifest_handlers/offline_enabled_info.h"
+#include "extensions/common/manifest_url_handlers.h"
 #include "google_apis/gaia/gaia_auth_consumer.h"
 #include "google_apis/gaia/gaia_constants.h"
+#include "net/base/load_flags.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_status.h"
+#include "url/gurl.h"
 
 using content::BrowserThread;
 using extensions::Extension;
-using extensions::WebstoreStartupInstaller;
 
 namespace chromeos {
 
@@ -47,47 +60,53 @@ const char kOAuthClientSecret[] = "client_secret";
 const base::FilePath::CharType kOAuthFileName[] =
     FILE_PATH_LITERAL("kiosk_auth");
 
-bool IsAppInstalled(Profile* profile, const std::string& app_id) {
-  return extensions::ExtensionSystem::Get(profile)->extension_service()->
-      GetInstalledExtension(app_id);
-}
+const int kMaxLaunchAttempt = 5;
 
 }  // namespace
 
-
 StartupAppLauncher::StartupAppLauncher(Profile* profile,
-                                       const std::string& app_id)
+                                       const std::string& app_id,
+                                       bool diagnostic_mode,
+                                       StartupAppLauncher::Delegate* delegate)
     : profile_(profile),
       app_id_(app_id),
-      ready_to_launch_(false) {
+      diagnostic_mode_(diagnostic_mode),
+      delegate_(delegate),
+      network_ready_handled_(false),
+      launch_attempt_(0),
+      ready_to_launch_(false),
+      wait_for_crx_update_(false) {
   DCHECK(profile_);
-  DCHECK(Extension::IdIsValid(app_id_));
+  DCHECK(crx_file::id_util::IdIsValid(app_id_));
+  KioskAppManager::Get()->AddObserver(this);
 }
 
 StartupAppLauncher::~StartupAppLauncher() {
+  KioskAppManager::Get()->RemoveObserver(this);
+
   // StartupAppLauncher can be deleted at anytime during the launch process
   // through a user bailout shortcut.
   ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
       ->RemoveObserver(this);
-  net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
 }
 
 void StartupAppLauncher::Initialize() {
-  DVLOG(1) << "Starting... connection = "
-           <<  net::NetworkChangeNotifier::GetConnectionType();
   StartLoadingOAuthFile();
 }
 
-void StartupAppLauncher::AddObserver(Observer* observer) {
-  observer_list_.AddObserver(observer);
-}
-
-void StartupAppLauncher::RemoveObserver(Observer* observer) {
-  observer_list_.RemoveObserver(observer);
+void StartupAppLauncher::ContinueWithNetworkReady() {
+  if (!network_ready_handled_) {
+    network_ready_handled_ = true;
+    // The network might not be ready when KioskAppManager tries to update
+    // external cache initially. Update the external cache now that the network
+    // is ready for sure.
+    wait_for_crx_update_ = true;
+    KioskAppManager::Get()->UpdateExternalCache();
+  }
 }
 
 void StartupAppLauncher::StartLoadingOAuthFile() {
-  FOR_EACH_OBSERVER(Observer, observer_list_, OnLoadingOAuthFile());
+  delegate_->OnLoadingOAuthFile();
 
   KioskOAuthParams* auth_params = new KioskOAuthParams();
   BrowserThread::PostBlockingPoolTaskAndReply(
@@ -128,63 +147,85 @@ void StartupAppLauncher::OnOAuthFileLoaded(KioskOAuthParams* auth_params) {
   // Override chrome client_id and secret that will be used for identity
   // API token minting.
   if (!auth_params_.client_id.empty() && !auth_params_.client_secret.empty()) {
-    UserManager::Get()->SetAppModeChromeClientOAuthInfo(
+    UserSessionManager::GetInstance()->SetAppModeChromeClientOAuthInfo(
             auth_params_.client_id,
             auth_params_.client_secret);
   }
 
   // If we are restarting chrome (i.e. on crash), we need to initialize
-  // TokenService as well.
+  // OAuth2TokenService as well.
   InitializeTokenService();
 }
 
-void StartupAppLauncher::InitializeNetwork() {
-  FOR_EACH_OBSERVER(Observer, observer_list_, OnInitializingNetwork());
+void StartupAppLauncher::RestartLauncher() {
+  // If the installer is still running in the background, we don't need to
+  // restart the launch process. We will just wait until it completes and
+  // launches the kiosk app.
+  if (extensions::ExtensionSystem::Get(profile_)
+          ->extension_service()
+          ->pending_extension_manager()
+          ->IsIdPending(app_id_)) {
+    LOG(WARNING) << "Installer still running";
+    return;
+  }
 
-  // TODO(tengs): Use NetworkStateInformer instead because it can handle
-  // portal and proxy detection. We will need to do some refactoring to
-  // make NetworkStateInformer more independent from the WebUI handlers.
-  net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
-  OnNetworkChanged(net::NetworkChangeNotifier::GetConnectionType());
+  MaybeInitializeNetwork();
 }
 
-void StartupAppLauncher::InitializeTokenService() {
-  FOR_EACH_OBSERVER(Observer, observer_list_, OnInitializingTokenService());
+void StartupAppLauncher::MaybeInitializeNetwork() {
+  network_ready_handled_ = false;
 
-  ProfileOAuth2TokenService* profile_token_service =
-      ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
-  if (profile_token_service->RefreshTokenIsAvailable(
-          profile_token_service->GetPrimaryAccountId())) {
-    InitializeNetwork();
+  const Extension* extension = extensions::ExtensionSystem::Get(profile_)->
+      extension_service()->GetInstalledExtension(app_id_);
+  bool crx_cached = KioskAppManager::Get()->HasCachedCrx(app_id_);
+  const bool requires_network =
+      (!extension && !crx_cached) ||
+      (extension &&
+       !extensions::OfflineEnabledInfo::IsOfflineEnabled(extension));
+
+  if (requires_network) {
+    delegate_->InitializeNetwork();
     return;
   }
 
-  // At the end of this method, the execution will be put on hold until
-  // ProfileOAuth2TokenService triggers either OnRefreshTokenAvailable or
-  // OnRefreshTokensLoaded. Given that we want to handle exactly one event,
-  // whichever comes first, both handlers call RemoveObserver on PO2TS. Handling
-  // any of the two events is the only way to resume the execution and enable
-  // Cleanup method to be called, self-invoking a destructor. In destructor
-  // StartupAppLauncher is no longer an observer of PO2TS and there is no need
-  // to call RemoveObserver again.
-  profile_token_service->AddObserver(this);
-
-  TokenService* token_service = TokenServiceFactory::GetForProfile(profile_);
-  token_service->Initialize(GaiaConstants::kChromeSource, profile_);
-
-  // Pass oauth2 refresh token from the auth file.
-  // TODO(zelidrag): We should probably remove this option after M27.
-  // TODO(fgorski): This can go when we have persistence implemented on PO2TS.
-  // Unless the code is no longer needed.
-  if (!auth_params_.refresh_token.empty()) {
-    token_service->UpdateCredentialsWithOAuth2(
-        GaiaAuthConsumer::ClientOAuthResult(
-            auth_params_.refresh_token,
-            std::string(),  // access_token
-            0));            // new_expires_in_secs
+  // Update the offline enabled crx cache if the network is ready;
+  // or just install the app.
+  if (delegate_->IsNetworkReady())
+    ContinueWithNetworkReady();
+  else
+    BeginInstall();
+}
+
+void StartupAppLauncher::InitializeTokenService() {
+  delegate_->OnInitializingTokenService();
+
+  ProfileOAuth2TokenService* profile_token_service =
+      ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
+  SigninManagerBase* signin_manager =
+      SigninManagerFactory::GetForProfile(profile_);
+  const std::string primary_account_id =
+      signin_manager->GetAuthenticatedAccountId();
+  if (profile_token_service->RefreshTokenIsAvailable(primary_account_id) ||
+      auth_params_.refresh_token.empty()) {
+    MaybeInitializeNetwork();
   } else {
-    // Load whatever tokens we have stored there last time around.
-    token_service->LoadTokensFromDB();
+    // Pass oauth2 refresh token from the auth file.
+    // TODO(zelidrag): We should probably remove this option after M27.
+    // TODO(fgorski): This can go when we have persistence implemented on PO2TS.
+    // Unless the code is no longer needed.
+    // TODO(rogerta): Now that this CL implements token persistence in PO2TS, is
+    // this code still needed?  See above two TODOs.
+    //
+    // ProfileOAuth2TokenService triggers either OnRefreshTokenAvailable or
+    // OnRefreshTokensLoaded. Given that we want to handle exactly one event,
+    // whichever comes first, both handlers call RemoveObserver on PO2TS.
+    // Handling any of the two events is the only way to resume the execution
+    // and enable Cleanup method to be called, self-invoking a destructor.
+    profile_token_service->AddObserver(this);
+
+    profile_token_service->UpdateCredentials(
+        primary_account_id,
+        auth_params_.refresh_token);
   }
 }
 
@@ -192,24 +233,84 @@ void StartupAppLauncher::OnRefreshTokenAvailable(
     const std::string& account_id) {
   ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
       ->RemoveObserver(this);
-  InitializeNetwork();
+  MaybeInitializeNetwork();
 }
 
 void StartupAppLauncher::OnRefreshTokensLoaded() {
   ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
       ->RemoveObserver(this);
-  InitializeNetwork();
+  MaybeInitializeNetwork();
 }
 
-void StartupAppLauncher::OnLaunchSuccess() {
-  FOR_EACH_OBSERVER(Observer, observer_list_, OnLaunchSucceeded());
+void StartupAppLauncher::MaybeLaunchApp() {
+  // Check if the app is offline enabled.
+  const Extension* extension = extensions::ExtensionSystem::Get(profile_)
+                                   ->extension_service()
+                                   ->GetInstalledExtension(app_id_);
+  DCHECK(extension);
+  const bool offline_enabled =
+      extensions::OfflineEnabledInfo::IsOfflineEnabled(extension);
+  if (offline_enabled || delegate_->IsNetworkReady()) {
+    BrowserThread::PostTask(
+        BrowserThread::UI,
+        FROM_HERE,
+        base::Bind(&StartupAppLauncher::OnReadyToLaunch, AsWeakPtr()));
+  } else {
+    ++launch_attempt_;
+    if (launch_attempt_ < kMaxLaunchAttempt) {
+      BrowserThread::PostTask(
+          BrowserThread::UI,
+          FROM_HERE,
+          base::Bind(&StartupAppLauncher::MaybeInitializeNetwork, AsWeakPtr()));
+      return;
+    }
+    OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_LAUNCH);
+  }
 }
 
-void StartupAppLauncher::OnLaunchFailure(KioskAppLaunchError::Error error) {
-  LOG(ERROR) << "App launch failed, error: " << error;
-  DCHECK_NE(KioskAppLaunchError::NONE, error);
+void StartupAppLauncher::OnFinishCrxInstall(const std::string& extension_id,
+                                            bool success) {
+  if (extension_id != app_id_)
+    return;
 
-  FOR_EACH_OBSERVER(Observer, observer_list_, OnLaunchFailed(error));
+  extensions::InstallTracker* tracker =
+      extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
+  tracker->RemoveObserver(this);
+  if (delegate_->IsShowingNetworkConfigScreen()) {
+    LOG(WARNING) << "Showing network config screen";
+    return;
+  }
+
+  if (success) {
+    MaybeLaunchApp();
+    return;
+  }
+
+  LOG(ERROR) << "Failed to install the kiosk application app_id: "
+             << extension_id;
+  OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
+}
+
+void StartupAppLauncher::OnKioskExtensionLoadedInCache(
+    const std::string& app_id) {
+  OnKioskAppDataLoadStatusChanged(app_id);
+}
+
+void StartupAppLauncher::OnKioskExtensionDownloadFailed(
+    const std::string& app_id) {
+  OnKioskAppDataLoadStatusChanged(app_id);
+}
+
+void StartupAppLauncher::OnKioskAppDataLoadStatusChanged(
+    const std::string& app_id) {
+  if (app_id != app_id_ || !wait_for_crx_update_)
+    return;
+
+  wait_for_crx_update_ = false;
+  if (KioskAppManager::Get()->HasCachedCrx(app_id_))
+    BeginInstall();
+  else
+    OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_DOWNLOAD);
 }
 
 void StartupAppLauncher::LaunchApp() {
@@ -229,73 +330,68 @@ void StartupAppLauncher::LaunchApp() {
 
   // Always open the app in a window.
   OpenApplication(AppLaunchParams(profile_, extension,
-                                  extension_misc::LAUNCH_WINDOW, NEW_WINDOW));
+                                  extensions::LAUNCH_CONTAINER_WINDOW,
+                                  NEW_WINDOW));
   InitAppSession(profile_, app_id_);
 
-  UserManager::Get()->SessionStarted();
+  user_manager::UserManager::Get()->SessionStarted();
 
   content::NotificationService::current()->Notify(
       chrome::NOTIFICATION_KIOSK_APP_LAUNCHED,
       content::NotificationService::AllSources(),
       content::NotificationService::NoDetails());
 
+  if (diagnostic_mode_)
+    KioskDiagnosisRunner::Run(profile_, app_id_);
+
   OnLaunchSuccess();
 }
 
-void StartupAppLauncher::BeginInstall() {
-  FOR_EACH_OBSERVER(Observer, observer_list_, OnInstallingApp());
-
-  DVLOG(1) << "BeginInstall... connection = "
-           <<  net::NetworkChangeNotifier::GetConnectionType();
+void StartupAppLauncher::OnLaunchSuccess() {
+  delegate_->OnLaunchSucceeded();
+}
 
-  if (IsAppInstalled(profile_, app_id_)) {
-    OnReadyToLaunch();
-    return;
-  }
+void StartupAppLauncher::OnLaunchFailure(KioskAppLaunchError::Error error) {
+  LOG(ERROR) << "App launch failed, error: " << error;
+  DCHECK_NE(KioskAppLaunchError::NONE, error);
 
-  installer_ = new WebstoreStartupInstaller(
-      app_id_,
-      profile_,
-      false,
-      base::Bind(&StartupAppLauncher::InstallCallback, AsWeakPtr()));
-  installer_->BeginInstall();
+  delegate_->OnLaunchFailed(error);
 }
 
-void StartupAppLauncher::InstallCallback(bool success,
-                                         const std::string& error) {
-  installer_ = NULL;
-  if (success) {
-    // Finish initialization after the callback returns.
-    // So that the app finishes its installation.
-    BrowserThread::PostTask(
-        BrowserThread::UI,
-        FROM_HERE,
-        base::Bind(&StartupAppLauncher::OnReadyToLaunch,
-                   AsWeakPtr()));
+void StartupAppLauncher::BeginInstall() {
+  KioskAppManager::Get()->InstallFromCache(app_id_);
+  if (extensions::ExtensionSystem::Get(profile_)
+          ->extension_service()
+          ->pending_extension_manager()
+          ->IsIdPending(app_id_)) {
+    delegate_->OnInstallingApp();
+    // Observe the crx installation events.
+    extensions::InstallTracker* tracker =
+        extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
+    tracker->AddObserver(this);
     return;
   }
 
-  LOG(ERROR) << "App install failed: " << error;
-  OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
+  if (extensions::ExtensionSystem::Get(profile_)
+          ->extension_service()
+          ->GetInstalledExtension(app_id_)) {
+    // Launch the app.
+    OnReadyToLaunch();
+  } else {
+    // The extension is skipped for installation due to some error.
+    OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
+  }
 }
 
 void StartupAppLauncher::OnReadyToLaunch() {
   ready_to_launch_ = true;
-  FOR_EACH_OBSERVER(Observer, observer_list_, OnReadyToLaunch());
+  UpdateAppData();
+  delegate_->OnReadyToLaunch();
 }
 
-void StartupAppLauncher::OnNetworkChanged(
-    net::NetworkChangeNotifier::ConnectionType type) {
-  DVLOG(1) << "OnNetworkChanged... connection = "
-           <<  net::NetworkChangeNotifier::GetConnectionType();
-  if (!net::NetworkChangeNotifier::IsOffline()) {
-    DVLOG(1) << "Network up and running!";
-    net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
-
-    BeginInstall();
-  } else {
-    DVLOG(1) << "Network not running yet!";
-  }
+void StartupAppLauncher::UpdateAppData() {
+  KioskAppManager::Get()->ClearAppData(app_id_);
+  KioskAppManager::Get()->UpdateAppDataFromProfile(app_id_, profile_, NULL);
 }
 
 }   // namespace chromeos