Fix FileManagerFileTasksComplexTest.FindFileHandlerTasks test failing
[chromium-blink-merge.git] / chrome / browser / chromeos / customization_document.cc
blobb5cce96203c8f6f41b8ad47724691ec59a37dcac
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_document.h"
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/prefs/pref_registry_simple.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/strings/string_split.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/time/time.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/browser/chromeos/login/wizard_controller.h"
23 #include "chrome/browser/extensions/external_loader.h"
24 #include "chrome/browser/extensions/external_provider_impl.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/common/extensions/extension_constants.h"
27 #include "chromeos/network/network_state.h"
28 #include "chromeos/network/network_state_handler.h"
29 #include "chromeos/system/statistics_provider.h"
30 #include "components/user_prefs/pref_registry_syncable.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "net/base/load_flags.h"
33 #include "net/http/http_response_headers.h"
34 #include "net/url_request/url_fetcher.h"
36 using content::BrowserThread;
38 // Manifest attributes names.
40 namespace {
42 const char kVersionAttr[] = "version";
43 const char kDefaultAttr[] = "default";
44 const char kInitialLocaleAttr[] = "initial_locale";
45 const char kInitialTimezoneAttr[] = "initial_timezone";
46 const char kKeyboardLayoutAttr[] = "keyboard_layout";
47 const char kHwidMapAttr[] = "hwid_map";
48 const char kHwidMaskAttr[] = "hwid_mask";
49 const char kSetupContentAttr[] = "setup_content";
50 const char kEulaPageAttr[] = "eula_page";
51 const char kDefaultWallpaperAttr[] = "default_wallpaper";
52 const char kDefaultAppsAttr[] = "default_apps";
54 const char kAcceptedManifestVersion[] = "1.0";
56 // Path to OEM partner startup customization manifest.
57 const char kStartupCustomizationManifestPath[] =
58 "/opt/oem/etc/startup_manifest.json";
60 // Name of local state option that tracks if services customization has been
61 // applied.
62 const char kServicesCustomizationAppliedPref[] = "ServicesCustomizationApplied";
64 // Maximum number of retries to fetch file if network is not available.
65 const int kMaxFetchRetries = 3;
67 // Delay between file fetch retries if network is not available.
68 const int kRetriesDelayInSec = 2;
70 // Name of profile option that tracks cached version of service customization.
71 const char kServicesCustomizationKey[] = "customization.manifest_cache";
73 } // anonymous namespace
75 namespace chromeos {
77 // Template URL where to fetch OEM services customization manifest from.
78 const char ServicesCustomizationDocument::kManifestUrl[] =
79 "https://ssl.gstatic.com/chrome/chromeos-customization/%s.json";
81 // A custom extensions::ExternalLoader that the ServicesCustomizationDocument
82 // creates and uses to publish OEM default apps to the extensions system.
83 class ServicesCustomizationExternalLoader
84 : public extensions::ExternalLoader,
85 public base::SupportsWeakPtr<ServicesCustomizationExternalLoader> {
86 public:
87 explicit ServicesCustomizationExternalLoader(Profile* profile)
88 : is_apps_set_(false), profile_(profile) {}
90 Profile* profile() { return profile_; }
92 // Used by the ServicesCustomizationDocument to update the current apps.
93 void SetCurrentApps(scoped_ptr<base::DictionaryValue> prefs) {
94 apps_.Swap(prefs.get());
95 is_apps_set_ = true;
96 StartLoading();
99 // Implementation of extensions::ExternalLoader:
100 virtual void StartLoading() OVERRIDE {
101 if (!is_apps_set_) {
102 ServicesCustomizationDocument::GetInstance()->StartFetching();
103 // No return here to call LoadFinished with empty list initially.
104 // When manifest is fetched, it will be called again with real list.
105 // It is safe to return empty list because this provider didn't install
106 // any app yet so no app can be removed due to returning empty list.
109 prefs_.reset(apps_.DeepCopy());
110 VLOG(1) << "ServicesCustomization extension loader publishing "
111 << apps_.size() << " apps.";
112 LoadFinished();
115 protected:
116 virtual ~ServicesCustomizationExternalLoader() {}
118 private:
119 bool is_apps_set_;
120 base::DictionaryValue apps_;
121 Profile* profile_;
123 DISALLOW_COPY_AND_ASSIGN(ServicesCustomizationExternalLoader);
126 // CustomizationDocument implementation. ---------------------------------------
128 CustomizationDocument::CustomizationDocument(
129 const std::string& accepted_version)
130 : accepted_version_(accepted_version) {}
132 CustomizationDocument::~CustomizationDocument() {}
134 bool CustomizationDocument::LoadManifestFromFile(
135 const base::FilePath& manifest_path) {
136 std::string manifest;
137 if (!base::ReadFileToString(manifest_path, &manifest))
138 return false;
139 return LoadManifestFromString(manifest);
142 bool CustomizationDocument::LoadManifestFromString(
143 const std::string& manifest) {
144 int error_code = 0;
145 std::string error;
146 scoped_ptr<base::Value> root(base::JSONReader::ReadAndReturnError(manifest,
147 base::JSON_ALLOW_TRAILING_COMMAS, &error_code, &error));
148 if (error_code != base::JSONReader::JSON_NO_ERROR)
149 LOG(ERROR) << error;
150 DCHECK(root.get() != NULL);
151 if (root.get() == NULL)
152 return false;
153 DCHECK(root->GetType() == base::Value::TYPE_DICTIONARY);
154 if (root->GetType() == base::Value::TYPE_DICTIONARY) {
155 root_.reset(static_cast<base::DictionaryValue*>(root.release()));
156 std::string result;
157 if (root_->GetString(kVersionAttr, &result) &&
158 result == accepted_version_)
159 return true;
161 LOG(ERROR) << "Wrong customization manifest version";
162 root_.reset(NULL);
164 return false;
167 std::string CustomizationDocument::GetLocaleSpecificString(
168 const std::string& locale,
169 const std::string& dictionary_name,
170 const std::string& entry_name) const {
171 base::DictionaryValue* dictionary_content = NULL;
172 if (!root_.get() ||
173 !root_->GetDictionary(dictionary_name, &dictionary_content))
174 return std::string();
176 base::DictionaryValue* locale_dictionary = NULL;
177 if (dictionary_content->GetDictionary(locale, &locale_dictionary)) {
178 std::string result;
179 if (locale_dictionary->GetString(entry_name, &result))
180 return result;
183 base::DictionaryValue* default_dictionary = NULL;
184 if (dictionary_content->GetDictionary(kDefaultAttr, &default_dictionary)) {
185 std::string result;
186 if (default_dictionary->GetString(entry_name, &result))
187 return result;
190 return std::string();
193 // StartupCustomizationDocument implementation. --------------------------------
195 StartupCustomizationDocument::StartupCustomizationDocument()
196 : CustomizationDocument(kAcceptedManifestVersion) {
198 // Loading manifest causes us to do blocking IO on UI thread.
199 // Temporarily allow it until we fix http://crosbug.com/11103
200 base::ThreadRestrictions::ScopedAllowIO allow_io;
201 LoadManifestFromFile(base::FilePath(kStartupCustomizationManifestPath));
203 Init(system::StatisticsProvider::GetInstance());
206 StartupCustomizationDocument::StartupCustomizationDocument(
207 system::StatisticsProvider* statistics_provider,
208 const std::string& manifest)
209 : CustomizationDocument(kAcceptedManifestVersion) {
210 LoadManifestFromString(manifest);
211 Init(statistics_provider);
214 StartupCustomizationDocument::~StartupCustomizationDocument() {}
216 StartupCustomizationDocument* StartupCustomizationDocument::GetInstance() {
217 return Singleton<StartupCustomizationDocument,
218 DefaultSingletonTraits<StartupCustomizationDocument> >::get();
221 void StartupCustomizationDocument::Init(
222 system::StatisticsProvider* statistics_provider) {
223 if (IsReady()) {
224 root_->GetString(kInitialLocaleAttr, &initial_locale_);
225 root_->GetString(kInitialTimezoneAttr, &initial_timezone_);
226 root_->GetString(kKeyboardLayoutAttr, &keyboard_layout_);
228 std::string hwid;
229 if (statistics_provider->GetMachineStatistic(
230 system::kHardwareClassKey, &hwid)) {
231 base::ListValue* hwid_list = NULL;
232 if (root_->GetList(kHwidMapAttr, &hwid_list)) {
233 for (size_t i = 0; i < hwid_list->GetSize(); ++i) {
234 base::DictionaryValue* hwid_dictionary = NULL;
235 std::string hwid_mask;
236 if (hwid_list->GetDictionary(i, &hwid_dictionary) &&
237 hwid_dictionary->GetString(kHwidMaskAttr, &hwid_mask)) {
238 if (MatchPattern(hwid, hwid_mask)) {
239 // If HWID for this machine matches some mask, use HWID specific
240 // settings.
241 std::string result;
242 if (hwid_dictionary->GetString(kInitialLocaleAttr, &result))
243 initial_locale_ = result;
245 if (hwid_dictionary->GetString(kInitialTimezoneAttr, &result))
246 initial_timezone_ = result;
248 if (hwid_dictionary->GetString(kKeyboardLayoutAttr, &result))
249 keyboard_layout_ = result;
251 // Don't break here to allow other entires to be applied if match.
252 } else {
253 LOG(ERROR) << "Syntax error in customization manifest";
257 } else {
258 LOG(ERROR) << "HWID is missing in machine statistics";
262 // If manifest doesn't exist still apply values from VPD.
263 statistics_provider->GetMachineStatistic(kInitialLocaleAttr,
264 &initial_locale_);
265 statistics_provider->GetMachineStatistic(kInitialTimezoneAttr,
266 &initial_timezone_);
267 statistics_provider->GetMachineStatistic(kKeyboardLayoutAttr,
268 &keyboard_layout_);
269 configured_locales_.resize(0);
270 base::SplitString(initial_locale_, ',', &configured_locales_);
271 // Let's always have configured_locales_.front() a valid entry.
272 if (configured_locales_.size() == 0)
273 configured_locales_.push_back(std::string());
276 const std::vector<std::string>&
277 StartupCustomizationDocument::configured_locales() const {
278 return configured_locales_;
281 const std::string& StartupCustomizationDocument::initial_locale_default()
282 const {
283 DCHECK(configured_locales_.size() > 0);
284 return configured_locales_.front();
287 std::string StartupCustomizationDocument::GetEULAPage(
288 const std::string& locale) const {
289 return GetLocaleSpecificString(locale, kSetupContentAttr, kEulaPageAttr);
292 // ServicesCustomizationDocument implementation. -------------------------------
294 ServicesCustomizationDocument::ServicesCustomizationDocument()
295 : CustomizationDocument(kAcceptedManifestVersion),
296 num_retries_(0),
297 fetch_started_(false) {
300 ServicesCustomizationDocument::ServicesCustomizationDocument(
301 const std::string& manifest)
302 : CustomizationDocument(kAcceptedManifestVersion) {
303 LoadManifestFromString(manifest);
306 ServicesCustomizationDocument::~ServicesCustomizationDocument() {}
308 // static
309 ServicesCustomizationDocument* ServicesCustomizationDocument::GetInstance() {
310 return Singleton<ServicesCustomizationDocument,
311 DefaultSingletonTraits<ServicesCustomizationDocument> >::get();
314 // static
315 void ServicesCustomizationDocument::RegisterPrefs(
316 PrefRegistrySimple* registry) {
317 registry->RegisterBooleanPref(kServicesCustomizationAppliedPref, false);
320 // static
321 void ServicesCustomizationDocument::RegisterProfilePrefs(
322 user_prefs::PrefRegistrySyncable* registry) {
323 registry->RegisterDictionaryPref(
324 kServicesCustomizationKey,
325 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
328 // static
329 bool ServicesCustomizationDocument::WasOOBECustomizationApplied() {
330 PrefService* prefs = g_browser_process->local_state();
331 return prefs->GetBoolean(kServicesCustomizationAppliedPref);
334 // static
335 void ServicesCustomizationDocument::SetApplied(bool val) {
336 PrefService* prefs = g_browser_process->local_state();
337 prefs->SetBoolean(kServicesCustomizationAppliedPref, val);
340 void ServicesCustomizationDocument::StartFetching() {
341 if (!fetch_started_) {
342 if (!url_.is_valid()) {
343 std::string customization_id;
344 chromeos::system::StatisticsProvider* provider =
345 chromeos::system::StatisticsProvider::GetInstance();
346 if (provider->GetMachineStatistic(system::kCustomizationIdKey,
347 &customization_id) &&
348 !customization_id.empty()) {
349 url_ = GURL(base::StringPrintf(
350 kManifestUrl, StringToLowerASCII(customization_id).c_str()));
354 if (url_.is_valid()) {
355 fetch_started_ = true;
356 if (url_.SchemeIsFile()) {
357 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
358 base::Bind(&ServicesCustomizationDocument::ReadFileInBackground,
359 base::Unretained(this), // this class is a singleton.
360 base::FilePath(url_.path())));
361 } else {
362 StartFileFetch();
368 void ServicesCustomizationDocument::ReadFileInBackground(
369 const base::FilePath& file) {
370 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
372 std::string manifest;
373 if (!base::ReadFileToString(file, &manifest)) {
374 manifest.clear();
375 LOG(ERROR) << "Failed to load services customization manifest from: "
376 << file.value();
379 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
380 base::Bind(&ServicesCustomizationDocument::OnManifesteRead,
381 base::Unretained(this), // this class is a singleton.
382 manifest));
385 void ServicesCustomizationDocument::OnManifesteRead(
386 const std::string& manifest) {
387 if (!manifest.empty())
388 LoadManifestFromString(manifest);
390 fetch_started_ = false;
393 void ServicesCustomizationDocument::StartFileFetch() {
394 url_fetcher_.reset(net::URLFetcher::Create(
395 url_, net::URLFetcher::GET, this));
396 url_fetcher_->SetRequestContext(g_browser_process->system_request_context());
397 url_fetcher_->AddExtraRequestHeader("Accept: application/json");
398 url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
399 net::LOAD_DO_NOT_SAVE_COOKIES |
400 net::LOAD_DISABLE_CACHE |
401 net::LOAD_DO_NOT_SEND_AUTH_DATA);
402 url_fetcher_->Start();
405 bool ServicesCustomizationDocument::LoadManifestFromString(
406 const std::string& manifest) {
407 if (CustomizationDocument::LoadManifestFromString(manifest)) {
408 OnManifestLoaded();
409 return true;
411 return false;
414 void ServicesCustomizationDocument::OnManifestLoaded() {
415 if (!ServicesCustomizationDocument::WasOOBECustomizationApplied())
416 ApplyOOBECustomization();
418 scoped_ptr<base::DictionaryValue> prefs =
419 GetDefaultAppsInProviderFormat(*root_);
420 for (ExternalLoaders::iterator it = external_loaders_.begin();
421 it != external_loaders_.end(); ++it) {
422 if (*it) {
423 UpdateCachedManifest((*it)->profile());
424 (*it)->SetCurrentApps(
425 scoped_ptr<base::DictionaryValue>(prefs->DeepCopy()));
430 void ServicesCustomizationDocument::OnURLFetchComplete(
431 const net::URLFetcher* source) {
432 std::string mime_type;
433 std::string data;
434 if (source->GetStatus().is_success() &&
435 source->GetResponseCode() == 200 &&
436 source->GetResponseHeaders()->GetMimeType(&mime_type) &&
437 mime_type == "application/json" &&
438 source->GetResponseAsString(&data)) {
439 LoadManifestFromString(data);
440 fetch_started_ = false;
441 } else {
442 const NetworkState* default_network =
443 NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
444 // TODO(dpolukhin): wait for network connected state, crbug.com/343589.
445 if (default_network && default_network->IsConnectedState() &&
446 num_retries_ < kMaxFetchRetries) {
447 num_retries_++;
448 retry_timer_.Start(FROM_HERE,
449 base::TimeDelta::FromSeconds(kRetriesDelayInSec),
450 this, &ServicesCustomizationDocument::StartFileFetch);
451 return;
453 LOG(ERROR) << "URL fetch for services customization failed:"
454 << " response code = " << source->GetResponseCode()
455 << " URL = " << source->GetURL().spec();
456 fetch_started_ = false;
460 bool ServicesCustomizationDocument::ApplyOOBECustomization() {
461 // TODO(dpolukhin): apply default wallpaper.
462 SetApplied(true);
463 return true;
466 GURL ServicesCustomizationDocument::GetDefaultWallpaperUrl() const {
467 if (!IsReady())
468 return GURL();
470 std::string url;
471 root_->GetString(kDefaultWallpaperAttr, &url);
472 return GURL(url);
475 bool ServicesCustomizationDocument::GetDefaultApps(
476 std::vector<std::string>* ids) const {
477 ids->clear();
478 if (!IsReady())
479 return false;
481 base::ListValue* apps_list = NULL;
482 if (!root_->GetList(kDefaultAppsAttr, &apps_list))
483 return false;
485 for (size_t i = 0; i < apps_list->GetSize(); ++i) {
486 std::string app_id;
487 if (apps_list->GetString(i, &app_id)) {
488 ids->push_back(app_id);
489 } else {
490 LOG(ERROR) << "Wrong format of default application list";
491 return false;
495 return true;
498 scoped_ptr<base::DictionaryValue>
499 ServicesCustomizationDocument::GetDefaultAppsInProviderFormat(
500 const base::DictionaryValue& root) {
501 scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
502 const base::ListValue* apps_list = NULL;
503 if (root.GetList(kDefaultAppsAttr, &apps_list)) {
504 for (size_t i = 0; i < apps_list->GetSize(); ++i) {
505 std::string app_id;
506 if (apps_list->GetString(i, &app_id)) {
507 base::DictionaryValue* entry = new base::DictionaryValue;
508 entry->SetString(extensions::ExternalProviderImpl::kExternalUpdateUrl,
509 extension_urls::GetWebstoreUpdateUrl().spec());
510 prefs->Set(app_id, entry);
511 } else {
512 LOG(ERROR) << "Wrong format of default application list";
513 prefs->Clear();
514 break;
519 return prefs.Pass();
522 void ServicesCustomizationDocument::UpdateCachedManifest(Profile* profile) {
523 profile->GetPrefs()->Set(kServicesCustomizationKey, *root_);
526 extensions::ExternalLoader* ServicesCustomizationDocument::CreateExternalLoader(
527 Profile* profile) {
528 ServicesCustomizationExternalLoader* loader =
529 new ServicesCustomizationExternalLoader(profile);
530 external_loaders_.push_back(loader->AsWeakPtr());
532 if (IsReady()) {
533 UpdateCachedManifest(profile);
534 loader->SetCurrentApps(GetDefaultAppsInProviderFormat(*root_));
535 } else {
536 const base::DictionaryValue* root =
537 profile->GetPrefs()->GetDictionary(kServicesCustomizationKey);
538 std::string version;
539 if (root && root->GetString(kVersionAttr, &version)) {
540 // If version exists, profile has cached version of customization.
541 loader->SetCurrentApps(GetDefaultAppsInProviderFormat(*root));
542 // TODO(dpolukhin): periodically refresh cached copy, crbug.com/343589.
543 } else {
544 // StartFetching will be called from ServicesCustomizationExternalLoader
545 // when StartLoading is called. We can't initiate manifest fetch here
546 // because caller may never call StartLoading for the provider.
550 return loader;
553 } // namespace chromeos