Only grant permissions to new extensions from sync if they have the expected version
[chromium-blink-merge.git] / chrome / browser / chromeos / customization / customization_document.cc
blob9363a57f4d4783d617496a1c2e17a1cd54915611
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.
5 #include "chrome/browser/chromeos/customization/customization_document.h"
7 #include <algorithm>
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/i18n/rtl.h"
14 #include "base/json/json_reader.h"
15 #include "base/logging.h"
16 #include "base/memory/weak_ptr.h"
17 #include "base/metrics/histogram.h"
18 #include "base/path_service.h"
19 #include "base/prefs/pref_registry_simple.h"
20 #include "base/prefs/pref_service.h"
21 #include "base/strings/pattern.h"
22 #include "base/strings/string_split.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/stringprintf.h"
25 #include "base/strings/utf_string_conversions.h"
26 #include "base/time/time.h"
27 #include "chrome/browser/browser_process.h"
28 #include "chrome/browser/chromeos/customization/customization_wallpaper_downloader.h"
29 #include "chrome/browser/chromeos/extensions/default_app_order.h"
30 #include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
31 #include "chrome/browser/chromeos/login/wizard_controller.h"
32 #include "chrome/browser/chromeos/net/delay_network_call.h"
33 #include "chrome/browser/extensions/external_loader.h"
34 #include "chrome/browser/extensions/external_provider_impl.h"
35 #include "chrome/browser/profiles/profile.h"
36 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
37 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
38 #include "chrome/common/chrome_paths.h"
39 #include "chrome/common/pref_names.h"
40 #include "chromeos/system/statistics_provider.h"
41 #include "components/pref_registry/pref_registry_syncable.h"
42 #include "content/public/browser/browser_thread.h"
43 #include "extensions/common/extension_urls.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"
49 using content::BrowserThread;
51 namespace chromeos {
52 namespace {
54 // Manifest attributes names.
55 const char kVersionAttr[] = "version";
56 const char kDefaultAttr[] = "default";
57 const char kInitialLocaleAttr[] = "initial_locale";
58 const char kInitialTimezoneAttr[] = "initial_timezone";
59 const char kKeyboardLayoutAttr[] = "keyboard_layout";
60 const char kHwidMapAttr[] = "hwid_map";
61 const char kHwidMaskAttr[] = "hwid_mask";
62 const char kSetupContentAttr[] = "setup_content";
63 const char kEulaPageAttr[] = "eula_page";
64 const char kDefaultWallpaperAttr[] = "default_wallpaper";
65 const char kDefaultAppsAttr[] = "default_apps";
66 const char kLocalizedContent[] = "localized_content";
67 const char kDefaultAppsFolderName[] = "default_apps_folder_name";
68 const char kIdAttr[] = "id";
70 const char kAcceptedManifestVersion[] = "1.0";
72 // Path to OEM partner startup customization manifest.
73 const char kStartupCustomizationManifestPath[] =
74 "/opt/oem/etc/startup_manifest.json";
76 // This is subdirectory relative to PathService(DIR_CHROMEOS_CUSTOM_WALLPAPERS),
77 // where downloaded (and resized) wallpaper is stored.
78 const char kCustomizationDefaultWallpaperDir[] = "customization";
80 // The original downloaded image file is stored under this name.
81 const char kCustomizationDefaultWallpaperDownloadedFile[] =
82 "default_downloaded_wallpaper.bin";
84 // Name of local state option that tracks if services customization has been
85 // applied.
86 const char kServicesCustomizationAppliedPref[] = "ServicesCustomizationApplied";
88 // Maximum number of retries to fetch file if network is not available.
89 const int kMaxFetchRetries = 3;
91 // Delay between file fetch retries if network is not available.
92 const int kRetriesDelayInSec = 2;
94 // Name of profile option that tracks cached version of service customization.
95 const char kServicesCustomizationKey[] = "customization.manifest_cache";
97 // Empty customization document that doesn't customize anything.
98 const char kEmptyServicesCustomizationManifest[] = "{ \"version\": \"1.0\" }";
100 // Global overrider for ServicesCustomizationDocument for tests.
101 ServicesCustomizationDocument* g_test_services_customization_document = NULL;
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
114 void LogManifestLoadResult(HistogramServicesCustomizationLoadResult result) {
115 UMA_HISTOGRAM_ENUMERATION("ServicesCustomization.LoadResult",
116 result,
117 HISTOGRAM_LOAD_RESULT_MAX_VALUE);
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();
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;
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;
143 return std::string();
146 void CheckWallpaperCacheExists(const base::FilePath& path, bool* exists) {
147 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
148 DCHECK(exists);
149 *exists = base::PathExists(path);
152 } // anonymous namespace
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";
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) {}
167 Profile* profile() { return profile_; }
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();
176 // Implementation of extensions::ExternalLoader:
177 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;
190 prefs_.reset(apps_.DeepCopy());
191 VLOG(1) << "ServicesCustomization extension loader publishing "
192 << apps_.size() << " apps.";
193 LoadFinished();
196 protected:
197 ~ServicesCustomizationExternalLoader() override {}
199 private:
200 bool is_apps_set_;
201 base::DictionaryValue apps_;
202 Profile* profile_;
204 DISALLOW_COPY_AND_ASSIGN(ServicesCustomizationExternalLoader);
207 // CustomizationDocument implementation. ---------------------------------------
209 CustomizationDocument::CustomizationDocument(
210 const std::string& accepted_version)
211 : accepted_version_(accepted_version) {}
213 CustomizationDocument::~CustomizationDocument() {}
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);
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(
228 manifest, 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;
242 LOG(ERROR) << "Wrong customization manifest version";
243 root_.reset(NULL);
245 return false;
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);
256 // StartupCustomizationDocument implementation. --------------------------------
258 StartupCustomizationDocument::StartupCustomizationDocument()
259 : CustomizationDocument(kAcceptedManifestVersion) {
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));
266 Init(system::StatisticsProvider::GetInstance());
269 StartupCustomizationDocument::StartupCustomizationDocument(
270 system::StatisticsProvider* statistics_provider,
271 const std::string& manifest)
272 : CustomizationDocument(kAcceptedManifestVersion) {
273 LoadManifestFromString(manifest);
274 Init(statistics_provider);
277 StartupCustomizationDocument::~StartupCustomizationDocument() {}
279 StartupCustomizationDocument* StartupCustomizationDocument::GetInstance() {
280 return base::Singleton<
281 StartupCustomizationDocument,
282 base::DefaultSingletonTraits<StartupCustomizationDocument>>::get();
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_);
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 (base::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;
309 if (hwid_dictionary->GetString(kInitialTimezoneAttr, &result))
310 initial_timezone_ = result;
312 if (hwid_dictionary->GetString(kKeyboardLayoutAttr, &result))
313 keyboard_layout_ = result;
315 // Don't break here to allow other entires to be applied if match.
316 } else {
317 LOG(ERROR) << "Syntax error in customization manifest";
321 } else {
322 LOG(ERROR) << "HWID is missing in machine statistics";
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_ = base::SplitString(
334 initial_locale_, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
336 // Convert ICU locale to chrome ("en_US" to "en-US", etc.).
337 std::for_each(configured_locales_.begin(), configured_locales_.end(),
338 base::i18n::GetCanonicalLocale);
340 // Let's always have configured_locales_.front() a valid entry.
341 if (configured_locales_.size() == 0)
342 configured_locales_.push_back(std::string());
345 const std::vector<std::string>&
346 StartupCustomizationDocument::configured_locales() const {
347 return configured_locales_;
350 const std::string& StartupCustomizationDocument::initial_locale_default()
351 const {
352 DCHECK(configured_locales_.size() > 0);
353 return configured_locales_.front();
356 std::string StartupCustomizationDocument::GetEULAPage(
357 const std::string& locale) const {
358 return GetLocaleSpecificString(locale, kSetupContentAttr, kEulaPageAttr);
361 // ServicesCustomizationDocument implementation. -------------------------------
363 class ServicesCustomizationDocument::ApplyingTask {
364 public:
365 // Registers in ServicesCustomizationDocument;
366 explicit ApplyingTask(ServicesCustomizationDocument* document);
368 // Do not automatically deregister as we might be called on invalid thread.
369 ~ApplyingTask();
371 // Mark task finished and check for customization applied.
372 void Finished(bool success);
374 private:
375 ServicesCustomizationDocument* document_;
377 // This is error-checking flag to prevent destroying unfinished task
378 // or double finish.
379 bool engaged_;
382 ServicesCustomizationDocument::ApplyingTask::ApplyingTask(
383 ServicesCustomizationDocument* document)
384 : document_(document), engaged_(true) {
385 document->ApplyingTaskStarted();
388 ServicesCustomizationDocument::ApplyingTask::~ApplyingTask() {
389 DCHECK(!engaged_);
392 void ServicesCustomizationDocument::ApplyingTask::Finished(bool success) {
393 DCHECK(engaged_);
394 if (engaged_) {
395 engaged_ = false;
396 document_->ApplyingTaskFinished(success);
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) {
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);
424 ServicesCustomizationDocument::~ServicesCustomizationDocument() {}
426 // static
427 ServicesCustomizationDocument* ServicesCustomizationDocument::GetInstance() {
428 if (g_test_services_customization_document)
429 return g_test_services_customization_document;
431 return base::Singleton<
432 ServicesCustomizationDocument,
433 base::DefaultSingletonTraits<ServicesCustomizationDocument>>::get();
436 // static
437 void ServicesCustomizationDocument::RegisterPrefs(
438 PrefRegistrySimple* registry) {
439 registry->RegisterBooleanPref(kServicesCustomizationAppliedPref, false);
440 registry->RegisterStringPref(prefs::kCustomizationDefaultWallpaperURL,
441 std::string());
444 // static
445 void ServicesCustomizationDocument::RegisterProfilePrefs(
446 user_prefs::PrefRegistrySyncable* registry) {
447 registry->RegisterDictionaryPref(kServicesCustomizationKey);
450 // static
451 bool ServicesCustomizationDocument::WasOOBECustomizationApplied() {
452 PrefService* prefs = g_browser_process->local_state();
453 // prefs can be NULL in some tests.
454 if (prefs)
455 return prefs->GetBoolean(kServicesCustomizationAppliedPref);
456 else
457 return false;
460 // static
461 void ServicesCustomizationDocument::SetApplied(bool val) {
462 PrefService* prefs = g_browser_process->local_state();
463 // prefs can be NULL in some tests.
464 if (prefs)
465 prefs->SetBoolean(kServicesCustomizationAppliedPref, val);
468 // static
469 base::FilePath ServicesCustomizationDocument::GetCustomizedWallpaperCacheDir() {
470 base::FilePath custom_wallpaper_dir;
471 if (!PathService::Get(chrome::DIR_CHROMEOS_CUSTOM_WALLPAPERS,
472 &custom_wallpaper_dir)) {
473 LOG(DFATAL) << "Unable to get custom wallpaper dir.";
474 return base::FilePath();
476 return custom_wallpaper_dir.Append(kCustomizationDefaultWallpaperDir);
479 // static
480 base::FilePath
481 ServicesCustomizationDocument::GetCustomizedWallpaperDownloadedFileName() {
482 const base::FilePath dir = GetCustomizedWallpaperCacheDir();
483 if (dir.empty()) {
484 NOTREACHED();
485 return dir;
487 return dir.Append(kCustomizationDefaultWallpaperDownloadedFile);
490 void ServicesCustomizationDocument::EnsureCustomizationApplied() {
491 if (WasOOBECustomizationApplied())
492 return;
494 // When customization manifest is fetched, applying will start automatically.
495 if (IsReady())
496 return;
498 StartFetching();
501 base::Closure
502 ServicesCustomizationDocument::EnsureCustomizationAppliedClosure() {
503 return base::Bind(&ServicesCustomizationDocument::EnsureCustomizationApplied,
504 weak_ptr_factory_.GetWeakPtr());
507 void ServicesCustomizationDocument::StartFetching() {
508 if (IsReady() || fetch_started_)
509 return;
511 if (!url_.is_valid()) {
512 std::string customization_id;
513 chromeos::system::StatisticsProvider* provider =
514 chromeos::system::StatisticsProvider::GetInstance();
515 if (provider->GetMachineStatistic(system::kCustomizationIdKey,
516 &customization_id) &&
517 !customization_id.empty()) {
518 url_ = GURL(base::StringPrintf(
519 kManifestUrl, base::ToLowerASCII(customization_id).c_str()));
520 } else {
521 // Remember that there is no customization ID in VPD.
522 OnCustomizationNotFound();
523 return;
527 if (url_.is_valid()) {
528 fetch_started_ = true;
529 if (url_.SchemeIsFile()) {
530 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
531 base::Bind(&ServicesCustomizationDocument::ReadFileInBackground,
532 weak_ptr_factory_.GetWeakPtr(),
533 base::FilePath(url_.path())));
534 } else {
535 StartFileFetch();
540 // static
541 void ServicesCustomizationDocument::ReadFileInBackground(
542 base::WeakPtr<ServicesCustomizationDocument> self,
543 const base::FilePath& file) {
544 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
546 std::string manifest;
547 if (!base::ReadFileToString(file, &manifest)) {
548 manifest.clear();
549 LOG(ERROR) << "Failed to load services customization manifest from: "
550 << file.value();
553 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
554 base::Bind(&ServicesCustomizationDocument::OnManifesteRead,
555 self,
556 manifest));
559 void ServicesCustomizationDocument::OnManifesteRead(
560 const std::string& manifest) {
561 if (!manifest.empty())
562 LoadManifestFromString(manifest);
564 fetch_started_ = false;
567 void ServicesCustomizationDocument::StartFileFetch() {
568 DelayNetworkCall(network_delay_,
569 base::Bind(&ServicesCustomizationDocument::DoStartFileFetch,
570 weak_ptr_factory_.GetWeakPtr()));
573 void ServicesCustomizationDocument::DoStartFileFetch() {
574 url_fetcher_ = net::URLFetcher::Create(url_, net::URLFetcher::GET, this);
575 url_fetcher_->SetRequestContext(g_browser_process->system_request_context());
576 url_fetcher_->AddExtraRequestHeader("Accept: application/json");
577 url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
578 net::LOAD_DO_NOT_SAVE_COOKIES |
579 net::LOAD_DISABLE_CACHE |
580 net::LOAD_DO_NOT_SEND_AUTH_DATA);
581 url_fetcher_->Start();
584 bool ServicesCustomizationDocument::LoadManifestFromString(
585 const std::string& manifest) {
586 if (CustomizationDocument::LoadManifestFromString(manifest)) {
587 LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_SUCCESS);
588 OnManifestLoaded();
589 return true;
592 LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_PARSING_ERROR);
593 return false;
596 void ServicesCustomizationDocument::OnManifestLoaded() {
597 if (!WasOOBECustomizationApplied())
598 ApplyOOBECustomization();
600 scoped_ptr<base::DictionaryValue> prefs =
601 GetDefaultAppsInProviderFormat(*root_);
602 for (ExternalLoaders::iterator it = external_loaders_.begin();
603 it != external_loaders_.end(); ++it) {
604 if (*it) {
605 UpdateCachedManifest((*it)->profile());
606 (*it)->SetCurrentApps(
607 scoped_ptr<base::DictionaryValue>(prefs->DeepCopy()));
608 SetOemFolderName((*it)->profile(), *root_);
613 void ServicesCustomizationDocument::OnURLFetchComplete(
614 const net::URLFetcher* source) {
615 std::string mime_type;
616 std::string data;
617 if (source->GetStatus().is_success() &&
618 source->GetResponseCode() == net::HTTP_OK &&
619 source->GetResponseHeaders()->GetMimeType(&mime_type) &&
620 mime_type == "application/json" &&
621 source->GetResponseAsString(&data)) {
622 LoadManifestFromString(data);
623 } else if (source->GetResponseCode() == net::HTTP_NOT_FOUND) {
624 LOG(ERROR) << "Customization manifest is missing on server: "
625 << source->GetURL().spec();
626 OnCustomizationNotFound();
627 } else {
628 if (num_retries_ < kMaxFetchRetries) {
629 num_retries_++;
630 content::BrowserThread::PostDelayedTask(
631 content::BrowserThread::UI,
632 FROM_HERE,
633 base::Bind(&ServicesCustomizationDocument::StartFileFetch,
634 weak_ptr_factory_.GetWeakPtr()),
635 base::TimeDelta::FromSeconds(kRetriesDelayInSec));
636 return;
638 // This doesn't stop fetching manifest on next restart.
639 LOG(ERROR) << "URL fetch for services customization failed:"
640 << " response code = " << source->GetResponseCode()
641 << " URL = " << source->GetURL().spec();
643 LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_RETRIES_FAIL);
645 fetch_started_ = false;
648 bool ServicesCustomizationDocument::ApplyOOBECustomization() {
649 if (apply_tasks_started_)
650 return false;
652 CheckAndApplyWallpaper();
653 return false;
656 bool ServicesCustomizationDocument::GetDefaultWallpaperUrl(
657 GURL* out_url) const {
658 if (!IsReady())
659 return false;
661 std::string url;
662 if (!root_->GetString(kDefaultWallpaperAttr, &url))
663 return false;
665 *out_url = GURL(url);
666 return true;
669 scoped_ptr<base::DictionaryValue>
670 ServicesCustomizationDocument::GetDefaultApps() const {
671 if (!IsReady())
672 return scoped_ptr<base::DictionaryValue>();
674 return GetDefaultAppsInProviderFormat(*root_);
677 std::string ServicesCustomizationDocument::GetOemAppsFolderName(
678 const std::string& locale) const {
679 if (!IsReady())
680 return std::string();
682 return GetOemAppsFolderNameImpl(locale, *root_);
685 scoped_ptr<base::DictionaryValue>
686 ServicesCustomizationDocument::GetDefaultAppsInProviderFormat(
687 const base::DictionaryValue& root) {
688 scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
689 const base::ListValue* apps_list = NULL;
690 if (root.GetList(kDefaultAppsAttr, &apps_list)) {
691 for (size_t i = 0; i < apps_list->GetSize(); ++i) {
692 std::string app_id;
693 const base::DictionaryValue* app_entry = nullptr;
694 scoped_ptr<base::DictionaryValue> entry;
695 if (apps_list->GetString(i, &app_id)) {
696 entry.reset(new base::DictionaryValue());
697 } else if (apps_list->GetDictionary(i, &app_entry)) {
698 if (!app_entry->GetString(kIdAttr, &app_id)) {
699 LOG(ERROR) << "Wrong format of default application list";
700 prefs->Clear();
701 break;
703 entry = app_entry->CreateDeepCopy();
704 entry->Remove(kIdAttr, nullptr);
705 } else {
706 LOG(ERROR) << "Wrong format of default application list";
707 prefs->Clear();
708 break;
710 if (!entry->HasKey(
711 extensions::ExternalProviderImpl::kExternalUpdateUrl)) {
712 entry->SetString(extensions::ExternalProviderImpl::kExternalUpdateUrl,
713 extension_urls::GetWebstoreUpdateUrl().spec());
715 prefs->Set(app_id, entry.Pass());
719 return prefs.Pass();
722 void ServicesCustomizationDocument::UpdateCachedManifest(Profile* profile) {
723 profile->GetPrefs()->Set(kServicesCustomizationKey, *root_);
726 extensions::ExternalLoader* ServicesCustomizationDocument::CreateExternalLoader(
727 Profile* profile) {
728 ServicesCustomizationExternalLoader* loader =
729 new ServicesCustomizationExternalLoader(profile);
730 external_loaders_.push_back(loader->AsWeakPtr());
732 if (IsReady()) {
733 UpdateCachedManifest(profile);
734 loader->SetCurrentApps(GetDefaultAppsInProviderFormat(*root_));
735 SetOemFolderName(profile, *root_);
736 } else {
737 const base::DictionaryValue* root =
738 profile->GetPrefs()->GetDictionary(kServicesCustomizationKey);
739 std::string version;
740 if (root && root->GetString(kVersionAttr, &version)) {
741 // If version exists, profile has cached version of customization.
742 loader->SetCurrentApps(GetDefaultAppsInProviderFormat(*root));
743 SetOemFolderName(profile, *root);
744 } else {
745 // StartFetching will be called from ServicesCustomizationExternalLoader
746 // when StartLoading is called. We can't initiate manifest fetch here
747 // because caller may never call StartLoading for the provider.
751 return loader;
754 void ServicesCustomizationDocument::OnCustomizationNotFound() {
755 LogManifestLoadResult(HISTOGRAM_LOAD_RESULT_FILE_NOT_FOUND);
756 LoadManifestFromString(kEmptyServicesCustomizationManifest);
759 void ServicesCustomizationDocument::SetOemFolderName(
760 Profile* profile,
761 const base::DictionaryValue& root) {
762 std::string locale = g_browser_process->GetApplicationLocale();
763 std::string name = GetOemAppsFolderNameImpl(locale, root);
764 if (name.empty())
765 name = default_app_order::GetOemAppsFolderName();
766 if (!name.empty()) {
767 app_list::AppListSyncableService* service =
768 app_list::AppListSyncableServiceFactory::GetForProfile(profile);
769 if (!service) {
770 LOG(WARNING) << "AppListSyncableService is not ready for setting OEM "
771 "folder name";
772 return;
774 service->SetOemFolderName(name);
778 std::string ServicesCustomizationDocument::GetOemAppsFolderNameImpl(
779 const std::string& locale,
780 const base::DictionaryValue& root) const {
781 return GetLocaleSpecificStringImpl(
782 &root, locale, kLocalizedContent, kDefaultAppsFolderName);
785 // static
786 void ServicesCustomizationDocument::InitializeForTesting() {
787 g_test_services_customization_document = new ServicesCustomizationDocument;
788 g_test_services_customization_document->network_delay_ = base::TimeDelta();
791 // static
792 void ServicesCustomizationDocument::ShutdownForTesting() {
793 delete g_test_services_customization_document;
794 g_test_services_customization_document = NULL;
797 void ServicesCustomizationDocument::StartOEMWallpaperDownload(
798 const GURL& wallpaper_url,
799 scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying) {
800 DCHECK(wallpaper_url.is_valid());
802 const base::FilePath dir = GetCustomizedWallpaperCacheDir();
803 const base::FilePath file = GetCustomizedWallpaperDownloadedFileName();
804 if (dir.empty() || file.empty()) {
805 NOTREACHED();
806 applying->Finished(false);
807 return;
810 wallpaper_downloader_.reset(new CustomizationWallpaperDownloader(
811 g_browser_process->system_request_context(),
812 wallpaper_url,
813 dir,
814 file,
815 base::Bind(&ServicesCustomizationDocument::OnOEMWallpaperDownloaded,
816 weak_ptr_factory_.GetWeakPtr(),
817 base::Passed(applying.Pass()))));
819 wallpaper_downloader_->Start();
822 void ServicesCustomizationDocument::CheckAndApplyWallpaper() {
823 if (wallpaper_downloader_.get()) {
824 VLOG(1) << "CheckAndApplyWallpaper(): download has already started.";
825 return;
827 scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying(
828 new ServicesCustomizationDocument::ApplyingTask(this));
830 GURL wallpaper_url;
831 if (!GetDefaultWallpaperUrl(&wallpaper_url)) {
832 PrefService* pref_service = g_browser_process->local_state();
833 std::string current_url =
834 pref_service->GetString(prefs::kCustomizationDefaultWallpaperURL);
835 if (!current_url.empty()) {
836 VLOG(1) << "ServicesCustomizationDocument::CheckAndApplyWallpaper() : "
837 << "No wallpaper URL attribute in customization document, "
838 << "but current value is non-empty: '" << current_url
839 << "'. Ignored.";
841 applying->Finished(true);
842 return;
845 // Should fail if this ever happens in tests.
846 DCHECK(wallpaper_url.is_valid());
847 if (!wallpaper_url.is_valid()) {
848 if (!wallpaper_url.is_empty()) {
849 LOG(WARNING) << "Invalid Customized Wallpaper URL '"
850 << wallpaper_url.spec() << "'.";
852 applying->Finished(false);
853 return;
856 scoped_ptr<bool> exists(new bool(false));
858 base::Closure check_file_exists =
859 base::Bind(&CheckWallpaperCacheExists,
860 GetCustomizedWallpaperDownloadedFileName(),
861 base::Unretained(exists.get()));
862 base::Closure on_checked_closure =
863 base::Bind(&ServicesCustomizationDocument::OnCheckedWallpaperCacheExists,
864 weak_ptr_factory_.GetWeakPtr(),
865 base::Passed(exists.Pass()),
866 base::Passed(applying.Pass()));
867 if (!content::BrowserThread::PostBlockingPoolTaskAndReply(
868 FROM_HERE, check_file_exists, on_checked_closure)) {
869 LOG(WARNING) << "Failed to start check Wallpaper cache exists.";
873 void ServicesCustomizationDocument::OnCheckedWallpaperCacheExists(
874 scoped_ptr<bool> exists,
875 scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying) {
876 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
877 DCHECK(exists);
878 DCHECK(applying);
880 ApplyWallpaper(*exists, applying.Pass());
883 void ServicesCustomizationDocument::ApplyWallpaper(
884 bool default_wallpaper_file_exists,
885 scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying) {
886 GURL wallpaper_url;
887 const bool wallpaper_url_present = GetDefaultWallpaperUrl(&wallpaper_url);
889 PrefService* pref_service = g_browser_process->local_state();
891 std::string current_url =
892 pref_service->GetString(prefs::kCustomizationDefaultWallpaperURL);
893 if (current_url != wallpaper_url.spec()) {
894 if (wallpaper_url_present) {
895 VLOG(1) << "ServicesCustomizationDocument::ApplyWallpaper() : "
896 << "Wallpaper URL in customization document '"
897 << wallpaper_url.spec() << "' differs from current '"
898 << current_url << "'."
899 << (GURL(current_url).is_valid() && default_wallpaper_file_exists
900 ? " Ignored."
901 : " Will refetch.");
902 } else {
903 VLOG(1) << "ServicesCustomizationDocument::ApplyWallpaper() : "
904 << "No wallpaper URL attribute in customization document, "
905 << "but current value is non-empty: '" << current_url
906 << "'. Ignored.";
909 if (!wallpaper_url_present) {
910 applying->Finished(true);
911 return;
914 DCHECK(wallpaper_url.is_valid());
916 // Never update system-wide wallpaper (i.e. do not check
917 // current_url == wallpaper_url.spec() )
918 if (GURL(current_url).is_valid() && default_wallpaper_file_exists) {
919 VLOG(1)
920 << "ServicesCustomizationDocument::ApplyWallpaper() : reuse existing";
921 OnOEMWallpaperDownloaded(applying.Pass(), true, GURL(current_url));
922 } else {
923 VLOG(1)
924 << "ServicesCustomizationDocument::ApplyWallpaper() : start download";
925 StartOEMWallpaperDownload(wallpaper_url, applying.Pass());
929 void ServicesCustomizationDocument::OnOEMWallpaperDownloaded(
930 scoped_ptr<ServicesCustomizationDocument::ApplyingTask> applying,
931 bool success,
932 const GURL& wallpaper_url) {
933 if (success) {
934 DCHECK(wallpaper_url.is_valid());
936 VLOG(1) << "Setting default wallpaper to '"
937 << GetCustomizedWallpaperDownloadedFileName().value() << "' ('"
938 << wallpaper_url.spec() << "')";
939 WallpaperManager::Get()->SetCustomizedDefaultWallpaper(
940 wallpaper_url,
941 GetCustomizedWallpaperDownloadedFileName(),
942 GetCustomizedWallpaperCacheDir());
944 wallpaper_downloader_.reset();
945 applying->Finished(success);
948 void ServicesCustomizationDocument::ApplyingTaskStarted() {
949 ++apply_tasks_started_;
952 void ServicesCustomizationDocument::ApplyingTaskFinished(bool success) {
953 DCHECK_GT(apply_tasks_started_, apply_tasks_finished_);
954 ++apply_tasks_finished_;
956 apply_tasks_success_ += success;
958 if (apply_tasks_started_ != apply_tasks_finished_)
959 return;
961 if (apply_tasks_success_ == apply_tasks_finished_)
962 SetApplied(true);
965 } // namespace chromeos