Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / net / base / directory_lister.cc
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.
4
5 #include "net/base/directory_lister.h"
6
7 #include <algorithm>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "base/files/file_enumerator.h"
12 #include "base/files/file_util.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"
18
19 namespace net {
20
21 namespace {
22
23 bool IsDotDot(const base::FilePath& path) {
24   return FILE_PATH_LITERAL("..") == path.BaseName().value();
25 }
26
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;
37
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;
43
44   return base::i18n::LocaleAwareCompareFilenames(a.info.GetName(),
45                                                  b.info.GetName());
46 }
47
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;
55
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();
62 }
63
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);
70 }
71
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);
84 }
85
86 }  // namespace
87
88 DirectoryLister::DirectoryLister(const base::FilePath& dir,
89                                  DirectoryListerDelegate* delegate)
90     : core_(new Core(dir, false, ALPHA_DIRS_FIRST, this)),
91       delegate_(delegate) {
92   DCHECK(delegate_);
93   DCHECK(!dir.value().empty());
94 }
95
96 DirectoryLister::DirectoryLister(const base::FilePath& dir,
97                                  bool recursive,
98                                  SortType sort,
99                                  DirectoryListerDelegate* delegate)
100     : core_(new Core(dir, recursive, sort, this)),
101       delegate_(delegate) {
102   DCHECK(delegate_);
103   DCHECK(!dir.value().empty());
104 }
105
106 DirectoryLister::~DirectoryLister() {
107   Cancel();
108 }
109
110 bool DirectoryLister::Start() {
111   return core_->Start();
112 }
113
114 void DirectoryLister::Cancel() {
115   return core_->Cancel();
116 }
117
118 DirectoryLister::Core::Core(const base::FilePath& dir,
119                             bool recursive,
120                             SortType sort,
121                             DirectoryLister* lister)
122     : dir_(dir),
123       recursive_(recursive),
124       sort_(sort),
125       lister_(lister) {
126   DCHECK(lister_);
127 }
128
129 DirectoryLister::Core::~Core() {}
130
131 bool DirectoryLister::Core::Start() {
132   origin_loop_ = base::MessageLoopProxy::current();
133
134   return base::WorkerPool::PostTask(
135       FROM_HERE, base::Bind(&Core::StartInternal, this), true);
136 }
137
138 void DirectoryLister::Core::Cancel() {
139   lister_ = NULL;
140 }
141
142 void DirectoryLister::Core::StartInternal() {
143
144   if (!base::DirectoryExists(dir_)) {
145     origin_loop_->PostTask(
146         FROM_HERE,
147         base::Bind(&DirectoryLister::Core::OnDone, this, ERR_FILE_NOT_FOUND));
148     return;
149   }
150
151   int types = base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES;
152   if (!recursive_)
153     types |= base::FileEnumerator::INCLUDE_DOT_DOT;
154
155   base::FileEnumerator file_enum(dir_, recursive_, types);
156
157   base::FilePath path;
158   std::vector<DirectoryListerData> file_data;
159   while (lister_ && !(path = file_enum.Next()).empty()) {
160     DirectoryListerData data;
161     data.info = file_enum.GetInfo();
162     data.path = path;
163     file_data.push_back(data);
164
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.
169
170     const int kFilesPerEvent = 8;
171     if (file_data.size() < kFilesPerEvent)
172       continue;
173
174     origin_loop_->PostTask(
175         FROM_HERE,
176         base::Bind(&DirectoryLister::Core::SendData, file_data));
177     file_data.clear();
178     */
179   }
180
181   SortData(&file_data, sort_);
182   origin_loop_->PostTask(
183       FROM_HERE,
184       base::Bind(&DirectoryLister::Core::SendData, this, file_data));
185
186   origin_loop_->PostTask(
187       FROM_HERE,
188       base::Bind(&DirectoryLister::Core::OnDone, this, OK));
189 }
190
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]);
198 }
199
200 void DirectoryLister::Core::OnDone(int error) {
201   DCHECK(origin_loop_->BelongsToCurrentThread());
202   if (lister_)
203     lister_->OnDone(error);
204 }
205
206 void DirectoryLister::OnReceivedData(const DirectoryListerData& data) {
207   delegate_->OnListFile(data);
208 }
209
210 void DirectoryLister::OnDone(int error) {
211   delegate_->OnListDone(error);
212 }
213
214 }  // namespace net