diff options
Diffstat (limited to 'chrome/browser/file_path_watcher_mac.cc')
| -rw-r--r-- | chrome/browser/file_path_watcher_mac.cc | 232 |
1 files changed, 0 insertions, 232 deletions
diff --git a/chrome/browser/file_path_watcher_mac.cc b/chrome/browser/file_path_watcher_mac.cc deleted file mode 100644 index 91d9c23..0000000 --- a/chrome/browser/file_path_watcher_mac.cc +++ /dev/null @@ -1,232 +0,0 @@ -// 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 "chrome/browser/file_path_watcher.h" - -#include <CoreServices/CoreServices.h> -#include <set> - -#include "base/file_path.h" -#include "base/file_util.h" -#include "base/logging.h" -#include "base/scoped_cftyperef.h" -#include "base/singleton.h" -#include "base/time.h" - -namespace { - -// The latency parameter passed to FSEventsStreamCreate(). -const CFAbsoluteTime kEventLatencySeconds = 0.3; - -// Mac-specific file watcher implementation based on the FSEvents API. -class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { - public: - FilePathWatcherImpl(); - virtual ~FilePathWatcherImpl() {} - - // Called from the FSEvents callback whenever there is a change to the paths - void OnFilePathChanged(); - - // (Re-)Initialize the event stream to start reporting events from - // |start_event|. - void UpdateEventStream(FSEventStreamEventId start_event); - - // FilePathWatcher::PlatformDelegate overrides. - virtual bool Watch(const FilePath& path, FilePathWatcher::Delegate* delegate); - virtual void Cancel(); - - private: - // Destroy the event stream. - void DestroyEventStream(); - - // Delegate to notify upon changes. - scoped_refptr<FilePathWatcher::Delegate> delegate_; - - // Target path to watch (passed to delegate). - FilePath target_; - - // Keep track of the last modified time of the file. We use nulltime - // to represent the file not existing. - base::Time last_modified_; - - // The time at which we processed the first notification with the - // |last_modified_| time stamp. - base::Time first_notification_; - - // Backend stream we receive event callbacks from (strong reference). - FSEventStreamRef fsevent_stream_; - - // Used to detect early cancellation. - bool canceled_; - - DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); -}; - -// The callback passed to FSEventStreamCreate(). -void FSEventsCallback(ConstFSEventStreamRef stream, - void* event_watcher, size_t num_events, - void* event_paths, const FSEventStreamEventFlags flags[], - const FSEventStreamEventId event_ids[]) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - - FilePathWatcherImpl* watcher = - reinterpret_cast<FilePathWatcherImpl*>(event_watcher); - bool root_changed = false; - FSEventStreamEventId root_change_at = FSEventStreamGetLatestEventId(stream); - for (size_t i = 0; i < num_events; i++) { - if (flags[i] & kFSEventStreamEventFlagRootChanged) - root_changed = true; - if (event_ids[i]) - root_change_at = std::min(root_change_at, event_ids[i]); - } - - // Reinitialize the event stream if we find changes to the root. This is - // necessary since FSEvents doesn't report any events for the subtree after - // the directory to be watched gets created. - if (root_changed) { - // Resetting the event stream from within the callback fails (FSEvents spews - // bad file descriptor errors), so post a task to do the reset. - ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, - NewRunnableMethod(watcher, &FilePathWatcherImpl::UpdateEventStream, - root_change_at)); - } - - ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE, - NewRunnableMethod(watcher, &FilePathWatcherImpl::OnFilePathChanged)); -} - -// FilePathWatcherImpl implementation: - -FilePathWatcherImpl::FilePathWatcherImpl() - : fsevent_stream_(NULL), - canceled_(false) { -} - -void FilePathWatcherImpl::OnFilePathChanged() { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); - DCHECK(!target_.empty()); - - file_util::FileInfo file_info; - bool file_exists = file_util::GetFileInfo(target_, &file_info); - if (file_exists && (last_modified_.is_null() || - last_modified_ != file_info.last_modified)) { - last_modified_ = file_info.last_modified; - first_notification_ = base::Time::Now(); - delegate_->OnFilePathChanged(target_); - } else if (file_exists && !first_notification_.is_null()) { - // The target's last modification time is equal to what's on record. This - // means that either an unrelated event occurred, or the target changed - // again (file modification times only have a resolution of 1s). Comparing - // file modification times against the wall clock is not reliable to find - // out whether the change is recent, since this code might just run too - // late. Moreover, there's no guarantee that file modification time and wall - // clock times come from the same source. - // - // Instead, the time at which the first notification carrying the current - // |last_notified_| time stamp is recorded. Later notifications that find - // the same file modification time only need to be forwarded until wall - // clock has advanced one second from the initial notification. After that - // interval, client code is guaranteed to having seen the current revision - // of the file. - if (base::Time::Now() - first_notification_ > - base::TimeDelta::FromSeconds(1)) { - // Stop further notifications for this |last_modification_| time stamp. - first_notification_ = base::Time(); - } - delegate_->OnFilePathChanged(target_); - } else if (!file_exists && !last_modified_.is_null()) { - last_modified_ = base::Time(); - delegate_->OnFilePathChanged(target_); - } -} - -bool FilePathWatcherImpl::Watch(const FilePath& path, - FilePathWatcher::Delegate* delegate) { - DCHECK(target_.value().empty()); - - target_ = path; - delegate_ = delegate; - - FSEventStreamEventId start_event = FSEventsGetCurrentEventId(); - - file_util::FileInfo file_info; - if (file_util::GetFileInfo(target_, &file_info)) { - last_modified_ = file_info.last_modified; - first_notification_ = base::Time::Now(); - } - - ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, &FilePathWatcherImpl::UpdateEventStream, - start_event)); - - return true; -} - -void FilePathWatcherImpl::Cancel() { - // Switch to the UI thread if necessary, so we can tear down the event stream. - if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { - ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, - NewRunnableMethod(this, &FilePathWatcherImpl::Cancel)); - return; - } - - canceled_ = true; - if (fsevent_stream_) - DestroyEventStream(); -} - -void FilePathWatcherImpl::UpdateEventStream(FSEventStreamEventId start_event) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - - // It can happen that the watcher gets canceled while tasks that call this - // function are still in flight, so abort if this situation is detected. - if (canceled_) - return; - - if (fsevent_stream_) - DestroyEventStream(); - - scoped_cftyperef<CFStringRef> cf_path(CFStringCreateWithCString( - NULL, target_.value().c_str(), kCFStringEncodingMacHFS)); - scoped_cftyperef<CFStringRef> cf_dir_path(CFStringCreateWithCString( - NULL, target_.DirName().value().c_str(), kCFStringEncodingMacHFS)); - CFStringRef paths_array[] = { cf_path.get(), cf_dir_path.get() }; - scoped_cftyperef<CFArrayRef> watched_paths(CFArrayCreate( - NULL, reinterpret_cast<const void**>(paths_array), arraysize(paths_array), - &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, - start_event, - kEventLatencySeconds, - kFSEventStreamCreateFlagWatchRoot); - FSEventStreamScheduleWithRunLoop(fsevent_stream_, CFRunLoopGetCurrent(), - kCFRunLoopDefaultMode); - if (!FSEventStreamStart(fsevent_stream_)) { - ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE, - NewRunnableMethod(delegate_.get(), - &FilePathWatcher::Delegate::OnError)); - } -} - -void FilePathWatcherImpl::DestroyEventStream() { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - FSEventStreamStop(fsevent_stream_); - FSEventStreamInvalidate(fsevent_stream_); - FSEventStreamRelease(fsevent_stream_); - fsevent_stream_ = NULL; -} - -} // namespace - -FilePathWatcher::FilePathWatcher() { - impl_ = new FilePathWatcherImpl(); -} |
