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