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"
10 #include "base/bind.h"
11 #include "base/file_util.h"
12 #include "base/files/file_enumerator.h"
13 #include "base/i18n/file_util_icu.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"
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.
30 bool CompareAlphaDirsFirst(const DirectoryLister::DirectoryListerData
& a
,
31 const DirectoryLister::DirectoryListerData
& b
) {
32 // Parent directory before all else.
33 if (IsDotDot(a
.info
.GetName()))
35 if (IsDotDot(b
.info
.GetName()))
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 file_util::LocaleAwareCompareFilenames(a
.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()))
53 if (IsDotDot(b
.info
.GetName()))
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.
67 bool CompareFullPath(const DirectoryLister::DirectoryListerData
& a
,
68 const DirectoryLister::DirectoryListerData
& b
) {
69 return file_util::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
);
83 DCHECK_EQ(DirectoryLister::NO_SORT
, sort_type
);
88 DirectoryLister::DirectoryLister(const base::FilePath
& dir
,
89 DirectoryListerDelegate
* delegate
)
90 : core_(new Core(dir
, false, ALPHA_DIRS_FIRST
, this)),
93 DCHECK(!dir
.value().empty());
96 DirectoryLister::DirectoryLister(const base::FilePath
& dir
,
99 DirectoryListerDelegate
* delegate
)
100 : core_(new Core(dir
, recursive
, sort
, this)),
101 delegate_(delegate
) {
103 DCHECK(!dir
.value().empty());
106 DirectoryLister::~DirectoryLister() {
110 bool DirectoryLister::Start() {
111 return core_
->Start();
114 void DirectoryLister::Cancel() {
115 return core_
->Cancel();
118 DirectoryLister::Core::Core(const base::FilePath
& dir
,
121 DirectoryLister
* lister
)
123 recursive_(recursive
),
129 DirectoryLister::Core::~Core() {}
131 bool DirectoryLister::Core::Start() {
132 origin_loop_
= base::MessageLoopProxy::current();
134 return base::WorkerPool::PostTask(
135 FROM_HERE
, base::Bind(&Core::StartInternal
, this), true);
138 void DirectoryLister::Core::Cancel() {
142 void DirectoryLister::Core::StartInternal() {
144 if (!base::DirectoryExists(dir_
)) {
145 origin_loop_
->PostTask(
147 base::Bind(&DirectoryLister::Core::OnDone
, this, ERR_FILE_NOT_FOUND
));
151 int types
= base::FileEnumerator::FILES
| base::FileEnumerator::DIRECTORIES
;
153 types
|= base::FileEnumerator::INCLUDE_DOT_DOT
;
155 base::FileEnumerator
file_enum(dir_
, recursive_
, types
);
158 std::vector
<DirectoryListerData
> file_data
;
159 while (lister_
&& !(path
= file_enum
.Next()).empty()) {
160 DirectoryListerData data
;
161 data
.info
= file_enum
.GetInfo();
163 file_data
.push_back(data
);
165 /* TODO(brettw) bug 24107: It would be nice to send incremental updates.
166 We gather them all so they can be sorted, but eventually the sorting
167 should be done from JS to give more flexibility in the page. When we do
168 that, we can uncomment this to send incremental updates to the page.
170 const int kFilesPerEvent = 8;
171 if (file_data.size() < kFilesPerEvent)
174 origin_loop_->PostTask(
176 base::Bind(&DirectoryLister::Core::SendData, file_data));
181 SortData(&file_data
, sort_
);
182 origin_loop_
->PostTask(
184 base::Bind(&DirectoryLister::Core::SendData
, this, file_data
));
186 origin_loop_
->PostTask(
188 base::Bind(&DirectoryLister::Core::OnDone
, this, OK
));
191 void DirectoryLister::Core::SendData(
192 const std::vector
<DirectoryLister::DirectoryListerData
>& data
) {
193 DCHECK(origin_loop_
->BelongsToCurrentThread());
194 // We need to check for cancellation (indicated by NULL'ing of |lister_|)
195 // which can happen during each callback.
196 for (size_t i
= 0; lister_
&& i
< data
.size(); ++i
)
197 lister_
->OnReceivedData(data
[i
]);
200 void DirectoryLister::Core::OnDone(int error
) {
201 DCHECK(origin_loop_
->BelongsToCurrentThread());
203 lister_
->OnDone(error
);
206 void DirectoryLister::OnReceivedData(const DirectoryListerData
& data
) {
207 delegate_
->OnListFile(data
);
210 void DirectoryLister::OnDone(int error
) {
211 delegate_
->OnListDone(error
);