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 <utility>
8 
9 #include "ash/public/cpp/app_list/internal_app_id_constants.h"
10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/json/json_file_value_serializer.h"
15 #include "base/path_service.h"
16 #include "base/stl_util.h"
17 #include "base/task/post_task.h"
18 #include "base/task/thread_pool.h"
19 #include "base/time/time.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/chromeos/web_applications/default_web_app_ids.h"
22 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
23 #include "chrome/browser/ui/app_list/page_break_constants.h"
24 #include "chrome/common/extensions/extension_constants.h"
25 #include "chromeos/constants/chromeos_paths.h"
26 #include "extensions/common/constants.h"
27 
28 namespace chromeos {
29 namespace default_app_order {
30 namespace {
31 
32 // The single ExternalLoader instance.
33 ExternalLoader* loader_instance = NULL;
34 
35 // Names used in JSON file.
36 const char kOemAppsFolderAttr[] = "oem_apps_folder";
37 const char kLocalizedContentAttr[] = "localized_content";
38 const char kDefaultAttr[] = "default";
39 const char kNameAttr[] = "name";
40 const char kImportDefaultOrderAttr[] = "import_default_order";
41 
42 // Canonical ordering specified in: go/default-apps
43 const char* const kDefaultAppOrder[] = {
44     extension_misc::kChromeAppId,
45     arc::kPlayStoreAppId,
46     extension_misc::kFilesManagerAppId,
47 
48     arc::kGmailAppId,
49     extension_misc::kGmailAppId,
50     default_web_apps::kGmailAppId,
51 
52     extension_misc::kGoogleDocAppId,
53     default_web_apps::kGoogleDocsAppId,
54 
55     extension_misc::kGoogleSlidesAppId,
56     default_web_apps::kGoogleSlidesAppId,
57 
58     extension_misc::kGoogleSheetsAppId,
59     default_web_apps::kGoogleSheetsAppId,
60 
61     extension_misc::kDriveHostedAppId,
62     default_web_apps::kGoogleDriveAppId,
63 
64     extension_misc::kGoogleKeepAppId,
65 
66     arc::kGoogleCalendarAppId,
67     extension_misc::kCalendarAppId,
68     default_web_apps::kGoogleCalendarAppId,
69 
70     arc::kYoutubeAppId,
71     extension_misc::kYoutubeAppId,
72     default_web_apps::kYoutubeAppId,
73 
74     arc::kYoutubeMusicAppId,
75     default_web_apps::kYoutubeMusicAppId,
76 
77     arc::kPlayMoviesAppId,
78     extension_misc::kGooglePlayMoviesAppId,
79 
80     arc::kPlayMusicAppId,
81     extension_misc::kGooglePlayMusicAppId,
82 
83     arc::kPlayGamesAppId,
84 
85     arc::kPlayBooksAppId,
86     extension_misc::kGooglePlayBooksAppId,
87     default_web_apps::kPlayBooksAppId,
88 
89     extension_misc::kCameraAppId,
90     default_web_apps::kCameraAppId,
91 
92     arc::kGooglePhotosAppId,
93     extension_misc::kGooglePhotosAppId,
94 
95     arc::kGoogleDuoAppId,
96     default_web_apps::kStadiaAppId,
97 
98     // First default page break
99     app_list::kDefaultPageBreak1,
100 
101     arc::kGoogleMapsAppId,
102     extension_misc::kGoogleMapsAppId,  // TODO(crbug.com/976578): Remove.
103     default_web_apps::kGoogleMapsAppId,
104 
105     ash::kInternalAppIdSettings,
106     default_web_apps::kSettingsAppId,
107     default_web_apps::kOsSettingsAppId,
108 
109     default_web_apps::kHelpAppId,
110     extension_misc::kCalculatorAppId,
111     default_web_apps::kCanvasAppId,
112     extension_misc::kTextEditorAppId,
113     default_web_apps::kYoutubeTVAppId,
114     default_web_apps::kGoogleNewsAppId,
115     extensions::kWebStoreAppId,
116     arc::kLightRoomAppId,
117     arc::kInfinitePainterAppId,
118     default_web_apps::kShowtimeAppId,
119     extension_misc::kGooglePlusAppId,
120     extension_misc::kChromeRemoteDesktopAppId,
121 };
122 
123 // Reads external ordinal json file and returned the parsed value. Returns NULL
124 // if the file does not exist or could not be parsed properly. Caller takes
125 // ownership of the returned value.
ReadExternalOrdinalFile(const base::FilePath & path)126 std::unique_ptr<base::ListValue> ReadExternalOrdinalFile(
127     const base::FilePath& path) {
128   if (!base::PathExists(path))
129     return NULL;
130 
131   JSONFileValueDeserializer deserializer(path);
132   std::string error_msg;
133   std::unique_ptr<base::Value> value =
134       deserializer.Deserialize(NULL, &error_msg);
135   if (!value) {
136     LOG(WARNING) << "Unable to deserialize default app ordinals json data:"
137         << error_msg << ", file=" << path.value();
138     return NULL;
139   }
140 
141   std::unique_ptr<base::ListValue> ordinal_list_value =
142       base::ListValue::From(std::move(value));
143   if (!ordinal_list_value)
144     LOG(WARNING) << "Expect a JSON list in file " << path.value();
145 
146   return ordinal_list_value;
147 }
148 
GetLocaleSpecificStringImpl(const base::DictionaryValue * root,const std::string & locale,const std::string & dictionary_name,const std::string & entry_name)149 std::string GetLocaleSpecificStringImpl(
150     const base::DictionaryValue* root,
151     const std::string& locale,
152     const std::string& dictionary_name,
153     const std::string& entry_name) {
154   const base::DictionaryValue* dictionary_content = NULL;
155   if (!root || !root->GetDictionary(dictionary_name, &dictionary_content))
156     return std::string();
157 
158   const base::DictionaryValue* locale_dictionary = NULL;
159   if (dictionary_content->GetDictionary(locale, &locale_dictionary)) {
160     std::string result;
161     if (locale_dictionary->GetString(entry_name, &result))
162       return result;
163   }
164 
165   const base::DictionaryValue* default_dictionary = NULL;
166   if (dictionary_content->GetDictionary(kDefaultAttr, &default_dictionary)) {
167     std::string result;
168     if (default_dictionary->GetString(entry_name, &result))
169       return result;
170   }
171 
172   return std::string();
173 }
174 
175 // Gets built-in default app order.
GetDefault(std::vector<std::string> * app_ids)176 void GetDefault(std::vector<std::string>* app_ids) {
177   for (size_t i = 0; i < base::size(kDefaultAppOrder); ++i)
178     app_ids->push_back(std::string(kDefaultAppOrder[i]));
179 }
180 
181 }  // namespace
182 
183 const size_t kDefaultAppOrderCount = base::size(kDefaultAppOrder);
184 
ExternalLoader(bool async)185 ExternalLoader::ExternalLoader(bool async)
186     : loaded_(base::WaitableEvent::ResetPolicy::MANUAL,
187               base::WaitableEvent::InitialState::NOT_SIGNALED) {
188   DCHECK(!loader_instance);
189   loader_instance = this;
190 
191   if (async) {
192     base::ThreadPool::PostTask(
193         FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
194         base::BindOnce(&ExternalLoader::Load, base::Unretained(this)));
195   } else {
196     Load();
197   }
198 }
199 
~ExternalLoader()200 ExternalLoader::~ExternalLoader() {
201   DCHECK(loaded_.IsSignaled());
202   DCHECK_EQ(loader_instance, this);
203   loader_instance = NULL;
204 }
205 
GetAppIds()206 const std::vector<std::string>& ExternalLoader::GetAppIds() {
207   if (!loaded_.IsSignaled())
208     LOG(ERROR) << "GetAppIds() called before loaded.";
209   return app_ids_;
210 }
211 
GetOemAppsFolderName()212 const std::string& ExternalLoader::GetOemAppsFolderName() {
213   if (!loaded_.IsSignaled())
214     LOG(ERROR) << "GetOemAppsFolderName() called before loaded.";
215   return oem_apps_folder_name_;
216 }
217 
Load()218 void ExternalLoader::Load() {
219   base::FilePath ordinals_file;
220   CHECK(
221       base::PathService::Get(chromeos::FILE_DEFAULT_APP_ORDER, &ordinals_file));
222 
223   std::unique_ptr<base::ListValue> ordinals_value =
224       ReadExternalOrdinalFile(ordinals_file);
225   if (ordinals_value) {
226     std::string locale = g_browser_process->GetApplicationLocale();
227     for (size_t i = 0; i < ordinals_value->GetSize(); ++i) {
228       std::string app_id;
229       base::DictionaryValue* dict = NULL;
230       if (ordinals_value->GetString(i, &app_id)) {
231         app_ids_.push_back(app_id);
232       } else if (ordinals_value->GetDictionary(i, &dict)) {
233         bool flag = false;
234         if (dict->GetBoolean(kOemAppsFolderAttr, &flag) && flag) {
235           oem_apps_folder_name_ = GetLocaleSpecificStringImpl(
236               dict, locale, kLocalizedContentAttr, kNameAttr);
237         } else if (dict->GetBoolean(kImportDefaultOrderAttr, &flag) && flag) {
238           GetDefault(&app_ids_);
239         } else {
240           LOG(ERROR) << "Invalid syntax in default_app_order.json";
241         }
242       } else {
243         LOG(ERROR) << "Invalid entry in default_app_order.json";
244       }
245     }
246   } else {
247     GetDefault(&app_ids_);
248   }
249 
250   loaded_.Signal();
251 }
252 
Get(std::vector<std::string> * app_ids)253 void Get(std::vector<std::string>* app_ids) {
254   // |loader_instance| could be NULL for test.
255   if (!loader_instance) {
256     GetDefault(app_ids);
257     return;
258   }
259 
260   *app_ids = loader_instance->GetAppIds();
261 }
262 
GetOemAppsFolderName()263 std::string GetOemAppsFolderName() {
264   // |loader_instance| could be NULL for test.
265   if (!loader_instance)
266     return std::string();
267   else
268     return loader_instance->GetOemAppsFolderName();
269 }
270 
271 }  // namespace default_app_order
272 }  // namespace chromeos
273