saved_files_service.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. // Copyright 2013 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. #include "apps/saved_files_service.h"
  5. #include <stdint.h>
  6. #include <algorithm>
  7. #include <map>
  8. #include <memory>
  9. #include <unordered_map>
  10. #include <utility>
  11. #include "apps/saved_files_service_factory.h"
  12. #include "base/json/values_util.h"
  13. #include "base/memory/raw_ptr.h"
  14. #include "build/chromeos_buildflags.h"
  15. #include "content/public/browser/browser_context.h"
  16. #include "extensions/browser/api/file_system/saved_file_entry.h"
  17. #include "extensions/browser/extension_host.h"
  18. #include "extensions/browser/extension_prefs.h"
  19. #include "extensions/browser/extension_system.h"
  20. #include "extensions/common/permissions/api_permission.h"
  21. #include "extensions/common/permissions/permission_set.h"
  22. #include "extensions/common/permissions/permissions_data.h"
  23. namespace apps {
  24. using extensions::APIPermission;
  25. using extensions::Extension;
  26. using extensions::ExtensionHost;
  27. using extensions::ExtensionPrefs;
  28. using extensions::SavedFileEntry;
  29. namespace {
  30. // Preference keys
  31. // The file entries that the app has permission to access.
  32. const char kFileEntries[] = "file_entries";
  33. // The path to a file entry that the app had permission to access.
  34. const char kFileEntryPath[] = "path";
  35. // Whether or not the the entry refers to a directory.
  36. const char kFileEntryIsDirectory[] = "is_directory";
  37. // The sequence number in the LRU of the file entry.
  38. const char kFileEntrySequenceNumber[] = "sequence_number";
  39. const size_t kMaxSavedFileEntries = 500;
  40. const int kMaxSequenceNumber = INT32_MAX;
  41. // These might be different to the constant values in tests.
  42. size_t g_max_saved_file_entries = kMaxSavedFileEntries;
  43. int g_max_sequence_number = kMaxSequenceNumber;
  44. // Persists a SavedFileEntry in ExtensionPrefs.
  45. void AddSavedFileEntry(ExtensionPrefs* prefs,
  46. const std::string& extension_id,
  47. const SavedFileEntry& file_entry) {
  48. ExtensionPrefs::ScopedDictionaryUpdate update(
  49. prefs, extension_id, kFileEntries);
  50. auto file_entries = update.Create();
  51. DCHECK(!file_entries->GetDictionaryWithoutPathExpansion(file_entry.id, NULL));
  52. std::unique_ptr<base::DictionaryValue> file_entry_dict =
  53. std::make_unique<base::DictionaryValue>();
  54. file_entry_dict->SetKey(kFileEntryPath,
  55. base::FilePathToValue(file_entry.path));
  56. file_entry_dict->SetBoolean(kFileEntryIsDirectory, file_entry.is_directory);
  57. file_entry_dict->SetInteger(kFileEntrySequenceNumber,
  58. file_entry.sequence_number);
  59. file_entries->SetWithoutPathExpansion(file_entry.id,
  60. std::move(file_entry_dict));
  61. }
  62. // Updates the sequence_number of a SavedFileEntry persisted in ExtensionPrefs.
  63. void UpdateSavedFileEntry(ExtensionPrefs* prefs,
  64. const std::string& extension_id,
  65. const SavedFileEntry& file_entry) {
  66. ExtensionPrefs::ScopedDictionaryUpdate update(
  67. prefs, extension_id, kFileEntries);
  68. auto file_entries = update.Get();
  69. DCHECK(file_entries);
  70. std::unique_ptr<prefs::DictionaryValueUpdate> file_entry_dict;
  71. file_entries->GetDictionaryWithoutPathExpansion(file_entry.id,
  72. &file_entry_dict);
  73. DCHECK(file_entry_dict);
  74. file_entry_dict->SetInteger(kFileEntrySequenceNumber,
  75. file_entry.sequence_number);
  76. }
  77. // Removes a SavedFileEntry from ExtensionPrefs.
  78. void RemoveSavedFileEntry(ExtensionPrefs* prefs,
  79. const std::string& extension_id,
  80. const std::string& file_entry_id) {
  81. ExtensionPrefs::ScopedDictionaryUpdate update(
  82. prefs, extension_id, kFileEntries);
  83. auto file_entries = update.Create();
  84. file_entries->RemoveWithoutPathExpansion(file_entry_id, NULL);
  85. }
  86. // Clears all SavedFileEntry for the app from ExtensionPrefs.
  87. void ClearSavedFileEntries(ExtensionPrefs* prefs,
  88. const std::string& extension_id) {
  89. prefs->UpdateExtensionPref(extension_id, kFileEntries, nullptr);
  90. }
  91. // Returns all SavedFileEntries for the app.
  92. std::vector<SavedFileEntry> GetSavedFileEntries(
  93. ExtensionPrefs* prefs,
  94. const std::string& extension_id) {
  95. std::vector<SavedFileEntry> result;
  96. const base::DictionaryValue* file_entries = NULL;
  97. if (!prefs->ReadPrefAsDictionary(extension_id, kFileEntries, &file_entries))
  98. return result;
  99. for (base::DictionaryValue::Iterator it(*file_entries); !it.IsAtEnd();
  100. it.Advance()) {
  101. const base::DictionaryValue* file_entry = NULL;
  102. if (!it.value().GetAsDictionary(&file_entry))
  103. continue;
  104. const base::Value* path_value;
  105. if (!file_entry->Get(kFileEntryPath, &path_value))
  106. continue;
  107. absl::optional<base::FilePath> file_path =
  108. base::ValueToFilePath(*path_value);
  109. if (!file_path)
  110. continue;
  111. bool is_directory =
  112. file_entry->FindBoolPath(kFileEntryIsDirectory).value_or(false);
  113. int sequence_number = 0;
  114. if (!file_entry->GetInteger(kFileEntrySequenceNumber, &sequence_number))
  115. continue;
  116. if (!sequence_number)
  117. continue;
  118. result.push_back(
  119. SavedFileEntry(it.key(), *file_path, is_directory, sequence_number));
  120. }
  121. return result;
  122. }
  123. } // namespace
  124. class SavedFilesService::SavedFiles {
  125. public:
  126. SavedFiles(content::BrowserContext* context, const std::string& extension_id);
  127. SavedFiles(const SavedFiles&) = delete;
  128. SavedFiles& operator=(const SavedFiles&) = delete;
  129. ~SavedFiles();
  130. void RegisterFileEntry(const std::string& id,
  131. const base::FilePath& file_path,
  132. bool is_directory);
  133. void EnqueueFileEntry(const std::string& id);
  134. bool IsRegistered(const std::string& id) const;
  135. const SavedFileEntry* GetFileEntry(const std::string& id) const;
  136. std::vector<SavedFileEntry> GetAllFileEntries() const;
  137. private:
  138. // Compacts sequence numbers if the largest sequence number is
  139. // g_max_sequence_number. Outside of testing, it is set to kint32max, so this
  140. // will almost never do any real work.
  141. void MaybeCompactSequenceNumbers();
  142. void LoadSavedFileEntriesFromPreferences();
  143. raw_ptr<content::BrowserContext> context_;
  144. const std::string extension_id_;
  145. // Contains all file entries that have been registered, keyed by ID.
  146. std::unordered_map<std::string, std::unique_ptr<SavedFileEntry>>
  147. registered_file_entries_;
  148. // The queue of file entries that have been retained, keyed by
  149. // sequence_number. Values are a subset of values in registered_file_entries_.
  150. // This should be kept in sync with file entries stored in extension prefs.
  151. std::map<int, SavedFileEntry*> saved_file_lru_;
  152. };
  153. // static
  154. SavedFilesService* SavedFilesService::Get(content::BrowserContext* context) {
  155. return SavedFilesServiceFactory::GetForBrowserContext(context);
  156. }
  157. SavedFilesService::SavedFilesService(content::BrowserContext* context)
  158. : context_(context) {
  159. extension_host_registry_observation_.Observe(
  160. extensions::ExtensionHostRegistry::Get(context_));
  161. }
  162. SavedFilesService::~SavedFilesService() = default;
  163. void SavedFilesService::OnExtensionHostDestroyed(
  164. content::BrowserContext* browser_context,
  165. extensions::ExtensionHost* host) {
  166. const Extension* extension = host->extension();
  167. if (extension) {
  168. ClearQueueIfNoRetainPermission(extension);
  169. Clear(extension->id());
  170. }
  171. }
  172. void SavedFilesService::RegisterFileEntry(const std::string& extension_id,
  173. const std::string& id,
  174. const base::FilePath& file_path,
  175. bool is_directory) {
  176. GetOrInsert(extension_id)->RegisterFileEntry(id, file_path, is_directory);
  177. }
  178. void SavedFilesService::EnqueueFileEntry(const std::string& extension_id,
  179. const std::string& id) {
  180. GetOrInsert(extension_id)->EnqueueFileEntry(id);
  181. }
  182. std::vector<SavedFileEntry> SavedFilesService::GetAllFileEntries(
  183. const std::string& extension_id) {
  184. SavedFiles* saved_files = Get(extension_id);
  185. if (saved_files)
  186. return saved_files->GetAllFileEntries();
  187. return GetSavedFileEntries(ExtensionPrefs::Get(context_), extension_id);
  188. }
  189. bool SavedFilesService::IsRegistered(const std::string& extension_id,
  190. const std::string& id) {
  191. return GetOrInsert(extension_id)->IsRegistered(id);
  192. }
  193. const SavedFileEntry* SavedFilesService::GetFileEntry(
  194. const std::string& extension_id,
  195. const std::string& id) {
  196. return GetOrInsert(extension_id)->GetFileEntry(id);
  197. }
  198. void SavedFilesService::ClearQueueIfNoRetainPermission(
  199. const Extension* extension) {
  200. if (!extension->permissions_data()->active_permissions().HasAPIPermission(
  201. extensions::mojom::APIPermissionID::kFileSystemRetainEntries)) {
  202. ClearQueue(extension);
  203. }
  204. }
  205. void SavedFilesService::ClearQueue(const extensions::Extension* extension) {
  206. ClearSavedFileEntries(ExtensionPrefs::Get(context_), extension->id());
  207. Clear(extension->id());
  208. }
  209. void SavedFilesService::OnApplicationTerminating() {
  210. // Stop listening to ExtensionHost shutdown as all extension hosts will be
  211. // destroyed as a result of shutdown.
  212. extension_host_registry_observation_.Reset();
  213. }
  214. SavedFilesService::SavedFiles* SavedFilesService::Get(
  215. const std::string& extension_id) const {
  216. auto it = extension_id_to_saved_files_.find(extension_id);
  217. if (it != extension_id_to_saved_files_.end())
  218. return it->second.get();
  219. return NULL;
  220. }
  221. SavedFilesService::SavedFiles* SavedFilesService::GetOrInsert(
  222. const std::string& extension_id) {
  223. SavedFiles* saved_files = Get(extension_id);
  224. if (saved_files)
  225. return saved_files;
  226. std::unique_ptr<SavedFiles> scoped_saved_files(
  227. new SavedFiles(context_, extension_id));
  228. saved_files = scoped_saved_files.get();
  229. extension_id_to_saved_files_.insert(
  230. std::make_pair(extension_id, std::move(scoped_saved_files)));
  231. return saved_files;
  232. }
  233. void SavedFilesService::Clear(const std::string& extension_id) {
  234. extension_id_to_saved_files_.erase(extension_id);
  235. }
  236. SavedFilesService::SavedFiles::SavedFiles(content::BrowserContext* context,
  237. const std::string& extension_id)
  238. : context_(context), extension_id_(extension_id) {
  239. LoadSavedFileEntriesFromPreferences();
  240. }
  241. SavedFilesService::SavedFiles::~SavedFiles() = default;
  242. void SavedFilesService::SavedFiles::RegisterFileEntry(
  243. const std::string& id,
  244. const base::FilePath& file_path,
  245. bool is_directory) {
  246. auto it = registered_file_entries_.find(id);
  247. if (it != registered_file_entries_.end())
  248. return;
  249. registered_file_entries_[id] =
  250. std::make_unique<SavedFileEntry>(id, file_path, is_directory, 0);
  251. }
  252. void SavedFilesService::SavedFiles::EnqueueFileEntry(const std::string& id) {
  253. auto id_it = registered_file_entries_.find(id);
  254. DCHECK(id_it != registered_file_entries_.end());
  255. SavedFileEntry* file_entry = id_it->second.get();
  256. int old_sequence_number = file_entry->sequence_number;
  257. #if BUILDFLAG(IS_CHROMEOS_ASH)
  258. // crbug.com/983844 Convert path from legacy Download/ to MyFiles/Downloads/
  259. // so entries saved before MyFiles don't fail. TODO(lucmult): Remove this
  260. // after M-83.
  261. const auto legacy_downloads = context_->GetPath().AppendASCII("Downloads");
  262. auto to_myfiles =
  263. context_->GetPath().AppendASCII("MyFiles").AppendASCII("Downloads");
  264. if (legacy_downloads.AppendRelativePath(file_entry->path, &to_myfiles))
  265. file_entry->path = to_myfiles;
  266. #endif
  267. if (!saved_file_lru_.empty()) {
  268. // Get the sequence number after the last file entry in the LRU.
  269. std::map<int, SavedFileEntry*>::reverse_iterator it =
  270. saved_file_lru_.rbegin();
  271. if (it->second == file_entry)
  272. return;
  273. file_entry->sequence_number = it->first + 1;
  274. } else {
  275. // The first sequence number is 1, as 0 means the entry is not in the LRU.
  276. file_entry->sequence_number = 1;
  277. }
  278. saved_file_lru_.insert(
  279. std::make_pair(file_entry->sequence_number, file_entry));
  280. ExtensionPrefs* prefs = ExtensionPrefs::Get(context_);
  281. if (old_sequence_number) {
  282. saved_file_lru_.erase(old_sequence_number);
  283. UpdateSavedFileEntry(prefs, extension_id_, *file_entry);
  284. } else {
  285. AddSavedFileEntry(prefs, extension_id_, *file_entry);
  286. if (saved_file_lru_.size() > g_max_saved_file_entries) {
  287. std::map<int, SavedFileEntry*>::iterator it = saved_file_lru_.begin();
  288. it->second->sequence_number = 0;
  289. RemoveSavedFileEntry(prefs, extension_id_, it->second->id);
  290. saved_file_lru_.erase(it);
  291. }
  292. }
  293. MaybeCompactSequenceNumbers();
  294. }
  295. bool SavedFilesService::SavedFiles::IsRegistered(const std::string& id) const {
  296. auto it = registered_file_entries_.find(id);
  297. return it != registered_file_entries_.end();
  298. }
  299. const SavedFileEntry* SavedFilesService::SavedFiles::GetFileEntry(
  300. const std::string& id) const {
  301. auto it = registered_file_entries_.find(id);
  302. if (it == registered_file_entries_.end())
  303. return NULL;
  304. return it->second.get();
  305. }
  306. std::vector<SavedFileEntry> SavedFilesService::SavedFiles::GetAllFileEntries()
  307. const {
  308. std::vector<SavedFileEntry> result;
  309. for (auto it = registered_file_entries_.begin();
  310. it != registered_file_entries_.end(); ++it) {
  311. result.push_back(*it->second.get());
  312. }
  313. return result;
  314. }
  315. void SavedFilesService::SavedFiles::MaybeCompactSequenceNumbers() {
  316. DCHECK_GE(g_max_sequence_number, 0);
  317. DCHECK_GE(static_cast<size_t>(g_max_sequence_number),
  318. g_max_saved_file_entries);
  319. std::map<int, SavedFileEntry*>::reverse_iterator last_it =
  320. saved_file_lru_.rbegin();
  321. if (last_it == saved_file_lru_.rend())
  322. return;
  323. // Only compact sequence numbers if the last entry's sequence number is the
  324. // maximum value. This should almost never be the case.
  325. if (last_it->first < g_max_sequence_number)
  326. return;
  327. int sequence_number = 0;
  328. ExtensionPrefs* prefs = ExtensionPrefs::Get(context_);
  329. for (std::map<int, SavedFileEntry*>::iterator it = saved_file_lru_.begin();
  330. it != saved_file_lru_.end();
  331. ++it) {
  332. sequence_number++;
  333. if (it->second->sequence_number == sequence_number)
  334. continue;
  335. SavedFileEntry* file_entry = it->second;
  336. file_entry->sequence_number = sequence_number;
  337. UpdateSavedFileEntry(prefs, extension_id_, *file_entry);
  338. saved_file_lru_.erase(it++);
  339. // Provide the following element as an insert hint. While optimized
  340. // insertion time with the following element as a hint is only supported by
  341. // the spec in C++11, the implementations do support this.
  342. it = saved_file_lru_.insert(
  343. it, std::make_pair(file_entry->sequence_number, file_entry));
  344. }
  345. }
  346. void SavedFilesService::SavedFiles::LoadSavedFileEntriesFromPreferences() {
  347. ExtensionPrefs* prefs = ExtensionPrefs::Get(context_);
  348. std::vector<SavedFileEntry> saved_entries =
  349. GetSavedFileEntries(prefs, extension_id_);
  350. for (std::vector<SavedFileEntry>::iterator it = saved_entries.begin();
  351. it != saved_entries.end();
  352. ++it) {
  353. std::unique_ptr<SavedFileEntry> file_entry(new SavedFileEntry(*it));
  354. const std::string& id = file_entry->id;
  355. saved_file_lru_.insert(
  356. std::make_pair(file_entry->sequence_number, file_entry.get()));
  357. registered_file_entries_[id] = std::move(file_entry);
  358. }
  359. }
  360. // static
  361. void SavedFilesService::SetMaxSequenceNumberForTest(int max_value) {
  362. g_max_sequence_number = max_value;
  363. }
  364. // static
  365. void SavedFilesService::ClearMaxSequenceNumberForTest() {
  366. g_max_sequence_number = kMaxSequenceNumber;
  367. }
  368. // static
  369. void SavedFilesService::SetLruSizeForTest(int size) {
  370. g_max_saved_file_entries = size;
  371. }
  372. // static
  373. void SavedFilesService::ClearLruSizeForTest() {
  374. g_max_saved_file_entries = kMaxSavedFileEntries;
  375. }
  376. } // namespace apps