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