- add sources.
[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/prefs/pref_registry_simple.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/time/time.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/chromeos/login/wizard_controller.h"
20 #include "chrome/browser/profiles/profile_manager.h"
21 #include "chromeos/network/network_state.h"
22 #include "chromeos/network/network_state_handler.h"
23 #include "chromeos/system/statistics_provider.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "net/url_request/url_fetcher.h"
26
27 using content::BrowserThread;
28
29 // Manifest attributes names.
30
31 namespace {
32
33 const char kVersionAttr[] = "version";
34 const char kDefaultAttr[] = "default";
35 const char kInitialLocaleAttr[] = "initial_locale";
36 const char kInitialTimezoneAttr[] = "initial_timezone";
37 const char kKeyboardLayoutAttr[] = "keyboard_layout";
38 const char kRegistrationUrlAttr[] = "registration_url";
39 const char kHwidMapAttr[] = "hwid_map";
40 const char kHwidMaskAttr[] = "hwid_mask";
41 const char kSetupContentAttr[] = "setup_content";
42 const char kHelpPageAttr[] = "help_page";
43 const char kEulaPageAttr[] = "eula_page";
44 const char kAppContentAttr[] = "app_content";
45 const char kInitialStartPageAttr[] = "initial_start_page";
46 const char kSupportPageAttr[] = "support_page";
47
48 const char kAcceptedManifestVersion[] = "1.0";
49
50 // Path to OEM partner startup customization manifest.
51 const char kStartupCustomizationManifestPath[] =
52     "/opt/oem/etc/startup_manifest.json";
53
54 // URL where to fetch OEM services customization manifest from.
55 const char kServicesCustomizationManifestUrl[] =
56     "file:///opt/oem/etc/services_manifest.json";
57
58 // Name of local state option that tracks if services customization has been
59 // applied.
60 const char kServicesCustomizationAppliedPref[] = "ServicesCustomizationApplied";
61
62 // Maximum number of retries to fetch file if network is not available.
63 const int kMaxFetchRetries = 3;
64
65 // Delay between file fetch retries if network is not available.
66 const int kRetriesDelayInSec = 2;
67
68 }  // anonymous namespace
69
70 namespace chromeos {
71
72 // CustomizationDocument implementation. ---------------------------------------
73
74 CustomizationDocument::CustomizationDocument(
75     const std::string& accepted_version)
76     : accepted_version_(accepted_version) {}
77
78 CustomizationDocument::~CustomizationDocument() {}
79
80 bool CustomizationDocument::LoadManifestFromFile(
81     const base::FilePath& manifest_path) {
82   std::string manifest;
83   if (!base::ReadFileToString(manifest_path, &manifest))
84     return false;
85   return LoadManifestFromString(manifest);
86 }
87
88 bool CustomizationDocument::LoadManifestFromString(
89     const std::string& manifest) {
90   int error_code = 0;
91   std::string error;
92   scoped_ptr<Value> root(base::JSONReader::ReadAndReturnError(manifest,
93       base::JSON_ALLOW_TRAILING_COMMAS, &error_code, &error));
94   if (error_code != base::JSONReader::JSON_NO_ERROR)
95     LOG(ERROR) << error;
96   DCHECK(root.get() != NULL);
97   if (root.get() == NULL)
98     return false;
99   DCHECK(root->GetType() == Value::TYPE_DICTIONARY);
100   if (root->GetType() == Value::TYPE_DICTIONARY) {
101     root_.reset(static_cast<DictionaryValue*>(root.release()));
102     std::string result;
103     if (root_->GetString(kVersionAttr, &result) &&
104         result == accepted_version_)
105       return true;
106
107     LOG(ERROR) << "Wrong customization manifest version";
108     root_.reset(NULL);
109   }
110   return false;
111 }
112
113 std::string CustomizationDocument::GetLocaleSpecificString(
114     const std::string& locale,
115     const std::string& dictionary_name,
116     const std::string& entry_name) const {
117   DictionaryValue* dictionary_content = NULL;
118   if (!root_.get() ||
119       !root_->GetDictionary(dictionary_name, &dictionary_content))
120     return std::string();
121
122   DictionaryValue* locale_dictionary = NULL;
123   if (dictionary_content->GetDictionary(locale, &locale_dictionary)) {
124     std::string result;
125     if (locale_dictionary->GetString(entry_name, &result))
126       return result;
127   }
128
129   DictionaryValue* default_dictionary = NULL;
130   if (dictionary_content->GetDictionary(kDefaultAttr, &default_dictionary)) {
131     std::string result;
132     if (default_dictionary->GetString(entry_name, &result))
133       return result;
134   }
135
136   return std::string();
137 }
138
139 // StartupCustomizationDocument implementation. --------------------------------
140
141 StartupCustomizationDocument::StartupCustomizationDocument()
142     : CustomizationDocument(kAcceptedManifestVersion) {
143   {
144     // Loading manifest causes us to do blocking IO on UI thread.
145     // Temporarily allow it until we fix http://crosbug.com/11103
146     base::ThreadRestrictions::ScopedAllowIO allow_io;
147     LoadManifestFromFile(base::FilePath(kStartupCustomizationManifestPath));
148   }
149   Init(chromeos::system::StatisticsProvider::GetInstance());
150 }
151
152 StartupCustomizationDocument::StartupCustomizationDocument(
153     chromeos::system::StatisticsProvider* statistics_provider,
154     const std::string& manifest)
155     : CustomizationDocument(kAcceptedManifestVersion) {
156   LoadManifestFromString(manifest);
157   Init(statistics_provider);
158 }
159
160 StartupCustomizationDocument::~StartupCustomizationDocument() {}
161
162 StartupCustomizationDocument* StartupCustomizationDocument::GetInstance() {
163   return Singleton<StartupCustomizationDocument,
164       DefaultSingletonTraits<StartupCustomizationDocument> >::get();
165 }
166
167 void StartupCustomizationDocument::Init(
168     chromeos::system::StatisticsProvider* statistics_provider) {
169   if (IsReady()) {
170     root_->GetString(kInitialLocaleAttr, &initial_locale_);
171     root_->GetString(kInitialTimezoneAttr, &initial_timezone_);
172     root_->GetString(kKeyboardLayoutAttr, &keyboard_layout_);
173     root_->GetString(kRegistrationUrlAttr, &registration_url_);
174
175     std::string hwid;
176     if (statistics_provider->GetMachineStatistic(
177             chromeos::system::kHardwareClassKey, &hwid)) {
178       ListValue* hwid_list = NULL;
179       if (root_->GetList(kHwidMapAttr, &hwid_list)) {
180         for (size_t i = 0; i < hwid_list->GetSize(); ++i) {
181           DictionaryValue* hwid_dictionary = NULL;
182           std::string hwid_mask;
183           if (hwid_list->GetDictionary(i, &hwid_dictionary) &&
184               hwid_dictionary->GetString(kHwidMaskAttr, &hwid_mask)) {
185             if (MatchPattern(hwid, hwid_mask)) {
186               // If HWID for this machine matches some mask, use HWID specific
187               // settings.
188               std::string result;
189               if (hwid_dictionary->GetString(kInitialLocaleAttr, &result))
190                 initial_locale_ = result;
191
192               if (hwid_dictionary->GetString(kInitialTimezoneAttr, &result))
193                 initial_timezone_ = result;
194
195               if (hwid_dictionary->GetString(kKeyboardLayoutAttr, &result))
196                 keyboard_layout_ = result;
197             }
198             // Don't break here to allow other entires to be applied if match.
199           } else {
200             LOG(ERROR) << "Syntax error in customization manifest";
201           }
202         }
203       }
204     } else {
205       LOG(ERROR) << "HWID is missing in machine statistics";
206     }
207   }
208
209   // If manifest doesn't exist still apply values from VPD.
210   statistics_provider->GetMachineStatistic(kInitialLocaleAttr,
211                                            &initial_locale_);
212   statistics_provider->GetMachineStatistic(kInitialTimezoneAttr,
213                                            &initial_timezone_);
214   statistics_provider->GetMachineStatistic(kKeyboardLayoutAttr,
215                                            &keyboard_layout_);
216 }
217
218 std::string StartupCustomizationDocument::GetHelpPage(
219     const std::string& locale) const {
220   return GetLocaleSpecificString(locale, kSetupContentAttr, kHelpPageAttr);
221 }
222
223 std::string StartupCustomizationDocument::GetEULAPage(
224     const std::string& locale) const {
225   return GetLocaleSpecificString(locale, kSetupContentAttr, kEulaPageAttr);
226 }
227
228 // ServicesCustomizationDocument implementation. -------------------------------
229
230 ServicesCustomizationDocument::ServicesCustomizationDocument()
231     : CustomizationDocument(kAcceptedManifestVersion),
232       url_(kServicesCustomizationManifestUrl) {
233 }
234
235 ServicesCustomizationDocument::ServicesCustomizationDocument(
236     const std::string& manifest)
237     : CustomizationDocument(kAcceptedManifestVersion) {
238   LoadManifestFromString(manifest);
239 }
240
241 ServicesCustomizationDocument::~ServicesCustomizationDocument() {}
242
243 // static
244 ServicesCustomizationDocument* ServicesCustomizationDocument::GetInstance() {
245   return Singleton<ServicesCustomizationDocument,
246       DefaultSingletonTraits<ServicesCustomizationDocument> >::get();
247 }
248
249 // static
250 void ServicesCustomizationDocument::RegisterPrefs(
251     PrefRegistrySimple* registry) {
252   registry->RegisterBooleanPref(kServicesCustomizationAppliedPref, false);
253 }
254
255 // static
256 bool ServicesCustomizationDocument::WasApplied() {
257   PrefService* prefs = g_browser_process->local_state();
258   return prefs->GetBoolean(kServicesCustomizationAppliedPref);
259 }
260
261 // static
262 void ServicesCustomizationDocument::SetApplied(bool val) {
263   PrefService* prefs = g_browser_process->local_state();
264   prefs->SetBoolean(kServicesCustomizationAppliedPref, val);
265 }
266
267 void ServicesCustomizationDocument::StartFetching() {
268   if (url_.SchemeIsFile()) {
269     BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
270         base::Bind(&ServicesCustomizationDocument::ReadFileInBackground,
271                    base::Unretained(this),  // this class is a singleton.
272                    base::FilePath(url_.path())));
273   } else {
274     StartFileFetch();
275   }
276 }
277
278 void ServicesCustomizationDocument::ReadFileInBackground(
279     const base::FilePath& file) {
280   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
281
282   std::string manifest;
283   if (base::ReadFileToString(file, &manifest)) {
284     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
285         base::Bind(
286            base::IgnoreResult(
287                &ServicesCustomizationDocument::LoadManifestFromString),
288            base::Unretained(this),  // this class is a singleton.
289            manifest));
290   } else {
291     VLOG(1) << "Failed to load services customization manifest from: "
292             << file.value();
293   }
294 }
295
296 void ServicesCustomizationDocument::StartFileFetch() {
297   DCHECK(url_.is_valid());
298   url_fetcher_.reset(net::URLFetcher::Create(
299       url_, net::URLFetcher::GET, this));
300   url_fetcher_->SetRequestContext(
301       ProfileManager::GetDefaultProfile()->GetRequestContext());
302   url_fetcher_->Start();
303 }
304
305 void ServicesCustomizationDocument::OnURLFetchComplete(
306     const net::URLFetcher* source) {
307   if (source->GetResponseCode() == 200) {
308     std::string data;
309     source->GetResponseAsString(&data);
310     LoadManifestFromString(data);
311   } else {
312     const NetworkState* default_network =
313         NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
314     if (default_network && default_network->IsConnectedState() &&
315         num_retries_ < kMaxFetchRetries) {
316       num_retries_++;
317       retry_timer_.Start(FROM_HERE,
318                          base::TimeDelta::FromSeconds(kRetriesDelayInSec),
319                          this, &ServicesCustomizationDocument::StartFileFetch);
320       return;
321     }
322     LOG(ERROR) << "URL fetch for services customization failed:"
323                << " response code = " << source->GetResponseCode()
324                << " URL = " << source->GetURL().spec();
325   }
326 }
327
328 bool ServicesCustomizationDocument::ApplyCustomization() {
329   // TODO(dpolukhin): apply customized apps, exts and support page.
330   SetApplied(true);
331   return true;
332 }
333
334 std::string ServicesCustomizationDocument::GetInitialStartPage(
335     const std::string& locale) const {
336   return GetLocaleSpecificString(
337       locale, kAppContentAttr, kInitialStartPageAttr);
338 }
339
340 std::string ServicesCustomizationDocument::GetSupportPage(
341     const std::string& locale) const {
342   return GetLocaleSpecificString(
343       locale, kAppContentAttr, kSupportPageAttr);
344 }
345
346 }  // namespace chromeos