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/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"
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.
32 bool CompareAlphaDirsFirst(const DirectoryLister::DirectoryListerData
& a
,
33 const DirectoryLister::DirectoryListerData
& b
) {
34 // Parent directory before all else.
35 if (IsDotDot(a
.info
.GetName()))
37 if (IsDotDot(b
.info
.GetName()))
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(),
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
) {
64 DirectoryLister::DirectoryLister(const base::FilePath
& dir
,
65 DirectoryListerDelegate
* delegate
)
66 : delegate_(delegate
) {
67 core_
= new Core(dir
, ALPHA_DIRS_FIRST
, this);
69 DCHECK(!dir
.value().empty());
72 DirectoryLister::DirectoryLister(const base::FilePath
& dir
,
74 DirectoryListerDelegate
* delegate
)
75 : delegate_(delegate
) {
76 core_
= new Core(dir
, type
, this);
78 DCHECK(!dir
.value().empty());
81 DirectoryLister::~DirectoryLister() {
85 bool DirectoryLister::Start() {
86 return base::WorkerPool::PostTask(
88 base::Bind(&Core::Start
, core_
),
92 void DirectoryLister::Cancel() {
93 core_
->CancelOnOriginThread();
96 DirectoryLister::Core::Core(const base::FilePath
& dir
,
98 DirectoryLister
* lister
)
101 origin_task_runner_(base::ThreadTaskRunnerHandle::Get()),
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
119 void DirectoryLister::Core::Start() {
120 scoped_ptr
<DirectoryList
> directory_list(new DirectoryList());
122 if (!base::DirectoryExists(dir_
)) {
123 origin_task_runner_
->PostTask(
125 base::Bind(&Core::DoneOnOriginThread
, this,
126 base::Passed(directory_list
.Pass()), ERR_FILE_NOT_FOUND
));
130 int types
= base::FileEnumerator::FILES
| base::FileEnumerator::DIRECTORIES
;
132 if (NO_SORT_RECURSIVE
!= type_
) {
133 types
|= base::FileEnumerator::INCLUDE_DOT_DOT
;
138 base::FileEnumerator
file_enum(dir_
, recursive
, types
);
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.
147 DirectoryListerData data
;
148 data
.info
= file_enum
.GetInfo();
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)
161 origin_loop_->PostTask(
163 base::Bind(&DirectoryLister::Core::SendData, file_data));
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.
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.
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
);