Roll src/third_party/WebKit 8b42d1d:744641d (svn 186770:186771)
[chromium-blink-merge.git] / chrome / browser / chromeos / customization / customization_document.cc
blob29ffe68c311bb0ca393c7cadd0df0b94088d0830
1 // Copyright (c) 2012 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/customization/customization_document.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/json/json_reader.h"
14 #include "base/logging.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/metrics/histogram.h"
17 #include "base/path_service.h"
18 #include "base/prefs/pref_registry_simple.h"
19 #include "base/prefs/pref_service.h"
20 #include "base/strings/string_split.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/stringprintf.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/time/time.h"
25 #include "chrome/browser/browser_process.h"
26 #include "chrome/browser/chromeos/customization/customization_wallpaper_downloader.h"
27 #include "chrome/browser/chromeos/extensions/default_app_order.h"
28 #include "chrome/browser/chromeos/login/wizard_controller.h"
29 #include "chrome/browser/chromeos/net/delay_network_call.h"
30 #include "chrome/browser/extensions/external_loader.h"
31 #include "chrome/browser/extensions/external_provider_impl.h"
32 #include "chrome/browser/profiles/profile.h"
33 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
34 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
35 #include "chrome/common/chrome_paths.h"
36 #include "chrome/common/pref_names.h"
37 #include "chromeos/system/statistics_provider.h"
38 #include "components/pref_registry/pref_registry_syncable.h"
39 #include "content/public/browser/browser_thread.h"
40 #include "extensions/common/extension_urls.h"
41 #include "net/base/load_flags.h"
42 #include "net/http/http_response_headers.h"
43 #include "net/http/http_status_code.h"
44 #include "net/url_request/url_fetcher.h"
45 #include "ui/base/l10n/l10n_util.h"
47 #if !defined(USE_ATHENA)
48 #include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
49 #endif
51 using content::BrowserThread;
53 namespace chromeos {
54 namespace {
56 // Manifest attributes names.
57 const char kVersionAttr[] = "version";
58 const char kDefaultAttr[] = "default";
59 const char kInitialLocaleAttr[] = "initial_locale";
60 const char kInitialTimezoneAttr[] = "initial_timezone";
61 const char kKeyboardLayoutAttr[] = "keyboard_layout";
62 const char kHwidMapAttr[] = "hwid_map";
63 const char kHwidMaskAttr[] = "hwid_mask";
64 const char kSetupContentAttr[] = "setup_content";
65 const char kEulaPageAttr[] = "eula_page";
66 const char kDefaultWallpaperAttr[] = "default_wallpaper";
67 const char kDefaultAppsAttr[] = "default_apps";
68 const char kLocalizedContent[] = "localized_content";
69 const char kDefaultAppsFolderName[] = "default_apps_folder_name";
71 const char kAcceptedManifestVersion[] = "1.0";
73 // Path to OEM partner startup customization manifest.
74 const char kStartupCustomizationManifestPath[] =
75 "/opt/oem/etc/startup_manifest.json";
77 // This is subdirectory relative to PathService(DIR_CHROMEOS_CUSTOM_WALLPAPERS),
78 // where downloaded (and resized) wallpaper is stored.
79 const char kCustomizationDefaultWallpaperDir[] = "customization";
81 // The original downloaded image file is stored under this name.
82 const char kCustomizationDefaultWallpaperDownloadedFile[] =
83 "default_downloaded_wallpaper.bin";
85 // Name of local state option that tracks if services customization has been
86 // applied.
87 const char kServicesCustomizationAppliedPref[] = "ServicesCustomizationApplied";
89 // Maximum number of retries to fetch file if network is not available.
90 const int kMaxFetchRetries = 3;
92 // Delay between file fetch retries if network is not available.
93 const int kRetriesDelayInSec = 2;
95 // Name of profile option that tracks cached version of service customization.
96 const char kServicesCustomizationKey[] = "customization.manifest_cache";
98 // Empty customization document that doesn't customize anything.
99 const char kEmptyServicesCustomizationManifest[] = "{ \"version\": \"1.0\" }";
101 // Global overrider for ServicesCustomizationDocument for tests.
102 ServicesCustomizationDocument* g_test_services_customization_document = NULL;
104 // Services customization document load results reported via the
105 // "ServicesCustomization.LoadResult" histogram.
106 // It is append-only enum due to use in a histogram!
107 enum HistogramServicesCustomizationLoadResult {
108 HISTOGRAM_LOAD_RESULT_SUCCESS = 0,
109 HISTOGRAM_LOAD_RESULT_FILE_NOT_FOUND = 1,
110 HISTOGRAM_LOAD_RESULT_PARSING_ERROR = 2,
111 HISTOGRAM_LOAD_RESULT_RETRIES_FAIL = 3,
112 HISTOGRAM_LOAD_RESULT_MAX_VALUE = 4
115 void LogManifestLoadResult(HistogramServicesCustomizationLoadResult result) {
116 UMA_HISTOGRAM_ENUMERATION("ServicesCustomization.LoadResult",
117 result,
118 HISTOGRAM_LOAD_RESULT_MAX_VALUE);
121 std::string GetLocaleSpecificStringImpl(
122 const base::DictionaryValue* root,
123 const std::string& locale,
124 const std::string& dictionary_name,
125 const std::string& entry_name) {
126 const base::DictionaryValue* dictionary_content = NULL;
127 if (!root || !root->GetDictionary(dictionary_name, &dictionary_content))
128 return std::string();
130 const base::DictionaryValue* locale_dictionary = NULL;
131 if (dictionary_content->GetDictionary(locale, &locale_dictionary)) {
132 std::string result;
133 if (locale_dictionary->GetString(entry_name, &result))
134 return result;
137 const base::DictionaryValue* default_dictionary = NULL;
138 if (dictionary_content->GetDictionary(kDefaultAttr, &default_dictionary)) {
139 std::string result;
140 if (default_dictionary->GetString(entry_name, &result))
141 return result;
144 return std::string();
147 void CheckWallpaperCacheExists(const base::FilePath& path, bool* exists) {
148 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
149 DCHECK(exists);
150 *exists = base::PathExists(path);
153 } // anonymous namespace
155 // Template URL where to fetch OEM services customization manifest from.
156 const char ServicesCustomizationDocument::kManifestUrl[] =
157 "https://ssl.gstatic.com/chrome/chromeos-customization/%s.json";
159 // A custom extensions::ExternalLoader that the ServicesCustomizationDocument
160 // creates and uses to publish OEM default apps to the extensions system.
161 class ServicesCustomizationExternalLoader
162 : public extensions::ExternalLoader,
163 public base::SupportsWeakPtr<ServicesCustomizationExternalLoader> {
164 public:
165 explicit ServicesCustomizationExternalLoader(Profile* profile)
166 : is_apps_set_(false), profile_(profile) {}
168 Profile* profile() { return profile_; }
170 // Used by the ServicesCustomizationDocument to update the current apps.
171 void SetCurrentApps(scoped_ptr<base::DictionaryValue> prefs) {
172 apps_.Swap(prefs.get());
173 is_apps_set_ = true;
174 StartLoading();
177 // Implementation of extensions::ExternalLoader:
178 virtual void StartLoading() override {
179 if (!is_apps_set_) {
180 ServicesCustomizationDocument::GetInstance()->StartFetching();
181 // In case of missing customization ID, SetCurrentApps will be called
182 // synchronously from StartFetching and this function will be called
183 // recursively so we need to return to avoid calling LoadFinished twice.
184 // In case of async load it is safe to return empty list because this
185 // provider didn't install any app yet so no app can be removed due to
186 // returning empty list.
187 if (is_apps_set_)
188 return;
191 prefs_.reset(apps_.DeepCopy());
192 VLOG(1) << "ServicesCustomization extension loader publishing "
193 << apps_.size() << " apps.";
194 LoadFinished();
197 protected:
198 virtual ~ServicesCustomizationExternalLoader() {}
200 private:
201 bool is_apps_set_;
202 base::DictionaryValue apps_;
203 Profile* profile_;
205 DISALLOW_COPY_AND_ASSIGN(ServicesCustomizationExternalLoader);
208 // CustomizationDocument implementation. ---------------------------------------
210 CustomizationDocument::CustomizationDocument(
211 const std::string& accepted_version)
212 : accepted_version_(accepted_version) {}
214 CustomizationDocument::~CustomizationDocument() {}
216 bool CustomizationDocument::LoadManifestFromFile(
217 const base::FilePath& manifest_path) {
218 std::string manifest;
219 if (!base::ReadFileToString(manifest_path, &manifest))
220 return false;
221 return LoadManifestFromString(manifest);
224 bool CustomizationDocument::LoadManifestFromString(
225 const std::string& manifest) {
226 int error_code = 0;
227 std::string error;
228 scoped_ptr<base::Value> root(base::JSONReader::ReadAndReturnError(manifest,
229 base::JSON_ALLOW_TRAILING_COMMAS, &error_code, &error));
230 if (error_code != base::JSONReader::JSON_NO_ERROR)
231 LOG(ERROR) << error;
232 DCHECK(root.get() != NULL);
233 if (root.get() == NULL)
234 return false;
235 DCHECK(root->GetType() == base::Value::TYPE_DICTIONARY);
236 if (root->GetType() == base::Value::TYPE_DICTIONARY) {
237 root_.reset(static_cast<base::DictionaryValue*>(root.release()));
238 std::string result;
239 if (root_->GetString(kVersionAttr, &result) &&
240 result == accepted_version_)
241 return true;
243 LOG(ERROR) << "Wrong customization manifest version";
244 root_.reset(NULL);
246 return false;
249 std::string CustomizationDocument::GetLocaleSpecificString(
250 const std::string& locale,
251 const std::string& dictionary_name,
252 const std::string& entry_name) const {
253 return GetLocaleSpecificStringImpl(
254 root_.get(), locale, dictionary_name, entry_name);
257 // StartupCustomizationDocument implementation. --------------------------------
259 StartupCustomizationDocument::StartupCustomizationDocument()
260 : CustomizationDocument(kAcceptedManifestVersion) {
262 // Loading manifest causes us to do blocking IO on UI thread.
263 // Temporarily allow it until we fix http://crosbug.com/11103
264 base::ThreadRestrictions::ScopedAllowIO allow_io;
265 LoadManifestFromFile(base::FilePath(kStartupCustomizationManifestPath));
267 Init(system::StatisticsProvider::GetInstance());
270 StartupCustomizationDocument::StartupCustomizationDocument(
271 system::StatisticsProvider* statistics_provider,
272 const std::string& manifest)
273 : CustomizationDocument(kAcceptedManifestVersion) {
274 LoadManifestFromString(manifest);
275 Init(statistics_provider);
278 StartupCustomizationDocument::~StartupCustomizationDocument() {}
280 StartupCustomizationDocument* StartupCustomizationDocument::GetInstance() {
281 return Singleton<StartupCustomizationDocument,
282 DefaultSingletonTraits<StartupCustomizationDocument> >::get();
285 void StartupCustomizationDocument::Init(
286 system::StatisticsProvider* statistics_provider) {
287 if (IsReady()) {
288 root_->GetString(kInitialLocaleAttr, &initial_locale_);
289 root_->GetString(kInitialTimezoneAttr, &initial_timezone_);
290 root_->GetString(kKeyboardLayoutAttr, &keyboard_layout_);
292 std::string hwid;
293 if (statistics_provider->GetMachineStatistic(
294 system::kHardwareClassKey, &hwid)) {
295 base::ListValue* hwid_list = NULL;
296 if (root_->GetList(kHwidMapAttr, &hwid_list)) {
297 for (size_t i = 0; i < hwid_list->GetSize(); ++i) {
298 base::DictionaryValue* hwid_dictionary = NULL;
299 std::string hwid_mask;
300 if (hwid_list->GetDictionary(i, &hwid_dictionary) &&
301 hwid_dictionary->GetString(kHwidMaskAttr, &hwid_mask)) {
302 if (MatchPattern(hwid, hwid_mask)) {
303 // If HWID for this machine matches some mask, use HWID specific
304 // settings.
305 std::string result;
306 if (hwid_dictionary->GetString(kInitialLocaleAttr, &result))
307 initial_locale_ = result;
309 if (hwid_dictionary->GetString(kInitialTimezoneAttr, &result))
310 initial_timezone_ = result;
312 if (hwid_dictionary->GetString(kKeyboardLayoutAttr, &result))
313 keyboard_layout_ = result;
315 // Don't break here to allow other entires to be applied if match.
316 } else {
317 LOG(ERROR) << "Syntax error in customization manifest";
321 } else {
322 LOG(ERROR) << "HWID is missing in machine statistics";
326 // If manifest doesn't exist still apply values from VPD.
327 statistics_provider->GetMachineStatistic(kInitialLocaleAttr,
328 &initial_locale_);
329 statistics_provider->GetMachineStatistic(kInitialTimezoneAttr,
330 &initial_timezone_);
331 statistics_provider->GetMachineStatistic(kKeyboardLayoutAttr,
332 &keyboard_layout_);
333 configured_locales_.resize(0);
334 base::SplitString(initial_locale_, ',', &configured_locales_);
336 // Convert ICU locale to chrome ("en_US" to "en-US", etc.).
337 std::for_each(configured_locales_.begin(),
338 configured_locales_.end(),
339 l10n_util::GetCanonicalLocale);
341 // Let's always have configured_locales_.front() a valid entry.
342 if (configured_locales_.size() == 0)
343 configured_locales_.push_back(std::string());
346 const std::vector<std::string>&
347 StartupCustomizationDocument::configured_locales() const {
348 return configured_locales_;
351 const std::string& StartupCustomizationDocument::initial_locale_default()
352 const {
353 DCHECK(configured_locales_.size() > 0);
354 return configured_locales_.front();
357 std::string StartupCustomizationDocument::GetEULAPage(
358 const std::string& locale) const {
359 return GetLocaleSpecificString(locale, kSetupContentAttr, kEulaPageAttr);
362 // ServicesCustomizationDocument implementation. -------------------------------
364 class ServicesCustomizationDocument::ApplyingTask {
365 public:
366 // Registers in ServicesCustomizationDocument;
367 explicit ApplyingTask(ServicesCustomizationDocument* document);
369 // Do not automatically deregister as we might be called on invalid thread.
370 ~ApplyingTask();
372 // Mark task finished and check for customization applied.
373 void Finished(bool success);
375 private:
376 ServicesCustomizationDocument* document_;
378 // This is error-checking flag to prevent destroying unfinished task
379 // or double finish.
380 bool engaged_;
383 ServicesCustomizationDocument::ApplyingTask::ApplyingTask(
384 ServicesCustomizationDocument* document)
385 : document_(document), engaged_(true) {
386 document->ApplyingTaskStarted();
389 ServicesCustomizationDocument::ApplyingTask::~ApplyingTask() {
390 DCHECK(!engaged_);
393 void ServicesCustomizationDocument::ApplyingTask::Finished(bool success) {
394 DCHECK(engaged_);
395 if (engaged_) {
396 engaged_ = false;
397 document_->ApplyingTaskFinished(success);
401 ServicesCustomizationDocument::ServicesCustomizationDocument()
402 : CustomizationDocument(kAcceptedManifestVersion),
403 num_retries_(0),
404 fetch_started_(false),
405 network_delay_(
406 base::TimeDelta::FromMilliseconds(kDefaultNetworkRetryDelayMS)),
407 apply_tasks_started_(0),
408 apply_tasks_finished_(0),
409 apply_tasks_success_(0),
410 weak_ptr_factory_(this) {
413 ServicesCustomizationDocument::ServicesCustomizationDocument(
414 const std::string& manifest)
415 : CustomizationDocument(kAcceptedManifestVersion),
416 network_delay_(
417 base::TimeDelta::FromMilliseconds(kDefaultNetworkRetryDelayMS)),
418 apply_tasks_started_(0),
419 apply_tasks_finished_(0),
420 apply_tasks_success_(0),
421 weak_ptr_factory_(this) {
422 LoadManifestFromString(manifest);
425 ServicesCustomizationDocument::~ServicesCustomizationDocument() {}
427 // static
428 ServicesCustomizationDocument* ServicesCustomizationDocument::GetInstance() {
429 if (g_test_services_customization_document)
430 return g_test_services_customization_document;
432 return Singleton<ServicesCustomizationDocument,
433 DefaultSingletonTraits<ServicesCustomizationDocument> >::get();
436 // static
437 void ServicesCustomizationDocument::RegisterPrefs(
438 PrefRegistrySimple* registry) {
439 registry->RegisterBooleanPref(kServicesCustomizationAppliedPref, false);
440 registry->RegisterStringPref(prefs::kCustomizationDefaultWallpaperURL,
441 std::string());
444 // static
445 void ServicesCustomizationDocument::RegisterProfilePrefs(
446 user_prefs::PrefRegistrySyncable* registry) {
447 registry->RegisterDictionaryPref(
448 kServicesCustomizationKey,
449 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
452 // static
453 bool ServicesCustomizationDocument::WasOOBECustomizationApplied() {
454 PrefService* prefs = g_browser_process->local_state();
455 // prefs can be NULL in some tests.
456 if (prefs)
457 return prefs->GetBoolean(kServicesCustomizationAppliedPref);
458 else
459 return false;
462 // static
463 void ServicesCustomizationDocument::SetApplied(bool val) {
464 PrefService* prefs = g_browser_process->local_state();
465 // prefs can be NULL in some tests.
466 if (prefs)
467 prefs->SetBoolean(kServicesCustomizationAppliedPref, val);
470 // static
471 base::FilePath ServicesCustomizationDocument::GetCustomizedWallpaperCacheDir() {
472 base::FilePath custom_wallpaper_dir;
473 if (!PathService::Get(chrome::DIR_CHROMEOS_CUSTOM_WALLPAPERS,
474 &custom_wallpaper_dir)) {
475 LOG(DFATAL) << "Unable to get custom wallpaper dir.";
476 return base::FilePath();
478 return custom_wallpaper_dir.Append(kCustomizationDefaultWallpaperDir);
481 // static
482 base::FilePath
483 ServicesCustomizationDocument::GetCustomizedWallpaperDownloadedFileName() {
484 const base::FilePath dir = GetCustomizedWallpaperCacheDir();
485 if (dir.empty()) {
486 NOTREACHED();
487 return dir;
489 return dir.Append(kCustomizationDefaultWallpaperDownloadedFile);
492 void ServicesCustomizationDocument::EnsureCustomizationApplied() {
493 if (WasOOBECustomizationApplied())
494 return;
496 // When customization manifest is fetched, applying will start automatically.
497 if (IsReady())
498 return;
500 StartFetching();
503 base::Closure
504 ServicesCustomizationDocument::EnsureCustomizationAppliedClosure() {
505 return base::Bind(&ServicesCustomizationDocument::EnsureCustomizationApplied,
506 weak_ptr_factory_.GetWeakPtr());
509 void ServicesCustomizationDocument::StartFetching() {
510 if (IsReady() || fetch_started_)
511 return;
513 if (!url_.is_valid()) {
514 std::string customization_id;
515 chromeos::system::StatisticsProvider* provider =
516 chromeos::system::StatisticsProvider::GetInstance();
517 if (provider->GetMachineStatistic(system::kCustomizationIdKey,
518 &customization_id) &&
519 !customization_id.empty()) {
520 url_ = GURL(base::StringPrintf(
521 kManifestUrl, base::StringToLowerASCII(customization_id).c_str()));
522 } else {
523 // Remember that there is no customization ID in VPD.
524 OnCustomizationNotFound();
525 return;
529 if (url_.is_valid()) {
530 fetch_started_ = true;
531 if (url_.SchemeIsFile()) {
532 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
533 base::Bind(&ServicesCustomizationDocument::ReadFileInBackground,
534 weak_ptr_factory_.GetWeakPtr(),
535 base::FilePath(url_.path())));
536 } else {
537 StartFileFetch();
542 // static
543 void ServicesCustomizationDocument::ReadFileInBackground(
544 base::WeakPtr<ServicesCustomizationDocument> self,
545 const base::FilePath& file) {
546 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
548 std::string manifest;
549 if (!base::ReadFileToString(file, &manifest)) {
550 manifest.clear();
551 LOG(ERROR) << "Failed to load services customization manifest from: "
552 << file.value();
555 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
556 base::Bind(&ServicesCustomizationDocument::OnManifesteRead,
557 self,
558 manifest));
561 void ServicesCustomizationDocument::OnManifesteRead(
562 const std::string& manifest) {
563 if (!manifest.empty())
564 LoadManifestFromString(manifest);
566 fetch_started_ = false;
569 void ServicesCustomizationDocument::StartFileFetch() {
570 DelayNetworkCall(base::Bind(&ServicesCustomizationDocument::DoStartFileFetch,
571 weak_ptr_factory_.GetWeakPtr()),
572 network_delay_);
575 void ServicesCustomizationDocument::DoStartFileFetch() {
576 url_fetcher_.reset(net::URLFetcher::Create(
577 url_, net::URLFetcher::GET, this));
578 url_fetcher_->SetRequestContext(g_browser_process->system_request_context());
579 url_fetcher_->AddExtraRequestHeader("Accept: application/json");
580 url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
581 net::LOAD_DO_NOT_SAVE_COOKIES |
582 net::LOAD_DISABLE_CACHE |
583 net::LOAD_DO_NOT_SEND_AUTH_DATA);
584 url_fetcher_->Start();
587 bool ServicesCustomizationDocument::LoadManifestFromString(
588 const std::string& manifest) {
589 if (CustomizationDocument::LoadManifestFromString(manifest)) {
590 LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_SUCCESS);
591 OnManifestLoaded();
592 return true;
595 LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_PARSING_ERROR);
596 return false;
599 void ServicesCustomizationDocument::OnManifestLoaded() {
600 if (!WasOOBECustomizationApplied())
601 ApplyOOBECustomization();
603 scoped_ptr<base::DictionaryValue> prefs =
604 GetDefaultAppsInProviderFormat(*root_);
605 for (ExternalLoaders::iterator it = external_loaders_.begin();
606 it != external_loaders_.end(); ++it) {
607 if (*it) {
608 UpdateCachedManifest((*it)->profile());
609 (*it)->SetCurrentApps(
610 scoped_ptr<base::DictionaryValue>(prefs->DeepCopy()));
611 SetOemFolderName((*it)->profile(), *root_);
616 void ServicesCustomizationDocument::OnURLFetchComplete(
617 const net::URLFetcher* source) {
618 std::string mime_type;
619 std::string data;
620 if (source->GetStatus().is_success() &&
621 source->GetResponseCode() == net::HTTP_OK &&
622 source->GetResponseHeaders()->GetMimeType(&mime_type) &&
623 mime_type == "application/json" &&
624 source->GetResponseAsString(&data)) {
625 LoadManifestFromString(data);
626 } else if (source->GetResponseCode() == net::HTTP_NOT_FOUND) {
627 LOG(ERROR) << "Customization manifest is missing on server: "
628 << source->GetURL().spec();
629 OnCustomizationNotFound();
630 } else {
631 if (num_retries_ < kMaxFetchRetries) {
632 num_retries_++;
633 content::BrowserThread::PostDelayedTask(
634 content::BrowserThread::UI,
635 FROM_HERE,
636 base::Bind(&ServicesCustomizationDocument::StartFileFetch,
637 weak_ptr_factory_.GetWeakPtr()),
638 base::TimeDelta::FromSeconds(kRetriesDelayInSec));
639 return;
641 // This doesn't stop fetching manifest on next restart.
642 LOG(ERROR) << "URL fetch for services customization failed:"
643 << " response code = " << source->GetResponseCode()
644 << " URL = " << source->GetURL().spec();
646 LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_RETRIES_FAIL);
648 fetch_started_ = false;
651 bool ServicesCustomizationDocument::ApplyOOBECustomization() {
652 if (apply_tasks_started_)
653 return false;
655 CheckAndApplyWallpaper();
656 return false;
659 bool ServicesCustomizationDocument::GetDefaultWallpaperUrl(
660 GURL* out_url) const {
661 if (!IsReady())
662 return false;
664 std::string url;
665 if (!root_->GetString(kDefaultWallpaperAttr, &url))
666 return false;
668 *out_url = GURL(url);
669 return true;
672 bool ServicesCustomizationDocument::GetDefaultApps(
673 std::vector<std::string>* ids) const {
674 ids->clear();
675 if (!IsReady())
676 return false;
678 base::ListValue* apps_list = NULL;
679 if (!root_->GetList(kDefaultAppsAttr, &apps_list))
680 return false;
682 for (size_t i = 0; i < apps_list->GetSize(); ++i) {
683 std::string app_id;
684 if (apps_list->GetString(i, &app_id)) {
685 ids->push_back(app_id);
686 } else {
687 LOG(ERROR) << "Wrong format of default application list";
688 return false;
692 return true;
695 std::string ServicesCustomizationDocument::GetOemAppsFolderName(
696 const std::string& locale) const {
697 if (!IsReady())
698 return std::string();
700 return GetOemAppsFolderNameImpl(locale, *root_);
703 scoped_ptr<base::DictionaryValue>
704 ServicesCustomizationDocument::GetDefaultAppsInProviderFormat(
705 const base::DictionaryValue& root) {
706 scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
707 const base::ListValue* apps_list = NULL;
708 if (root.GetList(kDefaultAppsAttr, &apps_list)) {
709 for (size_t i = 0; i < apps_list->GetSize(); ++i) {
710 std::string app_id;
711 if (apps_list->GetString(i, &app_id)) {
712 base::DictionaryValue* entry = new base::DictionaryValue;
713 entry->SetString(extensions::ExternalProviderImpl::kExternalUpdateUrl,
714 extension_urls::GetWebstoreUpdateUrl().spec());
715 prefs->Set(app_id, entry);
716 } else {
717 LOG(ERROR) << "Wrong format of default application list";
718 prefs->Clear();
719 break;
724 return prefs.Pass();
727 void ServicesCustomizationDocument::UpdateCachedManifest(Profile* profile) {
728 profile->GetPrefs()->Set(kServicesCustomizationKey, *root_);
731 extensions::ExternalLoader* ServicesCustomizationDocument::CreateExternalLoader(
732 Profile* profile) {
733 ServicesCustomizationExternalLoader* loader =
734 new ServicesCustomizationExternalLoader(profile);
735 external_loaders_.push_back(loader->AsWeakPtr());
737 if (IsReady()) {
738 UpdateCachedManifest(profile);
739 loader->SetCurrentApps(GetDefaultAppsInProviderFormat(*root_));
740 SetOemFolderName(profile, *root_);
741 } else {
742 const base::DictionaryValue* root =
743 profile->GetPrefs()->GetDictionary(kServicesCustomizationKey);
744 std::string version;
745 if (root && root->GetString(kVersionAttr, &version)) {
746 // If version exists, profile has cached version of customization.
747 loader->SetCurrentApps(GetDefaultAppsInProviderFormat(*root));
748 SetOemFolderName(profile, *root);
749 } else {
750 // StartFetching will be called from ServicesCustomizationExternalLoader
751 // when StartLoading is called. We can't initiate manifest fetch here
752 // because caller may never call StartLoading for the provider.
756 return loader;
759 void ServicesCustomizationDocument::OnCustomizationNotFound() {
760 LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_FILE_NOT_FOUND);
761 LoadManifestFromString(kEmptyServicesCustomizationManifest);
764 void ServicesCustomizationDocument::SetOemFolderName(
765 Profile* profile,
766 const base::DictionaryValue& root) {
767 std::string locale = g_browser_process->GetApplicationLocale();
768 std::string name = GetOemAppsFolderNameImpl(locale, root);
769 if (name.empty())
770 name = default_app_order::GetOemAppsFolderName();
771 if (!name.empty()) {
772 app_list::AppListSyncableService* service =
773 app_list::AppListSyncableServiceFactory::GetForProfile(profile);
774 if (!service) {
775 LOG(WARNING) << "AppListSyncableService is not ready for setting OEM "
776 "folder name";
777 return;
779 service->SetOemFolderName(name);
783 std::string ServicesCustomizationDocument::GetOemAppsFolderNameImpl(
784 const std::string& locale,
785 const base::DictionaryValue& root) const {
786 return GetLocaleSpecificStringImpl(
787 &root, locale, kLocalizedContent, kDefaultAppsFolderName);
790 // static
791 void ServicesCustomizationDocument::InitializeForTesting() {
792 g_test_services_customization_document = new ServicesCustomizationDocument;
793 g_test_services_customization_document->network_delay_ = base::TimeDelta();
796 // static
797 void ServicesCustomizationDocument::ShutdownForTesting() {
798 delete g_test_services_customization_document;
799 g_test_services_customization_document = NULL;
802 void ServicesCustomizationDocument::StartOEMWallpaperDownload(
803 const GURL& wallpaper_url,
804 scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying) {
805 DCHECK(wallpaper_url.is_valid());
807 const base::FilePath dir = GetCustomizedWallpaperCacheDir();
808 const base::FilePath file = GetCustomizedWallpaperDownloadedFileName();
809 if (dir.empty() || file.empty()) {
810 NOTREACHED();
811 applying->Finished(false);
812 return;
815 wallpaper_downloader_.reset(new CustomizationWallpaperDownloader(
816 g_browser_process->system_request_context(),
817 wallpaper_url,
818 dir,
819 file,
820 base::Bind(&ServicesCustomizationDocument::OnOEMWallpaperDownloaded,
821 weak_ptr_factory_.GetWeakPtr(),
822 base::Passed(applying.Pass()))));
824 wallpaper_downloader_->Start();
827 void ServicesCustomizationDocument::CheckAndApplyWallpaper() {
828 if (wallpaper_downloader_.get()) {
829 VLOG(1) << "CheckAndApplyWallpaper(): download has already started.";
830 return;
832 scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying(
833 new ServicesCustomizationDocument::ApplyingTask(this));
835 GURL wallpaper_url;
836 if (!GetDefaultWallpaperUrl(&wallpaper_url)) {
837 PrefService* pref_service = g_browser_process->local_state();
838 std::string current_url =
839 pref_service->GetString(prefs::kCustomizationDefaultWallpaperURL);
840 if (!current_url.empty()) {
841 VLOG(1) << "ServicesCustomizationDocument::CheckAndApplyWallpaper() : "
842 << "No wallpaper URL attribute in customization document, "
843 << "but current value is non-empty: '" << current_url
844 << "'. Ignored.";
846 applying->Finished(true);
847 return;
850 // Should fail if this ever happens in tests.
851 DCHECK(wallpaper_url.is_valid());
852 if (!wallpaper_url.is_valid()) {
853 if (!wallpaper_url.is_empty()) {
854 LOG(WARNING) << "Invalid Customized Wallpaper URL '"
855 << wallpaper_url.spec() << "'.";
857 applying->Finished(false);
858 return;
861 scoped_ptr<bool> exists(new bool(false));
863 base::Closure check_file_exists =
864 base::Bind(&CheckWallpaperCacheExists,
865 GetCustomizedWallpaperDownloadedFileName(),
866 base::Unretained(exists.get()));
867 base::Closure on_checked_closure =
868 base::Bind(&ServicesCustomizationDocument::OnCheckedWallpaperCacheExists,
869 weak_ptr_factory_.GetWeakPtr(),
870 base::Passed(exists.Pass()),
871 base::Passed(applying.Pass()));
872 if (!content::BrowserThread::PostBlockingPoolTaskAndReply(
873 FROM_HERE, check_file_exists, on_checked_closure)) {
874 LOG(WARNING) << "Failed to start check Wallpaper cache exists.";
878 void ServicesCustomizationDocument::OnCheckedWallpaperCacheExists(
879 scoped_ptr<bool> exists,
880 scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying) {
881 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
882 DCHECK(exists);
883 DCHECK(applying);
885 ApplyWallpaper(*exists, applying.Pass());
888 void ServicesCustomizationDocument::ApplyWallpaper(
889 bool default_wallpaper_file_exists,
890 scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying) {
891 GURL wallpaper_url;
892 const bool wallpaper_url_present = GetDefaultWallpaperUrl(&wallpaper_url);
894 PrefService* pref_service = g_browser_process->local_state();
896 std::string current_url =
897 pref_service->GetString(prefs::kCustomizationDefaultWallpaperURL);
898 if (current_url != wallpaper_url.spec()) {
899 if (wallpaper_url_present) {
900 VLOG(1) << "ServicesCustomizationDocument::ApplyWallpaper() : "
901 << "Wallpaper URL in customization document '"
902 << wallpaper_url.spec() << "' differs from current '"
903 << current_url << "'."
904 << (GURL(current_url).is_valid() && default_wallpaper_file_exists
905 ? " Ignored."
906 : " Will refetch.");
907 } else {
908 VLOG(1) << "ServicesCustomizationDocument::ApplyWallpaper() : "
909 << "No wallpaper URL attribute in customization document, "
910 << "but current value is non-empty: '" << current_url
911 << "'. Ignored.";
914 if (!wallpaper_url_present) {
915 applying->Finished(true);
916 return;
919 DCHECK(wallpaper_url.is_valid());
921 // Never update system-wide wallpaper (i.e. do not check
922 // current_url == wallpaper_url.spec() )
923 if (GURL(current_url).is_valid() && default_wallpaper_file_exists) {
924 VLOG(1)
925 << "ServicesCustomizationDocument::ApplyWallpaper() : reuse existing";
926 OnOEMWallpaperDownloaded(applying.Pass(), true, GURL(current_url));
927 } else {
928 VLOG(1)
929 << "ServicesCustomizationDocument::ApplyWallpaper() : start download";
930 StartOEMWallpaperDownload(wallpaper_url, applying.Pass());
934 void ServicesCustomizationDocument::OnOEMWallpaperDownloaded(
935 scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying,
936 bool success,
937 const GURL& wallpaper_url) {
938 if (success) {
939 DCHECK(wallpaper_url.is_valid());
941 VLOG(1) << "Setting default wallpaper to '"
942 << GetCustomizedWallpaperDownloadedFileName().value() << "' ('"
943 << wallpaper_url.spec() << "')";
944 #if !defined(USE_ATHENA)
945 WallpaperManager::Get()->SetCustomizedDefaultWallpaper(
946 wallpaper_url,
947 GetCustomizedWallpaperDownloadedFileName(),
948 GetCustomizedWallpaperCacheDir());
949 #endif
951 wallpaper_downloader_.reset();
952 applying->Finished(success);
955 void ServicesCustomizationDocument::ApplyingTaskStarted() {
956 ++apply_tasks_started_;
959 void ServicesCustomizationDocument::ApplyingTaskFinished(bool success) {
960 DCHECK_GT(apply_tasks_started_, apply_tasks_finished_);
961 ++apply_tasks_finished_;
963 apply_tasks_success_ += success;
965 if (apply_tasks_started_ != apply_tasks_finished_)
966 return;
968 if (apply_tasks_success_ == apply_tasks_finished_)
969 SetApplied(true);
972 } // namespace chromeos