summaryrefslogtreecommitdiffstats
path: root/base/file_watcher_mac.cc
diff options
context:
space:
mode:
authortony@chromium.org <tony@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-09 01:12:33 +0000
committertony@chromium.org <tony@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-09 01:12:33 +0000
commitb0a5bf2c9aa8a248c0ca44eec4d0c6ed92ca563b (patch)
treec5a83696b18d1e42f07c5dea122d0f72f39948b1 /base/file_watcher_mac.cc
parent32d8383bc71b0f2652287081c54b9acee911bd57 (diff)
downloadchromium_src-b0a5bf2c9aa8a248c0ca44eec4d0c6ed92ca563b.zip
chromium_src-b0a5bf2c9aa8a248c0ca44eec4d0c6ed92ca563b.tar.gz
chromium_src-b0a5bf2c9aa8a248c0ca44eec4d0c6ed92ca563b.tar.bz2
Add a FileWatcher to base/.
This is like Directory Watcher, but only for a single file. Review URL: http://codereview.chromium.org/661359 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40980 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/file_watcher_mac.cc')
-rw-r--r--base/file_watcher_mac.cc134
1 files changed, 134 insertions, 0 deletions
diff --git a/base/file_watcher_mac.cc b/base/file_watcher_mac.cc
new file mode 100644
index 0000000..2d85133
--- /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();
+}