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_document.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_wallpaper_downloader.h"
27 #include "chrome/browser/chromeos/extensions/default_app_order.h"
28 #include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
29 #include "chrome/browser/chromeos/login/wizard_controller.h"
30 #include "chrome/browser/chromeos/net/delay_network_call.h"
31 #include "chrome/browser/extensions/external_loader.h"
32 #include "chrome/browser/extensions/external_provider_impl.h"
33 #include "chrome/browser/profiles/profile.h"
34 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
35 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
36 #include "chrome/common/chrome_paths.h"
37 #include "chrome/common/pref_names.h"
38 #include "chromeos/system/statistics_provider.h"
39 #include "components/pref_registry/pref_registry_syncable.h"
40 #include "content/public/browser/browser_thread.h"
41 #include "extensions/common/extension_urls.h"
42 #include "net/base/load_flags.h"
43 #include "net/http/http_response_headers.h"
44 #include "net/http/http_status_code.h"
45 #include "net/url_request/url_fetcher.h"
46 #include "ui/base/l10n/l10n_util.h"
48 using content::BrowserThread;
53 // Manifest attributes names.
54 const char kVersionAttr[] = "version";
55 const char kDefaultAttr[] = "default";
56 const char kInitialLocaleAttr[] = "initial_locale";
57 const char kInitialTimezoneAttr[] = "initial_timezone";
58 const char kKeyboardLayoutAttr[] = "keyboard_layout";
59 const char kHwidMapAttr[] = "hwid_map";
60 const char kHwidMaskAttr[] = "hwid_mask";
61 const char kSetupContentAttr[] = "setup_content";
62 const char kEulaPageAttr[] = "eula_page";
63 const char kDefaultWallpaperAttr[] = "default_wallpaper";
64 const char kDefaultAppsAttr[] = "default_apps";
65 const char kLocalizedContent[] = "localized_content";
66 const char kDefaultAppsFolderName[] = "default_apps_folder_name";
68 const char kAcceptedManifestVersion[] = "1.0";
70 // Path to OEM partner startup customization manifest.
71 const char kStartupCustomizationManifestPath[] =
72 "/opt/oem/etc/startup_manifest.json";
74 // This is subdirectory relative to PathService(DIR_CHROMEOS_CUSTOM_WALLPAPERS),
75 // where downloaded (and resized) wallpaper is stored.
76 const char kCustomizationDefaultWallpaperDir[] = "customization";
78 // The original downloaded image file is stored under this name.
79 const char kCustomizationDefaultWallpaperDownloadedFile[] =
80 "default_downloaded_wallpaper.bin";
82 // Name of local state option that tracks if services customization has been
84 const char kServicesCustomizationAppliedPref[] = "ServicesCustomizationApplied";
86 // Maximum number of retries to fetch file if network is not available.
87 const int kMaxFetchRetries = 3;
89 // Delay between file fetch retries if network is not available.
90 const int kRetriesDelayInSec = 2;
92 // Name of profile option that tracks cached version of service customization.
93 const char kServicesCustomizationKey[] = "customization.manifest_cache";
95 // Empty customization document that doesn't customize anything.
96 const char kEmptyServicesCustomizationManifest[] = "{ \"version\": \"1.0\" }";
98 // Global overrider for ServicesCustomizationDocument for tests.
99 ServicesCustomizationDocument* g_test_services_customization_document = NULL;
101 // Services customization document load results reported via the
102 // "ServicesCustomization.LoadResult" histogram.
103 // It is append-only enum due to use in a histogram!
104 enum HistogramServicesCustomizationLoadResult {
105 HISTOGRAM_LOAD_RESULT_SUCCESS = 0,
106 HISTOGRAM_LOAD_RESULT_FILE_NOT_FOUND = 1,
107 HISTOGRAM_LOAD_RESULT_PARSING_ERROR = 2,
108 HISTOGRAM_LOAD_RESULT_RETRIES_FAIL = 3,
109 HISTOGRAM_LOAD_RESULT_MAX_VALUE = 4
112 void LogManifestLoadResult(HistogramServicesCustomizationLoadResult result) {
113 UMA_HISTOGRAM_ENUMERATION("ServicesCustomization.LoadResult",
115 HISTOGRAM_LOAD_RESULT_MAX_VALUE);
118 std::string GetLocaleSpecificStringImpl(
119 const base::DictionaryValue* root,
120 const std::string& locale,
121 const std::string& dictionary_name,
122 const std::string& entry_name) {
123 const base::DictionaryValue* dictionary_content = NULL;
124 if (!root || !root->GetDictionary(dictionary_name, &dictionary_content))
125 return std::string();
127 const base::DictionaryValue* locale_dictionary = NULL;
128 if (dictionary_content->GetDictionary(locale, &locale_dictionary)) {
130 if (locale_dictionary->GetString(entry_name, &result))
134 const base::DictionaryValue* default_dictionary = NULL;
135 if (dictionary_content->GetDictionary(kDefaultAttr, &default_dictionary)) {
137 if (default_dictionary->GetString(entry_name, &result))
141 return std::string();
144 void CheckWallpaperCacheExists(const base::FilePath& path, bool* exists) {
145 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
147 *exists = base::PathExists(path);
150 } // anonymous namespace
152 // Template URL where to fetch OEM services customization manifest from.
153 const char ServicesCustomizationDocument::kManifestUrl[] =
154 "https://ssl.gstatic.com/chrome/chromeos-customization/%s.json";
156 // A custom extensions::ExternalLoader that the ServicesCustomizationDocument
157 // creates and uses to publish OEM default apps to the extensions system.
158 class ServicesCustomizationExternalLoader
159 : public extensions::ExternalLoader,
160 public base::SupportsWeakPtr<ServicesCustomizationExternalLoader> {
162 explicit ServicesCustomizationExternalLoader(Profile* profile)
163 : is_apps_set_(false), profile_(profile) {}
165 Profile* profile() { return profile_; }
167 // Used by the ServicesCustomizationDocument to update the current apps.
168 void SetCurrentApps(scoped_ptr<base::DictionaryValue> prefs) {
169 apps_.Swap(prefs.get());
174 // Implementation of extensions::ExternalLoader:
175 virtual void StartLoading() OVERRIDE {
177 ServicesCustomizationDocument::GetInstance()->StartFetching();
178 // In case of missing customization ID, SetCurrentApps will be called
179 // synchronously from StartFetching and this function will be called
180 // recursively so we need to return to avoid calling LoadFinished twice.
181 // In case of async load it is safe to return empty list because this
182 // provider didn't install any app yet so no app can be removed due to
183 // returning empty list.
188 prefs_.reset(apps_.DeepCopy());
189 VLOG(1) << "ServicesCustomization extension loader publishing "
190 << apps_.size() << " apps.";
195 virtual ~ServicesCustomizationExternalLoader() {}
199 base::DictionaryValue apps_;
202 DISALLOW_COPY_AND_ASSIGN(ServicesCustomizationExternalLoader);
205 // CustomizationDocument implementation. ---------------------------------------
207 CustomizationDocument::CustomizationDocument(
208 const std::string& accepted_version)
209 : accepted_version_(accepted_version) {}
211 CustomizationDocument::~CustomizationDocument() {}
213 bool CustomizationDocument::LoadManifestFromFile(
214 const base::FilePath& manifest_path) {
215 std::string manifest;
216 if (!base::ReadFileToString(manifest_path, &manifest))
218 return LoadManifestFromString(manifest);
221 bool CustomizationDocument::LoadManifestFromString(
222 const std::string& manifest) {
225 scoped_ptr<base::Value> root(base::JSONReader::ReadAndReturnError(manifest,
226 base::JSON_ALLOW_TRAILING_COMMAS, &error_code, &error));
227 if (error_code != base::JSONReader::JSON_NO_ERROR)
229 DCHECK(root.get() != NULL);
230 if (root.get() == NULL)
232 DCHECK(root->GetType() == base::Value::TYPE_DICTIONARY);
233 if (root->GetType() == base::Value::TYPE_DICTIONARY) {
234 root_.reset(static_cast<base::DictionaryValue*>(root.release()));
236 if (root_->GetString(kVersionAttr, &result) &&
237 result == accepted_version_)
240 LOG(ERROR) << "Wrong customization manifest version";
246 std::string CustomizationDocument::GetLocaleSpecificString(
247 const std::string& locale,
248 const std::string& dictionary_name,
249 const std::string& entry_name) const {
250 return GetLocaleSpecificStringImpl(
251 root_.get(), locale, dictionary_name, entry_name);
254 // StartupCustomizationDocument implementation. --------------------------------
256 StartupCustomizationDocument::StartupCustomizationDocument()
257 : CustomizationDocument(kAcceptedManifestVersion) {
259 // Loading manifest causes us to do blocking IO on UI thread.
260 // Temporarily allow it until we fix http://crosbug.com/11103
261 base::ThreadRestrictions::ScopedAllowIO allow_io;
262 LoadManifestFromFile(base::FilePath(kStartupCustomizationManifestPath));
264 Init(system::StatisticsProvider::GetInstance());
267 StartupCustomizationDocument::StartupCustomizationDocument(
268 system::StatisticsProvider* statistics_provider,
269 const std::string& manifest)
270 : CustomizationDocument(kAcceptedManifestVersion) {
271 LoadManifestFromString(manifest);
272 Init(statistics_provider);
275 StartupCustomizationDocument::~StartupCustomizationDocument() {}
277 StartupCustomizationDocument* StartupCustomizationDocument::GetInstance() {
278 return Singleton<StartupCustomizationDocument,
279 DefaultSingletonTraits<StartupCustomizationDocument> >::get();
282 void StartupCustomizationDocument::Init(
283 system::StatisticsProvider* statistics_provider) {
285 root_->GetString(kInitialLocaleAttr, &initial_locale_);
286 root_->GetString(kInitialTimezoneAttr, &initial_timezone_);
287 root_->GetString(kKeyboardLayoutAttr, &keyboard_layout_);
290 if (statistics_provider->GetMachineStatistic(
291 system::kHardwareClassKey, &hwid)) {
292 base::ListValue* hwid_list = NULL;
293 if (root_->GetList(kHwidMapAttr, &hwid_list)) {
294 for (size_t i = 0; i < hwid_list->GetSize(); ++i) {
295 base::DictionaryValue* hwid_dictionary = NULL;
296 std::string hwid_mask;
297 if (hwid_list->GetDictionary(i, &hwid_dictionary) &&
298 hwid_dictionary->GetString(kHwidMaskAttr, &hwid_mask)) {
299 if (MatchPattern(hwid, hwid_mask)) {
300 // If HWID for this machine matches some mask, use HWID specific
303 if (hwid_dictionary->GetString(kInitialLocaleAttr, &result))
304 initial_locale_ = result;
306 if (hwid_dictionary->GetString(kInitialTimezoneAttr, &result))
307 initial_timezone_ = result;
309 if (hwid_dictionary->GetString(kKeyboardLayoutAttr, &result))
310 keyboard_layout_ = result;
312 // Don't break here to allow other entires to be applied if match.
314 LOG(ERROR) << "Syntax error in customization manifest";
319 LOG(ERROR) << "HWID is missing in machine statistics";
323 // If manifest doesn't exist still apply values from VPD.
324 statistics_provider->GetMachineStatistic(kInitialLocaleAttr,
326 statistics_provider->GetMachineStatistic(kInitialTimezoneAttr,
328 statistics_provider->GetMachineStatistic(kKeyboardLayoutAttr,
330 configured_locales_.resize(0);
331 base::SplitString(initial_locale_, ',', &configured_locales_);
333 // Convert ICU locale to chrome ("en_US" to "en-US", etc.).
334 std::for_each(configured_locales_.begin(),
335 configured_locales_.end(),
336 l10n_util::GetCanonicalLocale);
338 // Let's always have configured_locales_.front() a valid entry.
339 if (configured_locales_.size() == 0)
340 configured_locales_.push_back(std::string());
343 const std::vector<std::string>&
344 StartupCustomizationDocument::configured_locales() const {
345 return configured_locales_;
348 const std::string& StartupCustomizationDocument::initial_locale_default()
350 DCHECK(configured_locales_.size() > 0);
351 return configured_locales_.front();
354 std::string StartupCustomizationDocument::GetEULAPage(
355 const std::string& locale) const {
356 return GetLocaleSpecificString(locale, kSetupContentAttr, kEulaPageAttr);
359 // ServicesCustomizationDocument implementation. -------------------------------
361 class ServicesCustomizationDocument::ApplyingTask {
363 // Registers in ServicesCustomizationDocument;
364 explicit ApplyingTask(ServicesCustomizationDocument* document);
366 // Do not automatically deregister as we might be called on invalid thread.
369 // Mark task finished and check for customization applied.
370 void Finished(bool success);
373 ServicesCustomizationDocument* document_;
375 // This is error-checking flag to prevent destroying unfinished task
380 ServicesCustomizationDocument::ApplyingTask::ApplyingTask(
381 ServicesCustomizationDocument* document)
382 : document_(document), engaged_(true) {
383 document->ApplyingTaskStarted();
386 ServicesCustomizationDocument::ApplyingTask::~ApplyingTask() {
390 void ServicesCustomizationDocument::ApplyingTask::Finished(bool success) {
394 document_->ApplyingTaskFinished(success);
398 ServicesCustomizationDocument::ServicesCustomizationDocument()
399 : CustomizationDocument(kAcceptedManifestVersion),
401 fetch_started_(false),
403 base::TimeDelta::FromMilliseconds(kDefaultNetworkRetryDelayMS)),
404 apply_tasks_started_(0),
405 apply_tasks_finished_(0),
406 apply_tasks_success_(0),
407 weak_ptr_factory_(this) {
410 ServicesCustomizationDocument::ServicesCustomizationDocument(
411 const std::string& manifest)
412 : CustomizationDocument(kAcceptedManifestVersion),
414 base::TimeDelta::FromMilliseconds(kDefaultNetworkRetryDelayMS)),
415 apply_tasks_started_(0),
416 apply_tasks_finished_(0),
417 apply_tasks_success_(0),
418 weak_ptr_factory_(this) {
419 LoadManifestFromString(manifest);
422 ServicesCustomizationDocument::~ServicesCustomizationDocument() {}
425 ServicesCustomizationDocument* ServicesCustomizationDocument::GetInstance() {
426 if (g_test_services_customization_document)
427 return g_test_services_customization_document;
429 return Singleton<ServicesCustomizationDocument,
430 DefaultSingletonTraits<ServicesCustomizationDocument> >::get();
434 void ServicesCustomizationDocument::RegisterPrefs(
435 PrefRegistrySimple* registry) {
436 registry->RegisterBooleanPref(kServicesCustomizationAppliedPref, false);
437 registry->RegisterStringPref(prefs::kCustomizationDefaultWallpaperURL,
442 void ServicesCustomizationDocument::RegisterProfilePrefs(
443 user_prefs::PrefRegistrySyncable* registry) {
444 registry->RegisterDictionaryPref(
445 kServicesCustomizationKey,
446 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
450 bool ServicesCustomizationDocument::WasOOBECustomizationApplied() {
451 PrefService* prefs = g_browser_process->local_state();
452 // prefs can be NULL in some tests.
454 return prefs->GetBoolean(kServicesCustomizationAppliedPref);
460 void ServicesCustomizationDocument::SetApplied(bool val) {
461 PrefService* prefs = g_browser_process->local_state();
462 // prefs can be NULL in some tests.
464 prefs->SetBoolean(kServicesCustomizationAppliedPref, val);
468 base::FilePath ServicesCustomizationDocument::GetCustomizedWallpaperCacheDir() {
469 base::FilePath custom_wallpaper_dir;
470 if (!PathService::Get(chrome::DIR_CHROMEOS_CUSTOM_WALLPAPERS,
471 &custom_wallpaper_dir)) {
472 LOG(DFATAL) << "Unable to get custom wallpaper dir.";
473 return base::FilePath();
475 return custom_wallpaper_dir.Append(kCustomizationDefaultWallpaperDir);
480 ServicesCustomizationDocument::GetCustomizedWallpaperDownloadedFileName() {
481 const base::FilePath dir = GetCustomizedWallpaperCacheDir();
486 return dir.Append(kCustomizationDefaultWallpaperDownloadedFile);
489 void ServicesCustomizationDocument::EnsureCustomizationApplied() {
490 if (WasOOBECustomizationApplied())
493 // When customization manifest is fetched, applying will start automatically.
501 ServicesCustomizationDocument::EnsureCustomizationAppliedClosure() {
502 return base::Bind(&ServicesCustomizationDocument::EnsureCustomizationApplied,
503 weak_ptr_factory_.GetWeakPtr());
506 void ServicesCustomizationDocument::StartFetching() {
507 if (IsReady() || fetch_started_)
510 if (!url_.is_valid()) {
511 std::string customization_id;
512 chromeos::system::StatisticsProvider* provider =
513 chromeos::system::StatisticsProvider::GetInstance();
514 if (provider->GetMachineStatistic(system::kCustomizationIdKey,
515 &customization_id) &&
516 !customization_id.empty()) {
517 url_ = GURL(base::StringPrintf(
518 kManifestUrl, base::StringToLowerASCII(customization_id).c_str()));
520 // Remember that there is no customization ID in VPD.
521 OnCustomizationNotFound();
526 if (url_.is_valid()) {
527 fetch_started_ = true;
528 if (url_.SchemeIsFile()) {
529 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
530 base::Bind(&ServicesCustomizationDocument::ReadFileInBackground,
531 weak_ptr_factory_.GetWeakPtr(),
532 base::FilePath(url_.path())));
540 void ServicesCustomizationDocument::ReadFileInBackground(
541 base::WeakPtr<ServicesCustomizationDocument> self,
542 const base::FilePath& file) {
543 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
545 std::string manifest;
546 if (!base::ReadFileToString(file, &manifest)) {
548 LOG(ERROR) << "Failed to load services customization manifest from: "
552 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
553 base::Bind(&ServicesCustomizationDocument::OnManifesteRead,
558 void ServicesCustomizationDocument::OnManifesteRead(
559 const std::string& manifest) {
560 if (!manifest.empty())
561 LoadManifestFromString(manifest);
563 fetch_started_ = false;
566 void ServicesCustomizationDocument::StartFileFetch() {
567 DelayNetworkCall(base::Bind(&ServicesCustomizationDocument::DoStartFileFetch,
568 weak_ptr_factory_.GetWeakPtr()),
572 void ServicesCustomizationDocument::DoStartFileFetch() {
573 url_fetcher_.reset(net::URLFetcher::Create(
574 url_, net::URLFetcher::GET, this));
575 url_fetcher_->SetRequestContext(g_browser_process->system_request_context());
576 url_fetcher_->AddExtraRequestHeader("Accept: application/json");
577 url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
578 net::LOAD_DO_NOT_SAVE_COOKIES |
579 net::LOAD_DISABLE_CACHE |
580 net::LOAD_DO_NOT_SEND_AUTH_DATA);
581 url_fetcher_->Start();
584 bool ServicesCustomizationDocument::LoadManifestFromString(
585 const std::string& manifest) {
586 if (CustomizationDocument::LoadManifestFromString(manifest)) {
587 LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_SUCCESS);
592 LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_PARSING_ERROR);
596 void ServicesCustomizationDocument::OnManifestLoaded() {
597 if (!WasOOBECustomizationApplied())
598 ApplyOOBECustomization();
600 scoped_ptr<base::DictionaryValue> prefs =
601 GetDefaultAppsInProviderFormat(*root_);
602 for (ExternalLoaders::iterator it = external_loaders_.begin();
603 it != external_loaders_.end(); ++it) {
605 UpdateCachedManifest((*it)->profile());
606 (*it)->SetCurrentApps(
607 scoped_ptr<base::DictionaryValue>(prefs->DeepCopy()));
608 SetOemFolderName((*it)->profile(), *root_);
613 void ServicesCustomizationDocument::OnURLFetchComplete(
614 const net::URLFetcher* source) {
615 std::string mime_type;
617 if (source->GetStatus().is_success() &&
618 source->GetResponseCode() == net::HTTP_OK &&
619 source->GetResponseHeaders()->GetMimeType(&mime_type) &&
620 mime_type == "application/json" &&
621 source->GetResponseAsString(&data)) {
622 LoadManifestFromString(data);
623 } else if (source->GetResponseCode() == net::HTTP_NOT_FOUND) {
624 LOG(ERROR) << "Customization manifest is missing on server: "
625 << source->GetURL().spec();
626 OnCustomizationNotFound();
628 if (num_retries_ < kMaxFetchRetries) {
630 content::BrowserThread::PostDelayedTask(
631 content::BrowserThread::UI,
633 base::Bind(&ServicesCustomizationDocument::StartFileFetch,
634 weak_ptr_factory_.GetWeakPtr()),
635 base::TimeDelta::FromSeconds(kRetriesDelayInSec));
638 // This doesn't stop fetching manifest on next restart.
639 LOG(ERROR) << "URL fetch for services customization failed:"
640 << " response code = " << source->GetResponseCode()
641 << " URL = " << source->GetURL().spec();
643 LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_RETRIES_FAIL);
645 fetch_started_ = false;
648 bool ServicesCustomizationDocument::ApplyOOBECustomization() {
649 if (apply_tasks_started_)
652 CheckAndApplyWallpaper();
656 bool ServicesCustomizationDocument::GetDefaultWallpaperUrl(
657 GURL* out_url) const {
662 if (!root_->GetString(kDefaultWallpaperAttr, &url))
665 *out_url = GURL(url);
669 bool ServicesCustomizationDocument::GetDefaultApps(
670 std::vector<std::string>* ids) const {
675 base::ListValue* apps_list = NULL;
676 if (!root_->GetList(kDefaultAppsAttr, &apps_list))
679 for (size_t i = 0; i < apps_list->GetSize(); ++i) {
681 if (apps_list->GetString(i, &app_id)) {
682 ids->push_back(app_id);
684 LOG(ERROR) << "Wrong format of default application list";
692 std::string ServicesCustomizationDocument::GetOemAppsFolderName(
693 const std::string& locale) const {
695 return std::string();
697 return GetOemAppsFolderNameImpl(locale, *root_);
700 scoped_ptr<base::DictionaryValue>
701 ServicesCustomizationDocument::GetDefaultAppsInProviderFormat(
702 const base::DictionaryValue& root) {
703 scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
704 const base::ListValue* apps_list = NULL;
705 if (root.GetList(kDefaultAppsAttr, &apps_list)) {
706 for (size_t i = 0; i < apps_list->GetSize(); ++i) {
708 if (apps_list->GetString(i, &app_id)) {
709 base::DictionaryValue* entry = new base::DictionaryValue;
710 entry->SetString(extensions::ExternalProviderImpl::kExternalUpdateUrl,
711 extension_urls::GetWebstoreUpdateUrl().spec());
712 prefs->Set(app_id, entry);
714 LOG(ERROR) << "Wrong format of default application list";
724 void ServicesCustomizationDocument::UpdateCachedManifest(Profile* profile) {
725 profile->GetPrefs()->Set(kServicesCustomizationKey, *root_);
728 extensions::ExternalLoader* ServicesCustomizationDocument::CreateExternalLoader(
730 ServicesCustomizationExternalLoader* loader =
731 new ServicesCustomizationExternalLoader(profile);
732 external_loaders_.push_back(loader->AsWeakPtr());
735 UpdateCachedManifest(profile);
736 loader->SetCurrentApps(GetDefaultAppsInProviderFormat(*root_));
737 SetOemFolderName(profile, *root_);
739 const base::DictionaryValue* root =
740 profile->GetPrefs()->GetDictionary(kServicesCustomizationKey);
742 if (root && root->GetString(kVersionAttr, &version)) {
743 // If version exists, profile has cached version of customization.
744 loader->SetCurrentApps(GetDefaultAppsInProviderFormat(*root));
745 SetOemFolderName(profile, *root);
747 // StartFetching will be called from ServicesCustomizationExternalLoader
748 // when StartLoading is called. We can't initiate manifest fetch here
749 // because caller may never call StartLoading for the provider.
756 void ServicesCustomizationDocument::OnCustomizationNotFound() {
757 LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_FILE_NOT_FOUND);
758 LoadManifestFromString(kEmptyServicesCustomizationManifest);
761 void ServicesCustomizationDocument::SetOemFolderName(
763 const base::DictionaryValue& root) {
764 std::string locale = g_browser_process->GetApplicationLocale();
765 std::string name = GetOemAppsFolderNameImpl(locale, root);
767 name = default_app_order::GetOemAppsFolderName();
769 app_list::AppListSyncableService* service =
770 app_list::AppListSyncableServiceFactory::GetForProfile(profile);
772 LOG(WARNING) << "AppListSyncableService is not ready for setting OEM "
776 service->SetOemFolderName(name);
780 std::string ServicesCustomizationDocument::GetOemAppsFolderNameImpl(
781 const std::string& locale,
782 const base::DictionaryValue& root) const {
783 return GetLocaleSpecificStringImpl(
784 &root, locale, kLocalizedContent, kDefaultAppsFolderName);
788 void ServicesCustomizationDocument::InitializeForTesting() {
789 g_test_services_customization_document = new ServicesCustomizationDocument;
790 g_test_services_customization_document->network_delay_ = base::TimeDelta();
794 void ServicesCustomizationDocument::ShutdownForTesting() {
795 delete g_test_services_customization_document;
796 g_test_services_customization_document = NULL;
799 void ServicesCustomizationDocument::StartOEMWallpaperDownload(
800 const GURL& wallpaper_url,
801 scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying) {
802 DCHECK(wallpaper_url.is_valid());
804 const base::FilePath dir = GetCustomizedWallpaperCacheDir();
805 const base::FilePath file = GetCustomizedWallpaperDownloadedFileName();
806 if (dir.empty() || file.empty()) {
808 applying->Finished(false);
812 wallpaper_downloader_.reset(new CustomizationWallpaperDownloader(
813 g_browser_process->system_request_context(),
817 base::Bind(&ServicesCustomizationDocument::OnOEMWallpaperDownloaded,
818 weak_ptr_factory_.GetWeakPtr(),
819 base::Passed(applying.Pass()))));
821 wallpaper_downloader_->Start();
824 void ServicesCustomizationDocument::CheckAndApplyWallpaper() {
825 if (wallpaper_downloader_.get()) {
826 VLOG(1) << "CheckAndApplyWallpaper(): download has already started.";
829 scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying(
830 new ServicesCustomizationDocument::ApplyingTask(this));
833 if (!GetDefaultWallpaperUrl(&wallpaper_url)) {
834 PrefService* pref_service = g_browser_process->local_state();
835 std::string current_url =
836 pref_service->GetString(prefs::kCustomizationDefaultWallpaperURL);
837 if (!current_url.empty()) {
838 VLOG(1) << "ServicesCustomizationDocument::CheckAndApplyWallpaper() : "
839 << "No wallpaper URL attribute in customization document, "
840 << "but current value is non-empty: '" << current_url
843 applying->Finished(true);
847 // Should fail if this ever happens in tests.
848 DCHECK(wallpaper_url.is_valid());
849 if (!wallpaper_url.is_valid()) {
850 if (!wallpaper_url.is_empty()) {
851 LOG(WARNING) << "Invalid Customized Wallpaper URL '"
852 << wallpaper_url.spec() << "'.";
854 applying->Finished(false);
858 scoped_ptr<bool> exists(new bool(false));
860 base::Closure check_file_exists =
861 base::Bind(&CheckWallpaperCacheExists,
862 GetCustomizedWallpaperDownloadedFileName(),
863 base::Unretained(exists.get()));
864 base::Closure on_checked_closure =
865 base::Bind(&ServicesCustomizationDocument::OnCheckedWallpaperCacheExists,
866 weak_ptr_factory_.GetWeakPtr(),
867 base::Passed(exists.Pass()),
868 base::Passed(applying.Pass()));
869 if (!content::BrowserThread::PostBlockingPoolTaskAndReply(
870 FROM_HERE, check_file_exists, on_checked_closure)) {
871 LOG(WARNING) << "Failed to start check Wallpaper cache exists.";
875 void ServicesCustomizationDocument::OnCheckedWallpaperCacheExists(
876 scoped_ptr<bool> exists,
877 scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying) {
878 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
882 ApplyWallpaper(*exists, applying.Pass());
885 void ServicesCustomizationDocument::ApplyWallpaper(
886 bool default_wallpaper_file_exists,
887 scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying) {
889 const bool wallpaper_url_present = GetDefaultWallpaperUrl(&wallpaper_url);
891 PrefService* pref_service = g_browser_process->local_state();
893 std::string current_url =
894 pref_service->GetString(prefs::kCustomizationDefaultWallpaperURL);
895 if (current_url != wallpaper_url.spec()) {
896 if (wallpaper_url_present) {
897 VLOG(1) << "ServicesCustomizationDocument::ApplyWallpaper() : "
898 << "Wallpaper URL in customization document '"
899 << wallpaper_url.spec() << "' differs from current '"
900 << current_url << "'."
901 << (GURL(current_url).is_valid() && default_wallpaper_file_exists
905 VLOG(1) << "ServicesCustomizationDocument::ApplyWallpaper() : "
906 << "No wallpaper URL attribute in customization document, "
907 << "but current value is non-empty: '" << current_url
911 if (!wallpaper_url_present) {
912 applying->Finished(true);
916 DCHECK(wallpaper_url.is_valid());
918 // Never update system-wide wallpaper (i.e. do not check
919 // current_url == wallpaper_url.spec() )
920 if (GURL(current_url).is_valid() && default_wallpaper_file_exists) {
922 << "ServicesCustomizationDocument::ApplyWallpaper() : reuse existing";
923 OnOEMWallpaperDownloaded(applying.Pass(), true, GURL(current_url));
926 << "ServicesCustomizationDocument::ApplyWallpaper() : start download";
927 StartOEMWallpaperDownload(wallpaper_url, applying.Pass());
931 void ServicesCustomizationDocument::OnOEMWallpaperDownloaded(
932 scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying,
934 const GURL& wallpaper_url) {
936 DCHECK(wallpaper_url.is_valid());
938 VLOG(1) << "Setting default wallpaper to '"
939 << GetCustomizedWallpaperDownloadedFileName().value() << "' ('"
940 << wallpaper_url.spec() << "')";
941 WallpaperManager::Get()->SetCustomizedDefaultWallpaper(
943 GetCustomizedWallpaperDownloadedFileName(),
944 GetCustomizedWallpaperCacheDir());
946 wallpaper_downloader_.reset();
947 applying->Finished(success);
950 void ServicesCustomizationDocument::ApplyingTaskStarted() {
951 ++apply_tasks_started_;
954 void ServicesCustomizationDocument::ApplyingTaskFinished(bool success) {
955 DCHECK_GT(apply_tasks_started_, apply_tasks_finished_);
956 ++apply_tasks_finished_;
958 apply_tasks_success_ += success;
960 if (apply_tasks_started_ != apply_tasks_finished_)
963 if (apply_tasks_success_ == apply_tasks_finished_)
967 } // namespace chromeos