Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / net / base / directory_lister.cc
blobefdc53c4a7b08a558edc26ed664a8f6c801ac030
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.
5 #include "net/base/directory_lister.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/files/file_enumerator.h"
11 #include "base/files/file_util.h"
12 #include "base/i18n/file_util_icu.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "base/threading/thread_restrictions.h"
18 #include "base/threading/worker_pool.h"
19 #include "net/base/net_errors.h"
21 namespace net {
23 namespace {
25 bool IsDotDot(const base::FilePath& path) {
26 return FILE_PATH_LITERAL("..") == path.BaseName().value();
29 // Comparator for sorting lister results. This uses the locale aware filename
30 // comparison function on the filenames for sorting in the user's locale.
31 // Static.
32 bool CompareAlphaDirsFirst(const DirectoryLister::DirectoryListerData& a,
33 const DirectoryLister::DirectoryListerData& b) {
34 // Parent directory before all else.
35 if (IsDotDot(a.info.GetName()))
36 return true;
37 if (IsDotDot(b.info.GetName()))
38 return false;
40 // Directories before regular files.
41 bool a_is_directory = a.info.IsDirectory();
42 bool b_is_directory = b.info.IsDirectory();
43 if (a_is_directory != b_is_directory)
44 return a_is_directory;
46 return base::i18n::LocaleAwareCompareFilenames(a.info.GetName(),
47 b.info.GetName());
50 void SortData(std::vector<DirectoryLister::DirectoryListerData>* data,
51 DirectoryLister::ListingType listing_type) {
52 // Sort the results. See the TODO below (this sort should be removed and we
53 // should do it from JS).
54 if (listing_type == DirectoryLister::ALPHA_DIRS_FIRST) {
55 std::sort(data->begin(), data->end(), CompareAlphaDirsFirst);
56 } else if (listing_type != DirectoryLister::NO_SORT &&
57 listing_type != DirectoryLister::NO_SORT_RECURSIVE) {
58 NOTREACHED();
62 } // namespace
64 DirectoryLister::DirectoryLister(const base::FilePath& dir,
65 DirectoryListerDelegate* delegate)
66 : delegate_(delegate) {
67 core_ = new Core(dir, ALPHA_DIRS_FIRST, this);
68 DCHECK(delegate_);
69 DCHECK(!dir.value().empty());
72 DirectoryLister::DirectoryLister(const base::FilePath& dir,
73 ListingType type,
74 DirectoryListerDelegate* delegate)
75 : delegate_(delegate) {
76 core_ = new Core(dir, type, this);
77 DCHECK(delegate_);
78 DCHECK(!dir.value().empty());
81 DirectoryLister::~DirectoryLister() {
82 Cancel();
85 bool DirectoryLister::Start() {
86 return base::WorkerPool::PostTask(
87 FROM_HERE,
88 base::Bind(&Core::Start, core_),
89 true);
92 void DirectoryLister::Cancel() {
93 core_->CancelOnOriginThread();
96 DirectoryLister::Core::Core(const base::FilePath& dir,
97 ListingType type,
98 DirectoryLister* lister)
99 : dir_(dir),
100 type_(type),
101 origin_task_runner_(base::ThreadTaskRunnerHandle::Get()),
102 lister_(lister),
103 cancelled_(0) {
104 DCHECK(lister_);
107 DirectoryLister::Core::~Core() {}
109 void DirectoryLister::Core::CancelOnOriginThread() {
110 DCHECK(origin_task_runner_->BelongsToCurrentThread());
112 base::subtle::NoBarrier_Store(&cancelled_, 1);
113 // Core must not call into |lister_| after cancellation, as the |lister_| may
114 // have been destroyed. Setting |lister_| to NULL ensures any such access will
115 // cause a crash.
116 lister_ = nullptr;
119 void DirectoryLister::Core::Start() {
120 scoped_ptr<DirectoryList> directory_list(new DirectoryList());
122 if (!base::DirectoryExists(dir_)) {
123 origin_task_runner_->PostTask(
124 FROM_HERE,
125 base::Bind(&Core::DoneOnOriginThread, this,
126 base::Passed(directory_list.Pass()), ERR_FILE_NOT_FOUND));
127 return;
130 int types = base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES;
131 bool recursive;
132 if (NO_SORT_RECURSIVE != type_) {
133 types |= base::FileEnumerator::INCLUDE_DOT_DOT;
134 recursive = false;
135 } else {
136 recursive = true;
138 base::FileEnumerator file_enum(dir_, recursive, types);
140 base::FilePath path;
141 while (!(path = file_enum.Next()).empty()) {
142 // Abort on cancellation. This is purely for performance reasons.
143 // Correctness guarantees are made by checks in DoneOnOriginThread.
144 if (IsCancelled())
145 return;
147 DirectoryListerData data;
148 data.info = file_enum.GetInfo();
149 data.path = path;
150 directory_list->push_back(data);
152 /* TODO(brettw) bug 24107: It would be nice to send incremental updates.
153 We gather them all so they can be sorted, but eventually the sorting
154 should be done from JS to give more flexibility in the page. When we do
155 that, we can uncomment this to send incremental updates to the page.
157 const int kFilesPerEvent = 8;
158 if (file_data.size() < kFilesPerEvent)
159 continue;
161 origin_loop_->PostTask(
162 FROM_HERE,
163 base::Bind(&DirectoryLister::Core::SendData, file_data));
164 file_data.clear();
168 SortData(directory_list.get(), type_);
170 origin_task_runner_->PostTask(
171 FROM_HERE, base::Bind(&Core::DoneOnOriginThread, this,
172 base::Passed(directory_list.Pass()), OK));
175 bool DirectoryLister::Core::IsCancelled() const {
176 return !!base::subtle::NoBarrier_Load(&cancelled_);
179 void DirectoryLister::Core::DoneOnOriginThread(
180 scoped_ptr<DirectoryList> directory_list, int error) const {
181 DCHECK(origin_task_runner_->BelongsToCurrentThread());
183 // Need to check if the operation was before first callback.
184 if (IsCancelled())
185 return;
187 for (const auto& lister_data : *directory_list) {
188 lister_->OnListFile(lister_data);
189 // Need to check if the operation was cancelled during the callback.
190 if (IsCancelled())
191 return;
193 lister_->OnListDone(error);
196 void DirectoryLister::OnListFile(const DirectoryListerData& data) {
197 delegate_->OnListFile(data);
200 void DirectoryLister::OnListDone(int error) {
201 delegate_->OnListDone(error);
204 } // namespace net