blob: 76f8137563b9d0aea6224fe67d1fb771f1c16058 [file] [log] [blame]
// Copyright 2013 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 "chrome/browser/ash/file_manager/file_watcher.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/logging.h"
#include "base/task/post_task.h"
#include "base/task/task_runner_util_forward.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/ash/crostini/crostini_manager.h"
#include "chrome/browser/ash/crostini/crostini_util.h"
#include "chrome/browser/ash/file_manager/path_util.h"
#include "content/public/browser/browser_thread.h"
#include "google_apis/common/task_util.h"
using content::BrowserThread;
namespace file_manager {
namespace {
// Creates a base::FilePathWatcher and starts watching at |watch_path| with
// |callback|. Returns NULL on failure.
base::FilePathWatcher* CreateAndStartFilePathWatcher(
const base::FilePath& watch_path,
const base::FilePathWatcher::Callback& callback) {
DCHECK(!callback.is_null());
std::unique_ptr<base::FilePathWatcher> watcher(new base::FilePathWatcher);
if (!watcher->Watch(watch_path, base::FilePathWatcher::Type::kNonRecursive,
callback))
return nullptr;
return watcher.release();
}
} // namespace
class FileWatcher::CrostiniFileWatcher
: public crostini::CrostiniFileChangeObserver {
public:
static std::unique_ptr<CrostiniFileWatcher> GetForPath(
Profile* profile,
const base::FilePath& local_path) {
base::FilePath crostini_mount = util::GetCrostiniMountDirectory(profile);
base::FilePath crostini_path;
if (local_path == crostini_mount ||
crostini_mount.AppendRelativePath(local_path, &crostini_path)) {
crostini::CrostiniManager* crostini_manager =
crostini::CrostiniManager::GetForProfile(profile);
if (crostini_manager) {
return std::make_unique<CrostiniFileWatcher>(crostini_manager,
std::move(crostini_mount),
std::move(crostini_path));
}
}
return nullptr;
}
CrostiniFileWatcher(crostini::CrostiniManager* crostini_manager,
base::FilePath crostini_mount,
base::FilePath crostini_path)
: crostini_manager_(crostini_manager),
crostini_mount_(std::move(crostini_mount)),
crostini_path_(std::move(crostini_path)),
container_id_(crostini::ContainerId::GetDefault()) {}
~CrostiniFileWatcher() override {
if (file_watcher_callback_) {
crostini_manager_->RemoveFileChangeObserver(this);
crostini_manager_->RemoveFileWatch(container_id_, crostini_path_);
}
}
void Watch(base::FilePathWatcher::Callback file_watcher_callback,
FileWatcher::BoolCallback callback) {
DCHECK(!file_watcher_callback_);
file_watcher_callback_ = std::move(file_watcher_callback);
crostini_manager_->AddFileChangeObserver(this);
crostini_manager_->AddFileWatch(container_id_, crostini_path_,
std::move(callback));
}
private:
// crostini::CrostiniFileChangeObserver overrides
void OnCrostiniFileChanged(const crostini::ContainerId& container_id,
const base::FilePath& path) override {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (container_id != container_id_) {
return;
}
DCHECK(file_watcher_callback_);
file_watcher_callback_.Run(crostini_mount_.Append(path), /*error=*/false);
}
crostini::CrostiniManager* crostini_manager_;
const base::FilePath crostini_mount_;
const base::FilePath crostini_path_;
const crostini::ContainerId container_id_;
base::FilePathWatcher::Callback file_watcher_callback_;
};
FileWatcher::FileWatcher(const base::FilePath& virtual_path)
: sequenced_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE})),
local_file_watcher_(nullptr),
virtual_path_(virtual_path) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
FileWatcher::~FileWatcher() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
sequenced_task_runner_->DeleteSoon(FROM_HERE, local_file_watcher_);
}
void FileWatcher::AddListener(const url::Origin& listener) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
origins_[listener]++;
}
void FileWatcher::RemoveListener(const url::Origin& listener) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto it = origins_.find(listener);
if (it == origins_.end()) {
LOG(ERROR) << " Listener [" << listener
<< "] tries to unsubscribe from virtual path ["
<< virtual_path_.value() << "] it isn't subscribed";
return;
}
// If entry found - decrease it's count and remove if necessary
--it->second;
if (it->second == 0)
origins_.erase(it);
}
std::vector<url::Origin> FileWatcher::GetListeners() const {
std::vector<url::Origin> origins;
for (const auto& kv : origins_) {
origins.push_back(kv.first);
}
return origins;
}
void FileWatcher::WatchLocalFile(
Profile* profile,
const base::FilePath& local_path,
const base::FilePathWatcher::Callback& file_watcher_callback,
BoolCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!callback.is_null());
DCHECK(!local_file_watcher_);
// If this is a crostini SSHFS path, use CrostiniFileWatcher.
crostini_file_watcher_ = CrostiniFileWatcher::GetForPath(profile, local_path);
if (crostini_file_watcher_) {
crostini_file_watcher_->Watch(std::move(file_watcher_callback),
std::move(callback));
return;
}
base::PostTaskAndReplyWithResult(
sequenced_task_runner_.get(), FROM_HERE,
base::BindOnce(&CreateAndStartFilePathWatcher, local_path,
google_apis::CreateRelayCallback(file_watcher_callback)),
base::BindOnce(&FileWatcher::OnWatcherStarted,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void FileWatcher::OnWatcherStarted(BoolCallback callback,
base::FilePathWatcher* file_watcher) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!callback.is_null());
DCHECK(!local_file_watcher_);
if (file_watcher) {
local_file_watcher_ = file_watcher;
std::move(callback).Run(true);
} else {
std::move(callback).Run(false);
}
}
} // namespace file_manager