From dc3723c7bfe689fb0b8c04695b34d1b51b735279 Mon Sep 17 00:00:00 2001 From: "xiyuan@chromium.org" Date: Tue, 14 Jan 2014 02:01:11 +0000 Subject: [PATCH] kiosk: Do update check during launch. - Check app update during launching; - Update cached app data when app is installed/updated; BUG=330713 R=tengs@chromium.org Review URL: https://codereview.chromium.org/137343003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@244632 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/browser/chromeos/app_mode/kiosk_app_data.cc | 76 ++++++-- chrome/browser/chromeos/app_mode/kiosk_app_data.h | 16 ++ .../browser/chromeos/app_mode/kiosk_app_manager.cc | 24 +++ .../browser/chromeos/app_mode/kiosk_app_manager.h | 20 ++- .../app_mode/kiosk_app_manager_browsertest.cc | 142 +++++++++++---- .../chromeos/app_mode/kiosk_app_update_service.cc | 7 + .../chromeos/app_mode/startup_app_launcher.cc | 195 ++++++++++++++++++--- .../chromeos/app_mode/startup_app_launcher.h | 15 ++ 8 files changed, 425 insertions(+), 70 deletions(-) diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_data.cc b/chrome/browser/chromeos/app_mode/kiosk_app_data.cc index f481ea787418..be18cc1966cd 100644 --- a/chrome/browser/chromeos/app_mode/kiosk_app_data.cc +++ b/chrome/browser/chromeos/app_mode/kiosk_app_data.cc @@ -17,14 +17,20 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/app_mode/kiosk_app_data_delegate.h" #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/image_loader.h" #include "chrome/browser/extensions/webstore_data_fetcher.h" #include "chrome/browser/extensions/webstore_install_helper.h" #include "chrome/browser/image_decoder.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/common/extensions/extension_constants.h" +#include "chrome/common/extensions/manifest_handlers/icons_handler.h" #include "content/public/browser/browser_thread.h" #include "extensions/common/manifest.h" #include "extensions/common/manifest_constants.h" #include "ui/gfx/codec/png_codec.h" +#include "ui/gfx/image/image.h" using content::BrowserThread; @@ -288,6 +294,28 @@ void KioskAppData::ClearCache() { } } +void KioskAppData::LoadFromInstalledApp(Profile* profile, + const extensions::Extension* app) { + SetStatus(STATUS_LOADING); + + if (!app) { + app = extensions::ExtensionSystem::Get(profile) + ->extension_service() + ->GetInstalledExtension(app_id_); + } + + DCHECK_EQ(app_id_, app->id()); + + name_ = app->name(); + + const int kIconSize = extension_misc::EXTENSION_ICON_LARGE; + extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource( + app, kIconSize, ExtensionIconSet::MATCH_BIGGER); + extensions::ImageLoader::Get(profile)->LoadImageAsync( + app, image, gfx::Size(kIconSize, kIconSize), + base::Bind(&KioskAppData::OnExtensionIconLoaded, AsWeakPtr())); +} + bool KioskAppData::IsLoading() const { return status_ == STATUS_LOADING; } @@ -354,21 +382,7 @@ void KioskAppData::SetCache(const std::string& name, icon_path_ = icon_path; } -void KioskAppData::OnIconLoadSuccess( - const scoped_refptr& raw_icon, - const gfx::ImageSkia& icon) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - raw_icon_ = raw_icon; - icon_ = icon; - SetStatus(STATUS_LOADED); -} - -void KioskAppData::OnIconLoadFailure() { - // Re-fetch data from web store when failed to load cached data. - StartFetch(); -} - -void KioskAppData::OnWebstoreParseSuccess(const SkBitmap& icon) { +void KioskAppData::SetCache(const std::string& name, const SkBitmap& icon) { icon_ = gfx::ImageSkia::CreateFrom1xBitmap(icon); icon_.MakeThreadSafe(); @@ -387,7 +401,37 @@ void KioskAppData::OnWebstoreParseSuccess(const SkBitmap& icon) { FROM_HERE, base::Bind(&SaveIconToLocalOnBlockingPool, icon_path, raw_icon_)); - SetCache(name_, icon_path); + SetCache(name, icon_path); +} + +void KioskAppData::OnExtensionIconLoaded(const gfx::Image& icon) { + if (icon.IsEmpty()) { + LOG(WARNING) << "Failed to load icon from installed app" + << ", id=" << app_id_; + SetCache(name_, *extensions::IconsInfo::GetDefaultAppIcon().bitmap()); + } else { + SetCache(name_, icon.AsBitmap()); + } + + SetStatus(STATUS_LOADED); +} + +void KioskAppData::OnIconLoadSuccess( + const scoped_refptr& raw_icon, + const gfx::ImageSkia& icon) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + raw_icon_ = raw_icon; + icon_ = icon; + SetStatus(STATUS_LOADED); +} + +void KioskAppData::OnIconLoadFailure() { + // Re-fetch data from web store when failed to load cached data. + StartFetch(); +} + +void KioskAppData::OnWebstoreParseSuccess(const SkBitmap& icon) { + SetCache(name_, icon); SetStatus(STATUS_LOADED); } diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_data.h b/chrome/browser/chromeos/app_mode/kiosk_app_data.h index d685e2249550..4bf061c4ae1e 100644 --- a/chrome/browser/chromeos/app_mode/kiosk_app_data.h +++ b/chrome/browser/chromeos/app_mode/kiosk_app_data.h @@ -14,14 +14,21 @@ #include "chrome/browser/extensions/webstore_data_fetcher_delegate.h" #include "ui/gfx/image/image_skia.h" +class Profile; + namespace base { class RefCountedString; } namespace extensions { +class Extension; class WebstoreDataFetcher; } +namespace gfx { +class Image; +} + namespace net { class URLRequestContextGetter; } @@ -54,6 +61,9 @@ class KioskAppData : public base::SupportsWeakPtr, // Clears locally cached data. void ClearCache(); + // Loads app data from the app installed in the given profile. + void LoadFromInstalledApp(Profile* profile, const extensions::Extension* app); + // Returns true if web store data fetching is in progress. bool IsLoading() const; @@ -81,6 +91,12 @@ class KioskAppData : public base::SupportsWeakPtr, // Sets the cached data. void SetCache(const std::string& name, const base::FilePath& icon_path); + // Helper to set the cached data using a SkBitmap icon. + void SetCache(const std::string& name, const SkBitmap& icon); + + // Callback for extensions::ImageLoader. + void OnExtensionIconLoaded(const gfx::Image& icon); + // Callbacks for IconLoader. void OnIconLoadSuccess(const scoped_refptr& raw_icon, const gfx::ImageSkia& icon); diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc b/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc index 71bfa83873ce..74f33566a2b3 100644 --- a/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc +++ b/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc @@ -269,6 +269,7 @@ void KioskAppManager::RemoveApp(const std::string& app_id) { } void KioskAppManager::GetApps(Apps* apps) const { + apps->clear(); apps->reserve(apps_.size()); for (size_t i = 0; i < apps_.size(); ++i) apps->push_back(App(*apps_[i])); @@ -302,6 +303,25 @@ bool KioskAppManager::GetDisableBailoutShortcut() const { return false; } +void KioskAppManager::ClearAppData(const std::string& app_id) { + KioskAppData* app_data = GetAppDataMutable(app_id); + if (!app_data) + return; + + app_data->ClearCache(); +} + +void KioskAppManager::UpdateAppDataFromProfile( + const std::string& app_id, + Profile* profile, + const extensions::Extension* app) { + KioskAppData* app_data = GetAppDataMutable(app_id); + if (!app_data) + return; + + app_data->LoadFromInstalledApp(profile, app); +} + void KioskAppManager::AddObserver(KioskAppManagerObserver* observer) { observers_.AddObserver(observer); } @@ -341,6 +361,10 @@ const KioskAppData* KioskAppManager::GetAppData( return NULL; } +KioskAppData* KioskAppManager::GetAppDataMutable(const std::string& app_id) { + return const_cast(GetAppData(app_id)); +} + void KioskAppManager::UpdateAppData() { // Gets app id to data mapping for existing apps. std::map old_apps; diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_manager.h b/chrome/browser/chromeos/app_mode/kiosk_app_manager.h index 4b826e8b3138..e67b86ceec9b 100644 --- a/chrome/browser/chromeos/app_mode/kiosk_app_manager.h +++ b/chrome/browser/chromeos/app_mode/kiosk_app_manager.h @@ -20,11 +20,16 @@ #include "ui/gfx/image/image_skia.h" class PrefRegistrySimple; +class Profile; namespace base { class RefCountedString; } +namespace extensions { +class Extension; +} + namespace chromeos { class KioskAppData; @@ -124,6 +129,18 @@ class KioskAppManager : public KioskAppDataDelegate { // Gets whether the bailout shortcut is disabled. bool GetDisableBailoutShortcut() const; + // Clears locally cached app data. + void ClearAppData(const std::string& app_id); + + // Updates app data from the |app| in |profile|. |app| is provided to cover + // the case of app update case where |app| is the new version and is not + // finished installing (e.g. because old version is still running). Otherwise, + // |app| could be NULL and the current installed app in |profile| will be + // used. + void UpdateAppDataFromProfile(const std::string& app_id, + Profile* profile, + const extensions::Extension* app); + void AddObserver(KioskAppManagerObserver* observer); void RemoveObserver(KioskAppManagerObserver* observer); @@ -148,6 +165,7 @@ class KioskAppManager : public KioskAppDataDelegate { // Gets KioskAppData for the given app id. const KioskAppData* GetAppData(const std::string& app_id) const; + KioskAppData* GetAppDataMutable(const std::string& app_id); // Update app data |apps_| based on CrosSettings. void UpdateAppData(); @@ -171,7 +189,7 @@ class KioskAppManager : public KioskAppDataDelegate { // Callback for reading handling checks of the owner public. void OnOwnerFileChecked( const GetConsumerKioskModeStatusCallback& callback, - bool *owner_present); + bool* owner_present); // Reads/writes auto login state from/to local state. AutoLoginState GetAutoLoginState() const; diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc b/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc index ad615533b491..8fe5ca4c27b1 100644 --- a/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc +++ b/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc @@ -5,6 +5,8 @@ #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" #include "base/command_line.h" +#include "base/file_util.h" +#include "base/files/scoped_temp_dir.h" #include "base/message_loop/message_loop.h" #include "base/path_service.h" #include "base/prefs/scoped_user_pref_update.h" @@ -15,11 +17,13 @@ #include "chrome/browser/chromeos/policy/device_local_account.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/policy/browser_policy_connector.h" +#include "chrome/browser/ui/browser.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/test/base/in_process_browser_test.h" #include "chromeos/settings/cros_settings_names.h" #include "content/public/test/test_utils.h" +#include "extensions/common/extension.h" #include "net/base/host_port_pair.h" #include "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/embedded_test_server.h" @@ -60,6 +64,27 @@ void OnEnterpriseDeviceLock( runner_quit_task.Run(); } +scoped_refptr MakeApp(const std::string& name, + const std::string& version, + const std::string& url, + const std::string& id) { + std::string err; + base::DictionaryValue value; + value.SetString("name", name); + value.SetString("version", version); + value.SetString("app.launch.web_url", url); + scoped_refptr app = + extensions::Extension::Create( + base::FilePath(), + extensions::Manifest::INTERNAL, + value, + extensions::Extension::WAS_INSTALLED_BY_DEFAULT, + id, + &err); + EXPECT_EQ(err, ""); + return app; +} + class TestKioskAppManagerObserver : public KioskAppManagerObserver { public: explicit TestKioskAppManagerObserver(KioskAppManager* manager) @@ -153,6 +178,8 @@ class KioskAppManagerTest : public InProcessBrowserTest { // spawning sandbox host process. See crbug.com/322732. embedded_test_server()->StopThread(); + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + InProcessBrowserTest::SetUp(); } @@ -198,8 +225,6 @@ class KioskAppManagerTest : public InProcessBrowserTest { return str; } - KioskAppManager* manager() const { return KioskAppManager::Get(); } - // Locks device for enterprise. policy::EnterpriseInstallAttributes::LockResult LockDeviceForEnterprise() { scoped_ptr lock_result( @@ -219,7 +244,49 @@ class KioskAppManagerTest : public InProcessBrowserTest { return *lock_result.get(); } + void SetExistingApp(const std::string& app_id, + const std::string& app_name, + const std::string& icon_file_name) { + base::FilePath test_dir; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir)); + base::FilePath data_dir = test_dir.AppendASCII("chromeos/app_mode/"); + + // Copy the icon file to temp dir for using because ClearAppData test + // deletes it. + base::FilePath icon_path = temp_dir_.path().AppendASCII(icon_file_name); + base::CopyFile(data_dir.AppendASCII(icon_file_name), icon_path); + + scoped_ptr apps_dict(new base::DictionaryValue); + apps_dict->SetString(app_id + ".name", app_name); + apps_dict->SetString(app_id + ".icon", icon_path.MaybeAsASCII()); + + PrefService* local_state = g_browser_process->local_state(); + DictionaryPrefUpdate dict_update(local_state, + KioskAppManager::kKioskDictionaryName); + dict_update->Set(KioskAppManager::kKeyApps, apps_dict.release()); + + // Make the app appear in device settings. + base::ListValue device_local_accounts; + scoped_ptr entry(new base::DictionaryValue); + entry->SetStringWithoutPathExpansion( + kAccountsPrefDeviceLocalAccountsKeyId, + app_id + "_id"); + entry->SetIntegerWithoutPathExpansion( + kAccountsPrefDeviceLocalAccountsKeyType, + policy::DeviceLocalAccount::TYPE_KIOSK_APP); + entry->SetStringWithoutPathExpansion( + kAccountsPrefDeviceLocalAccountsKeyKioskAppId, + app_id); + device_local_accounts.Append(entry.release()); + CrosSettings::Get()->Set(kAccountsPrefDeviceLocalAccounts, + device_local_accounts); + } + + KioskAppManager* manager() const { return KioskAppManager::Get(); } + private: + base::ScopedTempDir temp_dir_; + DISALLOW_COPY_AND_ASSIGN(KioskAppManagerTest); }; @@ -272,36 +339,37 @@ IN_PROC_BROWSER_TEST_F(KioskAppManagerTest, Basic) { } IN_PROC_BROWSER_TEST_F(KioskAppManagerTest, LoadCached) { - base::FilePath test_dir; - ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir)); - base::FilePath data_dir = test_dir.AppendASCII("chromeos/app_mode/"); + SetExistingApp("app_1", "Cached App1 Name", "red16x16.png"); + + AppDataLoadWaiter waiter(manager()); + waiter.Wait(); + EXPECT_TRUE(waiter.loaded()); + + KioskAppManager::Apps apps; + manager()->GetApps(&apps); + EXPECT_EQ(1u, apps.size()); + EXPECT_EQ("app_1", apps[0].app_id); + EXPECT_EQ("Cached App1 Name", apps[0].name); + EXPECT_EQ(gfx::Size(16, 16), apps[0].icon.size()); +} - scoped_ptr apps_dict(new base::DictionaryValue); - apps_dict->SetString("app_1.name", "App1 Name"); - std::string icon_path = - base::StringPrintf("%s/red16x16.png", data_dir.value().c_str()); - apps_dict->SetString("app_1.icon", icon_path); +IN_PROC_BROWSER_TEST_F(KioskAppManagerTest, ClearAppData) { + SetExistingApp("app_1", "Cached App1 Name", "red16x16.png"); PrefService* local_state = g_browser_process->local_state(); - DictionaryPrefUpdate dict_update(local_state, - KioskAppManager::kKioskDictionaryName); - dict_update->Set(KioskAppManager::kKeyApps, apps_dict.release()); - - // Make the app appear in device settings. - base::ListValue device_local_accounts; - scoped_ptr entry(new base::DictionaryValue); - entry->SetStringWithoutPathExpansion( - kAccountsPrefDeviceLocalAccountsKeyId, - "app_1_id"); - entry->SetIntegerWithoutPathExpansion( - kAccountsPrefDeviceLocalAccountsKeyType, - policy::DeviceLocalAccount::TYPE_KIOSK_APP); - entry->SetStringWithoutPathExpansion( - kAccountsPrefDeviceLocalAccountsKeyKioskAppId, - "app_1"); - device_local_accounts.Append(entry.release()); - CrosSettings::Get()->Set(kAccountsPrefDeviceLocalAccounts, - device_local_accounts); + const base::DictionaryValue* dict = + local_state->GetDictionary(KioskAppManager::kKioskDictionaryName); + const base::DictionaryValue* apps_dict; + EXPECT_TRUE(dict->GetDictionary(KioskAppManager::kKeyApps, &apps_dict)); + EXPECT_TRUE(apps_dict->HasKey("app_1")); + + manager()->ClearAppData("app_1"); + + EXPECT_FALSE(apps_dict->HasKey("app_1")); +} + +IN_PROC_BROWSER_TEST_F(KioskAppManagerTest, UpdateAppDataFromProfile) { + SetExistingApp("app_1", "Cached App1 Name", "red16x16.png"); AppDataLoadWaiter waiter(manager()); waiter.Wait(); @@ -311,8 +379,20 @@ IN_PROC_BROWSER_TEST_F(KioskAppManagerTest, LoadCached) { manager()->GetApps(&apps); EXPECT_EQ(1u, apps.size()); EXPECT_EQ("app_1", apps[0].app_id); - EXPECT_EQ("App1 Name", apps[0].name); - EXPECT_EQ(gfx::Size(16, 16), apps[0].icon.size()); + EXPECT_EQ("Cached App1 Name", apps[0].name); + + scoped_refptr updated_app = + MakeApp("Updated App1 Name", "2.0", "http://localhost/", "app_1"); + manager()->UpdateAppDataFromProfile( + "app_1", browser()->profile(), updated_app.get()); + + waiter.Wait(); + EXPECT_TRUE(waiter.loaded()); + + manager()->GetApps(&apps); + EXPECT_EQ(1u, apps.size()); + EXPECT_EQ("app_1", apps[0].app_id); + EXPECT_EQ("Updated App1 Name", apps[0].name); } IN_PROC_BROWSER_TEST_F(KioskAppManagerTest, BadApp) { diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_update_service.cc b/chrome/browser/chromeos/app_mode/kiosk_app_update_service.cc index f96eab155d3a..9b2763158b89 100644 --- a/chrome/browser/chromeos/app_mode/kiosk_app_update_service.cc +++ b/chrome/browser/chromeos/app_mode/kiosk_app_update_service.cc @@ -8,6 +8,7 @@ #include "chrome/browser/app_mode/app_mode_utils.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process_platform_part_chromeos.h" +#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" #include "chrome/browser/chromeos/system/automatic_reboot_manager.h" #include "chrome/browser/extensions/api/runtime/runtime_api.h" #include "chrome/browser/extensions/extension_service.h" @@ -71,6 +72,12 @@ void KioskAppUpdateService::OnAppUpdateAvailable( if (extension->id() != app_id_) return; + // Clears cached app data so that it will be reloaded if update from app + // does not finish in this run. + KioskAppManager::Get()->ClearAppData(app_id_); + KioskAppManager::Get()->UpdateAppDataFromProfile( + app_id_, profile_, extension); + extensions::RuntimeEventRouter::DispatchOnRestartRequiredEvent( profile_, app_id_, diff --git a/chrome/browser/chromeos/app_mode/startup_app_launcher.cc b/chrome/browser/chromeos/app_mode/startup_app_launcher.cc index 1339567b1a72..8561504d7f5c 100644 --- a/chrome/browser/chromeos/app_mode/startup_app_launcher.cc +++ b/chrome/browser/chromeos/app_mode/startup_app_launcher.cc @@ -16,6 +16,8 @@ #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/updater/manifest_fetch_data.h" +#include "chrome/browser/extensions/updater/safe_manifest_parser.h" #include "chrome/browser/extensions/webstore_startup_installer.h" #include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/signin/profile_oauth2_token_service.h" @@ -23,12 +25,20 @@ #include "chrome/browser/ui/extensions/application_launch.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/chrome_version_info.h" +#include "chrome/common/extensions/manifest_url_handler.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" #include "extensions/common/extension.h" #include "extensions/common/manifest_handlers/kiosk_mode_info.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; @@ -45,13 +55,123 @@ 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); -} - } // namespace +class StartupAppLauncher::AppUpdateChecker + : public base::SupportsWeakPtr, + public net::URLFetcherDelegate { + public: + explicit AppUpdateChecker(StartupAppLauncher* launcher) + : launcher_(launcher), + profile_(launcher->profile_), + app_id_(launcher->app_id_) {} + virtual ~AppUpdateChecker() {} + + void Start() { + const Extension* app = GetInstalledApp(); + if (!app) { + launcher_->OnUpdateCheckNotInstalled(); + return; + } + + GURL update_url = extensions::ManifestURL::GetUpdateURL(app); + if (update_url.is_empty()) + update_url = extension_urls::GetWebstoreUpdateUrl(); + if (!update_url.is_valid()) { + launcher_->OnUpdateCheckNoUpdate(); + return; + } + + manifest_fetch_data_.reset( + new extensions::ManifestFetchData(update_url, 0)); + manifest_fetch_data_->AddExtension( + app_id_, app->version()->GetString(), NULL, "", ""); + + manifest_fetcher_.reset(net::URLFetcher::Create( + manifest_fetch_data_->full_url(), net::URLFetcher::GET, this)); + manifest_fetcher_->SetRequestContext(profile_->GetRequestContext()); + manifest_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | + net::LOAD_DO_NOT_SAVE_COOKIES | + net::LOAD_DISABLE_CACHE); + manifest_fetcher_->SetAutomaticallyRetryOnNetworkChanges(3); + manifest_fetcher_->Start(); + } + + private: + const Extension* GetInstalledApp() { + ExtensionService* extension_service = + extensions::ExtensionSystem::Get(profile_)->extension_service(); + return extension_service->GetInstalledExtension(app_id_); + } + + void HandleManifestResults(const extensions::ManifestFetchData& fetch_data, + const UpdateManifest::Results* results) { + if (!results || results->list.empty()) { + launcher_->OnUpdateCheckNoUpdate(); + return; + } + + DCHECK_EQ(1u, results->list.size()); + + const UpdateManifest::Result& update = results->list[0]; + + if (update.browser_min_version.length() > 0) { + Version browser_version; + chrome::VersionInfo version_info; + if (version_info.is_valid()) + browser_version = Version(version_info.Version()); + + Version browser_min_version(update.browser_min_version); + if (browser_version.IsValid() && + browser_min_version.IsValid() && + browser_min_version.CompareTo(browser_version) > 0) { + launcher_->OnUpdateCheckNoUpdate(); + return; + } + } + + const Version& existing_version = *GetInstalledApp()->version(); + Version update_version(update.version); + if (existing_version.IsValid() && + update_version.IsValid() && + update_version.CompareTo(existing_version) <= 0) { + launcher_->OnUpdateCheckNoUpdate(); + return; + } + + launcher_->OnUpdateCheckUpdateAvailable(); + } + + // net::URLFetcherDelegate implementation. + virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE { + DCHECK_EQ(source, manifest_fetcher_.get()); + + if (source->GetStatus().status() != net::URLRequestStatus::SUCCESS || + source->GetResponseCode() != 200) { + launcher_->OnUpdateCheckNoUpdate(); + return; + } + + std::string data; + source->GetResponseAsString(&data); + scoped_refptr safe_parser( + new extensions::SafeManifestParser( + data, + manifest_fetch_data_.release(), + base::Bind(&AppUpdateChecker::HandleManifestResults, + AsWeakPtr()))); + safe_parser->Start(); + } + + StartupAppLauncher* launcher_; + Profile* profile_; + const std::string app_id_; + + scoped_ptr manifest_fetch_data_; + scoped_ptr manifest_fetcher_; + + DISALLOW_COPY_AND_ASSIGN(AppUpdateChecker); +}; StartupAppLauncher::StartupAppLauncher(Profile* profile, const std::string& app_id, @@ -80,7 +200,7 @@ void StartupAppLauncher::ContinueWithNetworkReady() { // Starts install if it is not started. if (!install_attempted_) { install_attempted_ = true; - BeginInstall(); + MaybeInstall(); } } @@ -183,17 +303,6 @@ void StartupAppLauncher::OnRefreshTokensLoaded() { InitializeNetwork(); } -void StartupAppLauncher::OnLaunchSuccess() { - delegate_->OnLaunchSucceeded(); -} - -void StartupAppLauncher::OnLaunchFailure(KioskAppLaunchError::Error error) { - LOG(ERROR) << "App launch failed, error: " << error; - DCHECK_NE(KioskAppLaunchError::NONE, error); - - delegate_->OnLaunchFailed(error); -} - void StartupAppLauncher::LaunchApp() { if (!ready_to_launch_) { NOTREACHED(); @@ -225,14 +334,44 @@ void StartupAppLauncher::LaunchApp() { OnLaunchSuccess(); } -void StartupAppLauncher::BeginInstall() { +void StartupAppLauncher::OnLaunchSuccess() { + delegate_->OnLaunchSucceeded(); +} + +void StartupAppLauncher::OnLaunchFailure(KioskAppLaunchError::Error error) { + LOG(ERROR) << "App launch failed, error: " << error; + DCHECK_NE(KioskAppLaunchError::NONE, error); + + delegate_->OnLaunchFailed(error); +} + +void StartupAppLauncher::MaybeInstall() { delegate_->OnInstallingApp(); - if (IsAppInstalled(profile_, app_id_)) { - OnReadyToLaunch(); - return; - } + update_checker_.reset(new AppUpdateChecker(this)); + update_checker_->Start(); +} + +void StartupAppLauncher::OnUpdateCheckNotInstalled() { + BeginInstall(); +} +void StartupAppLauncher::OnUpdateCheckUpdateAvailable() { + // Uninstall to force a re-install. + // TODO(xiyuan): Find a better way. Either download CRX and install it + // directly or integrate with ExtensionUpdater in someway. + ExtensionService* extension_service = + extensions::ExtensionSystem::Get(profile_)->extension_service(); + extension_service->UninstallExtension(app_id_, false, NULL); + + OnUpdateCheckNotInstalled(); +} + +void StartupAppLauncher::OnUpdateCheckNoUpdate() { + OnReadyToLaunch(); +} + +void StartupAppLauncher::BeginInstall() { installer_ = new WebstoreStartupInstaller( app_id_, profile_, @@ -252,6 +391,13 @@ void StartupAppLauncher::InstallCallback(bool success, FROM_HERE, base::Bind(&StartupAppLauncher::OnReadyToLaunch, AsWeakPtr())); + + // Schedule app data update after installation. + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&StartupAppLauncher::UpdateAppData, + AsWeakPtr())); return; } @@ -264,4 +410,9 @@ void StartupAppLauncher::OnReadyToLaunch() { delegate_->OnReadyToLaunch(); } +void StartupAppLauncher::UpdateAppData() { + KioskAppManager::Get()->ClearAppData(app_id_); + KioskAppManager::Get()->UpdateAppDataFromProfile(app_id_, profile_, NULL); +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/app_mode/startup_app_launcher.h b/chrome/browser/chromeos/app_mode/startup_app_launcher.h index 43f7cbf0c910..72a45979f55c 100644 --- a/chrome/browser/chromeos/app_mode/startup_app_launcher.h +++ b/chrome/browser/chromeos/app_mode/startup_app_launcher.h @@ -74,12 +74,25 @@ class StartupAppLauncher std::string client_secret; }; + // A class to check if the app has an update. It invokes BeginInstall + // if the app is not installed or not up-to-date. Otherwise, it invokes + // OnReadyToLaunch. + class AppUpdateChecker; + void OnLaunchSuccess(); void OnLaunchFailure(KioskAppLaunchError::Error error); + void MaybeInstall(); + + // Callbacks from AppUpdateChecker + void OnUpdateCheckNotInstalled(); + void OnUpdateCheckUpdateAvailable(); + void OnUpdateCheckNoUpdate(); + void BeginInstall(); void InstallCallback(bool success, const std::string& error); void OnReadyToLaunch(); + void UpdateAppData(); void InitializeTokenService(); void InitializeNetwork(); @@ -101,6 +114,8 @@ class StartupAppLauncher scoped_refptr installer_; KioskOAuthParams auth_params_; + scoped_ptr update_checker_; + DISALLOW_COPY_AND_ASSIGN(StartupAppLauncher); }; -- 2.11.4.GIT