This source file includes following definitions.
- recursive_watch_
- Cancel
- CancelOnMessageLoopThread
- WillDestroyCurrentMessageLoop
- OnObjectSignaled
- SetupWatchHandle
- UpdateWatch
- DestroyWatch
#include "base/files/file_path_watcher.h"
#include "base/bind.h"
#include "base/file_util.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/time/time.h"
#include "base/win/object_watcher.h"
namespace base {
namespace {
class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
public base::win::ObjectWatcher::Delegate,
public MessageLoop::DestructionObserver {
public:
FilePathWatcherImpl()
: handle_(INVALID_HANDLE_VALUE),
recursive_watch_(false) {}
virtual bool Watch(const FilePath& path,
bool recursive,
const FilePathWatcher::Callback& callback) OVERRIDE;
virtual void Cancel() OVERRIDE;
virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
virtual void OnObjectSignaled(HANDLE object);
private:
virtual ~FilePathWatcherImpl() {}
static bool SetupWatchHandle(const FilePath& dir,
bool recursive,
HANDLE* handle) WARN_UNUSED_RESULT;
bool UpdateWatch() WARN_UNUSED_RESULT;
void DestroyWatch();
void CancelOnMessageLoopThread() OVERRIDE;
FilePathWatcher::Callback callback_;
FilePath target_;
HANDLE handle_;
base::win::ObjectWatcher watcher_;
bool recursive_watch_;
Time last_modified_;
Time first_notification_;
DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
};
bool FilePathWatcherImpl::Watch(const FilePath& path,
bool recursive,
const FilePathWatcher::Callback& callback) {
DCHECK(target_.value().empty());
set_message_loop(MessageLoopProxy::current());
callback_ = callback;
target_ = path;
recursive_watch_ = recursive;
MessageLoop::current()->AddDestructionObserver(this);
if (!UpdateWatch())
return false;
watcher_.StartWatching(handle_, this);
return true;
}
void FilePathWatcherImpl::Cancel() {
if (callback_.is_null()) {
set_cancelled();
return;
}
if (!message_loop()->BelongsToCurrentThread()) {
message_loop()->PostTask(FROM_HERE,
Bind(&FilePathWatcher::CancelWatch,
make_scoped_refptr(this)));
} else {
CancelOnMessageLoopThread();
}
}
void FilePathWatcherImpl::CancelOnMessageLoopThread() {
set_cancelled();
if (handle_ != INVALID_HANDLE_VALUE)
DestroyWatch();
if (!callback_.is_null()) {
MessageLoop::current()->RemoveDestructionObserver(this);
callback_.Reset();
}
}
void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() {
CancelOnMessageLoopThread();
}
void FilePathWatcherImpl::OnObjectSignaled(HANDLE object) {
DCHECK(object == handle_);
scoped_refptr<FilePathWatcherImpl> keep_alive(this);
if (!UpdateWatch()) {
callback_.Run(target_, true );
return;
}
File::Info file_info;
bool file_exists = GetFileInfo(target_, &file_info);
if (file_exists && (last_modified_.is_null() ||
last_modified_ != file_info.last_modified)) {
last_modified_ = file_info.last_modified;
first_notification_ = Time::Now();
callback_.Run(target_, false);
} else if (file_exists && !first_notification_.is_null()) {
if (Time::Now() - first_notification_ > TimeDelta::FromSeconds(1)) {
first_notification_ = Time();
}
callback_.Run(target_, false);
} else if (!file_exists && !last_modified_.is_null()) {
last_modified_ = Time();
callback_.Run(target_, false);
}
if (handle_ != INVALID_HANDLE_VALUE)
watcher_.StartWatching(handle_, this);
}
bool FilePathWatcherImpl::SetupWatchHandle(const FilePath& dir,
bool recursive,
HANDLE* handle) {
*handle = FindFirstChangeNotification(
dir.value().c_str(),
recursive,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE |
FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SECURITY);
if (*handle != INVALID_HANDLE_VALUE) {
if (!DirectoryExists(dir)) {
FindCloseChangeNotification(*handle);
*handle = INVALID_HANDLE_VALUE;
}
return true;
}
DWORD error_code = GetLastError();
if (error_code != ERROR_FILE_NOT_FOUND &&
error_code != ERROR_PATH_NOT_FOUND &&
error_code != ERROR_ACCESS_DENIED &&
error_code != ERROR_SHARING_VIOLATION &&
error_code != ERROR_DIRECTORY) {
using ::operator<<;
DPLOG(ERROR) << "FindFirstChangeNotification failed for "
<< dir.value();
return false;
}
return true;
}
bool FilePathWatcherImpl::UpdateWatch() {
if (handle_ != INVALID_HANDLE_VALUE)
DestroyWatch();
File::Info file_info;
if (GetFileInfo(target_, &file_info)) {
last_modified_ = file_info.last_modified;
first_notification_ = Time::Now();
}
std::vector<FilePath> child_dirs;
FilePath watched_path(target_);
while (true) {
if (!SetupWatchHandle(watched_path, recursive_watch_, &handle_))
return false;
if (handle_ != INVALID_HANDLE_VALUE)
break;
child_dirs.push_back(watched_path.BaseName());
FilePath parent(watched_path.DirName());
if (parent == watched_path) {
DLOG(ERROR) << "Reached the root directory";
return false;
}
watched_path = parent;
}
while (!child_dirs.empty()) {
watched_path = watched_path.Append(child_dirs.back());
child_dirs.pop_back();
HANDLE temp_handle = INVALID_HANDLE_VALUE;
if (!SetupWatchHandle(watched_path, recursive_watch_, &temp_handle))
return false;
if (temp_handle == INVALID_HANDLE_VALUE)
break;
FindCloseChangeNotification(handle_);
handle_ = temp_handle;
}
return true;
}
void FilePathWatcherImpl::DestroyWatch() {
watcher_.StopWatching();
FindCloseChangeNotification(handle_);
handle_ = INVALID_HANDLE_VALUE;
}
}
FilePathWatcher::FilePathWatcher() {
impl_ = new FilePathWatcherImpl();
}
}