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"
10 #include "base/bind_helpers.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/i18n/rtl.h"
14 #include "base/json/json_reader.h"
15 #include "base/logging.h"
16 #include "base/memory/weak_ptr.h"
17 #include "base/metrics/histogram.h"
18 #include "base/path_service.h"
19 #include "base/prefs/pref_registry_simple.h"
20 #include "base/prefs/pref_service.h"
21 #include "base/strings/pattern.h"
22 #include "base/strings/string_split.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/stringprintf.h"
25 #include "base/strings/utf_string_conversions.h"
26 #include "base/time/time.h"
27 #include "chrome/browser/browser_process.h"
28 #include "chrome/browser/chromeos/customization/customization_wallpaper_downloader.h"
29 #include "chrome/browser/chromeos/extensions/default_app_order.h"
30 #include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
31 #include "chrome/browser/chromeos/login/wizard_controller.h"
32 #include "chrome/browser/chromeos/net/delay_network_call.h"
33 #include "chrome/browser/extensions/external_loader.h"
34 #include "chrome/browser/extensions/external_provider_impl.h"
35 #include "chrome/browser/profiles/profile.h"
36 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
37 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
38 #include "chrome/common/chrome_paths.h"
39 #include "chrome/common/pref_names.h"
40 #include "chromeos/system/statistics_provider.h"
41 #include "components/pref_registry/pref_registry_syncable.h"
42 #include "content/public/browser/browser_thread.h"
43 #include "extensions/common/extension_urls.h"
44 #include "net/base/load_flags.h"
45 #include "net/http/http_response_headers.h"
46 #include "net/http/http_status_code.h"
47 #include "net/url_request/url_fetcher.h"
49 using content::BrowserThread
;
54 // Manifest attributes names.
55 const char kVersionAttr
[] = "version";
56 const char kDefaultAttr
[] = "default";
57 const char kInitialLocaleAttr
[] = "initial_locale";
58 const char kInitialTimezoneAttr
[] = "initial_timezone";
59 const char kKeyboardLayoutAttr
[] = "keyboard_layout";
60 const char kHwidMapAttr
[] = "hwid_map";
61 const char kHwidMaskAttr
[] = "hwid_mask";
62 const char kSetupContentAttr
[] = "setup_content";
63 const char kEulaPageAttr
[] = "eula_page";
64 const char kDefaultWallpaperAttr
[] = "default_wallpaper";
65 const char kDefaultAppsAttr
[] = "default_apps";
66 const char kLocalizedContent
[] = "localized_content";
67 const char kDefaultAppsFolderName
[] = "default_apps_folder_name";
68 const char kIdAttr
[] = "id";
70 const char kAcceptedManifestVersion
[] = "1.0";
72 // Path to OEM partner startup customization manifest.
73 const char kStartupCustomizationManifestPath
[] =
74 "/opt/oem/etc/startup_manifest.json";
76 // This is subdirectory relative to PathService(DIR_CHROMEOS_CUSTOM_WALLPAPERS),
77 // where downloaded (and resized) wallpaper is stored.
78 const char kCustomizationDefaultWallpaperDir
[] = "customization";
80 // The original downloaded image file is stored under this name.
81 const char kCustomizationDefaultWallpaperDownloadedFile
[] =
82 "default_downloaded_wallpaper.bin";
84 // Name of local state option that tracks if services customization has been
86 const char kServicesCustomizationAppliedPref
[] = "ServicesCustomizationApplied";
88 // Maximum number of retries to fetch file if network is not available.
89 const int kMaxFetchRetries
= 3;
91 // Delay between file fetch retries if network is not available.
92 const int kRetriesDelayInSec
= 2;
94 // Name of profile option that tracks cached version of service customization.
95 const char kServicesCustomizationKey
[] = "customization.manifest_cache";
97 // Empty customization document that doesn't customize anything.
98 const char kEmptyServicesCustomizationManifest
[] = "{ \"version\": \"1.0\" }";
100 // Global overrider for ServicesCustomizationDocument for tests.
101 ServicesCustomizationDocument
* g_test_services_customization_document
= NULL
;
103 // Services customization document load results reported via the
104 // "ServicesCustomization.LoadResult" histogram.
105 // It is append-only enum due to use in a histogram!
106 enum HistogramServicesCustomizationLoadResult
{
107 HISTOGRAM_LOAD_RESULT_SUCCESS
= 0,
108 HISTOGRAM_LOAD_RESULT_FILE_NOT_FOUND
= 1,
109 HISTOGRAM_LOAD_RESULT_PARSING_ERROR
= 2,
110 HISTOGRAM_LOAD_RESULT_RETRIES_FAIL
= 3,
111 HISTOGRAM_LOAD_RESULT_MAX_VALUE
= 4
114 void LogManifestLoadResult(HistogramServicesCustomizationLoadResult result
) {
115 UMA_HISTOGRAM_ENUMERATION("ServicesCustomization.LoadResult",
117 HISTOGRAM_LOAD_RESULT_MAX_VALUE
);
120 std::string
GetLocaleSpecificStringImpl(
121 const base::DictionaryValue
* root
,
122 const std::string
& locale
,
123 const std::string
& dictionary_name
,
124 const std::string
& entry_name
) {
125 const base::DictionaryValue
* dictionary_content
= NULL
;
126 if (!root
|| !root
->GetDictionary(dictionary_name
, &dictionary_content
))
127 return std::string();
129 const base::DictionaryValue
* locale_dictionary
= NULL
;
130 if (dictionary_content
->GetDictionary(locale
, &locale_dictionary
)) {
132 if (locale_dictionary
->GetString(entry_name
, &result
))
136 const base::DictionaryValue
* default_dictionary
= NULL
;
137 if (dictionary_content
->GetDictionary(kDefaultAttr
, &default_dictionary
)) {
139 if (default_dictionary
->GetString(entry_name
, &result
))
143 return std::string();
146 void CheckWallpaperCacheExists(const base::FilePath
& path
, bool* exists
) {
147 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
149 *exists
= base::PathExists(path
);
152 } // anonymous namespace
154 // Template URL where to fetch OEM services customization manifest from.
155 const char ServicesCustomizationDocument::kManifestUrl
[] =
156 "https://ssl.gstatic.com/chrome/chromeos-customization/%s.json";
158 // A custom extensions::ExternalLoader that the ServicesCustomizationDocument
159 // creates and uses to publish OEM default apps to the extensions system.
160 class ServicesCustomizationExternalLoader
161 : public extensions::ExternalLoader
,
162 public base::SupportsWeakPtr
<ServicesCustomizationExternalLoader
> {
164 explicit ServicesCustomizationExternalLoader(Profile
* profile
)
165 : is_apps_set_(false), profile_(profile
) {}
167 Profile
* profile() { return profile_
; }
169 // Used by the ServicesCustomizationDocument to update the current apps.
170 void SetCurrentApps(scoped_ptr
<base::DictionaryValue
> prefs
) {
171 apps_
.Swap(prefs
.get());
176 // Implementation of extensions::ExternalLoader:
177 void StartLoading() override
{
179 ServicesCustomizationDocument::GetInstance()->StartFetching();
180 // In case of missing customization ID, SetCurrentApps will be called
181 // synchronously from StartFetching and this function will be called
182 // recursively so we need to return to avoid calling LoadFinished twice.
183 // In case of async load it is safe to return empty list because this
184 // provider didn't install any app yet so no app can be removed due to
185 // returning empty list.
190 prefs_
.reset(apps_
.DeepCopy());
191 VLOG(1) << "ServicesCustomization extension loader publishing "
192 << apps_
.size() << " apps.";
197 ~ServicesCustomizationExternalLoader() override
{}
201 base::DictionaryValue apps_
;
204 DISALLOW_COPY_AND_ASSIGN(ServicesCustomizationExternalLoader
);
207 // CustomizationDocument implementation. ---------------------------------------
209 CustomizationDocument::CustomizationDocument(
210 const std::string
& accepted_version
)
211 : accepted_version_(accepted_version
) {}
213 CustomizationDocument::~CustomizationDocument() {}
215 bool CustomizationDocument::LoadManifestFromFile(
216 const base::FilePath
& manifest_path
) {
217 std::string manifest
;
218 if (!base::ReadFileToString(manifest_path
, &manifest
))
220 return LoadManifestFromString(manifest
);
223 bool CustomizationDocument::LoadManifestFromString(
224 const std::string
& manifest
) {
227 scoped_ptr
<base::Value
> root
= base::JSONReader::ReadAndReturnError(
228 manifest
, base::JSON_ALLOW_TRAILING_COMMAS
, &error_code
, &error
);
229 if (error_code
!= base::JSONReader::JSON_NO_ERROR
)
231 DCHECK(root
.get() != NULL
);
232 if (root
.get() == NULL
)
234 DCHECK(root
->GetType() == base::Value::TYPE_DICTIONARY
);
235 if (root
->GetType() == base::Value::TYPE_DICTIONARY
) {
236 root_
.reset(static_cast<base::DictionaryValue
*>(root
.release()));
238 if (root_
->GetString(kVersionAttr
, &result
) &&
239 result
== accepted_version_
)
242 LOG(ERROR
) << "Wrong customization manifest version";
248 std::string
CustomizationDocument::GetLocaleSpecificString(
249 const std::string
& locale
,
250 const std::string
& dictionary_name
,
251 const std::string
& entry_name
) const {
252 return GetLocaleSpecificStringImpl(
253 root_
.get(), locale
, dictionary_name
, entry_name
);
256 // StartupCustomizationDocument implementation. --------------------------------
258 StartupCustomizationDocument::StartupCustomizationDocument()
259 : CustomizationDocument(kAcceptedManifestVersion
) {
261 // Loading manifest causes us to do blocking IO on UI thread.
262 // Temporarily allow it until we fix http://crosbug.com/11103
263 base::ThreadRestrictions::ScopedAllowIO allow_io
;
264 LoadManifestFromFile(base::FilePath(kStartupCustomizationManifestPath
));
266 Init(system::StatisticsProvider::GetInstance());
269 StartupCustomizationDocument::StartupCustomizationDocument(
270 system::StatisticsProvider
* statistics_provider
,
271 const std::string
& manifest
)
272 : CustomizationDocument(kAcceptedManifestVersion
) {
273 LoadManifestFromString(manifest
);
274 Init(statistics_provider
);
277 StartupCustomizationDocument::~StartupCustomizationDocument() {}
279 StartupCustomizationDocument
* StartupCustomizationDocument::GetInstance() {
280 return Singleton
<StartupCustomizationDocument
,
281 DefaultSingletonTraits
<StartupCustomizationDocument
> >::get();
284 void StartupCustomizationDocument::Init(
285 system::StatisticsProvider
* statistics_provider
) {
287 root_
->GetString(kInitialLocaleAttr
, &initial_locale_
);
288 root_
->GetString(kInitialTimezoneAttr
, &initial_timezone_
);
289 root_
->GetString(kKeyboardLayoutAttr
, &keyboard_layout_
);
292 if (statistics_provider
->GetMachineStatistic(
293 system::kHardwareClassKey
, &hwid
)) {
294 base::ListValue
* hwid_list
= NULL
;
295 if (root_
->GetList(kHwidMapAttr
, &hwid_list
)) {
296 for (size_t i
= 0; i
< hwid_list
->GetSize(); ++i
) {
297 base::DictionaryValue
* hwid_dictionary
= NULL
;
298 std::string hwid_mask
;
299 if (hwid_list
->GetDictionary(i
, &hwid_dictionary
) &&
300 hwid_dictionary
->GetString(kHwidMaskAttr
, &hwid_mask
)) {
301 if (base::MatchPattern(hwid
, hwid_mask
)) {
302 // If HWID for this machine matches some mask, use HWID specific
305 if (hwid_dictionary
->GetString(kInitialLocaleAttr
, &result
))
306 initial_locale_
= result
;
308 if (hwid_dictionary
->GetString(kInitialTimezoneAttr
, &result
))
309 initial_timezone_
= result
;
311 if (hwid_dictionary
->GetString(kKeyboardLayoutAttr
, &result
))
312 keyboard_layout_
= result
;
314 // Don't break here to allow other entires to be applied if match.
316 LOG(ERROR
) << "Syntax error in customization manifest";
321 LOG(ERROR
) << "HWID is missing in machine statistics";
325 // If manifest doesn't exist still apply values from VPD.
326 statistics_provider
->GetMachineStatistic(kInitialLocaleAttr
,
328 statistics_provider
->GetMachineStatistic(kInitialTimezoneAttr
,
330 statistics_provider
->GetMachineStatistic(kKeyboardLayoutAttr
,
332 configured_locales_
= base::SplitString(
333 initial_locale_
, ",", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
335 // Convert ICU locale to chrome ("en_US" to "en-US", etc.).
336 std::for_each(configured_locales_
.begin(), configured_locales_
.end(),
337 base::i18n::GetCanonicalLocale
);
339 // Let's always have configured_locales_.front() a valid entry.
340 if (configured_locales_
.size() == 0)
341 configured_locales_
.push_back(std::string());
344 const std::vector
<std::string
>&
345 StartupCustomizationDocument::configured_locales() const {
346 return configured_locales_
;
349 const std::string
& StartupCustomizationDocument::initial_locale_default()
351 DCHECK(configured_locales_
.size() > 0);
352 return configured_locales_
.front();
355 std::string
StartupCustomizationDocument::GetEULAPage(
356 const std::string
& locale
) const {
357 return GetLocaleSpecificString(locale
, kSetupContentAttr
, kEulaPageAttr
);
360 // ServicesCustomizationDocument implementation. -------------------------------
362 class ServicesCustomizationDocument::ApplyingTask
{
364 // Registers in ServicesCustomizationDocument;
365 explicit ApplyingTask(ServicesCustomizationDocument
* document
);
367 // Do not automatically deregister as we might be called on invalid thread.
370 // Mark task finished and check for customization applied.
371 void Finished(bool success
);
374 ServicesCustomizationDocument
* document_
;
376 // This is error-checking flag to prevent destroying unfinished task
381 ServicesCustomizationDocument::ApplyingTask::ApplyingTask(
382 ServicesCustomizationDocument
* document
)
383 : document_(document
), engaged_(true) {
384 document
->ApplyingTaskStarted();
387 ServicesCustomizationDocument::ApplyingTask::~ApplyingTask() {
391 void ServicesCustomizationDocument::ApplyingTask::Finished(bool success
) {
395 document_
->ApplyingTaskFinished(success
);
399 ServicesCustomizationDocument::ServicesCustomizationDocument()
400 : CustomizationDocument(kAcceptedManifestVersion
),
402 fetch_started_(false),
404 base::TimeDelta::FromMilliseconds(kDefaultNetworkRetryDelayMS
)),
405 apply_tasks_started_(0),
406 apply_tasks_finished_(0),
407 apply_tasks_success_(0),
408 weak_ptr_factory_(this) {
411 ServicesCustomizationDocument::ServicesCustomizationDocument(
412 const std::string
& manifest
)
413 : CustomizationDocument(kAcceptedManifestVersion
),
415 base::TimeDelta::FromMilliseconds(kDefaultNetworkRetryDelayMS
)),
416 apply_tasks_started_(0),
417 apply_tasks_finished_(0),
418 apply_tasks_success_(0),
419 weak_ptr_factory_(this) {
420 LoadManifestFromString(manifest
);
423 ServicesCustomizationDocument::~ServicesCustomizationDocument() {}
426 ServicesCustomizationDocument
* ServicesCustomizationDocument::GetInstance() {
427 if (g_test_services_customization_document
)
428 return g_test_services_customization_document
;
430 return Singleton
<ServicesCustomizationDocument
,
431 DefaultSingletonTraits
<ServicesCustomizationDocument
> >::get();
435 void ServicesCustomizationDocument::RegisterPrefs(
436 PrefRegistrySimple
* registry
) {
437 registry
->RegisterBooleanPref(kServicesCustomizationAppliedPref
, false);
438 registry
->RegisterStringPref(prefs::kCustomizationDefaultWallpaperURL
,
443 void ServicesCustomizationDocument::RegisterProfilePrefs(
444 user_prefs::PrefRegistrySyncable
* registry
) {
445 registry
->RegisterDictionaryPref(kServicesCustomizationKey
);
449 bool ServicesCustomizationDocument::WasOOBECustomizationApplied() {
450 PrefService
* prefs
= g_browser_process
->local_state();
451 // prefs can be NULL in some tests.
453 return prefs
->GetBoolean(kServicesCustomizationAppliedPref
);
459 void ServicesCustomizationDocument::SetApplied(bool val
) {
460 PrefService
* prefs
= g_browser_process
->local_state();
461 // prefs can be NULL in some tests.
463 prefs
->SetBoolean(kServicesCustomizationAppliedPref
, val
);
467 base::FilePath
ServicesCustomizationDocument::GetCustomizedWallpaperCacheDir() {
468 base::FilePath custom_wallpaper_dir
;
469 if (!PathService::Get(chrome::DIR_CHROMEOS_CUSTOM_WALLPAPERS
,
470 &custom_wallpaper_dir
)) {
471 LOG(DFATAL
) << "Unable to get custom wallpaper dir.";
472 return base::FilePath();
474 return custom_wallpaper_dir
.Append(kCustomizationDefaultWallpaperDir
);
479 ServicesCustomizationDocument::GetCustomizedWallpaperDownloadedFileName() {
480 const base::FilePath dir
= GetCustomizedWallpaperCacheDir();
485 return dir
.Append(kCustomizationDefaultWallpaperDownloadedFile
);
488 void ServicesCustomizationDocument::EnsureCustomizationApplied() {
489 if (WasOOBECustomizationApplied())
492 // When customization manifest is fetched, applying will start automatically.
500 ServicesCustomizationDocument::EnsureCustomizationAppliedClosure() {
501 return base::Bind(&ServicesCustomizationDocument::EnsureCustomizationApplied
,
502 weak_ptr_factory_
.GetWeakPtr());
505 void ServicesCustomizationDocument::StartFetching() {
506 if (IsReady() || fetch_started_
)
509 if (!url_
.is_valid()) {
510 std::string customization_id
;
511 chromeos::system::StatisticsProvider
* provider
=
512 chromeos::system::StatisticsProvider::GetInstance();
513 if (provider
->GetMachineStatistic(system::kCustomizationIdKey
,
514 &customization_id
) &&
515 !customization_id
.empty()) {
516 url_
= GURL(base::StringPrintf(
517 kManifestUrl
, base::ToLowerASCII(customization_id
).c_str()));
519 // Remember that there is no customization ID in VPD.
520 OnCustomizationNotFound();
525 if (url_
.is_valid()) {
526 fetch_started_
= true;
527 if (url_
.SchemeIsFile()) {
528 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE
,
529 base::Bind(&ServicesCustomizationDocument::ReadFileInBackground
,
530 weak_ptr_factory_
.GetWeakPtr(),
531 base::FilePath(url_
.path())));
539 void ServicesCustomizationDocument::ReadFileInBackground(
540 base::WeakPtr
<ServicesCustomizationDocument
> self
,
541 const base::FilePath
& file
) {
542 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
544 std::string manifest
;
545 if (!base::ReadFileToString(file
, &manifest
)) {
547 LOG(ERROR
) << "Failed to load services customization manifest from: "
551 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
552 base::Bind(&ServicesCustomizationDocument::OnManifesteRead
,
557 void ServicesCustomizationDocument::OnManifesteRead(
558 const std::string
& manifest
) {
559 if (!manifest
.empty())
560 LoadManifestFromString(manifest
);
562 fetch_started_
= false;
565 void ServicesCustomizationDocument::StartFileFetch() {
566 DelayNetworkCall(network_delay_
,
567 base::Bind(&ServicesCustomizationDocument::DoStartFileFetch
,
568 weak_ptr_factory_
.GetWeakPtr()));
571 void ServicesCustomizationDocument::DoStartFileFetch() {
572 url_fetcher_
= net::URLFetcher::Create(url_
, net::URLFetcher::GET
, this);
573 url_fetcher_
->SetRequestContext(g_browser_process
->system_request_context());
574 url_fetcher_
->AddExtraRequestHeader("Accept: application/json");
575 url_fetcher_
->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES
|
576 net::LOAD_DO_NOT_SAVE_COOKIES
|
577 net::LOAD_DISABLE_CACHE
|
578 net::LOAD_DO_NOT_SEND_AUTH_DATA
);
579 url_fetcher_
->Start();
582 bool ServicesCustomizationDocument::LoadManifestFromString(
583 const std::string
& manifest
) {
584 if (CustomizationDocument::LoadManifestFromString(manifest
)) {
585 LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_SUCCESS
);
590 LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_PARSING_ERROR
);
594 void ServicesCustomizationDocument::OnManifestLoaded() {
595 if (!WasOOBECustomizationApplied())
596 ApplyOOBECustomization();
598 scoped_ptr
<base::DictionaryValue
> prefs
=
599 GetDefaultAppsInProviderFormat(*root_
);
600 for (ExternalLoaders::iterator it
= external_loaders_
.begin();
601 it
!= external_loaders_
.end(); ++it
) {
603 UpdateCachedManifest((*it
)->profile());
604 (*it
)->SetCurrentApps(
605 scoped_ptr
<base::DictionaryValue
>(prefs
->DeepCopy()));
606 SetOemFolderName((*it
)->profile(), *root_
);
611 void ServicesCustomizationDocument::OnURLFetchComplete(
612 const net::URLFetcher
* source
) {
613 std::string mime_type
;
615 if (source
->GetStatus().is_success() &&
616 source
->GetResponseCode() == net::HTTP_OK
&&
617 source
->GetResponseHeaders()->GetMimeType(&mime_type
) &&
618 mime_type
== "application/json" &&
619 source
->GetResponseAsString(&data
)) {
620 LoadManifestFromString(data
);
621 } else if (source
->GetResponseCode() == net::HTTP_NOT_FOUND
) {
622 LOG(ERROR
) << "Customization manifest is missing on server: "
623 << source
->GetURL().spec();
624 OnCustomizationNotFound();
626 if (num_retries_
< kMaxFetchRetries
) {
628 content::BrowserThread::PostDelayedTask(
629 content::BrowserThread::UI
,
631 base::Bind(&ServicesCustomizationDocument::StartFileFetch
,
632 weak_ptr_factory_
.GetWeakPtr()),
633 base::TimeDelta::FromSeconds(kRetriesDelayInSec
));
636 // This doesn't stop fetching manifest on next restart.
637 LOG(ERROR
) << "URL fetch for services customization failed:"
638 << " response code = " << source
->GetResponseCode()
639 << " URL = " << source
->GetURL().spec();
641 LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_RETRIES_FAIL
);
643 fetch_started_
= false;
646 bool ServicesCustomizationDocument::ApplyOOBECustomization() {
647 if (apply_tasks_started_
)
650 CheckAndApplyWallpaper();
654 bool ServicesCustomizationDocument::GetDefaultWallpaperUrl(
655 GURL
* out_url
) const {
660 if (!root_
->GetString(kDefaultWallpaperAttr
, &url
))
663 *out_url
= GURL(url
);
667 scoped_ptr
<base::DictionaryValue
>
668 ServicesCustomizationDocument::GetDefaultApps() const {
670 return scoped_ptr
<base::DictionaryValue
>();
672 return GetDefaultAppsInProviderFormat(*root_
);
675 std::string
ServicesCustomizationDocument::GetOemAppsFolderName(
676 const std::string
& locale
) const {
678 return std::string();
680 return GetOemAppsFolderNameImpl(locale
, *root_
);
683 scoped_ptr
<base::DictionaryValue
>
684 ServicesCustomizationDocument::GetDefaultAppsInProviderFormat(
685 const base::DictionaryValue
& root
) {
686 scoped_ptr
<base::DictionaryValue
> prefs(new base::DictionaryValue
);
687 const base::ListValue
* apps_list
= NULL
;
688 if (root
.GetList(kDefaultAppsAttr
, &apps_list
)) {
689 for (size_t i
= 0; i
< apps_list
->GetSize(); ++i
) {
691 const base::DictionaryValue
* app_entry
= nullptr;
692 scoped_ptr
<base::DictionaryValue
> entry
;
693 if (apps_list
->GetString(i
, &app_id
)) {
694 entry
.reset(new base::DictionaryValue());
695 } else if (apps_list
->GetDictionary(i
, &app_entry
)) {
696 if (!app_entry
->GetString(kIdAttr
, &app_id
)) {
697 LOG(ERROR
) << "Wrong format of default application list";
701 entry
= app_entry
->CreateDeepCopy();
702 entry
->Remove(kIdAttr
, nullptr);
704 LOG(ERROR
) << "Wrong format of default application list";
709 extensions::ExternalProviderImpl::kExternalUpdateUrl
)) {
710 entry
->SetString(extensions::ExternalProviderImpl::kExternalUpdateUrl
,
711 extension_urls::GetWebstoreUpdateUrl().spec());
713 prefs
->Set(app_id
, entry
.Pass());
720 void ServicesCustomizationDocument::UpdateCachedManifest(Profile
* profile
) {
721 profile
->GetPrefs()->Set(kServicesCustomizationKey
, *root_
);
724 extensions::ExternalLoader
* ServicesCustomizationDocument::CreateExternalLoader(
726 ServicesCustomizationExternalLoader
* loader
=
727 new ServicesCustomizationExternalLoader(profile
);
728 external_loaders_
.push_back(loader
->AsWeakPtr());
731 UpdateCachedManifest(profile
);
732 loader
->SetCurrentApps(GetDefaultAppsInProviderFormat(*root_
));
733 SetOemFolderName(profile
, *root_
);
735 const base::DictionaryValue
* root
=
736 profile
->GetPrefs()->GetDictionary(kServicesCustomizationKey
);
738 if (root
&& root
->GetString(kVersionAttr
, &version
)) {
739 // If version exists, profile has cached version of customization.
740 loader
->SetCurrentApps(GetDefaultAppsInProviderFormat(*root
));
741 SetOemFolderName(profile
, *root
);
743 // StartFetching will be called from ServicesCustomizationExternalLoader
744 // when StartLoading is called. We can't initiate manifest fetch here
745 // because caller may never call StartLoading for the provider.
752 void ServicesCustomizationDocument::OnCustomizationNotFound() {
753 LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_FILE_NOT_FOUND
);
754 LoadManifestFromString(kEmptyServicesCustomizationManifest
);
757 void ServicesCustomizationDocument::SetOemFolderName(
759 const base::DictionaryValue
& root
) {
760 std::string locale
= g_browser_process
->GetApplicationLocale();
761 std::string name
= GetOemAppsFolderNameImpl(locale
, root
);
763 name
= default_app_order::GetOemAppsFolderName();
765 app_list::AppListSyncableService
* service
=
766 app_list::AppListSyncableServiceFactory::GetForProfile(profile
);
768 LOG(WARNING
) << "AppListSyncableService is not ready for setting OEM "
772 service
->SetOemFolderName(name
);
776 std::string
ServicesCustomizationDocument::GetOemAppsFolderNameImpl(
777 const std::string
& locale
,
778 const base::DictionaryValue
& root
) const {
779 return GetLocaleSpecificStringImpl(
780 &root
, locale
, kLocalizedContent
, kDefaultAppsFolderName
);
784 void ServicesCustomizationDocument::InitializeForTesting() {
785 g_test_services_customization_document
= new ServicesCustomizationDocument
;
786 g_test_services_customization_document
->network_delay_
= base::TimeDelta();
790 void ServicesCustomizationDocument::ShutdownForTesting() {
791 delete g_test_services_customization_document
;
792 g_test_services_customization_document
= NULL
;
795 void ServicesCustomizationDocument::StartOEMWallpaperDownload(
796 const GURL
& wallpaper_url
,
797 scoped_ptr
<ServicesCustomizationDocument::ApplyingTask
> applying
) {
798 DCHECK(wallpaper_url
.is_valid());
800 const base::FilePath dir
= GetCustomizedWallpaperCacheDir();
801 const base::FilePath file
= GetCustomizedWallpaperDownloadedFileName();
802 if (dir
.empty() || file
.empty()) {
804 applying
->Finished(false);
808 wallpaper_downloader_
.reset(new CustomizationWallpaperDownloader(
809 g_browser_process
->system_request_context(),
813 base::Bind(&ServicesCustomizationDocument::OnOEMWallpaperDownloaded
,
814 weak_ptr_factory_
.GetWeakPtr(),
815 base::Passed(applying
.Pass()))));
817 wallpaper_downloader_
->Start();
820 void ServicesCustomizationDocument::CheckAndApplyWallpaper() {
821 if (wallpaper_downloader_
.get()) {
822 VLOG(1) << "CheckAndApplyWallpaper(): download has already started.";
825 scoped_ptr
<ServicesCustomizationDocument::ApplyingTask
> applying(
826 new ServicesCustomizationDocument::ApplyingTask(this));
829 if (!GetDefaultWallpaperUrl(&wallpaper_url
)) {
830 PrefService
* pref_service
= g_browser_process
->local_state();
831 std::string current_url
=
832 pref_service
->GetString(prefs::kCustomizationDefaultWallpaperURL
);
833 if (!current_url
.empty()) {
834 VLOG(1) << "ServicesCustomizationDocument::CheckAndApplyWallpaper() : "
835 << "No wallpaper URL attribute in customization document, "
836 << "but current value is non-empty: '" << current_url
839 applying
->Finished(true);
843 // Should fail if this ever happens in tests.
844 DCHECK(wallpaper_url
.is_valid());
845 if (!wallpaper_url
.is_valid()) {
846 if (!wallpaper_url
.is_empty()) {
847 LOG(WARNING
) << "Invalid Customized Wallpaper URL '"
848 << wallpaper_url
.spec() << "'.";
850 applying
->Finished(false);
854 scoped_ptr
<bool> exists(new bool(false));
856 base::Closure check_file_exists
=
857 base::Bind(&CheckWallpaperCacheExists
,
858 GetCustomizedWallpaperDownloadedFileName(),
859 base::Unretained(exists
.get()));
860 base::Closure on_checked_closure
=
861 base::Bind(&ServicesCustomizationDocument::OnCheckedWallpaperCacheExists
,
862 weak_ptr_factory_
.GetWeakPtr(),
863 base::Passed(exists
.Pass()),
864 base::Passed(applying
.Pass()));
865 if (!content::BrowserThread::PostBlockingPoolTaskAndReply(
866 FROM_HERE
, check_file_exists
, on_checked_closure
)) {
867 LOG(WARNING
) << "Failed to start check Wallpaper cache exists.";
871 void ServicesCustomizationDocument::OnCheckedWallpaperCacheExists(
872 scoped_ptr
<bool> exists
,
873 scoped_ptr
<ServicesCustomizationDocument::ApplyingTask
> applying
) {
874 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
878 ApplyWallpaper(*exists
, applying
.Pass());
881 void ServicesCustomizationDocument::ApplyWallpaper(
882 bool default_wallpaper_file_exists
,
883 scoped_ptr
<ServicesCustomizationDocument::ApplyingTask
> applying
) {
885 const bool wallpaper_url_present
= GetDefaultWallpaperUrl(&wallpaper_url
);
887 PrefService
* pref_service
= g_browser_process
->local_state();
889 std::string current_url
=
890 pref_service
->GetString(prefs::kCustomizationDefaultWallpaperURL
);
891 if (current_url
!= wallpaper_url
.spec()) {
892 if (wallpaper_url_present
) {
893 VLOG(1) << "ServicesCustomizationDocument::ApplyWallpaper() : "
894 << "Wallpaper URL in customization document '"
895 << wallpaper_url
.spec() << "' differs from current '"
896 << current_url
<< "'."
897 << (GURL(current_url
).is_valid() && default_wallpaper_file_exists
901 VLOG(1) << "ServicesCustomizationDocument::ApplyWallpaper() : "
902 << "No wallpaper URL attribute in customization document, "
903 << "but current value is non-empty: '" << current_url
907 if (!wallpaper_url_present
) {
908 applying
->Finished(true);
912 DCHECK(wallpaper_url
.is_valid());
914 // Never update system-wide wallpaper (i.e. do not check
915 // current_url == wallpaper_url.spec() )
916 if (GURL(current_url
).is_valid() && default_wallpaper_file_exists
) {
918 << "ServicesCustomizationDocument::ApplyWallpaper() : reuse existing";
919 OnOEMWallpaperDownloaded(applying
.Pass(), true, GURL(current_url
));
922 << "ServicesCustomizationDocument::ApplyWallpaper() : start download";
923 StartOEMWallpaperDownload(wallpaper_url
, applying
.Pass());
927 void ServicesCustomizationDocument::OnOEMWallpaperDownloaded(
928 scoped_ptr
<ServicesCustomizationDocument::ApplyingTask
> applying
,
930 const GURL
& wallpaper_url
) {
932 DCHECK(wallpaper_url
.is_valid());
934 VLOG(1) << "Setting default wallpaper to '"
935 << GetCustomizedWallpaperDownloadedFileName().value() << "' ('"
936 << wallpaper_url
.spec() << "')";
937 WallpaperManager::Get()->SetCustomizedDefaultWallpaper(
939 GetCustomizedWallpaperDownloadedFileName(),
940 GetCustomizedWallpaperCacheDir());
942 wallpaper_downloader_
.reset();
943 applying
->Finished(success
);
946 void ServicesCustomizationDocument::ApplyingTaskStarted() {
947 ++apply_tasks_started_
;
950 void ServicesCustomizationDocument::ApplyingTaskFinished(bool success
) {
951 DCHECK_GT(apply_tasks_started_
, apply_tasks_finished_
);
952 ++apply_tasks_finished_
;
954 apply_tasks_success_
+= success
;
956 if (apply_tasks_started_
!= apply_tasks_finished_
)
959 if (apply_tasks_success_
== apply_tasks_finished_
)
963 } // namespace chromeos