Index: base/directory_watcher_mac.cc |
diff --git a/base/directory_watcher_mac.cc b/base/directory_watcher_mac.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d4b3082e994b67bb925d658c0567985573292722 |
--- /dev/null |
+++ b/base/directory_watcher_mac.cc |
@@ -0,0 +1,121 @@ |
+// 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/directory_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" |
+ |
+namespace { |
+ |
+const CFAbsoluteTime kEventLatencySeconds = 0.3; |
+ |
+class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate { |
+ public: |
+ DirectoryWatcherImpl() {} |
+ ~DirectoryWatcherImpl() { |
+ if (!path_.value().empty()) { |
+ FSEventStreamStop(fsevent_stream_); |
+ FSEventStreamInvalidate(fsevent_stream_); |
+ FSEventStreamRelease(fsevent_stream_); |
+ } |
+ } |
+ |
+ virtual bool Watch(const FilePath& path, DirectoryWatcher::Delegate* delegate, |
+ bool recursive); |
+ |
+ void OnFSEventsCallback(const FilePath& event_path) { |
+ DCHECK(!path_.value().empty()); |
+ if (!recursive_) { |
+ FilePath absolute_event_path = event_path; |
+ if (!file_util::AbsolutePath(&absolute_event_path)) |
+ return; |
+ if (absolute_event_path != path_) |
+ return; |
+ } |
+ delegate_->OnDirectoryChanged(path_); |
+ } |
+ |
+ private: |
+ // Delegate to notify upon changes. |
+ DirectoryWatcher::Delegate* delegate_; |
+ |
+ // Path we're watching (passed to delegate). |
+ FilePath path_; |
+ |
+ // Indicates recursive watch. |
+ bool recursive_; |
+ |
+ // Backend stream we receive event callbacks from (strong reference). |
+ FSEventStreamRef fsevent_stream_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(DirectoryWatcherImpl); |
+}; |
+ |
+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); |
+ DirectoryWatcherImpl* watcher = |
+ reinterpret_cast<DirectoryWatcherImpl*> (event_watcher); |
+ for (size_t i = 0; i < num_events; i++) { |
+ watcher->OnFSEventsCallback(FilePath(paths[i])); |
+ } |
+} |
+ |
+bool DirectoryWatcherImpl::Watch(const FilePath& path, |
+ DirectoryWatcher::Delegate* delegate, |
+ bool recursive) { |
+ DCHECK(path_.value().empty()); // Can only watch one path. |
+ |
+ DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI); |
+ |
+ if (!file_util::PathExists(path)) |
+ return false; |
+ |
+ path_ = path; |
+ if (!file_util::AbsolutePath(&path_)) { |
+ path_ = FilePath(); // Make sure we're marked as not-in-use. |
+ return false; |
+ } |
+ delegate_ = delegate; |
+ recursive_ = recursive; |
+ |
+ scoped_cftyperef<CFStringRef> cf_path(CFStringCreateWithCString( |
+ NULL, path.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 |
+ |
+DirectoryWatcher::DirectoryWatcher() { |
+ impl_ = new DirectoryWatcherImpl(); |
+} |