260531a7670847bfffebaeaa4f471a9df79f76cc
[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 "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/file_util.h"
10 #include "base/files/file_path.h"
11 #include "base/json/json_reader.h"
12 #include "base/logging.h"
13 #include "base/memory/weak_ptr.h"
14 #include "base/metrics/histogram.h"
15 #include "base/prefs/pref_registry_simple.h"
16 #include "base/prefs/pref_service.h"
17 #include "base/strings/string_split.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/time/time.h"
22 #include "chrome/browser/browser_process.h"
23 #include "chrome/browser/chromeos/login/wizard_controller.h"
24 #include "chrome/browser/chromeos/net/delay_network_call.h"
25 #include "chrome/browser/extensions/external_loader.h"
26 #include "chrome/browser/extensions/external_provider_impl.h"
27 #include "chrome/browser/profiles/profile.h"
28 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
29 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
30 #include "chrome/common/extensions/extension_constants.h"
31 #include "chromeos/network/network_state.h"
32 #include "chromeos/network/network_state_handler.h"
33 #include "chromeos/system/statistics_provider.h"
34 #include "components/user_prefs/pref_registry_syncable.h"
35 #include "content/public/browser/browser_thread.h"
36 #include "net/base/load_flags.h"
37 #include "net/http/http_response_headers.h"
38 #include "net/http/http_status_code.h"
39 #include "net/url_request/url_fetcher.h"
40
41 using content::BrowserThread;
42
43 namespace chromeos {
44 namespace {
45
46   // Manifest attributes names.
47 const char kVersionAttr[] = "version";
48 const char kDefaultAttr[] = "default";
49 const char kInitialLocaleAttr[] = "initial_locale";
50 const char kInitialTimezoneAttr[] = "initial_timezone";
51 const char kKeyboardLayoutAttr[] = "keyboard_layout";
52 const char kHwidMapAttr[] = "hwid_map";
53 const char kHwidMaskAttr[] = "hwid_mask";
54 const char kSetupContentAttr[] = "setup_content";
55 const char kEulaPageAttr[] = "eula_page";
56 const char kDefaultWallpaperAttr[] = "default_wallpaper";
57 const char kDefaultAppsAttr[] = "default_apps";
58 const char kLocalizedContent[] = "localized_content";
59 const char kDefaultAppsFolderName[] = "default_apps_folder_name";
60
61 const char kAcceptedManifestVersion[] = "1.0";
62
63 // Path to OEM partner startup customization manifest.
64 const char kStartupCustomizationManifestPath[] =
65     "/opt/oem/etc/startup_manifest.json";
66
67 // Name of local state option that tracks if services customization has been
68 // applied.
69 const char kServicesCustomizationAppliedPref[] = "ServicesCustomizationApplied";
70
71 // Maximum number of retries to fetch file if network is not available.
72 const int kMaxFetchRetries = 3;
73
74 // Delay between file fetch retries if network is not available.
75 const int kRetriesDelayInSec = 2;
76
77 // Name of profile option that tracks cached version of service customization.
78 const char kServicesCustomizationKey[] = "customization.manifest_cache";
79
80 // Empty customization document that doesn't customize anything.
81 const char kEmptyServicesCustomizationManifest[] = "{ \"version\": \"1.0\" }";
82
83 // Global overrider for ServicesCustomizationDocument for tests.
84 ServicesCustomizationDocument* g_test_services_customization_document = NULL;
85
86 // Services customization document load results reported via the
87 // "ServicesCustomization.LoadResult" histogram.
88 // It is append-only enum due to use in a histogram!
89 enum HistogramServicesCustomizationLoadResult {
90   HISTOGRAM_LOAD_RESULT_SUCCESS = 0,
91   HISTOGRAM_LOAD_RESULT_FILE_NOT_FOUND = 1,
92   HISTOGRAM_LOAD_RESULT_PARSING_ERROR = 2,
93   HISTOGRAM_LOAD_RESULT_RETRIES_FAIL = 3,
94   HISTOGRAM_LOAD_RESULT_MAX_VALUE = 4
95 };
96
97 void LogManifestLoadResult(HistogramServicesCustomizationLoadResult result) {
98   UMA_HISTOGRAM_ENUMERATION("ServicesCustomization.LoadResult",
99                             result,
100                             HISTOGRAM_LOAD_RESULT_MAX_VALUE);
101 }
102
103 std::string GetLocaleSpecificStringImpl(
104     const base::DictionaryValue* root,
105     const std::string& locale,
106     const std::string& dictionary_name,
107     const std::string& entry_name) {
108   const base::DictionaryValue* dictionary_content = NULL;
109   if (!root || !root->GetDictionary(dictionary_name, &dictionary_content))
110     return std::string();
111
112   const base::DictionaryValue* locale_dictionary = NULL;
113   if (dictionary_content->GetDictionary(locale, &locale_dictionary)) {
114     std::string result;
115     if (locale_dictionary->GetString(entry_name, &result))
116       return result;
117   }
118
119   const base::DictionaryValue* default_dictionary = NULL;
120   if (dictionary_content->GetDictionary(kDefaultAttr, &default_dictionary)) {
121     std::string result;
122     if (default_dictionary->GetString(entry_name, &result))
123       return result;
124   }
125
126   return std::string();
127 }
128
129 }  // anonymous namespace
130
131 // Template URL where to fetch OEM services customization manifest from.
132 const char ServicesCustomizationDocument::kManifestUrl[] =
133     "https://ssl.gstatic.com/chrome/chromeos-customization/%s.json";
134
135 // A custom extensions::ExternalLoader that the ServicesCustomizationDocument
136 // creates and uses to publish OEM default apps to the extensions system.
137 class ServicesCustomizationExternalLoader
138     : public extensions::ExternalLoader,
139       public base::SupportsWeakPtr<ServicesCustomizationExternalLoader> {
140  public:
141   explicit ServicesCustomizationExternalLoader(Profile* profile)
142       : is_apps_set_(false), profile_(profile) {}
143
144   Profile* profile() { return profile_; }
145
146   // Used by the ServicesCustomizationDocument to update the current apps.
147   void SetCurrentApps(scoped_ptr<base::DictionaryValue> prefs) {
148     apps_.Swap(prefs.get());
149     is_apps_set_ = true;
150     StartLoading();
151   }
152
153   // Implementation of extensions::ExternalLoader:
154   virtual void StartLoading() OVERRIDE {
155     if (!is_apps_set_) {
156       ServicesCustomizationDocument::GetInstance()->StartFetching();
157       // No return here to call LoadFinished with empty list initially.
158       // When manifest is fetched, it will be called again with real list.
159       // It is safe to return empty list because this provider didn't install
160       // any app yet so no app can be removed due to returning empty list.
161     }
162
163     prefs_.reset(apps_.DeepCopy());
164     VLOG(1) << "ServicesCustomization extension loader publishing "
165             << apps_.size() << " apps.";
166     LoadFinished();
167   }
168
169  protected:
170   virtual ~ServicesCustomizationExternalLoader() {}
171
172  private:
173   bool is_apps_set_;
174   base::DictionaryValue apps_;
175   Profile* profile_;
176
177   DISALLOW_COPY_AND_ASSIGN(ServicesCustomizationExternalLoader);
178 };
179
180 // CustomizationDocument implementation. ---------------------------------------
181
182 CustomizationDocument::CustomizationDocument(
183     const std::string& accepted_version)
184     : accepted_version_(accepted_version) {}
185
186 CustomizationDocument::~CustomizationDocument() {}
187
188 bool CustomizationDocument::LoadManifestFromFile(
189     const base::FilePath& manifest_path) {
190   std::string manifest;
191   if (!base::ReadFileToString(manifest_path, &manifest))
192     return false;
193   return LoadManifestFromString(manifest);
194 }
195
196 bool CustomizationDocument::LoadManifestFromString(
197     const std::string& manifest) {
198   int error_code = 0;
199   std::string error;
200   scoped_ptr<base::Value> root(base::JSONReader::ReadAndReturnError(manifest,
201       base::JSON_ALLOW_TRAILING_COMMAS, &error_code, &error));
202   if (error_code != base::JSONReader::JSON_NO_ERROR)
203     LOG(ERROR) << error;
204   DCHECK(root.get() != NULL);
205   if (root.get() == NULL)
206     return false;
207   DCHECK(root->GetType() == base::Value::TYPE_DICTIONARY);
208   if (root->GetType() == base::Value::TYPE_DICTIONARY) {
209     root_.reset(static_cast<base::DictionaryValue*>(root.release()));
210     std::string result;
211     if (root_->GetString(kVersionAttr, &result) &&
212         result == accepted_version_)
213       return true;
214
215     LOG(ERROR) << "Wrong customization manifest version";
216     root_.reset(NULL);
217   }
218   return false;
219 }
220
221 std::string CustomizationDocument::GetLocaleSpecificString(
222     const std::string& locale,
223     const std::string& dictionary_name,
224     const std::string& entry_name) const {
225   return GetLocaleSpecificStringImpl(
226       root_.get(), locale, dictionary_name, entry_name);
227 }
228
229 // StartupCustomizationDocument implementation. --------------------------------
230
231 StartupCustomizationDocument::StartupCustomizationDocument()
232     : CustomizationDocument(kAcceptedManifestVersion) {
233   {
234     // Loading manifest causes us to do blocking IO on UI thread.
235     // Temporarily allow it until we fix http://crosbug.com/11103
236     base::ThreadRestrictions::ScopedAllowIO allow_io;
237     LoadManifestFromFile(base::FilePath(kStartupCustomizationManifestPath));
238   }
239   Init(system::StatisticsProvider::GetInstance());
240 }
241
242 StartupCustomizationDocument::StartupCustomizationDocument(
243     system::StatisticsProvider* statistics_provider,
244     const std::string& manifest)
245     : CustomizationDocument(kAcceptedManifestVersion) {
246   LoadManifestFromString(manifest);
247   Init(statistics_provider);
248 }
249
250 StartupCustomizationDocument::~StartupCustomizationDocument() {}
251
252 StartupCustomizationDocument* StartupCustomizationDocument::GetInstance() {
253   return Singleton<StartupCustomizationDocument,
254       DefaultSingletonTraits<StartupCustomizationDocument> >::get();
255 }
256
257 void StartupCustomizationDocument::Init(
258     system::StatisticsProvider* statistics_provider) {
259   if (IsReady()) {
260     root_->GetString(kInitialLocaleAttr, &initial_locale_);
261     root_->GetString(kInitialTimezoneAttr, &initial_timezone_);
262     root_->GetString(kKeyboardLayoutAttr, &keyboard_layout_);
263
264     std::string hwid;
265     if (statistics_provider->GetMachineStatistic(
266             system::kHardwareClassKey, &hwid)) {
267       base::ListValue* hwid_list = NULL;
268       if (root_->GetList(kHwidMapAttr, &hwid_list)) {
269         for (size_t i = 0; i < hwid_list->GetSize(); ++i) {
270           base::DictionaryValue* hwid_dictionary = NULL;
271           std::string hwid_mask;
272           if (hwid_list->GetDictionary(i, &hwid_dictionary) &&
273               hwid_dictionary->GetString(kHwidMaskAttr, &hwid_mask)) {
274             if (MatchPattern(hwid, hwid_mask)) {
275               // If HWID for this machine matches some mask, use HWID specific
276               // settings.
277               std::string result;
278               if (hwid_dictionary->GetString(kInitialLocaleAttr, &result))
279                 initial_locale_ = result;
280
281               if (hwid_dictionary->GetString(kInitialTimezoneAttr, &result))
282                 initial_timezone_ = result;
283
284               if (hwid_dictionary->GetString(kKeyboardLayoutAttr, &result))
285                 keyboard_layout_ = result;
286             }
287             // Don't break here to allow other entires to be applied if match.
288           } else {
289             LOG(ERROR) << "Syntax error in customization manifest";
290           }
291         }
292       }
293     } else {
294       LOG(ERROR) << "HWID is missing in machine statistics";
295     }
296   }
297
298   // If manifest doesn't exist still apply values from VPD.
299   statistics_provider->GetMachineStatistic(kInitialLocaleAttr,
300                                            &initial_locale_);
301   statistics_provider->GetMachineStatistic(kInitialTimezoneAttr,
302                                            &initial_timezone_);
303   statistics_provider->GetMachineStatistic(kKeyboardLayoutAttr,
304                                            &keyboard_layout_);
305   configured_locales_.resize(0);
306   base::SplitString(initial_locale_, ',', &configured_locales_);
307   // Let's always have configured_locales_.front() a valid entry.
308   if (configured_locales_.size() == 0)
309     configured_locales_.push_back(std::string());
310 }
311
312 const std::vector<std::string>&
313 StartupCustomizationDocument::configured_locales() const {
314   return configured_locales_;
315 }
316
317 const std::string& StartupCustomizationDocument::initial_locale_default()
318     const {
319   DCHECK(configured_locales_.size() > 0);
320   return configured_locales_.front();
321 }
322
323 std::string StartupCustomizationDocument::GetEULAPage(
324     const std::string& locale) const {
325   return GetLocaleSpecificString(locale, kSetupContentAttr, kEulaPageAttr);
326 }
327
328 // ServicesCustomizationDocument implementation. -------------------------------
329
330 ServicesCustomizationDocument::ServicesCustomizationDocument()
331     : CustomizationDocument(kAcceptedManifestVersion),
332       num_retries_(0),
333       fetch_started_(false),
334       network_delay_(base::TimeDelta::FromMilliseconds(
335           kDefaultNetworkRetryDelayMS)),
336       weak_ptr_factory_(this) {
337 }
338
339 ServicesCustomizationDocument::ServicesCustomizationDocument(
340     const std::string& manifest)
341     : CustomizationDocument(kAcceptedManifestVersion),
342       network_delay_(base::TimeDelta::FromMilliseconds(
343           kDefaultNetworkRetryDelayMS)),
344       weak_ptr_factory_(this) {
345   LoadManifestFromString(manifest);
346 }
347
348 ServicesCustomizationDocument::~ServicesCustomizationDocument() {}
349
350 // static
351 ServicesCustomizationDocument* ServicesCustomizationDocument::GetInstance() {
352   if (g_test_services_customization_document)
353     return g_test_services_customization_document;
354
355   return Singleton<ServicesCustomizationDocument,
356       DefaultSingletonTraits<ServicesCustomizationDocument> >::get();
357 }
358
359 // static
360 void ServicesCustomizationDocument::RegisterPrefs(
361     PrefRegistrySimple* registry) {
362   registry->RegisterBooleanPref(kServicesCustomizationAppliedPref, false);
363 }
364
365 // static
366 void ServicesCustomizationDocument::RegisterProfilePrefs(
367     user_prefs::PrefRegistrySyncable* registry) {
368   registry->RegisterDictionaryPref(
369       kServicesCustomizationKey,
370       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
371 }
372
373 // static
374 bool ServicesCustomizationDocument::WasOOBECustomizationApplied() {
375   PrefService* prefs = g_browser_process->local_state();
376   return prefs->GetBoolean(kServicesCustomizationAppliedPref);
377 }
378
379 // static
380 void ServicesCustomizationDocument::SetApplied(bool val) {
381   PrefService* prefs = g_browser_process->local_state();
382   prefs->SetBoolean(kServicesCustomizationAppliedPref, val);
383 }
384
385 void ServicesCustomizationDocument::StartFetching() {
386   if (IsReady() || fetch_started_)
387     return;
388
389   if (!url_.is_valid()) {
390     std::string customization_id;
391     chromeos::system::StatisticsProvider* provider =
392         chromeos::system::StatisticsProvider::GetInstance();
393     if (provider->GetMachineStatistic(system::kCustomizationIdKey,
394                                       &customization_id) &&
395         !customization_id.empty()) {
396       url_ = GURL(base::StringPrintf(
397           kManifestUrl, StringToLowerASCII(customization_id).c_str()));
398     }
399   }
400
401   if (url_.is_valid()) {
402     fetch_started_ = true;
403     if (url_.SchemeIsFile()) {
404       BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
405           base::Bind(&ServicesCustomizationDocument::ReadFileInBackground,
406                      weak_ptr_factory_.GetWeakPtr(),
407                      base::FilePath(url_.path())));
408     } else {
409       StartFileFetch();
410     }
411   }
412 }
413
414 // static
415 void ServicesCustomizationDocument::ReadFileInBackground(
416     base::WeakPtr<ServicesCustomizationDocument> self,
417     const base::FilePath& file) {
418   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
419
420   std::string manifest;
421   if (!base::ReadFileToString(file, &manifest)) {
422     manifest.clear();
423     LOG(ERROR) << "Failed to load services customization manifest from: "
424                << file.value();
425   }
426
427   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
428       base::Bind(&ServicesCustomizationDocument::OnManifesteRead,
429                  self,
430                  manifest));
431 }
432
433 void ServicesCustomizationDocument::OnManifesteRead(
434     const std::string& manifest) {
435   if (!manifest.empty())
436     LoadManifestFromString(manifest);
437
438   fetch_started_ = false;
439 }
440
441 void ServicesCustomizationDocument::StartFileFetch() {
442   DelayNetworkCall(base::Bind(&ServicesCustomizationDocument::DoStartFileFetch,
443                               weak_ptr_factory_.GetWeakPtr()),
444       network_delay_);
445 }
446
447 void ServicesCustomizationDocument::DoStartFileFetch() {
448   url_fetcher_.reset(net::URLFetcher::Create(
449       url_, net::URLFetcher::GET, this));
450   url_fetcher_->SetRequestContext(g_browser_process->system_request_context());
451   url_fetcher_->AddExtraRequestHeader("Accept: application/json");
452   url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
453                              net::LOAD_DO_NOT_SAVE_COOKIES |
454                              net::LOAD_DISABLE_CACHE |
455                              net::LOAD_DO_NOT_SEND_AUTH_DATA);
456   url_fetcher_->Start();
457 }
458
459 bool ServicesCustomizationDocument::LoadManifestFromString(
460     const std::string& manifest) {
461   if (CustomizationDocument::LoadManifestFromString(manifest)) {
462     LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_SUCCESS);
463     OnManifestLoaded();
464     return true;
465   }
466
467   LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_PARSING_ERROR);
468   return false;
469 }
470
471 void ServicesCustomizationDocument::OnManifestLoaded() {
472   if (!ServicesCustomizationDocument::WasOOBECustomizationApplied())
473     ApplyOOBECustomization();
474
475   scoped_ptr<base::DictionaryValue> prefs =
476       GetDefaultAppsInProviderFormat(*root_);
477   for (ExternalLoaders::iterator it = external_loaders_.begin();
478        it != external_loaders_.end(); ++it) {
479     if (*it) {
480       UpdateCachedManifest((*it)->profile());
481       (*it)->SetCurrentApps(
482           scoped_ptr<base::DictionaryValue>(prefs->DeepCopy()));
483       SetOemFolderName((*it)->profile(), *root_);
484     }
485   }
486 }
487
488 void ServicesCustomizationDocument::OnURLFetchComplete(
489     const net::URLFetcher* source) {
490   std::string mime_type;
491   std::string data;
492   if (source->GetStatus().is_success() &&
493       source->GetResponseCode() == net::HTTP_OK &&
494       source->GetResponseHeaders()->GetMimeType(&mime_type) &&
495       mime_type == "application/json" &&
496       source->GetResponseAsString(&data)) {
497     LoadManifestFromString(data);
498   } else if (source->GetResponseCode() == net::HTTP_NOT_FOUND) {
499     LOG(ERROR) << "Customization manifest is missing on server: "
500                << source->GetURL().spec();
501     OnCustomizationNotFound();
502   } else {
503     if (num_retries_ < kMaxFetchRetries) {
504       num_retries_++;
505       content::BrowserThread::PostDelayedTask(
506           content::BrowserThread::UI,
507           FROM_HERE,
508           base::Bind(&ServicesCustomizationDocument::StartFileFetch,
509                      weak_ptr_factory_.GetWeakPtr()),
510           base::TimeDelta::FromSeconds(kRetriesDelayInSec));
511       return;
512     }
513     // This doesn't stop fetching manifest on next restart.
514     LOG(ERROR) << "URL fetch for services customization failed:"
515                << " response code = " << source->GetResponseCode()
516                << " URL = " << source->GetURL().spec();
517
518     LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_RETRIES_FAIL);
519   }
520   fetch_started_ = false;
521 }
522
523 bool ServicesCustomizationDocument::ApplyOOBECustomization() {
524   // TODO(dpolukhin): apply default wallpaper, crbug.com/348136.
525   SetApplied(true);
526   return true;
527 }
528
529 GURL ServicesCustomizationDocument::GetDefaultWallpaperUrl() const {
530   if (!IsReady())
531     return GURL();
532
533   std::string url;
534   root_->GetString(kDefaultWallpaperAttr, &url);
535   return GURL(url);
536 }
537
538 bool ServicesCustomizationDocument::GetDefaultApps(
539     std::vector<std::string>* ids) const {
540   ids->clear();
541   if (!IsReady())
542     return false;
543
544   base::ListValue* apps_list = NULL;
545   if (!root_->GetList(kDefaultAppsAttr, &apps_list))
546     return false;
547
548   for (size_t i = 0; i < apps_list->GetSize(); ++i) {
549     std::string app_id;
550     if (apps_list->GetString(i, &app_id)) {
551       ids->push_back(app_id);
552     } else {
553       LOG(ERROR) << "Wrong format of default application list";
554       return false;
555     }
556   }
557
558   return true;
559 }
560
561 std::string ServicesCustomizationDocument::GetOemAppsFolderName(
562     const std::string& locale) const {
563   if (!IsReady())
564     return std::string();
565
566   return GetOemAppsFolderNameImpl(locale, *root_);
567 }
568
569 scoped_ptr<base::DictionaryValue>
570 ServicesCustomizationDocument::GetDefaultAppsInProviderFormat(
571     const base::DictionaryValue& root) {
572   scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
573   const base::ListValue* apps_list = NULL;
574   if (root.GetList(kDefaultAppsAttr, &apps_list)) {
575     for (size_t i = 0; i < apps_list->GetSize(); ++i) {
576       std::string app_id;
577       if (apps_list->GetString(i, &app_id)) {
578         base::DictionaryValue* entry = new base::DictionaryValue;
579         entry->SetString(extensions::ExternalProviderImpl::kExternalUpdateUrl,
580                          extension_urls::GetWebstoreUpdateUrl().spec());
581         prefs->Set(app_id, entry);
582       } else {
583         LOG(ERROR) << "Wrong format of default application list";
584         prefs->Clear();
585         break;
586       }
587     }
588   }
589
590   return prefs.Pass();
591 }
592
593 void ServicesCustomizationDocument::UpdateCachedManifest(Profile* profile) {
594   profile->GetPrefs()->Set(kServicesCustomizationKey, *root_);
595 }
596
597 extensions::ExternalLoader* ServicesCustomizationDocument::CreateExternalLoader(
598     Profile* profile) {
599   ServicesCustomizationExternalLoader* loader =
600       new ServicesCustomizationExternalLoader(profile);
601   external_loaders_.push_back(loader->AsWeakPtr());
602
603   if (IsReady()) {
604     UpdateCachedManifest(profile);
605     loader->SetCurrentApps(GetDefaultAppsInProviderFormat(*root_));
606     SetOemFolderName(profile, *root_);
607   } else {
608     const base::DictionaryValue* root =
609         profile->GetPrefs()->GetDictionary(kServicesCustomizationKey);
610     std::string version;
611     if (root && root->GetString(kVersionAttr, &version)) {
612       // If version exists, profile has cached version of customization.
613       loader->SetCurrentApps(GetDefaultAppsInProviderFormat(*root));
614       SetOemFolderName(profile, *root);
615     } else {
616       // StartFetching will be called from ServicesCustomizationExternalLoader
617       // when StartLoading is called. We can't initiate manifest fetch here
618       // because caller may never call StartLoading for the provider.
619     }
620   }
621
622   return loader;
623 }
624
625 void ServicesCustomizationDocument::OnCustomizationNotFound() {
626   LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_FILE_NOT_FOUND);
627   LoadManifestFromString(kEmptyServicesCustomizationManifest);
628 }
629
630 void ServicesCustomizationDocument::SetOemFolderName(
631     Profile* profile,
632     const base::DictionaryValue& root) {
633   std::string locale = g_browser_process->GetApplicationLocale();
634   std::string name = GetOemAppsFolderNameImpl(locale, root);
635   if (!name.empty()) {
636     app_list::AppListSyncableService* service =
637         app_list::AppListSyncableServiceFactory::GetForProfile(profile);
638     if (!service) {
639       LOG(WARNING) << "AppListSyncableService is not ready for setting OEM "
640                       "folder name";
641       return;
642     }
643     service->SetOemFolderName(name);
644   }
645 }
646
647 std::string ServicesCustomizationDocument::GetOemAppsFolderNameImpl(
648     const std::string& locale,
649     const base::DictionaryValue& root) const {
650   return GetLocaleSpecificStringImpl(
651       &root, locale, kLocalizedContent, kDefaultAppsFolderName);
652 }
653
654 // static
655 void ServicesCustomizationDocument::InitializeForTesting() {
656   g_test_services_customization_document = new ServicesCustomizationDocument;
657   g_test_services_customization_document->network_delay_ = base::TimeDelta();
658 }
659
660 // static
661 void ServicesCustomizationDocument::ShutdownForTesting() {
662   delete g_test_services_customization_document;
663   g_test_services_customization_document = NULL;
664 }
665
666 }  // namespace chromeos