15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/directory_lister.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
11868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/files/file_enumerator.h"
121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/file_util.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/file_util_icu.h"
149ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/threading/thread_restrictions.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/threading/worker_pool.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_errors.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace net {
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool IsDotDot(const base::FilePath& path) {
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return FILE_PATH_LITERAL("..") == path.BaseName().value();
25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Comparator for sorting lister results. This uses the locale aware filename
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// comparison function on the filenames for sorting in the user's locale.
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Static.
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool CompareAlphaDirsFirst(const DirectoryLister::DirectoryListerData& a,
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           const DirectoryLister::DirectoryListerData& b) {
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Parent directory before all else.
33868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (IsDotDot(a.info.GetName()))
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
35868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (IsDotDot(b.info.GetName()))
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Directories before regular files.
39868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  bool a_is_directory = a.info.IsDirectory();
40868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  bool b_is_directory = b.info.IsDirectory();
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (a_is_directory != b_is_directory)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return a_is_directory;
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return base::i18n::LocaleAwareCompareFilenames(a.info.GetName(),
456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                                 b.info.GetName());
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool CompareDate(const DirectoryLister::DirectoryListerData& a,
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 const DirectoryLister::DirectoryListerData& b) {
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Parent directory before all else.
51868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (IsDotDot(a.info.GetName()))
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
53868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (IsDotDot(b.info.GetName()))
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Directories before regular files.
57868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  bool a_is_directory = a.info.IsDirectory();
58868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  bool b_is_directory = b.info.IsDirectory();
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (a_is_directory != b_is_directory)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return a_is_directory;
61868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return a.info.GetLastModifiedTime() > b.info.GetLastModifiedTime();
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Comparator for sorting find result by paths. This uses the locale-aware
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// comparison function on the filenames for sorting in the user's locale.
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Static.
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool CompareFullPath(const DirectoryLister::DirectoryListerData& a,
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     const DirectoryLister::DirectoryListerData& b) {
696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return base::i18n::LocaleAwareCompareFilenames(a.path, b.path);
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SortData(std::vector<DirectoryLister::DirectoryListerData>* data,
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              DirectoryLister::SortType sort_type) {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Sort the results. See the TODO below (this sort should be removed and we
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // should do it from JS).
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (sort_type == DirectoryLister::DATE)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::sort(data->begin(), data->end(), CompareDate);
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else if (sort_type == DirectoryLister::FULL_PATH)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::sort(data->begin(), data->end(), CompareFullPath);
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else if (sort_type == DirectoryLister::ALPHA_DIRS_FIRST)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::sort(data->begin(), data->end(), CompareAlphaDirsFirst);
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_EQ(DirectoryLister::NO_SORT, sort_type);
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)DirectoryLister::DirectoryLister(const base::FilePath& dir,
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 DirectoryListerDelegate* delegate)
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    : core_(new Core(dir, false, ALPHA_DIRS_FIRST, this)),
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delegate_(delegate) {
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(delegate_);
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!dir.value().empty());
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)DirectoryLister::DirectoryLister(const base::FilePath& dir,
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 bool recursive,
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 SortType sort,
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 DirectoryListerDelegate* delegate)
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    : core_(new Core(dir, recursive, sort, this)),
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delegate_(delegate) {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(delegate_);
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!dir.value().empty());
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DirectoryLister::~DirectoryLister() {
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Cancel();
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool DirectoryLister::Start() {
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return core_->Start();
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DirectoryLister::Cancel() {
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return core_->Cancel();
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)DirectoryLister::Core::Core(const base::FilePath& dir,
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            bool recursive,
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            SortType sort,
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            DirectoryLister* lister)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : dir_(dir),
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      recursive_(recursive),
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sort_(sort),
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      lister_(lister) {
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(lister_);
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DirectoryLister::Core::~Core() {}
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool DirectoryLister::Core::Start() {
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  origin_loop_ = base::MessageLoopProxy::current();
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return base::WorkerPool::PostTask(
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      FROM_HERE, base::Bind(&Core::StartInternal, this), true);
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DirectoryLister::Core::Cancel() {
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  lister_ = NULL;
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DirectoryLister::Core::StartInternal() {
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1447dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (!base::DirectoryExists(dir_)) {
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    origin_loop_->PostTask(
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        FROM_HERE,
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::Bind(&DirectoryLister::Core::OnDone, this, ERR_FILE_NOT_FOUND));
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
151868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  int types = base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES;
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!recursive_)
153868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    types |= base::FileEnumerator::INCLUDE_DOT_DOT;
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
155868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  base::FileEnumerator file_enum(dir_, recursive_, types);
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath path;
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<DirectoryListerData> file_data;
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (lister_ && !(path = file_enum.Next()).empty()) {
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DirectoryListerData data;
161868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    data.info = file_enum.GetInfo();
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data.path = path;
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_data.push_back(data);
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    /* TODO(brettw) bug 24107: It would be nice to send incremental updates.
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       We gather them all so they can be sorted, but eventually the sorting
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       should be done from JS to give more flexibility in the page. When we do
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       that, we can uncomment this to send incremental updates to the page.
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    const int kFilesPerEvent = 8;
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (file_data.size() < kFilesPerEvent)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    origin_loop_->PostTask(
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        FROM_HERE,
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::Bind(&DirectoryLister::Core::SendData, file_data));
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_data.clear();
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    */
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SortData(&file_data, sort_);
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  origin_loop_->PostTask(
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      FROM_HERE,
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&DirectoryLister::Core::SendData, this, file_data));
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  origin_loop_->PostTask(
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      FROM_HERE,
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&DirectoryLister::Core::OnDone, this, OK));
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DirectoryLister::Core::SendData(
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::vector<DirectoryLister::DirectoryListerData>& data) {
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(origin_loop_->BelongsToCurrentThread());
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We need to check for cancellation (indicated by NULL'ing of |lister_|)
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // which can happen during each callback.
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; lister_ && i < data.size(); ++i)
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    lister_->OnReceivedData(data[i]);
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DirectoryLister::Core::OnDone(int error) {
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(origin_loop_->BelongsToCurrentThread());
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (lister_)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    lister_->OnDone(error);
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DirectoryLister::OnReceivedData(const DirectoryListerData& data) {
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  delegate_->OnListFile(data);
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DirectoryLister::OnDone(int error) {
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  delegate_->OnListDone(error);
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace net
215