|  | // Copyright 2016 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "extensions/browser/api/file_handlers/directory_util.h" | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/files/file_path.h" | 
|  | #include "base/files/file_util.h" | 
|  | #include "base/task/post_task.h" | 
|  | #include "base/task/thread_pool.h" | 
|  | #include "base/threading/thread_task_runner_handle.h" | 
|  | #include "build/chromeos_buildflags.h" | 
|  | #include "content/public/browser/browser_context.h" | 
|  | #include "net/base/filename_util.h" | 
|  | #include "storage/browser/file_system/file_system_url.h" | 
|  |  | 
|  | #if BUILDFLAG(IS_CHROMEOS_ASH) | 
|  | #include "extensions/browser/api/extensions_api_client.h" | 
|  | #include "extensions/browser/api/file_handlers/non_native_file_system_delegate.h" | 
|  | #endif | 
|  |  | 
|  | namespace extensions { | 
|  | namespace app_file_handler_util { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | bool GetIsDirectoryFromFileInfo(const base::FilePath& path) { | 
|  | base::File::Info file_info; | 
|  | return GetFileInfo(path, &file_info) && file_info.is_directory; | 
|  | } | 
|  |  | 
|  | // The callback parameter contains the result and is required to support | 
|  | // both native local directories to avoid UI thread and non native local | 
|  | // path directories for the IsNonNativeLocalPathDirectory API. | 
|  | void EntryIsDirectory(content::BrowserContext* context, | 
|  | const base::FilePath& path, | 
|  | base::OnceCallback<void(bool)> callback) { | 
|  | #if BUILDFLAG(IS_CHROMEOS_ASH) | 
|  | NonNativeFileSystemDelegate* delegate = | 
|  | ExtensionsAPIClient::Get()->GetNonNativeFileSystemDelegate(); | 
|  | if (delegate && delegate->IsUnderNonNativeLocalPath(context, path)) { | 
|  | delegate->IsNonNativeLocalPathDirectory(context, path, std::move(callback)); | 
|  | return; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | base::ThreadPool::PostTaskAndReplyWithResult( | 
|  | FROM_HERE, {base::MayBlock()}, | 
|  | base::BindOnce(&GetIsDirectoryFromFileInfo, path), std::move(callback)); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | IsDirectoryCollector::IsDirectoryCollector(content::BrowserContext* context) | 
|  | : context_(context), left_(0) {} | 
|  |  | 
|  | IsDirectoryCollector::~IsDirectoryCollector() {} | 
|  |  | 
|  | void IsDirectoryCollector::CollectForEntriesPaths( | 
|  | const std::vector<base::FilePath>& paths, | 
|  | CompletionCallback callback) { | 
|  | DCHECK(!callback.is_null()); | 
|  | paths_ = paths; | 
|  | callback_ = std::move(callback); | 
|  |  | 
|  | DCHECK(!result_.get()); | 
|  | result_ = std::make_unique<std::set<base::FilePath>>(); | 
|  | left_ = paths.size(); | 
|  |  | 
|  | if (!left_) { | 
|  | // Nothing to process. | 
|  | base::ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, base::BindOnce(std::move(callback_), std::move(result_))); | 
|  | callback_.Reset(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (size_t i = 0; i < paths.size(); ++i) { | 
|  | EntryIsDirectory( | 
|  | context_, paths[i], | 
|  | base::BindOnce(&IsDirectoryCollector::OnIsDirectoryCollected, | 
|  | weak_ptr_factory_.GetWeakPtr(), i)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void IsDirectoryCollector::OnIsDirectoryCollected(size_t index, | 
|  | bool is_directory) { | 
|  | if (is_directory) | 
|  | result_->insert(paths_[index]); | 
|  | if (!--left_) { | 
|  | base::ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, base::BindOnce(std::move(callback_), std::move(result_))); | 
|  | // Release the callback to avoid a circullar reference in case an instance | 
|  | // of this class is a member of a ref counted class, which instance is bound | 
|  | // to this callback. | 
|  | callback_.Reset(); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace app_file_handler_util | 
|  | }  // namespace extensions |