| Index: base/file_watcher_mac.cc
|
| diff --git a/base/file_watcher_mac.cc b/base/file_watcher_mac.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2d85133fdcc62596667e7e3fabbfb4cee93625d0
|
| --- /dev/null
|
| +++ b/base/file_watcher_mac.cc
|
| @@ -0,0 +1,134 @@
|
| +// Copyright (c) 2009 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 "base/file_watcher.h"
|
| +
|
| +#include <CoreServices/CoreServices.h>
|
| +
|
| +#include "base/file_path.h"
|
| +#include "base/file_util.h"
|
| +#include "base/logging.h"
|
| +#include "base/message_loop.h"
|
| +#include "base/scoped_cftyperef.h"
|
| +#include "base/time.h"
|
| +
|
| +namespace {
|
| +
|
| +const CFAbsoluteTime kEventLatencySeconds = 0.3;
|
| +
|
| +class FileWatcherImpl : public FileWatcher::PlatformDelegate {
|
| + public:
|
| + FileWatcherImpl() {}
|
| + ~FileWatcherImpl() {
|
| + if (!path_.value().empty()) {
|
| + FSEventStreamStop(fsevent_stream_);
|
| + FSEventStreamInvalidate(fsevent_stream_);
|
| + FSEventStreamRelease(fsevent_stream_);
|
| + }
|
| + }
|
| +
|
| + virtual bool Watch(const FilePath& path, FileWatcher::Delegate* delegate,
|
| + MessageLoop* backend_loop);
|
| +
|
| + void OnFSEventsCallback(const FilePath& event_path) {
|
| + DCHECK(!path_.value().empty());
|
| + FilePath absolute_event_path = event_path;
|
| + if (!file_util::AbsolutePath(&absolute_event_path))
|
| + return;
|
| +
|
| + file_util::FileInfo file_info;
|
| + bool file_exists = file_util::GetFileInfo(path_, &file_info);
|
| + if (file_exists && (last_modified_.is_null() ||
|
| + last_modified_ != file_info.last_modified)) {
|
| + last_modified_ = file_info.last_modified;
|
| + delegate_->OnFileChanged(path_);
|
| + } else if (file_exists && (base::Time::Now() - last_modified_ <
|
| + base::TimeDelta::FromSeconds(2))) {
|
| + // Since we only have a resolution of 1s, if we get a callback within
|
| + // 2s of the file having changed, go ahead and notify our observer. This
|
| + // might be from a different file change, but it's better to notify too
|
| + // much rather than miss a notification.
|
| + delegate_->OnFileChanged(path_);
|
| + } else if (!file_exists && !last_modified_.is_null()) {
|
| + last_modified_ = base::Time();
|
| + delegate_->OnFileChanged(path_);
|
| + }
|
| + }
|
| +
|
| + private:
|
| + // Delegate to notify upon changes.
|
| + FileWatcher::Delegate* delegate_;
|
| +
|
| + // Path we're watching (passed to delegate).
|
| + FilePath path_;
|
| +
|
| + // Backend stream we receive event callbacks from (strong reference).
|
| + FSEventStreamRef fsevent_stream_;
|
| +
|
| + // Keep track of the last modified time of the file. We use nulltime
|
| + // to represent the file not existing.
|
| + base::Time last_modified_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(FileWatcherImpl);
|
| +};
|
| +
|
| +void FSEventsCallback(ConstFSEventStreamRef stream,
|
| + void* event_watcher, size_t num_events,
|
| + void* event_paths, const FSEventStreamEventFlags flags[],
|
| + const FSEventStreamEventId event_ids[]) {
|
| + char** paths = reinterpret_cast<char**>(event_paths);
|
| + FileWatcherImpl* watcher =
|
| + reinterpret_cast<FileWatcherImpl*>(event_watcher);
|
| + for (size_t i = 0; i < num_events; i++)
|
| + watcher->OnFSEventsCallback(FilePath(paths[i]));
|
| +}
|
| +
|
| +bool FileWatcherImpl::Watch(const FilePath& path,
|
| + FileWatcher::Delegate* delegate,
|
| + MessageLoop* backend_loop) {
|
| + DCHECK(path_.value().empty()); // Can only watch one path.
|
| + DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI);
|
| +
|
| + FilePath parent_dir = path.DirName();
|
| + if (!file_util::AbsolutePath(&parent_dir))
|
| + return false;
|
| +
|
| + file_util::FileInfo file_info;
|
| + if (file_util::GetFileInfo(path, &file_info))
|
| + last_modified_ = file_info.last_modified;
|
| +
|
| + path_ = path;
|
| + delegate_ = delegate;
|
| +
|
| + scoped_cftyperef<CFStringRef> cf_path(CFStringCreateWithCString(
|
| + NULL, path.DirName().value().c_str(), kCFStringEncodingMacHFS));
|
| + CFStringRef path_for_array = cf_path.get();
|
| + scoped_cftyperef<CFArrayRef> watched_paths(CFArrayCreate(
|
| + NULL, reinterpret_cast<const void**>(&path_for_array), 1,
|
| + &kCFTypeArrayCallBacks));
|
| +
|
| + FSEventStreamContext context;
|
| + context.version = 0;
|
| + context.info = this;
|
| + context.retain = NULL;
|
| + context.release = NULL;
|
| + context.copyDescription = NULL;
|
| +
|
| + fsevent_stream_ = FSEventStreamCreate(NULL, &FSEventsCallback, &context,
|
| + watched_paths,
|
| + kFSEventStreamEventIdSinceNow,
|
| + kEventLatencySeconds,
|
| + kFSEventStreamCreateFlagNone);
|
| + FSEventStreamScheduleWithRunLoop(fsevent_stream_, CFRunLoopGetCurrent(),
|
| + kCFRunLoopDefaultMode);
|
| + FSEventStreamStart(fsevent_stream_);
|
| +
|
| + return true;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +FileWatcher::FileWatcher() {
|
| + impl_ = new FileWatcherImpl();
|
| +}
|
|
|