Index: chrome/browser/chromeos/app_mode/kiosk_app_data.cc |
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_data.cc b/chrome/browser/chromeos/app_mode/kiosk_app_data.cc |
index d8b961c2ae5896f96fdff04ad8980e03385d8849..8522d7b3c7f8ee370ade36750c0e428428fba8ee 100644 |
--- a/chrome/browser/chromeos/app_mode/kiosk_app_data.cc |
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_data.cc |
@@ -21,6 +21,7 @@ |
#include "chrome/browser/extensions/extension_util.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 "components/prefs/pref_service.h" |
#include "components/prefs/scoped_user_pref_update.h" |
@@ -44,10 +45,29 @@ |
namespace { |
// Keys for local state data. See sample layout in KioskAppManager. |
-constexpr char kKeyRequiredPlatformVersion[] = "required_platform_version"; |
- |
-constexpr char kInvalidWebstoreResponseError[] = |
- "Invalid Chrome Web Store reponse"; |
+const char kKeyName[] = "name"; |
+const char kKeyIcon[] = "icon"; |
+const char kKeyRequiredPlatformVersion[] = "required_platform_version"; |
+ |
+const char kInvalidWebstoreResponseError[] = "Invalid Chrome Web Store reponse"; |
+ |
+// Icon file extension. |
+const char kIconFileExtension[] = ".png"; |
+ |
+// Save |raw_icon| for given |app_id|. |
+void SaveIconToLocalOnBlockingPool( |
+ const base::FilePath& icon_path, |
+ scoped_refptr<base::RefCountedString> raw_icon) { |
+ DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); |
+ |
+ base::FilePath dir = icon_path.DirName(); |
+ if (!base::PathExists(dir)) |
+ CHECK(base::CreateDirectory(dir)); |
+ |
+ CHECK_EQ(static_cast<int>(raw_icon->size()), |
+ base::WriteFile(icon_path, |
+ raw_icon->data().c_str(), raw_icon->size())); |
+} |
// Returns true for valid kiosk app manifest. |
bool IsValidKioskAppManifest(const extensions::Manifest& manifest) { |
@@ -183,6 +203,119 @@ |
}; |
//////////////////////////////////////////////////////////////////////////////// |
+// KioskAppData::IconLoader |
+// Loads locally stored icon data and decode it. |
+ |
+class KioskAppData::IconLoader { |
+ public: |
+ enum LoadResult { |
+ SUCCESS, |
+ FAILED_TO_LOAD, |
+ FAILED_TO_DECODE, |
+ }; |
+ |
+ IconLoader(const base::WeakPtr<KioskAppData>& client, |
+ const base::FilePath& icon_path) |
+ : client_(client), |
+ icon_path_(icon_path), |
+ load_result_(SUCCESS) {} |
+ |
+ void Start() { |
+ base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool(); |
+ base::SequencedWorkerPool::SequenceToken token = pool->GetSequenceToken(); |
+ task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior( |
+ token, |
+ base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); |
+ task_runner_->PostTask(FROM_HERE, |
+ base::Bind(&IconLoader::LoadOnBlockingPool, |
+ base::Unretained(this))); |
+ } |
+ |
+ private: |
+ friend class base::RefCountedThreadSafe<IconLoader>; |
+ |
+ ~IconLoader() {} |
+ |
+ class IconImageRequest : public ImageDecoder::ImageRequest { |
+ public: |
+ IconImageRequest( |
+ const scoped_refptr<base::SequencedTaskRunner>& task_runner, |
+ IconLoader* icon_loader) |
+ : ImageRequest(task_runner), icon_loader_(icon_loader) {} |
+ |
+ void OnImageDecoded(const SkBitmap& decoded_image) override { |
+ icon_loader_->icon_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image); |
+ icon_loader_->icon_.MakeThreadSafe(); |
+ icon_loader_->ReportResultOnBlockingPool(SUCCESS); |
+ delete this; |
+ } |
+ |
+ void OnDecodeImageFailed() override { |
+ icon_loader_->ReportResultOnBlockingPool(FAILED_TO_DECODE); |
+ delete this; |
+ } |
+ |
+ private: |
+ ~IconImageRequest() override {} |
+ IconLoader* icon_loader_; |
+ }; |
+ |
+ // Loads the icon from locally stored |icon_path_| on the blocking pool |
+ void LoadOnBlockingPool() { |
+ DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
+ |
+ std::string data; |
+ if (!base::ReadFileToString(base::FilePath(icon_path_), &data)) { |
+ ReportResultOnBlockingPool(FAILED_TO_LOAD); |
+ return; |
+ } |
+ raw_icon_ = base::RefCountedString::TakeString(&data); |
+ |
+ IconImageRequest* image_request = new IconImageRequest(task_runner_, this); |
+ ImageDecoder::Start(image_request, raw_icon_->data()); |
+ } |
+ |
+ void ReportResultOnBlockingPool(LoadResult result) { |
+ DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
+ |
+ load_result_ = result; |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, |
+ FROM_HERE, |
+ base::Bind(&IconLoader::ReportResultOnUIThread, |
+ base::Unretained(this))); |
+ } |
+ |
+ void NotifyClient() { |
+ if (!client_) |
+ return; |
+ |
+ if (load_result_ == SUCCESS) |
+ client_->OnIconLoadSuccess(icon_); |
+ else |
+ client_->OnIconLoadFailure(); |
+ } |
+ |
+ void ReportResultOnUIThread() { |
+ DCHECK_CURRENTLY_ON(BrowserThread::UI); |
+ |
+ NotifyClient(); |
+ delete this; |
+ } |
+ |
+ base::WeakPtr<KioskAppData> client_; |
+ base::FilePath icon_path_; |
+ |
+ LoadResult load_result_; |
+ scoped_refptr<base::SequencedTaskRunner> task_runner_; |
+ |
+ gfx::ImageSkia icon_; |
+ scoped_refptr<base::RefCountedString> raw_icon_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(IconLoader); |
+}; |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
// KioskAppData::WebstoreDataParser |
// Use WebstoreInstallHelper to parse the manifest and decode the icon. |
@@ -266,14 +399,12 @@ |
const AccountId& account_id, |
const GURL& update_url, |
const base::FilePath& cached_crx) |
- : KioskAppDataBase(KioskAppManager::kKioskDictionaryName, |
- app_id, |
- account_id), |
- delegate_(delegate), |
+ : delegate_(delegate), |
status_(STATUS_INIT), |
+ app_id_(app_id), |
+ account_id_(account_id), |
update_url_(update_url), |
- crx_file_(cached_crx), |
- weak_factory_(this) {} |
+ crx_file_(cached_crx) {} |
KioskAppData::~KioskAppData() {} |
@@ -284,6 +415,23 @@ |
return; |
StartFetch(); |
+} |
+ |
+void KioskAppData::ClearCache() { |
+ PrefService* local_state = g_browser_process->local_state(); |
+ |
+ DictionaryPrefUpdate dict_update(local_state, |
+ KioskAppManager::kKioskDictionaryName); |
+ |
+ std::string app_key = std::string(KioskAppManager::kKeyApps) + '.' + app_id_; |
+ dict_update->Remove(app_key, NULL); |
+ |
+ if (!icon_path_.empty()) { |
+ base::PostTaskWithTraits( |
+ FROM_HERE, base::TaskTraits().MayBlock().WithPriority( |
+ base::TaskPriority::BACKGROUND), |
+ base::Bind(base::IgnoreResult(&base::DeleteFile), icon_path_, false)); |
+ } |
} |
void KioskAppData::LoadFromInstalledApp(Profile* profile, |
@@ -293,10 +441,10 @@ |
if (!app) { |
app = extensions::ExtensionSystem::Get(profile) |
->extension_service() |
- ->GetInstalledExtension(app_id()); |
- } |
- |
- DCHECK_EQ(app_id(), app->id()); |
+ ->GetInstalledExtension(app_id_); |
+ } |
+ |
+ DCHECK_EQ(app_id_, app->id()); |
name_ = app->name(); |
required_platform_version_ = |
@@ -307,8 +455,7 @@ |
app, kIconSize, ExtensionIconSet::MATCH_BIGGER); |
extensions::ImageLoader::Get(profile)->LoadImageAsync( |
app, image, gfx::Size(kIconSize, kIconSize), |
- base::Bind(&KioskAppData::OnExtensionIconLoaded, |
- weak_factory_.GetWeakPtr())); |
+ base::Bind(&KioskAppData::OnExtensionIconLoaded, AsWeakPtr())); |
} |
void KioskAppData::SetCachedCrx(const base::FilePath& crx_file) { |
@@ -360,10 +507,10 @@ |
break; |
case STATUS_LOADING: |
case STATUS_LOADED: |
- delegate_->OnKioskAppDataChanged(app_id()); |
+ delegate_->OnKioskAppDataChanged(app_id_); |
break; |
case STATUS_ERROR: |
- delegate_->OnKioskAppDataLoadFailure(app_id()); |
+ delegate_->OnKioskAppDataLoadFailure(app_id_); |
break; |
} |
} |
@@ -373,51 +520,83 @@ |
} |
bool KioskAppData::LoadFromCache() { |
+ const std::string app_key = |
+ std::string(KioskAppManager::kKeyApps) + '.' + app_id_; |
+ const std::string name_key = app_key + '.' + kKeyName; |
+ const std::string icon_path_key = app_key + '.' + kKeyIcon; |
+ const std::string required_platform_version_key = |
+ app_key + '.' + kKeyRequiredPlatformVersion; |
+ |
PrefService* local_state = g_browser_process->local_state(); |
const base::DictionaryValue* dict = |
- local_state->GetDictionary(dictionary_name()); |
- |
- if (!LoadFromDictionary(*dict)) |
+ local_state->GetDictionary(KioskAppManager::kKioskDictionaryName); |
+ |
+ icon_path_.clear(); |
+ std::string icon_path_string; |
+ if (!dict->GetString(name_key, &name_) || |
+ !dict->GetString(icon_path_key, &icon_path_string) || |
+ !dict->GetString(required_platform_version_key, |
+ &required_platform_version_)) { |
return false; |
- |
- const std::string app_key = std::string(kKeyApps) + '.' + app_id(); |
+ } |
+ icon_path_ = base::FilePath(icon_path_string); |
+ |
+ // IconLoader deletes itself when done. |
+ (new IconLoader(AsWeakPtr(), icon_path_))->Start(); |
+ return true; |
+} |
+ |
+void KioskAppData::SetCache(const std::string& name, |
+ const base::FilePath& icon_path, |
+ const std::string& required_platform_version) { |
+ name_ = name; |
+ icon_path_ = icon_path; |
+ required_platform_version_ = required_platform_version; |
+ |
+ const std::string app_key = |
+ std::string(KioskAppManager::kKeyApps) + '.' + app_id_; |
+ const std::string name_key = app_key + '.' + kKeyName; |
+ const std::string icon_path_key = app_key + '.' + kKeyIcon; |
const std::string required_platform_version_key = |
app_key + '.' + kKeyRequiredPlatformVersion; |
- return dict->GetString(required_platform_version_key, |
- &required_platform_version_); |
+ PrefService* local_state = g_browser_process->local_state(); |
+ DictionaryPrefUpdate dict_update(local_state, |
+ KioskAppManager::kKioskDictionaryName); |
+ dict_update->SetString(name_key, name); |
+ dict_update->SetString(icon_path_key, icon_path.value()); |
+ dict_update->SetString(required_platform_version_key, |
+ required_platform_version); |
} |
void KioskAppData::SetCache(const std::string& name, |
const SkBitmap& icon, |
const std::string& required_platform_version) { |
- name_ = name; |
- required_platform_version_ = required_platform_version; |
icon_ = gfx::ImageSkia::CreateFrom1xBitmap(icon); |
icon_.MakeThreadSafe(); |
+ |
+ std::vector<unsigned char> image_data; |
+ CHECK(gfx::PNGCodec::EncodeBGRASkBitmap(icon, false, &image_data)); |
+ scoped_refptr<base::RefCountedString> raw_icon(new base::RefCountedString); |
+ raw_icon->data().assign(image_data.begin(), image_data.end()); |
base::FilePath cache_dir; |
if (delegate_) |
delegate_->GetKioskAppIconCacheDir(&cache_dir); |
- SaveIcon(icon, cache_dir); |
- |
- PrefService* local_state = g_browser_process->local_state(); |
- DictionaryPrefUpdate dict_update(local_state, dictionary_name()); |
- SaveToDictionary(dict_update); |
- |
- const std::string app_key = std::string(kKeyApps) + '.' + app_id(); |
- const std::string required_platform_version_key = |
- app_key + '.' + kKeyRequiredPlatformVersion; |
- |
- dict_update->SetString(required_platform_version_key, |
- required_platform_version); |
+ base::FilePath icon_path = |
+ cache_dir.AppendASCII(app_id_).AddExtension(kIconFileExtension); |
+ BrowserThread::GetBlockingPool()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&SaveIconToLocalOnBlockingPool, icon_path, raw_icon)); |
+ |
+ SetCache(name, icon_path, required_platform_version); |
} |
void KioskAppData::OnExtensionIconLoaded(const gfx::Image& icon) { |
if (icon.IsEmpty()) { |
LOG(WARNING) << "Failed to load icon from installed app" |
- << ", id=" << app_id(); |
+ << ", id=" << app_id_; |
SetCache(name_, *extensions::util::GetDefaultAppIcon().bitmap(), |
required_platform_version_); |
} else { |
@@ -429,13 +608,11 @@ |
void KioskAppData::OnIconLoadSuccess(const gfx::ImageSkia& icon) { |
DCHECK_CURRENTLY_ON(BrowserThread::UI); |
- kiosk_app_icon_loader_.release(); |
icon_ = icon; |
SetStatus(STATUS_LOADED); |
} |
void KioskAppData::OnIconLoadFailure() { |
- kiosk_app_icon_loader_.release(); |
// Re-fetch data from web store when failed to load cached data. |
StartFetch(); |
} |
@@ -458,7 +635,10 @@ |
} |
webstore_fetcher_.reset(new extensions::WebstoreDataFetcher( |
- this, GetRequestContextGetter(), GURL(), app_id())); |
+ this, |
+ GetRequestContextGetter(), |
+ GURL(), |
+ app_id_)); |
webstore_fetcher_->set_max_auto_retries(3); |
webstore_fetcher_->Start(); |
} |
@@ -494,12 +674,15 @@ |
} |
// WebstoreDataParser deletes itself when done. |
- (new WebstoreDataParser(weak_factory_.GetWeakPtr())) |
- ->Start(app_id(), manifest, icon_url, GetRequestContextGetter()); |
+ (new WebstoreDataParser(AsWeakPtr()))->Start(app_id_, |
+ manifest, |
+ icon_url, |
+ GetRequestContextGetter()); |
} |
void KioskAppData::OnWebstoreResponseParseFailure(const std::string& error) { |
- LOG(ERROR) << "Webstore failed for kiosk app " << app_id() << ", " << error; |
+ LOG(ERROR) << "Webstore failed for kiosk app " << app_id_ |
+ << ", " << error; |
webstore_fetcher_.reset(); |
SetStatus(STATUS_ERROR); |
} |
@@ -520,8 +703,7 @@ |
if (crx_file_.empty()) |
return; |
- scoped_refptr<CrxLoader> crx_loader( |
- new CrxLoader(weak_factory_.GetWeakPtr(), crx_file_)); |
+ scoped_refptr<CrxLoader> crx_loader(new CrxLoader(AsWeakPtr(), crx_file_)); |
crx_loader->Start(); |
} |