| 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 |