Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/directory_watcher.h"
      6 
      7 #include <CoreServices/CoreServices.h>
      8 
      9 #include "base/file_path.h"
     10 #include "base/file_util.h"
     11 #include "base/logging.h"
     12 #include "base/message_loop.h"
     13 #include "base/scoped_cftyperef.h"
     14 
     15 namespace {
     16 
     17 const CFAbsoluteTime kEventLatencySeconds = 0.3;
     18 
     19 class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate {
     20  public:
     21   DirectoryWatcherImpl() {}
     22   ~DirectoryWatcherImpl() {
     23     if (!path_.value().empty()) {
     24       FSEventStreamStop(fsevent_stream_);
     25       FSEventStreamInvalidate(fsevent_stream_);
     26       FSEventStreamRelease(fsevent_stream_);
     27     }
     28   }
     29 
     30   virtual bool Watch(const FilePath& path, DirectoryWatcher::Delegate* delegate,
     31                      MessageLoop* backend_loop, bool recursive);
     32 
     33   void OnFSEventsCallback(const FilePath& event_path) {
     34     DCHECK(!path_.value().empty());
     35     if (!recursive_) {
     36       FilePath absolute_event_path = event_path;
     37       if (!file_util::AbsolutePath(&absolute_event_path))
     38         return;
     39       if (absolute_event_path != path_)
     40         return;
     41     }
     42     delegate_->OnDirectoryChanged(path_);
     43   }
     44 
     45  private:
     46   // Delegate to notify upon changes.
     47   DirectoryWatcher::Delegate* delegate_;
     48 
     49   // Path we're watching (passed to delegate).
     50   FilePath path_;
     51 
     52   // Indicates recursive watch.
     53   bool recursive_;
     54 
     55   // Backend stream we receive event callbacks from (strong reference).
     56   FSEventStreamRef fsevent_stream_;
     57 
     58   DISALLOW_COPY_AND_ASSIGN(DirectoryWatcherImpl);
     59 };
     60 
     61 void FSEventsCallback(ConstFSEventStreamRef stream,
     62                       void* event_watcher, size_t num_events,
     63                       void* event_paths, const FSEventStreamEventFlags flags[],
     64                       const FSEventStreamEventId event_ids[]) {
     65   char** paths = reinterpret_cast<char**>(event_paths);
     66   DirectoryWatcherImpl* watcher =
     67       reinterpret_cast<DirectoryWatcherImpl*> (event_watcher);
     68   for (size_t i = 0; i < num_events; i++) {
     69     watcher->OnFSEventsCallback(FilePath(paths[i]));
     70   }
     71 }
     72 
     73 bool DirectoryWatcherImpl::Watch(const FilePath& path,
     74                                  DirectoryWatcher::Delegate* delegate,
     75                                  MessageLoop* backend_loop,
     76                                  bool recursive) {
     77   DCHECK(path_.value().empty());  // Can only watch one path.
     78 
     79   DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI);
     80 
     81   if (!file_util::PathExists(path))
     82     return false;
     83 
     84   path_ = path;
     85   if (!file_util::AbsolutePath(&path_)) {
     86     path_ = FilePath();  // Make sure we're marked as not-in-use.
     87     return false;
     88   }
     89   delegate_ = delegate;
     90   recursive_ = recursive;
     91 
     92   scoped_cftyperef<CFStringRef> cf_path(CFStringCreateWithCString(
     93       NULL, path.value().c_str(), kCFStringEncodingMacHFS));
     94   CFStringRef path_for_array = cf_path.get();
     95   scoped_cftyperef<CFArrayRef> watched_paths(CFArrayCreate(
     96       NULL, reinterpret_cast<const void**>(&path_for_array), 1,
     97       &kCFTypeArrayCallBacks));
     98 
     99   FSEventStreamContext context;
    100   context.version = 0;
    101   context.info = this;
    102   context.retain = NULL;
    103   context.release = NULL;
    104   context.copyDescription = NULL;
    105 
    106   fsevent_stream_ = FSEventStreamCreate(NULL, &FSEventsCallback, &context,
    107                                         watched_paths,
    108                                         kFSEventStreamEventIdSinceNow,
    109                                         kEventLatencySeconds,
    110                                         kFSEventStreamCreateFlagNone);
    111   FSEventStreamScheduleWithRunLoop(fsevent_stream_, CFRunLoopGetCurrent(),
    112                                    kCFRunLoopDefaultMode);
    113   FSEventStreamStart(fsevent_stream_);
    114 
    115   return true;
    116 }
    117 
    118 }  // namespace
    119 
    120 DirectoryWatcher::DirectoryWatcher() {
    121   impl_ = new DirectoryWatcherImpl();
    122 }
    123