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/extensions/default_app_order.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/files/file_path.h"
10#include "base/files/file_util.h"
11#include "base/json/json_file_value_serializer.h"
12#include "base/path_service.h"
13#include "base/time/time.h"
14#include "chrome/browser/browser_process.h"
15#include "chrome/common/extensions/extension_constants.h"
16#include "chromeos/chromeos_paths.h"
17#include "content/public/browser/browser_thread.h"
18#include "extensions/common/constants.h"
19
20namespace chromeos {
21namespace default_app_order {
22namespace {
23
24// The single ExternalLoader instance.
25ExternalLoader* loader_instance = NULL;
26
27// Names used in JSON file.
28const char kOemAppsFolderAttr[] = "oem_apps_folder";
29const char kLocalizedContentAttr[] = "localized_content";
30const char kDefaultAttr[] = "default";
31const char kNameAttr[] = "name";
32const char kImportDefaultOrderAttr[] = "import_default_order";
33
34const char* kDefaultAppOrder[] = {
35    extension_misc::kChromeAppId,
36    extensions::kWebStoreAppId,
37    extension_misc::kGoogleSearchAppId,
38    extension_misc::kYoutubeAppId,
39    extension_misc::kGmailAppId,
40    "ejjicmeblgpmajnghnpcppodonldlgfn",  // Calendar
41    "kjebfhglflhjjjiceimfkgicifkhjlnm",  // Scratchpad
42    "lneaknkopdijkpnocmklfnjbeapigfbh",  // Google Maps
43    "apdfllckaahabafndbhieahigkjlhalf",  // Drive
44    extension_misc::kGoogleDocAppId,
45    extension_misc::kGoogleSheetsAppId,
46    extension_misc::kGoogleSlidesAppId,
47    "dlppkpafhbajpcmmoheippocdidnckmm",  // Google+
48    "kbpgddbgniojgndnhlkjbkpknjhppkbk",  // Google+ Hangouts
49    "hhaomjibdihmijegdhdafkllkbggdgoj",  // Files
50    extension_misc::kGooglePlayMusicAppId,
51    "mmimngoggfoobjdlefbcabngfnmieonb",  // Play Books
52    "gdijeikdkaembjbdobgfkoidjkpbmlkd",  // Play Movies & TV
53    "fobcpibfeplaikcclojfdhfdmbbeofai",  // Games
54    "joodangkbfjnajiiifokapkpmhfnpleo",  // Calculator
55    "hfhhnacclhffhdffklopdkcgdhifgngh",  // Camera
56    "gbchcmhmhahfdphkhkmpfmihenigjmpp",  // Chrome Remote Desktop
57};
58
59// Reads external ordinal json file and returned the parsed value. Returns NULL
60// if the file does not exist or could not be parsed properly. Caller takes
61// ownership of the returned value.
62base::ListValue* ReadExternalOrdinalFile(const base::FilePath& path) {
63  if (!base::PathExists(path))
64    return NULL;
65
66  JSONFileValueSerializer serializer(path);
67  std::string error_msg;
68  base::Value* value = serializer.Deserialize(NULL, &error_msg);
69  if (!value) {
70    LOG(WARNING) << "Unable to deserialize default app ordinals json data:"
71        << error_msg << ", file=" << path.value();
72    return NULL;
73  }
74
75  base::ListValue* ordinal_list_value = NULL;
76  if (value->GetAsList(&ordinal_list_value))
77    return ordinal_list_value;
78
79  LOG(WARNING) << "Expect a JSON list in file " << path.value();
80  return NULL;
81}
82
83std::string GetLocaleSpecificStringImpl(
84    const base::DictionaryValue* root,
85    const std::string& locale,
86    const std::string& dictionary_name,
87    const std::string& entry_name) {
88  const base::DictionaryValue* dictionary_content = NULL;
89  if (!root || !root->GetDictionary(dictionary_name, &dictionary_content))
90    return std::string();
91
92  const base::DictionaryValue* locale_dictionary = NULL;
93  if (dictionary_content->GetDictionary(locale, &locale_dictionary)) {
94    std::string result;
95    if (locale_dictionary->GetString(entry_name, &result))
96      return result;
97  }
98
99  const base::DictionaryValue* default_dictionary = NULL;
100  if (dictionary_content->GetDictionary(kDefaultAttr, &default_dictionary)) {
101    std::string result;
102    if (default_dictionary->GetString(entry_name, &result))
103      return result;
104  }
105
106  return std::string();
107}
108
109// Gets built-in default app order.
110void GetDefault(std::vector<std::string>* app_ids) {
111  for (size_t i = 0; i < arraysize(kDefaultAppOrder); ++i)
112    app_ids->push_back(std::string(kDefaultAppOrder[i]));
113}
114
115}  // namespace
116
117const size_t kDefaultAppOrderCount = arraysize(kDefaultAppOrder);
118
119ExternalLoader::ExternalLoader(bool async)
120    : loaded_(true /* manual_rest */, false /* initially_signaled */) {
121  DCHECK(!loader_instance);
122  loader_instance = this;
123
124  if (async) {
125    content::BrowserThread::PostBlockingPoolTask(FROM_HERE,
126        base::Bind(&ExternalLoader::Load, base::Unretained(this)));
127  } else {
128    Load();
129  }
130}
131
132ExternalLoader::~ExternalLoader() {
133  DCHECK(loaded_.IsSignaled());
134  DCHECK_EQ(loader_instance, this);
135  loader_instance = NULL;
136}
137
138const std::vector<std::string>& ExternalLoader::GetAppIds() {
139  if (!loaded_.IsSignaled())
140    LOG(ERROR) << "GetAppIds() called before loaded.";
141  return app_ids_;
142}
143
144const std::string& ExternalLoader::GetOemAppsFolderName() {
145  if (!loaded_.IsSignaled())
146    LOG(ERROR) << "GetOemAppsFolderName() called before loaded.";
147  return oem_apps_folder_name_;
148}
149
150void ExternalLoader::Load() {
151  base::FilePath ordinals_file;
152  CHECK(PathService::Get(chromeos::FILE_DEFAULT_APP_ORDER, &ordinals_file));
153
154  scoped_ptr<base::ListValue> ordinals_value(
155      ReadExternalOrdinalFile(ordinals_file));
156  if (ordinals_value) {
157    std::string locale = g_browser_process->GetApplicationLocale();
158    for (size_t i = 0; i < ordinals_value->GetSize(); ++i) {
159      std::string app_id;
160      base::DictionaryValue* dict = NULL;
161      if (ordinals_value->GetString(i, &app_id)) {
162        app_ids_.push_back(app_id);
163      } else if (ordinals_value->GetDictionary(i, &dict)) {
164        bool flag = false;
165        if (dict->GetBoolean(kOemAppsFolderAttr, &flag) && flag) {
166          oem_apps_folder_name_ = GetLocaleSpecificStringImpl(
167              dict, locale, kLocalizedContentAttr, kNameAttr);
168        } else if (dict->GetBoolean(kImportDefaultOrderAttr, &flag) && flag) {
169          GetDefault(&app_ids_);
170        } else {
171          LOG(ERROR) << "Invalid syntax in default_app_order.json";
172        }
173      } else {
174        LOG(ERROR) << "Invalid entry in default_app_order.json";
175      }
176    }
177  } else {
178    GetDefault(&app_ids_);
179  }
180
181  loaded_.Signal();
182}
183
184void Get(std::vector<std::string>* app_ids) {
185  // |loader_instance| could be NULL for test.
186  if (!loader_instance) {
187    GetDefault(app_ids);
188    return;
189  }
190
191  *app_ids = loader_instance->GetAppIds();
192}
193
194std::string GetOemAppsFolderName() {
195  // |loader_instance| could be NULL for test.
196  if (!loader_instance)
197    return std::string();
198  else
199    return loader_instance->GetOemAppsFolderName();
200}
201
202}  // namespace default_app_order
203}  // namespace chromeos
204