Add DirectoryWatcher implementation for Mac.

This uses FSEvents and supports both recursive and non-recursive modes.

TEST=Make sure that tests matching DirectoryWatcherTest.* from base_unittests pass on Mac.

http://crbug.com/10967

Review URL: http://codereview.chromium.org/99057

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@14977 0039d316-1c4b-4281-b951-d872f2087c98
parent 022a7edd
...@@ -88,6 +88,7 @@ ...@@ -88,6 +88,7 @@
'debug_util_win.cc', 'debug_util_win.cc',
'directory_watcher.h', 'directory_watcher.h',
'directory_watcher_inotify.cc', 'directory_watcher_inotify.cc',
'directory_watcher_mac.cc',
'directory_watcher_win.cc', 'directory_watcher_win.cc',
'event_recorder.cc', 'event_recorder.cc',
'event_recorder.h', 'event_recorder.h',
...@@ -641,11 +642,7 @@ ...@@ -641,11 +642,7 @@
'../build/linux/system.gyp:nss', '../build/linux/system.gyp:nss',
], ],
}], }],
['OS == "mac"', { ['OS != "mac"', {
'sources!': [
'directory_watcher_unittest.cc',
],
}, { # OS != "mac"
'sources!': [ 'sources!': [
'mac_util_unittest.cc', 'mac_util_unittest.cc',
], ],
......
// 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();
}
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment