diff options
author | tony@chromium.org <tony@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-09 01:12:33 +0000 |
---|---|---|
committer | tony@chromium.org <tony@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-09 01:12:33 +0000 |
commit | b0a5bf2c9aa8a248c0ca44eec4d0c6ed92ca563b (patch) | |
tree | c5a83696b18d1e42f07c5dea122d0f72f39948b1 /base/file_watcher_mac.cc | |
parent | 32d8383bc71b0f2652287081c54b9acee911bd57 (diff) | |
download | chromium_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.cc | 134 |
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(); +} |