// 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 #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, MessageLoop* backend_loop, 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(event_paths); DirectoryWatcherImpl* watcher = reinterpret_cast (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, MessageLoop* backend_loop, 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 cf_path(CFStringCreateWithCString( NULL, path.value().c_str(), kCFStringEncodingMacHFS)); CFStringRef path_for_array = cf_path.get(); scoped_cftyperef watched_paths(CFArrayCreate( NULL, reinterpret_cast(&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(); }