OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/base/directory_lister.h" | 5 #include "net/base/directory_lister.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/file_util.h" | 10 #include "base/file_util.h" |
11 #include "base/i18n/file_util_icu.h" | 11 #include "base/i18n/file_util_icu.h" |
12 #include "base/message_loop.h" | 12 #include "base/message_loop.h" |
13 #include "base/platform_thread.h" | 13 #include "base/platform_thread.h" |
14 #include "net/base/net_errors.h" | 14 #include "net/base/net_errors.h" |
15 | 15 |
16 namespace net { | 16 namespace net { |
17 | 17 |
18 static const int kFilesPerEvent = 8; | 18 static const int kFilesPerEvent = 8; |
19 | 19 |
| 20 // A task which is used to signal the delegate asynchronously. |
20 class DirectoryDataEvent : public Task { | 21 class DirectoryDataEvent : public Task { |
21 public: | 22 public: |
22 explicit DirectoryDataEvent(DirectoryLister* d) : lister(d), error(0) { | 23 explicit DirectoryDataEvent(DirectoryLister* d) : lister(d), error(0) { |
23 // Allocations of the FindInfo aren't super cheap, so reserve space. | 24 // Allocations of the FindInfo aren't super cheap, so reserve space. |
24 data.reserve(64); | 25 data.reserve(64); |
25 } | 26 } |
26 | 27 |
27 void Run() { | 28 void Run() { |
28 if (data.empty()) { | 29 if (data.empty()) { |
29 lister->OnDone(error); | 30 lister->OnDone(error); |
30 return; | 31 return; |
31 } | 32 } |
32 lister->OnReceivedData(&data[0], static_cast<int>(data.size())); | 33 lister->OnReceivedData(&data[0], static_cast<int>(data.size())); |
33 } | 34 } |
34 | 35 |
35 scoped_refptr<DirectoryLister> lister; | 36 scoped_refptr<DirectoryLister> lister; |
36 std::vector<file_util::FileEnumerator::FindInfo> data; | 37 std::vector<DirectoryLister::DirectoryListerData> data; |
37 int error; | 38 int error; |
38 }; | 39 }; |
39 | 40 |
40 // Comparator for sorting FindInfo's. This uses the locale aware filename | 41 // Comparator for sorting lister results. This uses the locale aware filename |
41 // comparison function on the filenames for sorting in the user's locale. | 42 // comparison function on the filenames for sorting in the user's locale. |
42 static bool CompareFindInfoAlpha(const file_util::FileEnumerator::FindInfo& a, | 43 // Static. |
43 const file_util::FileEnumerator::FindInfo& b) { | 44 bool DirectoryLister::CompareAlphaDirsFirst(const DirectoryListerData& a, |
| 45 const DirectoryListerData& b) { |
44 // Parent directory before all else. | 46 // Parent directory before all else. |
45 if (file_util::IsDotDot(file_util::FileEnumerator::GetFilename(a))) | 47 if (file_util::IsDotDot(file_util::FileEnumerator::GetFilename(a.info))) |
46 return true; | 48 return true; |
47 if (file_util::IsDotDot(file_util::FileEnumerator::GetFilename(b))) | 49 if (file_util::IsDotDot(file_util::FileEnumerator::GetFilename(b.info))) |
48 return false; | 50 return false; |
49 | 51 |
50 // Directories before regular files. | 52 // Directories before regular files. |
51 bool a_is_directory = file_util::FileEnumerator::IsDirectory(a); | 53 bool a_is_directory = file_util::FileEnumerator::IsDirectory(a.info); |
52 bool b_is_directory = file_util::FileEnumerator::IsDirectory(b); | 54 bool b_is_directory = file_util::FileEnumerator::IsDirectory(b.info); |
53 if (a_is_directory != b_is_directory) | 55 if (a_is_directory != b_is_directory) |
54 return a_is_directory; | 56 return a_is_directory; |
55 | 57 |
56 return file_util::LocaleAwareCompareFilenames( | 58 return file_util::LocaleAwareCompareFilenames( |
57 file_util::FileEnumerator::GetFilename(a), | 59 file_util::FileEnumerator::GetFilename(a.info), |
58 file_util::FileEnumerator::GetFilename(b)); | 60 file_util::FileEnumerator::GetFilename(b.info)); |
59 } | 61 } |
60 | 62 |
61 static bool CompareFindInfoDate(const file_util::FileEnumerator::FindInfo& a, | 63 // Static. |
62 const file_util::FileEnumerator::FindInfo& b) { | 64 bool DirectoryLister::CompareDate(const DirectoryListerData& a, |
| 65 const DirectoryListerData& b) { |
63 // Parent directory before all else. | 66 // Parent directory before all else. |
64 if (file_util::IsDotDot(file_util::FileEnumerator::GetFilename(a))) | 67 if (file_util::IsDotDot(file_util::FileEnumerator::GetFilename(a.info))) |
65 return true; | 68 return true; |
66 if (file_util::IsDotDot(file_util::FileEnumerator::GetFilename(b))) | 69 if (file_util::IsDotDot(file_util::FileEnumerator::GetFilename(b.info))) |
67 return false; | 70 return false; |
68 | 71 |
69 // Directories before regular files. | 72 // Directories before regular files. |
70 bool a_is_directory = file_util::FileEnumerator::IsDirectory(a); | 73 bool a_is_directory = file_util::FileEnumerator::IsDirectory(a.info); |
71 bool b_is_directory = file_util::FileEnumerator::IsDirectory(b); | 74 bool b_is_directory = file_util::FileEnumerator::IsDirectory(b.info); |
72 if (a_is_directory != b_is_directory) | 75 if (a_is_directory != b_is_directory) |
73 return a_is_directory; | 76 return a_is_directory; |
74 #if defined(OS_POSIX) | 77 #if defined(OS_POSIX) |
75 return a.stat.st_mtime > b.stat.st_mtime; | 78 return a.info.stat.st_mtime > b.info.stat.st_mtime; |
76 #elif defined(OS_WIN) | 79 #elif defined(OS_WIN) |
77 if (a.ftLastWriteTime.dwHighDateTime == b.ftLastWriteTime.dwHighDateTime) { | 80 if (a.info.ftLastWriteTime.dwHighDateTime == |
78 return a.ftLastWriteTime.dwLowDateTime > b.ftLastWriteTime.dwLowDateTime; | 81 b.info.ftLastWriteTime.dwHighDateTime) { |
| 82 return a.info.ftLastWriteTime.dwLowDateTime > |
| 83 b.info.ftLastWriteTime.dwLowDateTime; |
79 } else { | 84 } else { |
80 return a.ftLastWriteTime.dwHighDateTime > b.ftLastWriteTime.dwHighDateTime; | 85 return a.info.ftLastWriteTime.dwHighDateTime > |
| 86 b.info.ftLastWriteTime.dwHighDateTime; |
81 } | 87 } |
82 #endif | 88 #endif |
83 } | 89 } |
84 | 90 |
| 91 // Comparator for sorting find result by paths. This uses the locale-aware |
| 92 // comparison function on the filenames for sorting in the user's locale. |
| 93 // Static. |
| 94 bool DirectoryLister::CompareFullPath(const DirectoryListerData& a, |
| 95 const DirectoryListerData& b) { |
| 96 return file_util::LocaleAwareCompareFilenames(a.path, b.path); |
| 97 } |
85 | 98 |
86 DirectoryLister::DirectoryLister(const FilePath& dir, | 99 DirectoryLister::DirectoryLister(const FilePath& dir, |
87 DirectoryListerDelegate* delegate) | 100 DirectoryListerDelegate* delegate) |
88 : dir_(dir), | 101 : dir_(dir), |
| 102 recursive_(false), |
89 delegate_(delegate), | 103 delegate_(delegate), |
90 sort_(DEFAULT), | 104 sort_(ALPHA_DIRS_FIRST), |
91 message_loop_(NULL), | 105 message_loop_(NULL), |
92 thread_(kNullThreadHandle) { | 106 thread_(kNullThreadHandle) { |
93 DCHECK(!dir.value().empty()); | 107 DCHECK(!dir.value().empty()); |
94 } | 108 } |
95 | 109 |
96 DirectoryLister::DirectoryLister(const FilePath& dir, | 110 DirectoryLister::DirectoryLister(const FilePath& dir, |
| 111 bool recursive, |
97 SORT_TYPE sort, | 112 SORT_TYPE sort, |
98 DirectoryListerDelegate* delegate) | 113 DirectoryListerDelegate* delegate) |
99 : dir_(dir), | 114 : dir_(dir), |
| 115 recursive_(false), |
100 delegate_(delegate), | 116 delegate_(delegate), |
101 sort_(sort), | 117 sort_(sort), |
102 message_loop_(NULL), | 118 message_loop_(NULL), |
103 thread_(kNullThreadHandle) { | 119 thread_(kNullThreadHandle) { |
104 DCHECK(!dir.value().empty()); | 120 DCHECK(!dir.value().empty()); |
105 } | 121 } |
106 | 122 |
107 DirectoryLister::~DirectoryLister() { | 123 DirectoryLister::~DirectoryLister() { |
108 if (thread_) { | 124 if (thread_) { |
109 PlatformThread::Join(thread_); | 125 PlatformThread::Join(thread_); |
(...skipping 28 matching lines...) Expand all Loading... |
138 | 154 |
139 void DirectoryLister::ThreadMain() { | 155 void DirectoryLister::ThreadMain() { |
140 DirectoryDataEvent* e = new DirectoryDataEvent(this); | 156 DirectoryDataEvent* e = new DirectoryDataEvent(this); |
141 | 157 |
142 if (!file_util::DirectoryExists(dir_)) { | 158 if (!file_util::DirectoryExists(dir_)) { |
143 e->error = net::ERR_FILE_NOT_FOUND; | 159 e->error = net::ERR_FILE_NOT_FOUND; |
144 message_loop_->PostTask(FROM_HERE, e); | 160 message_loop_->PostTask(FROM_HERE, e); |
145 Release(); | 161 Release(); |
146 return; | 162 return; |
147 } | 163 } |
148 file_util::FileEnumerator file_enum(dir_, false, | |
149 static_cast<file_util::FileEnumerator::FILE_TYPE>( | |
150 file_util::FileEnumerator::FILES | | |
151 file_util::FileEnumerator::DIRECTORIES | | |
152 file_util::FileEnumerator::INCLUDE_DOT_DOT)); | |
153 | 164 |
154 while (!canceled_.IsSet() && !(file_enum.Next().value().empty())) { | 165 int types = file_util::FileEnumerator::FILES | |
155 e->data.push_back(file_util::FileEnumerator::FindInfo()); | 166 file_util::FileEnumerator::DIRECTORIES; |
156 file_enum.GetFindInfo(&e->data[e->data.size() - 1]); | 167 if (!recursive_) |
| 168 types |= file_util::FileEnumerator::INCLUDE_DOT_DOT; |
| 169 |
| 170 file_util::FileEnumerator file_enum(dir_, recursive_, |
| 171 static_cast<file_util::FileEnumerator::FILE_TYPE>(types)); |
| 172 |
| 173 FilePath path; |
| 174 while (!canceled_.IsSet() && !(path = file_enum.Next()).empty()) { |
| 175 DirectoryListerData data; |
| 176 file_enum.GetFindInfo(&data.info); |
| 177 data.path = path; |
| 178 e->data.push_back(data); |
157 | 179 |
158 /* TODO(brettw) bug 24107: It would be nice to send incremental updates. | 180 /* TODO(brettw) bug 24107: It would be nice to send incremental updates. |
159 We gather them all so they can be sorted, but eventually the sorting | 181 We gather them all so they can be sorted, but eventually the sorting |
160 should be done from JS to give more flexibility in the page. When we do | 182 should be done from JS to give more flexibility in the page. When we do |
161 that, we can uncomment this to send incremental updates to the page. | 183 that, we can uncomment this to send incremental updates to the page. |
162 if (++e->count == kFilesPerEvent) { | 184 if (++e->count == kFilesPerEvent) { |
163 message_loop_->PostTask(FROM_HERE, e); | 185 message_loop_->PostTask(FROM_HERE, e); |
164 e = new DirectoryDataEvent(this); | 186 e = new DirectoryDataEvent(this); |
165 } | 187 } |
166 */ | 188 */ |
167 } | 189 } |
168 | 190 |
169 if (!e->data.empty()) { | 191 if (!e->data.empty()) { |
170 // Sort the results. See the TODO above (this sort should be removed and we | 192 // Sort the results. See the TODO above (this sort should be removed and we |
171 // should do it from JS). | 193 // should do it from JS). |
172 if (sort_ == DATE) { | 194 if (sort_ == DATE) |
173 std::sort(e->data.begin(), e->data.end(), CompareFindInfoDate); | 195 std::sort(e->data.begin(), e->data.end(), CompareDate); |
174 } else { | 196 else if (sort_ == FULL_PATH) |
175 std::sort(e->data.begin(), e->data.end(), CompareFindInfoAlpha); | 197 std::sort(e->data.begin(), e->data.end(), CompareFullPath); |
176 } | 198 else if (sort_ == ALPHA_DIRS_FIRST) |
| 199 std::sort(e->data.begin(), e->data.end(), CompareAlphaDirsFirst); |
| 200 else |
| 201 DCHECK_EQ(NO_SORT, sort_); |
177 | 202 |
178 message_loop_->PostTask(FROM_HERE, e); | 203 message_loop_->PostTask(FROM_HERE, e); |
179 e = new DirectoryDataEvent(this); | 204 e = new DirectoryDataEvent(this); |
180 } | 205 } |
181 | 206 |
182 // Notify done | 207 // Notify done |
183 Release(); | 208 Release(); |
184 message_loop_->PostTask(FROM_HERE, e); | 209 message_loop_->PostTask(FROM_HERE, e); |
185 } | 210 } |
186 | 211 |
187 void DirectoryLister::OnReceivedData( | 212 void DirectoryLister::OnReceivedData(const DirectoryListerData* data, |
188 const file_util::FileEnumerator::FindInfo* data, int count) { | 213 int count) { |
189 // Since the delegate can clear itself during the OnListFile callback, we | 214 // Since the delegate can clear itself during the OnListFile callback, we |
190 // need to null check it during each iteration of the loop. Similarly, it is | 215 // need to null check it during each iteration of the loop. Similarly, it is |
191 // necessary to check the canceled_ flag to avoid sending data to a delegate | 216 // necessary to check the canceled_ flag to avoid sending data to a delegate |
192 // who doesn't want anymore. | 217 // who doesn't want anymore. |
193 for (int i = 0; !canceled_.IsSet() && delegate_ && i < count; ++i) | 218 for (int i = 0; !canceled_.IsSet() && delegate_ && i < count; ++i) |
194 delegate_->OnListFile(data[i]); | 219 delegate_->OnListFile(data[i]); |
195 } | 220 } |
196 | 221 |
197 void DirectoryLister::OnDone(int error) { | 222 void DirectoryLister::OnDone(int error) { |
198 // If canceled is set, we need to report some kind of error, | 223 // If canceled is set, we need to report some kind of error, |
199 // but don't overwrite the error condition if it is already set. | 224 // but don't overwrite the error condition if it is already set. |
200 if (!error && canceled_.IsSet()) | 225 if (!error && canceled_.IsSet()) |
201 error = net::ERR_ABORTED; | 226 error = net::ERR_ABORTED; |
202 | 227 |
203 if (delegate_) | 228 if (delegate_) |
204 delegate_->OnListDone(error); | 229 delegate_->OnListDone(error); |
205 } | 230 } |
206 | 231 |
207 } // namespace net | 232 } // namespace net |
OLD | NEW |