Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / chromeos / app_mode / kiosk_app_data.cc
blobbe18cc1966cd6985b4e3bedcb40dbd15fb59c3db
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
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/app_mode/kiosk_app_data.h"
7 #include <vector>
9 #include "base/bind.h"
10 #include "base/file_util.h"
11 #include "base/json/json_writer.h"
12 #include "base/memory/ref_counted_memory.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/prefs/scoped_user_pref_update.h"
15 #include "base/threading/sequenced_worker_pool.h"
16 #include "base/values.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/chromeos/app_mode/kiosk_app_data_delegate.h"
19 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
20 #include "chrome/browser/extensions/extension_service.h"
21 #include "chrome/browser/extensions/extension_system.h"
22 #include "chrome/browser/extensions/image_loader.h"
23 #include "chrome/browser/extensions/webstore_data_fetcher.h"
24 #include "chrome/browser/extensions/webstore_install_helper.h"
25 #include "chrome/browser/image_decoder.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/common/extensions/extension_constants.h"
28 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "extensions/common/manifest.h"
31 #include "extensions/common/manifest_constants.h"
32 #include "ui/gfx/codec/png_codec.h"
33 #include "ui/gfx/image/image.h"
35 using content::BrowserThread;
37 namespace chromeos {
39 namespace {
41 // Keys for local state data. See sample layout in KioskAppManager.
42 const char kKeyName[] = "name";
43 const char kKeyIcon[] = "icon";
45 // Web store data keys.
46 const char kManifestKey[] = "manifest";
47 const char kIconUrlKey[] = "icon_url";
48 const char kLocalizedNameKey[] = "localized_name";
50 const char kInvalidWebstoreResponseError[] = "Invalid Chrome Web Store reponse";
52 // Icon file extension.
53 const char kIconFileExtension[] = ".png";
55 // Save |raw_icon| for given |app_id|.
56 void SaveIconToLocalOnBlockingPool(
57 const base::FilePath& icon_path,
58 scoped_refptr<base::RefCountedString> raw_icon) {
59 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
61 base::FilePath dir = icon_path.DirName();
62 if (!base::PathExists(dir))
63 CHECK(base::CreateDirectory(dir));
65 CHECK_EQ(static_cast<int>(raw_icon->size()),
66 file_util::WriteFile(icon_path,
67 raw_icon->data().c_str(), raw_icon->size()));
70 // Returns true for valid kiosk app manifest.
71 bool IsValidKioskAppManifest(const extensions::Manifest& manifest) {
72 bool kiosk_enabled;
73 if (manifest.GetBoolean(extensions::manifest_keys::kKioskEnabled,
74 &kiosk_enabled)) {
75 return kiosk_enabled;
78 return false;
81 std::string ValueToString(const base::Value* value) {
82 std::string json;
83 base::JSONWriter::Write(value, &json);
84 return json;
87 } // namespace
89 ////////////////////////////////////////////////////////////////////////////////
90 // KioskAppData::IconLoader
91 // Loads locally stored icon data and decode it.
93 class KioskAppData::IconLoader : public ImageDecoder::Delegate {
94 public:
95 enum LoadResult {
96 SUCCESS,
97 FAILED_TO_LOAD,
98 FAILED_TO_DECODE,
101 IconLoader(const base::WeakPtr<KioskAppData>& client,
102 const base::FilePath& icon_path)
103 : client_(client),
104 icon_path_(icon_path),
105 load_result_(SUCCESS) {}
107 void Start() {
108 base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool();
109 base::SequencedWorkerPool::SequenceToken token = pool->GetSequenceToken();
110 task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior(
111 token,
112 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
113 task_runner_->PostTask(FROM_HERE,
114 base::Bind(&IconLoader::LoadOnBlockingPool,
115 base::Unretained(this)));
118 private:
119 friend class base::RefCountedThreadSafe<IconLoader>;
121 virtual ~IconLoader() {}
123 // Loads the icon from locally stored |icon_path_| on the blocking pool
124 void LoadOnBlockingPool() {
125 DCHECK(task_runner_->RunsTasksOnCurrentThread());
127 std::string data;
128 if (!base::ReadFileToString(base::FilePath(icon_path_), &data)) {
129 ReportResultOnBlockingPool(FAILED_TO_LOAD);
130 return;
132 raw_icon_ = base::RefCountedString::TakeString(&data);
134 scoped_refptr<ImageDecoder> image_decoder = new ImageDecoder(
135 this, raw_icon_->data(), ImageDecoder::DEFAULT_CODEC);
136 image_decoder->Start(task_runner_);
139 void ReportResultOnBlockingPool(LoadResult result) {
140 DCHECK(task_runner_->RunsTasksOnCurrentThread());
142 load_result_ = result;
143 BrowserThread::PostTask(
144 BrowserThread::UI,
145 FROM_HERE,
146 base::Bind(&IconLoader::ReportResultOnUIThread,
147 base::Unretained(this)));
150 void NotifyClient() {
151 if (!client_)
152 return;
154 if (load_result_ == SUCCESS)
155 client_->OnIconLoadSuccess(raw_icon_, icon_);
156 else
157 client_->OnIconLoadFailure();
160 void ReportResultOnUIThread() {
161 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
163 NotifyClient();
164 delete this;
167 // ImageDecoder::Delegate overrides:
168 virtual void OnImageDecoded(const ImageDecoder* decoder,
169 const SkBitmap& decoded_image) OVERRIDE {
170 icon_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image);
171 icon_.MakeThreadSafe();
172 ReportResultOnBlockingPool(SUCCESS);
175 virtual void OnDecodeImageFailed(const ImageDecoder* decoder) OVERRIDE {
176 ReportResultOnBlockingPool(FAILED_TO_DECODE);
179 base::WeakPtr<KioskAppData> client_;
180 base::FilePath icon_path_;
182 LoadResult load_result_;
183 scoped_refptr<base::SequencedTaskRunner> task_runner_;
185 gfx::ImageSkia icon_;
186 scoped_refptr<base::RefCountedString> raw_icon_;
188 DISALLOW_COPY_AND_ASSIGN(IconLoader);
191 ////////////////////////////////////////////////////////////////////////////////
192 // KioskAppData::WebstoreDataParser
193 // Use WebstoreInstallHelper to parse the manifest and decode the icon.
195 class KioskAppData::WebstoreDataParser
196 : public extensions::WebstoreInstallHelper::Delegate {
197 public:
198 explicit WebstoreDataParser(const base::WeakPtr<KioskAppData>& client)
199 : client_(client) {}
201 void Start(const std::string& app_id,
202 const std::string& manifest,
203 const GURL& icon_url,
204 net::URLRequestContextGetter* context_getter) {
205 scoped_refptr<extensions::WebstoreInstallHelper> webstore_helper =
206 new extensions::WebstoreInstallHelper(this,
207 app_id,
208 manifest,
209 "", // No icon data.
210 icon_url,
211 context_getter);
212 webstore_helper->Start();
215 private:
216 friend class base::RefCounted<WebstoreDataParser>;
218 virtual ~WebstoreDataParser() {}
220 void ReportFailure() {
221 if (client_)
222 client_->OnWebstoreParseFailure();
224 delete this;
227 // WebstoreInstallHelper::Delegate overrides:
228 virtual void OnWebstoreParseSuccess(
229 const std::string& id,
230 const SkBitmap& icon,
231 base::DictionaryValue* parsed_manifest) OVERRIDE {
232 // Takes ownership of |parsed_manifest|.
233 extensions::Manifest manifest(
234 extensions::Manifest::INVALID_LOCATION,
235 scoped_ptr<base::DictionaryValue>(parsed_manifest));
237 if (!IsValidKioskAppManifest(manifest)) {
238 ReportFailure();
239 return;
242 if (client_)
243 client_->OnWebstoreParseSuccess(icon);
244 delete this;
246 virtual void OnWebstoreParseFailure(
247 const std::string& id,
248 InstallHelperResultCode result_code,
249 const std::string& error_message) OVERRIDE {
250 ReportFailure();
253 base::WeakPtr<KioskAppData> client_;
255 DISALLOW_COPY_AND_ASSIGN(WebstoreDataParser);
258 ////////////////////////////////////////////////////////////////////////////////
259 // KioskAppData
261 KioskAppData::KioskAppData(KioskAppDataDelegate* delegate,
262 const std::string& app_id,
263 const std::string& user_id)
264 : delegate_(delegate),
265 status_(STATUS_INIT),
266 app_id_(app_id),
267 user_id_(user_id) {
270 KioskAppData::~KioskAppData() {}
272 void KioskAppData::Load() {
273 SetStatus(STATUS_LOADING);
275 if (LoadFromCache())
276 return;
278 StartFetch();
281 void KioskAppData::ClearCache() {
282 PrefService* local_state = g_browser_process->local_state();
284 DictionaryPrefUpdate dict_update(local_state,
285 KioskAppManager::kKioskDictionaryName);
287 std::string app_key = std::string(KioskAppManager::kKeyApps) + '.' + app_id_;
288 dict_update->Remove(app_key, NULL);
290 if (!icon_path_.empty()) {
291 BrowserThread::PostBlockingPoolTask(
292 FROM_HERE,
293 base::Bind(base::IgnoreResult(&base::DeleteFile), icon_path_, false));
297 void KioskAppData::LoadFromInstalledApp(Profile* profile,
298 const extensions::Extension* app) {
299 SetStatus(STATUS_LOADING);
301 if (!app) {
302 app = extensions::ExtensionSystem::Get(profile)
303 ->extension_service()
304 ->GetInstalledExtension(app_id_);
307 DCHECK_EQ(app_id_, app->id());
309 name_ = app->name();
311 const int kIconSize = extension_misc::EXTENSION_ICON_LARGE;
312 extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource(
313 app, kIconSize, ExtensionIconSet::MATCH_BIGGER);
314 extensions::ImageLoader::Get(profile)->LoadImageAsync(
315 app, image, gfx::Size(kIconSize, kIconSize),
316 base::Bind(&KioskAppData::OnExtensionIconLoaded, AsWeakPtr()));
319 bool KioskAppData::IsLoading() const {
320 return status_ == STATUS_LOADING;
323 void KioskAppData::SetStatus(Status status) {
324 if (status_ == status)
325 return;
327 status_ = status;
329 if (!delegate_)
330 return;
332 switch (status_) {
333 case STATUS_INIT:
334 break;
335 case STATUS_LOADING:
336 case STATUS_LOADED:
337 delegate_->OnKioskAppDataChanged(app_id_);
338 break;
339 case STATUS_ERROR:
340 delegate_->OnKioskAppDataLoadFailure(app_id_);
341 break;
345 net::URLRequestContextGetter* KioskAppData::GetRequestContextGetter() {
346 return g_browser_process->system_request_context();
349 bool KioskAppData::LoadFromCache() {
350 std::string app_key = std::string(KioskAppManager::kKeyApps) + '.' + app_id_;
351 std::string name_key = app_key + '.' + kKeyName;
352 std::string icon_path_key = app_key + '.' + kKeyIcon;
354 PrefService* local_state = g_browser_process->local_state();
355 const base::DictionaryValue* dict =
356 local_state->GetDictionary(KioskAppManager::kKioskDictionaryName);
358 icon_path_.clear();
359 std::string icon_path_string;
360 if (!dict->GetString(name_key, &name_) ||
361 !dict->GetString(icon_path_key, &icon_path_string)) {
362 return false;
364 icon_path_ = base::FilePath(icon_path_string);
366 // IconLoader deletes itself when done.
367 (new IconLoader(AsWeakPtr(), icon_path_))->Start();
368 return true;
371 void KioskAppData::SetCache(const std::string& name,
372 const base::FilePath& icon_path) {
373 std::string app_key = std::string(KioskAppManager::kKeyApps) + '.' + app_id_;
374 std::string name_key = app_key + '.' + kKeyName;
375 std::string icon_path_key = app_key + '.' + kKeyIcon;
377 PrefService* local_state = g_browser_process->local_state();
378 DictionaryPrefUpdate dict_update(local_state,
379 KioskAppManager::kKioskDictionaryName);
380 dict_update->SetString(name_key, name);
381 dict_update->SetString(icon_path_key, icon_path.value());
382 icon_path_ = icon_path;
385 void KioskAppData::SetCache(const std::string& name, const SkBitmap& icon) {
386 icon_ = gfx::ImageSkia::CreateFrom1xBitmap(icon);
387 icon_.MakeThreadSafe();
389 std::vector<unsigned char> image_data;
390 CHECK(gfx::PNGCodec::EncodeBGRASkBitmap(icon, false, &image_data));
391 raw_icon_ = new base::RefCountedString;
392 raw_icon_->data().assign(image_data.begin(), image_data.end());
394 base::FilePath cache_dir;
395 if (delegate_)
396 delegate_->GetKioskAppIconCacheDir(&cache_dir);
398 base::FilePath icon_path =
399 cache_dir.AppendASCII(app_id_).AddExtension(kIconFileExtension);
400 BrowserThread::GetBlockingPool()->PostTask(
401 FROM_HERE,
402 base::Bind(&SaveIconToLocalOnBlockingPool, icon_path, raw_icon_));
404 SetCache(name, icon_path);
407 void KioskAppData::OnExtensionIconLoaded(const gfx::Image& icon) {
408 if (icon.IsEmpty()) {
409 LOG(WARNING) << "Failed to load icon from installed app"
410 << ", id=" << app_id_;
411 SetCache(name_, *extensions::IconsInfo::GetDefaultAppIcon().bitmap());
412 } else {
413 SetCache(name_, icon.AsBitmap());
416 SetStatus(STATUS_LOADED);
419 void KioskAppData::OnIconLoadSuccess(
420 const scoped_refptr<base::RefCountedString>& raw_icon,
421 const gfx::ImageSkia& icon) {
422 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
423 raw_icon_ = raw_icon;
424 icon_ = icon;
425 SetStatus(STATUS_LOADED);
428 void KioskAppData::OnIconLoadFailure() {
429 // Re-fetch data from web store when failed to load cached data.
430 StartFetch();
433 void KioskAppData::OnWebstoreParseSuccess(const SkBitmap& icon) {
434 SetCache(name_, icon);
435 SetStatus(STATUS_LOADED);
438 void KioskAppData::OnWebstoreParseFailure() {
439 SetStatus(STATUS_ERROR);
442 void KioskAppData::StartFetch() {
443 webstore_fetcher_.reset(new extensions::WebstoreDataFetcher(
444 this,
445 GetRequestContextGetter(),
446 GURL(),
447 app_id_));
448 webstore_fetcher_->Start();
451 void KioskAppData::OnWebstoreRequestFailure() {
452 SetStatus(STATUS_ERROR);
455 void KioskAppData::OnWebstoreResponseParseSuccess(
456 scoped_ptr<base::DictionaryValue> webstore_data) {
457 // Takes ownership of |webstore_data|.
458 webstore_fetcher_.reset();
460 std::string manifest;
461 if (!CheckResponseKeyValue(webstore_data.get(), kManifestKey, &manifest))
462 return;
464 if (!CheckResponseKeyValue(webstore_data.get(), kLocalizedNameKey, &name_))
465 return;
467 std::string icon_url_string;
468 if (!CheckResponseKeyValue(webstore_data.get(), kIconUrlKey,
469 &icon_url_string))
470 return;
472 GURL icon_url = GURL(extension_urls::GetWebstoreLaunchURL()).Resolve(
473 icon_url_string);
474 if (!icon_url.is_valid()) {
475 LOG(ERROR) << "Webstore response error (icon url): "
476 << ValueToString(webstore_data.get());
477 OnWebstoreResponseParseFailure(kInvalidWebstoreResponseError);
478 return;
481 // WebstoreDataParser deletes itself when done.
482 (new WebstoreDataParser(AsWeakPtr()))->Start(app_id_,
483 manifest,
484 icon_url,
485 GetRequestContextGetter());
488 void KioskAppData::OnWebstoreResponseParseFailure(const std::string& error) {
489 LOG(ERROR) << "Webstore failed for kiosk app " << app_id_
490 << ", " << error;
491 webstore_fetcher_.reset();
492 SetStatus(STATUS_ERROR);
495 bool KioskAppData::CheckResponseKeyValue(const base::DictionaryValue* response,
496 const char* key,
497 std::string* value) {
498 if (!response->GetString(key, value)) {
499 LOG(ERROR) << "Webstore response error (" << key
500 << "): " << ValueToString(response);
501 OnWebstoreResponseParseFailure(kInvalidWebstoreResponseError);
502 return false;
504 return true;
507 } // namespace chromeos