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