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