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