Home | History | Annotate | Download | only in base
      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/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"
     18 
     19 namespace net {
     20 
     21 namespace {
     22 
     23 const int kFilesPerEvent = 8;
     24 
     25 bool IsDotDot(const base::FilePath& path) {
     26   return FILE_PATH_LITERAL("..") == path.BaseName().value();
     27 }
     28 
     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.
     31 // Static.
     32 bool CompareAlphaDirsFirst(const DirectoryLister::DirectoryListerData& a,
     33                            const DirectoryLister::DirectoryListerData& b) {
     34   // Parent directory before all else.
     35   if (IsDotDot(a.info.GetName()))
     36     return true;
     37   if (IsDotDot(b.info.GetName()))
     38     return false;
     39 
     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;
     45 
     46   return file_util::LocaleAwareCompareFilenames(a.info.GetName(),
     47                                                 b.info.GetName());
     48 }
     49 
     50 bool CompareDate(const DirectoryLister::DirectoryListerData& a,
     51                  const DirectoryLister::DirectoryListerData& b) {
     52   // Parent directory before all else.
     53   if (IsDotDot(a.info.GetName()))
     54     return true;
     55   if (IsDotDot(b.info.GetName()))
     56     return false;
     57 
     58   // Directories before regular files.
     59   bool a_is_directory = a.info.IsDirectory();
     60   bool b_is_directory = b.info.IsDirectory();
     61   if (a_is_directory != b_is_directory)
     62     return a_is_directory;
     63   return a.info.GetLastModifiedTime() > b.info.GetLastModifiedTime();
     64 }
     65 
     66 // Comparator for sorting find result by paths. This uses the locale-aware
     67 // comparison function on the filenames for sorting in the user's locale.
     68 // Static.
     69 bool CompareFullPath(const DirectoryLister::DirectoryListerData& a,
     70                      const DirectoryLister::DirectoryListerData& b) {
     71   return file_util::LocaleAwareCompareFilenames(a.path, b.path);
     72 }
     73 
     74 void SortData(std::vector<DirectoryLister::DirectoryListerData>* data,
     75               DirectoryLister::SortType sort_type) {
     76   // Sort the results. See the TODO below (this sort should be removed and we
     77   // should do it from JS).
     78   if (sort_type == DirectoryLister::DATE)
     79     std::sort(data->begin(), data->end(), CompareDate);
     80   else if (sort_type == DirectoryLister::FULL_PATH)
     81     std::sort(data->begin(), data->end(), CompareFullPath);
     82   else if (sort_type == DirectoryLister::ALPHA_DIRS_FIRST)
     83     std::sort(data->begin(), data->end(), CompareAlphaDirsFirst);
     84   else
     85     DCHECK_EQ(DirectoryLister::NO_SORT, sort_type);
     86 }
     87 
     88 }  // namespace
     89 
     90 DirectoryLister::DirectoryLister(const base::FilePath& dir,
     91                                  DirectoryListerDelegate* delegate)
     92     : core_(new Core(dir, false, ALPHA_DIRS_FIRST, this)),
     93       delegate_(delegate) {
     94   DCHECK(delegate_);
     95   DCHECK(!dir.value().empty());
     96 }
     97 
     98 DirectoryLister::DirectoryLister(const base::FilePath& dir,
     99                                  bool recursive,
    100                                  SortType sort,
    101                                  DirectoryListerDelegate* delegate)
    102     : core_(new Core(dir, recursive, sort, this)),
    103       delegate_(delegate) {
    104   DCHECK(delegate_);
    105   DCHECK(!dir.value().empty());
    106 }
    107 
    108 DirectoryLister::~DirectoryLister() {
    109   Cancel();
    110 }
    111 
    112 bool DirectoryLister::Start() {
    113   return core_->Start();
    114 }
    115 
    116 void DirectoryLister::Cancel() {
    117   return core_->Cancel();
    118 }
    119 
    120 DirectoryLister::Core::Core(const base::FilePath& dir,
    121                             bool recursive,
    122                             SortType sort,
    123                             DirectoryLister* lister)
    124     : dir_(dir),
    125       recursive_(recursive),
    126       sort_(sort),
    127       lister_(lister) {
    128   DCHECK(lister_);
    129 }
    130 
    131 DirectoryLister::Core::~Core() {}
    132 
    133 bool DirectoryLister::Core::Start() {
    134   origin_loop_ = base::MessageLoopProxy::current();
    135 
    136   return base::WorkerPool::PostTask(
    137       FROM_HERE, base::Bind(&Core::StartInternal, this), true);
    138 }
    139 
    140 void DirectoryLister::Core::Cancel() {
    141   lister_ = NULL;
    142 }
    143 
    144 void DirectoryLister::Core::StartInternal() {
    145 
    146   if (!base::DirectoryExists(dir_)) {
    147     origin_loop_->PostTask(
    148         FROM_HERE,
    149         base::Bind(&DirectoryLister::Core::OnDone, this, ERR_FILE_NOT_FOUND));
    150     return;
    151   }
    152 
    153   int types = base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES;
    154   if (!recursive_)
    155     types |= base::FileEnumerator::INCLUDE_DOT_DOT;
    156 
    157   base::FileEnumerator file_enum(dir_, recursive_, types);
    158 
    159   base::FilePath path;
    160   std::vector<DirectoryListerData> file_data;
    161   while (lister_ && !(path = file_enum.Next()).empty()) {
    162     DirectoryListerData data;
    163     data.info = file_enum.GetInfo();
    164     data.path = path;
    165     file_data.push_back(data);
    166 
    167     /* TODO(brettw) bug 24107: It would be nice to send incremental updates.
    168        We gather them all so they can be sorted, but eventually the sorting
    169        should be done from JS to give more flexibility in the page. When we do
    170        that, we can uncomment this to send incremental updates to the page.
    171 
    172     if (file_data.size() < kFilesPerEvent)
    173       continue;
    174 
    175     origin_loop_->PostTask(
    176         FROM_HERE,
    177         base::Bind(&DirectoryLister::Core::SendData, file_data));
    178     file_data.clear();
    179     */
    180   }
    181 
    182   SortData(&file_data, sort_);
    183   origin_loop_->PostTask(
    184       FROM_HERE,
    185       base::Bind(&DirectoryLister::Core::SendData, this, file_data));
    186 
    187   origin_loop_->PostTask(
    188       FROM_HERE,
    189       base::Bind(&DirectoryLister::Core::OnDone, this, OK));
    190 }
    191 
    192 void DirectoryLister::Core::SendData(
    193     const std::vector<DirectoryLister::DirectoryListerData>& data) {
    194   DCHECK(origin_loop_->BelongsToCurrentThread());
    195   // We need to check for cancellation (indicated by NULL'ing of |lister_|)
    196   // which can happen during each callback.
    197   for (size_t i = 0; lister_ && i < data.size(); ++i)
    198     lister_->OnReceivedData(data[i]);
    199 }
    200 
    201 void DirectoryLister::Core::OnDone(int error) {
    202   DCHECK(origin_loop_->BelongsToCurrentThread());
    203   if (lister_)
    204     lister_->OnDone(error);
    205 }
    206 
    207 void DirectoryLister::OnReceivedData(const DirectoryListerData& data) {
    208   delegate_->OnListFile(data);
    209 }
    210 
    211 void DirectoryLister::OnDone(int error) {
    212   delegate_->OnListDone(error);
    213 }
    214 
    215 }  // namespace net
    216