PlzNavigate: Improvements to RFHM commit logic.
[chromium-blink-merge.git] / net / base / directory_lister.cc
blobf746f9b2e476576098d8d5ea9c7400c4674c5ecb
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/logging.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/threading/thread_restrictions.h"
16 #include "base/threading/worker_pool.h"
17 #include "net/base/net_errors.h"
19 namespace net {
21 namespace {
23 bool IsDotDot(const base::FilePath& path) {
24 return FILE_PATH_LITERAL("..") == path.BaseName().value();
27 // Comparator for sorting lister results. This uses the locale aware filename
28 // comparison function on the filenames for sorting in the user's locale.
29 // Static.
30 bool CompareAlphaDirsFirst(const DirectoryLister::DirectoryListerData& a,
31 const DirectoryLister::DirectoryListerData& b) {
32 // Parent directory before all else.
33 if (IsDotDot(a.info.GetName()))
34 return true;
35 if (IsDotDot(b.info.GetName()))
36 return false;
38 // Directories before regular files.
39 bool a_is_directory = a.info.IsDirectory();
40 bool b_is_directory = b.info.IsDirectory();
41 if (a_is_directory != b_is_directory)
42 return a_is_directory;
44 return base::i18n::LocaleAwareCompareFilenames(a.info.GetName(),
45 b.info.GetName());
48 bool CompareDate(const DirectoryLister::DirectoryListerData& a,
49 const DirectoryLister::DirectoryListerData& b) {
50 // Parent directory before all else.
51 if (IsDotDot(a.info.GetName()))
52 return true;
53 if (IsDotDot(b.info.GetName()))
54 return false;
56 // Directories before regular files.
57 bool a_is_directory = a.info.IsDirectory();
58 bool b_is_directory = b.info.IsDirectory();
59 if (a_is_directory != b_is_directory)
60 return a_is_directory;
61 return a.info.GetLastModifiedTime() > b.info.GetLastModifiedTime();
64 // Comparator for sorting find result by paths. This uses the locale-aware
65 // comparison function on the filenames for sorting in the user's locale.
66 // Static.
67 bool CompareFullPath(const DirectoryLister::DirectoryListerData& a,
68 const DirectoryLister::DirectoryListerData& b) {
69 return base::i18n::LocaleAwareCompareFilenames(a.path, b.path);
72 void SortData(std::vector<DirectoryLister::DirectoryListerData>* data,
73 DirectoryLister::SortType sort_type) {
74 // Sort the results. See the TODO below (this sort should be removed and we
75 // should do it from JS).
76 if (sort_type == DirectoryLister::DATE) {
77 std::sort(data->begin(), data->end(), CompareDate);
78 } else if (sort_type == DirectoryLister::FULL_PATH) {
79 std::sort(data->begin(), data->end(), CompareFullPath);
80 } else if (sort_type == DirectoryLister::ALPHA_DIRS_FIRST) {
81 std::sort(data->begin(), data->end(), CompareAlphaDirsFirst);
82 } else {
83 DCHECK_EQ(DirectoryLister::NO_SORT, sort_type);
87 } // namespace
89 DirectoryLister::DirectoryLister(const base::FilePath& dir,
90 DirectoryListerDelegate* delegate)
91 : delegate_(delegate) {
92 core_ = new Core(dir, false, ALPHA_DIRS_FIRST, this);
93 DCHECK(delegate_);
94 DCHECK(!dir.value().empty());
97 DirectoryLister::DirectoryLister(const base::FilePath& dir,
98 bool recursive,
99 SortType sort,
100 DirectoryListerDelegate* delegate)
101 : delegate_(delegate) {
102 core_ = new Core(dir, recursive, sort, this);
103 DCHECK(delegate_);
104 DCHECK(!dir.value().empty());
107 DirectoryLister::~DirectoryLister() {
108 Cancel();
111 bool DirectoryLister::Start() {
112 return base::WorkerPool::PostTask(
113 FROM_HERE,
114 base::Bind(&Core::Start, core_),
115 true);
118 void DirectoryLister::Cancel() {
119 core_->CancelOnOriginThread();
122 DirectoryLister::Core::Core(const base::FilePath& dir,
123 bool recursive,
124 SortType sort,
125 DirectoryLister* lister)
126 : dir_(dir),
127 recursive_(recursive),
128 sort_(sort),
129 origin_loop_(base::MessageLoopProxy::current()),
130 lister_(lister),
131 cancelled_(0) {
132 DCHECK(lister_);
135 DirectoryLister::Core::~Core() {}
137 void DirectoryLister::Core::CancelOnOriginThread() {
138 DCHECK(origin_loop_->BelongsToCurrentThread());
140 base::subtle::NoBarrier_Store(&cancelled_, 1);
141 // Core must not call into |lister_| after cancellation, as the |lister_| may
142 // have been destroyed. Setting |lister_| to NULL ensures any such access will
143 // cause a crash.
144 lister_ = nullptr;
147 void DirectoryLister::Core::Start() {
148 scoped_ptr<DirectoryList> directory_list(new DirectoryList());
150 if (!base::DirectoryExists(dir_)) {
151 origin_loop_->PostTask(
152 FROM_HERE,
153 base::Bind(&Core::DoneOnOriginThread, this,
154 base::Passed(directory_list.Pass()), ERR_FILE_NOT_FOUND));
155 return;
158 int types = base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES;
159 if (!recursive_)
160 types |= base::FileEnumerator::INCLUDE_DOT_DOT;
162 base::FileEnumerator file_enum(dir_, recursive_, types);
164 base::FilePath path;
165 while (!(path = file_enum.Next()).empty()) {
166 // Abort on cancellation. This is purely for performance reasons.
167 // Correctness guarantees are made by checks in DoneOnOriginThread.
168 if (IsCancelled())
169 return;
171 DirectoryListerData data;
172 data.info = file_enum.GetInfo();
173 data.path = path;
174 directory_list->push_back(data);
176 /* TODO(brettw) bug 24107: It would be nice to send incremental updates.
177 We gather them all so they can be sorted, but eventually the sorting
178 should be done from JS to give more flexibility in the page. When we do
179 that, we can uncomment this to send incremental updates to the page.
181 const int kFilesPerEvent = 8;
182 if (file_data.size() < kFilesPerEvent)
183 continue;
185 origin_loop_->PostTask(
186 FROM_HERE,
187 base::Bind(&DirectoryLister::Core::SendData, file_data));
188 file_data.clear();
192 SortData(directory_list.get(), sort_);
194 origin_loop_->PostTask(
195 FROM_HERE,
196 base::Bind(&Core::DoneOnOriginThread, this,
197 base::Passed(directory_list.Pass()), OK));
200 bool DirectoryLister::Core::IsCancelled() const {
201 return !!base::subtle::NoBarrier_Load(&cancelled_);
204 void DirectoryLister::Core::DoneOnOriginThread(
205 scoped_ptr<DirectoryList> directory_list, int error) const {
206 DCHECK(origin_loop_->BelongsToCurrentThread());
208 // Need to check if the operation was before first callback.
209 if (IsCancelled())
210 return;
212 for (const auto& lister_data : *directory_list) {
213 lister_->OnListFile(lister_data);
214 // Need to check if the operation was cancelled during the callback.
215 if (IsCancelled())
216 return;
218 lister_->OnListDone(error);
221 void DirectoryLister::OnListFile(const DirectoryListerData& data) {
222 delegate_->OnListFile(data);
225 void DirectoryLister::OnListDone(int error) {
226 delegate_->OnListDone(error);
229 } // namespace net