Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / customization_document.cc
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.
4
5 #include "chrome/browser/chromeos/customization_document.h"
6
7 #include <algorithm>
8
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_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"
47
48 using content::BrowserThread;
49
50 namespace chromeos {
51 namespace {
52
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";
67
68 const char kAcceptedManifestVersion[] = "1.0";
69
70 // Path to OEM partner startup customization manifest.
71 const char kStartupCustomizationManifestPath[] =
72     "/opt/oem/etc/startup_manifest.json";
73
74 // This is subdirectory relative to PathService(DIR_CHROMEOS_CUSTOM_WALLPAPERS),
75 // where downloaded (and resized) wallpaper is stored.
76 const char kCustomizationDefaultWallpaperDir[] = "customization";
77
78 // The original downloaded image file is stored under this name.
79 const char kCustomizationDefaultWallpaperDownloadedFile[] =
80     "default_downloaded_wallpaper.bin";
81
82 // Name of local state option that tracks if services customization has been
83 // applied.
84 const char kServicesCustomizationAppliedPref[] = "ServicesCustomizationApplied";
85
86 // Maximum number of retries to fetch file if network is not available.
87 const int kMaxFetchRetries = 3;
88
89 // Delay between file fetch retries if network is not available.
90 const int kRetriesDelayInSec = 2;
91
92 // Name of profile option that tracks cached version of service customization.
93 const char kServicesCustomizationKey[] = "customization.manifest_cache";
94
95 // Empty customization document that doesn't customize anything.
96 const char kEmptyServicesCustomizationManifest[] = "{ \"version\": \"1.0\" }";
97
98 // Global overrider for ServicesCustomizationDocument for tests.
99 ServicesCustomizationDocument* g_test_services_customization_document = NULL;
100
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
110 };
111
112 void LogManifestLoadResult(HistogramServicesCustomizationLoadResult result) {
113   UMA_HISTOGRAM_ENUMERATION("ServicesCustomization.LoadResult",
114                             result,
115                             HISTOGRAM_LOAD_RESULT_MAX_VALUE);
116 }
117
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();
126
127   const base::DictionaryValue* locale_dictionary = NULL;
128   if (dictionary_content->GetDictionary(locale, &locale_dictionary)) {
129     std::string result;
130     if (locale_dictionary->GetString(entry_name, &result))
131       return result;
132   }
133
134   const base::DictionaryValue* default_dictionary = NULL;
135   if (dictionary_content->GetDictionary(kDefaultAttr, &default_dictionary)) {
136     std::string result;
137     if (default_dictionary->GetString(entry_name, &result))
138       return result;
139   }
140
141   return std::string();
142 }
143
144 void CheckWallpaperCacheExists(const base::FilePath& path, bool* exists) {
145   DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
146   DCHECK(exists);
147   *exists = base::PathExists(path);
148 }
149
150 }  // anonymous namespace
151
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";
155
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> {
161  public:
162   explicit ServicesCustomizationExternalLoader(Profile* profile)
163       : is_apps_set_(false), profile_(profile) {}
164
165   Profile* profile() { return profile_; }
166
167   // Used by the ServicesCustomizationDocument to update the current apps.
168   void SetCurrentApps(scoped_ptr<base::DictionaryValue> prefs) {
169     apps_.Swap(prefs.get());
170     is_apps_set_ = true;
171     StartLoading();
172   }
173
174   // Implementation of extensions::ExternalLoader:
175   virtual void StartLoading() OVERRIDE {
176     if (!is_apps_set_) {
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.
184       if (is_apps_set_)
185         return;
186     }
187
188     prefs_.reset(apps_.DeepCopy());
189     VLOG(1) << "ServicesCustomization extension loader publishing "
190             << apps_.size() << " apps.";
191     LoadFinished();
192   }
193
194  protected:
195   virtual ~ServicesCustomizationExternalLoader() {}
196
197  private:
198   bool is_apps_set_;
199   base::DictionaryValue apps_;
200   Profile* profile_;
201
202   DISALLOW_COPY_AND_ASSIGN(ServicesCustomizationExternalLoader);
203 };
204
205 // CustomizationDocument implementation. ---------------------------------------
206
207 CustomizationDocument::CustomizationDocument(
208     const std::string& accepted_version)
209     : accepted_version_(accepted_version) {}
210
211 CustomizationDocument::~CustomizationDocument() {}
212
213 bool CustomizationDocument::LoadManifestFromFile(
214     const base::FilePath& manifest_path) {
215   std::string manifest;
216   if (!base::ReadFileToString(manifest_path, &manifest))
217     return false;
218   return LoadManifestFromString(manifest);
219 }
220
221 bool CustomizationDocument::LoadManifestFromString(
222     const std::string& manifest) {
223   int error_code = 0;
224   std::string error;
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)
228     LOG(ERROR) << error;
229   DCHECK(root.get() != NULL);
230   if (root.get() == NULL)
231     return false;
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()));
235     std::string result;
236     if (root_->GetString(kVersionAttr, &result) &&
237         result == accepted_version_)
238       return true;
239
240     LOG(ERROR) << "Wrong customization manifest version";
241     root_.reset(NULL);
242   }
243   return false;
244 }
245
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);
252 }
253
254 // StartupCustomizationDocument implementation. --------------------------------
255
256 StartupCustomizationDocument::StartupCustomizationDocument()
257     : CustomizationDocument(kAcceptedManifestVersion) {
258   {
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));
263   }
264   Init(system::StatisticsProvider::GetInstance());
265 }
266
267 StartupCustomizationDocument::StartupCustomizationDocument(
268     system::StatisticsProvider* statistics_provider,
269     const std::string& manifest)
270     : CustomizationDocument(kAcceptedManifestVersion) {
271   LoadManifestFromString(manifest);
272   Init(statistics_provider);
273 }
274
275 StartupCustomizationDocument::~StartupCustomizationDocument() {}
276
277 StartupCustomizationDocument* StartupCustomizationDocument::GetInstance() {
278   return Singleton<StartupCustomizationDocument,
279       DefaultSingletonTraits<StartupCustomizationDocument> >::get();
280 }
281
282 void StartupCustomizationDocument::Init(
283     system::StatisticsProvider* statistics_provider) {
284   if (IsReady()) {
285     root_->GetString(kInitialLocaleAttr, &initial_locale_);
286     root_->GetString(kInitialTimezoneAttr, &initial_timezone_);
287     root_->GetString(kKeyboardLayoutAttr, &keyboard_layout_);
288
289     std::string hwid;
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
301               // settings.
302               std::string result;
303               if (hwid_dictionary->GetString(kInitialLocaleAttr, &result))
304                 initial_locale_ = result;
305
306               if (hwid_dictionary->GetString(kInitialTimezoneAttr, &result))
307                 initial_timezone_ = result;
308
309               if (hwid_dictionary->GetString(kKeyboardLayoutAttr, &result))
310                 keyboard_layout_ = result;
311             }
312             // Don't break here to allow other entires to be applied if match.
313           } else {
314             LOG(ERROR) << "Syntax error in customization manifest";
315           }
316         }
317       }
318     } else {
319       LOG(ERROR) << "HWID is missing in machine statistics";
320     }
321   }
322
323   // If manifest doesn't exist still apply values from VPD.
324   statistics_provider->GetMachineStatistic(kInitialLocaleAttr,
325                                            &initial_locale_);
326   statistics_provider->GetMachineStatistic(kInitialTimezoneAttr,
327                                            &initial_timezone_);
328   statistics_provider->GetMachineStatistic(kKeyboardLayoutAttr,
329                                            &keyboard_layout_);
330   configured_locales_.resize(0);
331   base::SplitString(initial_locale_, ',', &configured_locales_);
332
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);
337
338   // Let's always have configured_locales_.front() a valid entry.
339   if (configured_locales_.size() == 0)
340     configured_locales_.push_back(std::string());
341 }
342
343 const std::vector<std::string>&
344 StartupCustomizationDocument::configured_locales() const {
345   return configured_locales_;
346 }
347
348 const std::string& StartupCustomizationDocument::initial_locale_default()
349     const {
350   DCHECK(configured_locales_.size() > 0);
351   return configured_locales_.front();
352 }
353
354 std::string StartupCustomizationDocument::GetEULAPage(
355     const std::string& locale) const {
356   return GetLocaleSpecificString(locale, kSetupContentAttr, kEulaPageAttr);
357 }
358
359 // ServicesCustomizationDocument implementation. -------------------------------
360
361 class ServicesCustomizationDocument::ApplyingTask {
362  public:
363   // Registers in ServicesCustomizationDocument;
364   explicit ApplyingTask(ServicesCustomizationDocument* document);
365
366   // Do not automatically deregister as we might be called on invalid thread.
367   ~ApplyingTask();
368
369   // Mark task finished and check for customization applied.
370   void Finished(bool success);
371
372  private:
373   ServicesCustomizationDocument* document_;
374
375   // This is error-checking flag to prevent destroying unfinished task
376   // or double finish.
377   bool engaged_;
378 };
379
380 ServicesCustomizationDocument::ApplyingTask::ApplyingTask(
381     ServicesCustomizationDocument* document)
382     : document_(document), engaged_(true) {
383   document->ApplyingTaskStarted();
384 }
385
386 ServicesCustomizationDocument::ApplyingTask::~ApplyingTask() {
387   DCHECK(!engaged_);
388 }
389
390 void ServicesCustomizationDocument::ApplyingTask::Finished(bool success) {
391   DCHECK(engaged_);
392   if (engaged_) {
393     engaged_ = false;
394     document_->ApplyingTaskFinished(success);
395   }
396 }
397
398 ServicesCustomizationDocument::ServicesCustomizationDocument()
399     : CustomizationDocument(kAcceptedManifestVersion),
400       num_retries_(0),
401       fetch_started_(false),
402       network_delay_(
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) {
408 }
409
410 ServicesCustomizationDocument::ServicesCustomizationDocument(
411     const std::string& manifest)
412     : CustomizationDocument(kAcceptedManifestVersion),
413       network_delay_(
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);
420 }
421
422 ServicesCustomizationDocument::~ServicesCustomizationDocument() {}
423
424 // static
425 ServicesCustomizationDocument* ServicesCustomizationDocument::GetInstance() {
426   if (g_test_services_customization_document)
427     return g_test_services_customization_document;
428
429   return Singleton<ServicesCustomizationDocument,
430       DefaultSingletonTraits<ServicesCustomizationDocument> >::get();
431 }
432
433 // static
434 void ServicesCustomizationDocument::RegisterPrefs(
435     PrefRegistrySimple* registry) {
436   registry->RegisterBooleanPref(kServicesCustomizationAppliedPref, false);
437   registry->RegisterStringPref(prefs::kCustomizationDefaultWallpaperURL,
438                                std::string());
439 }
440
441 // static
442 void ServicesCustomizationDocument::RegisterProfilePrefs(
443     user_prefs::PrefRegistrySyncable* registry) {
444   registry->RegisterDictionaryPref(
445       kServicesCustomizationKey,
446       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
447 }
448
449 // static
450 bool ServicesCustomizationDocument::WasOOBECustomizationApplied() {
451   PrefService* prefs = g_browser_process->local_state();
452   // prefs can be NULL in some tests.
453   if (prefs)
454     return prefs->GetBoolean(kServicesCustomizationAppliedPref);
455   else
456     return false;
457 }
458
459 // static
460 void ServicesCustomizationDocument::SetApplied(bool val) {
461   PrefService* prefs = g_browser_process->local_state();
462   // prefs can be NULL in some tests.
463   if (prefs)
464     prefs->SetBoolean(kServicesCustomizationAppliedPref, val);
465 }
466
467 // static
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();
474   }
475   return custom_wallpaper_dir.Append(kCustomizationDefaultWallpaperDir);
476 }
477
478 // static
479 base::FilePath
480 ServicesCustomizationDocument::GetCustomizedWallpaperDownloadedFileName() {
481   const base::FilePath dir = GetCustomizedWallpaperCacheDir();
482   if (dir.empty()) {
483     NOTREACHED();
484     return dir;
485   }
486   return dir.Append(kCustomizationDefaultWallpaperDownloadedFile);
487 }
488
489 void ServicesCustomizationDocument::EnsureCustomizationApplied() {
490   if (WasOOBECustomizationApplied())
491     return;
492
493   // When customization manifest is fetched, applying will start automatically.
494   if (IsReady())
495     return;
496
497   StartFetching();
498 }
499
500 base::Closure
501 ServicesCustomizationDocument::EnsureCustomizationAppliedClosure() {
502   return base::Bind(&ServicesCustomizationDocument::EnsureCustomizationApplied,
503                     weak_ptr_factory_.GetWeakPtr());
504 }
505
506 void ServicesCustomizationDocument::StartFetching() {
507   if (IsReady() || fetch_started_)
508     return;
509
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()));
519     } else {
520       // Remember that there is no customization ID in VPD.
521       OnCustomizationNotFound();
522       return;
523     }
524   }
525
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())));
533     } else {
534       StartFileFetch();
535     }
536   }
537 }
538
539 // static
540 void ServicesCustomizationDocument::ReadFileInBackground(
541     base::WeakPtr<ServicesCustomizationDocument> self,
542     const base::FilePath& file) {
543   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
544
545   std::string manifest;
546   if (!base::ReadFileToString(file, &manifest)) {
547     manifest.clear();
548     LOG(ERROR) << "Failed to load services customization manifest from: "
549                << file.value();
550   }
551
552   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
553       base::Bind(&ServicesCustomizationDocument::OnManifesteRead,
554                  self,
555                  manifest));
556 }
557
558 void ServicesCustomizationDocument::OnManifesteRead(
559     const std::string& manifest) {
560   if (!manifest.empty())
561     LoadManifestFromString(manifest);
562
563   fetch_started_ = false;
564 }
565
566 void ServicesCustomizationDocument::StartFileFetch() {
567   DelayNetworkCall(base::Bind(&ServicesCustomizationDocument::DoStartFileFetch,
568                               weak_ptr_factory_.GetWeakPtr()),
569       network_delay_);
570 }
571
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();
582 }
583
584 bool ServicesCustomizationDocument::LoadManifestFromString(
585     const std::string& manifest) {
586   if (CustomizationDocument::LoadManifestFromString(manifest)) {
587     LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_SUCCESS);
588     OnManifestLoaded();
589     return true;
590   }
591
592   LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_PARSING_ERROR);
593   return false;
594 }
595
596 void ServicesCustomizationDocument::OnManifestLoaded() {
597   if (!WasOOBECustomizationApplied())
598     ApplyOOBECustomization();
599
600   scoped_ptr<base::DictionaryValue> prefs =
601       GetDefaultAppsInProviderFormat(*root_);
602   for (ExternalLoaders::iterator it = external_loaders_.begin();
603        it != external_loaders_.end(); ++it) {
604     if (*it) {
605       UpdateCachedManifest((*it)->profile());
606       (*it)->SetCurrentApps(
607           scoped_ptr<base::DictionaryValue>(prefs->DeepCopy()));
608       SetOemFolderName((*it)->profile(), *root_);
609     }
610   }
611 }
612
613 void ServicesCustomizationDocument::OnURLFetchComplete(
614     const net::URLFetcher* source) {
615   std::string mime_type;
616   std::string data;
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();
627   } else {
628     if (num_retries_ < kMaxFetchRetries) {
629       num_retries_++;
630       content::BrowserThread::PostDelayedTask(
631           content::BrowserThread::UI,
632           FROM_HERE,
633           base::Bind(&ServicesCustomizationDocument::StartFileFetch,
634                      weak_ptr_factory_.GetWeakPtr()),
635           base::TimeDelta::FromSeconds(kRetriesDelayInSec));
636       return;
637     }
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();
642
643     LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_RETRIES_FAIL);
644   }
645   fetch_started_ = false;
646 }
647
648 bool ServicesCustomizationDocument::ApplyOOBECustomization() {
649   if (apply_tasks_started_)
650     return false;
651
652   CheckAndApplyWallpaper();
653   return false;
654 }
655
656 bool ServicesCustomizationDocument::GetDefaultWallpaperUrl(
657     GURL* out_url) const {
658   if (!IsReady())
659     return false;
660
661   std::string url;
662   if (!root_->GetString(kDefaultWallpaperAttr, &url))
663     return false;
664
665   *out_url = GURL(url);
666   return true;
667 }
668
669 bool ServicesCustomizationDocument::GetDefaultApps(
670     std::vector<std::string>* ids) const {
671   ids->clear();
672   if (!IsReady())
673     return false;
674
675   base::ListValue* apps_list = NULL;
676   if (!root_->GetList(kDefaultAppsAttr, &apps_list))
677     return false;
678
679   for (size_t i = 0; i < apps_list->GetSize(); ++i) {
680     std::string app_id;
681     if (apps_list->GetString(i, &app_id)) {
682       ids->push_back(app_id);
683     } else {
684       LOG(ERROR) << "Wrong format of default application list";
685       return false;
686     }
687   }
688
689   return true;
690 }
691
692 std::string ServicesCustomizationDocument::GetOemAppsFolderName(
693     const std::string& locale) const {
694   if (!IsReady())
695     return std::string();
696
697   return GetOemAppsFolderNameImpl(locale, *root_);
698 }
699
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) {
707       std::string app_id;
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);
713       } else {
714         LOG(ERROR) << "Wrong format of default application list";
715         prefs->Clear();
716         break;
717       }
718     }
719   }
720
721   return prefs.Pass();
722 }
723
724 void ServicesCustomizationDocument::UpdateCachedManifest(Profile* profile) {
725   profile->GetPrefs()->Set(kServicesCustomizationKey, *root_);
726 }
727
728 extensions::ExternalLoader* ServicesCustomizationDocument::CreateExternalLoader(
729     Profile* profile) {
730   ServicesCustomizationExternalLoader* loader =
731       new ServicesCustomizationExternalLoader(profile);
732   external_loaders_.push_back(loader->AsWeakPtr());
733
734   if (IsReady()) {
735     UpdateCachedManifest(profile);
736     loader->SetCurrentApps(GetDefaultAppsInProviderFormat(*root_));
737     SetOemFolderName(profile, *root_);
738   } else {
739     const base::DictionaryValue* root =
740         profile->GetPrefs()->GetDictionary(kServicesCustomizationKey);
741     std::string version;
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);
746     } else {
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.
750     }
751   }
752
753   return loader;
754 }
755
756 void ServicesCustomizationDocument::OnCustomizationNotFound() {
757   LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_FILE_NOT_FOUND);
758   LoadManifestFromString(kEmptyServicesCustomizationManifest);
759 }
760
761 void ServicesCustomizationDocument::SetOemFolderName(
762     Profile* profile,
763     const base::DictionaryValue& root) {
764   std::string locale = g_browser_process->GetApplicationLocale();
765   std::string name = GetOemAppsFolderNameImpl(locale, root);
766   if (name.empty())
767     name = default_app_order::GetOemAppsFolderName();
768   if (!name.empty()) {
769     app_list::AppListSyncableService* service =
770         app_list::AppListSyncableServiceFactory::GetForProfile(profile);
771     if (!service) {
772       LOG(WARNING) << "AppListSyncableService is not ready for setting OEM "
773                       "folder name";
774       return;
775     }
776     service->SetOemFolderName(name);
777   }
778 }
779
780 std::string ServicesCustomizationDocument::GetOemAppsFolderNameImpl(
781     const std::string& locale,
782     const base::DictionaryValue& root) const {
783   return GetLocaleSpecificStringImpl(
784       &root, locale, kLocalizedContent, kDefaultAppsFolderName);
785 }
786
787 // static
788 void ServicesCustomizationDocument::InitializeForTesting() {
789   g_test_services_customization_document = new ServicesCustomizationDocument;
790   g_test_services_customization_document->network_delay_ = base::TimeDelta();
791 }
792
793 // static
794 void ServicesCustomizationDocument::ShutdownForTesting() {
795   delete g_test_services_customization_document;
796   g_test_services_customization_document = NULL;
797 }
798
799 void ServicesCustomizationDocument::StartOEMWallpaperDownload(
800     const GURL& wallpaper_url,
801     scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying) {
802   DCHECK(wallpaper_url.is_valid());
803
804   const base::FilePath dir = GetCustomizedWallpaperCacheDir();
805   const base::FilePath file = GetCustomizedWallpaperDownloadedFileName();
806   if (dir.empty() || file.empty()) {
807     NOTREACHED();
808     applying->Finished(false);
809     return;
810   }
811
812   wallpaper_downloader_.reset(new CustomizationWallpaperDownloader(
813       g_browser_process->system_request_context(),
814       wallpaper_url,
815       dir,
816       file,
817       base::Bind(&ServicesCustomizationDocument::OnOEMWallpaperDownloaded,
818                  weak_ptr_factory_.GetWeakPtr(),
819                  base::Passed(applying.Pass()))));
820
821   wallpaper_downloader_->Start();
822 }
823
824 void ServicesCustomizationDocument::CheckAndApplyWallpaper() {
825   if (wallpaper_downloader_.get()) {
826     VLOG(1) << "CheckAndApplyWallpaper(): download has already started.";
827     return;
828   }
829   scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying(
830       new ServicesCustomizationDocument::ApplyingTask(this));
831
832   GURL wallpaper_url;
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
841               << "'. Ignored.";
842     }
843     applying->Finished(true);
844     return;
845   }
846
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() << "'.";
853     }
854     applying->Finished(false);
855     return;
856   }
857
858   scoped_ptr<bool> exists(new bool(false));
859
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.";
872   }
873 }
874
875 void ServicesCustomizationDocument::OnCheckedWallpaperCacheExists(
876     scoped_ptr<bool> exists,
877     scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying) {
878   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
879   DCHECK(exists);
880   DCHECK(applying);
881
882   ApplyWallpaper(*exists, applying.Pass());
883 }
884
885 void ServicesCustomizationDocument::ApplyWallpaper(
886     bool default_wallpaper_file_exists,
887     scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying) {
888   GURL wallpaper_url;
889   const bool wallpaper_url_present = GetDefaultWallpaperUrl(&wallpaper_url);
890
891   PrefService* pref_service = g_browser_process->local_state();
892
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
902                       ? " Ignored."
903                       : " Will refetch.");
904     } else {
905       VLOG(1) << "ServicesCustomizationDocument::ApplyWallpaper() : "
906               << "No wallpaper URL attribute in customization document, "
907               << "but current value is non-empty: '" << current_url
908               << "'. Ignored.";
909     }
910   }
911   if (!wallpaper_url_present) {
912     applying->Finished(true);
913     return;
914   }
915
916   DCHECK(wallpaper_url.is_valid());
917
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) {
921     VLOG(1)
922         << "ServicesCustomizationDocument::ApplyWallpaper() : reuse existing";
923     OnOEMWallpaperDownloaded(applying.Pass(), true, GURL(current_url));
924   } else {
925     VLOG(1)
926         << "ServicesCustomizationDocument::ApplyWallpaper() : start download";
927     StartOEMWallpaperDownload(wallpaper_url, applying.Pass());
928   }
929 }
930
931 void ServicesCustomizationDocument::OnOEMWallpaperDownloaded(
932     scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying,
933     bool success,
934     const GURL& wallpaper_url) {
935   if (success) {
936     DCHECK(wallpaper_url.is_valid());
937
938     VLOG(1) << "Setting default wallpaper to '"
939             << GetCustomizedWallpaperDownloadedFileName().value() << "' ('"
940             << wallpaper_url.spec() << "')";
941     WallpaperManager::Get()->SetCustomizedDefaultWallpaper(
942         wallpaper_url,
943         GetCustomizedWallpaperDownloadedFileName(),
944         GetCustomizedWallpaperCacheDir());
945   }
946   wallpaper_downloader_.reset();
947   applying->Finished(success);
948 }
949
950 void ServicesCustomizationDocument::ApplyingTaskStarted() {
951   ++apply_tasks_started_;
952 }
953
954 void ServicesCustomizationDocument::ApplyingTaskFinished(bool success) {
955   DCHECK_GT(apply_tasks_started_, apply_tasks_finished_);
956   ++apply_tasks_finished_;
957
958   apply_tasks_success_ += success;
959
960   if (apply_tasks_started_ != apply_tasks_finished_)
961     return;
962
963   if (apply_tasks_success_ == apply_tasks_finished_)
964     SetApplied(true);
965 }
966
967 }  // namespace chromeos