OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/files/file_path_watcher.h" | 5 #include "base/files/file_path_watcher.h" |
6 | 6 |
7 #include <errno.h> | 7 #include <errno.h> |
8 #include <string.h> | 8 #include <string.h> |
9 #include <sys/inotify.h> | 9 #include <sys/inotify.h> |
10 #include <sys/ioctl.h> | 10 #include <sys/ioctl.h> |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
94 // the currently watched path for |fired_watch|. The flag |created| is true if | 94 // the currently watched path for |fired_watch|. The flag |created| is true if |
95 // the object appears. | 95 // the object appears. |
96 void OnFilePathChanged(InotifyReader::Watch fired_watch, | 96 void OnFilePathChanged(InotifyReader::Watch fired_watch, |
97 const FilePath::StringType& child, | 97 const FilePath::StringType& child, |
98 bool created); | 98 bool created); |
99 | 99 |
100 // Start watching |path| for changes and notify |delegate| on each change. | 100 // Start watching |path| for changes and notify |delegate| on each change. |
101 // Returns true if watch for |path| has been added successfully. | 101 // Returns true if watch for |path| has been added successfully. |
102 virtual bool Watch(const FilePath& path, | 102 virtual bool Watch(const FilePath& path, |
103 bool recursive, | 103 bool recursive, |
104 FilePathWatcher::Delegate* delegate) OVERRIDE; | 104 const FilePathWatcher::Callback& callback) OVERRIDE; |
105 | 105 |
106 // Cancel the watch. This unregisters the instance with InotifyReader. | 106 // Cancel the watch. This unregisters the instance with InotifyReader. |
107 virtual void Cancel() OVERRIDE; | 107 virtual void Cancel() OVERRIDE; |
108 | 108 |
109 // Deletion of the FilePathWatcher will call Cancel() to dispose of this | 109 // Deletion of the FilePathWatcher will call Cancel() to dispose of this |
110 // object in the right thread. This also observes destruction of the required | 110 // object in the right thread. This also observes destruction of the required |
111 // cleanup thread, in case it quits before Cancel() is called. | 111 // cleanup thread, in case it quits before Cancel() is called. |
112 virtual void WillDestroyCurrentMessageLoop() OVERRIDE; | 112 virtual void WillDestroyCurrentMessageLoop() OVERRIDE; |
113 | 113 |
114 protected: | 114 protected: |
(...skipping 15 matching lines...) Expand all Loading... |
130 InotifyReader::Watch watch_; | 130 InotifyReader::Watch watch_; |
131 FilePath::StringType subdir_; | 131 FilePath::StringType subdir_; |
132 FilePath::StringType linkname_; | 132 FilePath::StringType linkname_; |
133 }; | 133 }; |
134 typedef std::vector<WatchEntry> WatchVector; | 134 typedef std::vector<WatchEntry> WatchVector; |
135 | 135 |
136 // Reconfigure to watch for the most specific parent directory of |target_| | 136 // Reconfigure to watch for the most specific parent directory of |target_| |
137 // that exists. Updates |watched_path_|. Returns true on success. | 137 // that exists. Updates |watched_path_|. Returns true on success. |
138 bool UpdateWatches() WARN_UNUSED_RESULT; | 138 bool UpdateWatches() WARN_UNUSED_RESULT; |
139 | 139 |
140 // Delegate to notify upon changes. | 140 // Callback to notify upon changes. |
141 scoped_refptr<FilePathWatcher::Delegate> delegate_; | 141 FilePathWatcher::Callback callback_; |
142 | 142 |
143 // The file or directory we're supposed to watch. | 143 // The file or directory we're supposed to watch. |
144 FilePath target_; | 144 FilePath target_; |
145 | 145 |
146 // The vector of watches and next component names for all path components, | 146 // The vector of watches and next component names for all path components, |
147 // starting at the root directory. The last entry corresponds to the watch for | 147 // starting at the root directory. The last entry corresponds to the watch for |
148 // |target_| and always stores an empty next component name in |subdir_|. | 148 // |target_| and always stores an empty next component name in |subdir_|. |
149 WatchVector watches_; | 149 WatchVector watches_; |
150 | 150 |
151 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); | 151 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
288 | 288 |
289 for (WatcherSet::iterator watcher = watchers_[event->wd].begin(); | 289 for (WatcherSet::iterator watcher = watchers_[event->wd].begin(); |
290 watcher != watchers_[event->wd].end(); | 290 watcher != watchers_[event->wd].end(); |
291 ++watcher) { | 291 ++watcher) { |
292 (*watcher)->OnFilePathChanged(event->wd, | 292 (*watcher)->OnFilePathChanged(event->wd, |
293 child, | 293 child, |
294 event->mask & (IN_CREATE | IN_MOVED_TO)); | 294 event->mask & (IN_CREATE | IN_MOVED_TO)); |
295 } | 295 } |
296 } | 296 } |
297 | 297 |
298 FilePathWatcherImpl::FilePathWatcherImpl() | 298 FilePathWatcherImpl::FilePathWatcherImpl() { |
299 : delegate_(NULL) { | |
300 } | 299 } |
301 | 300 |
302 void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, | 301 void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, |
303 const FilePath::StringType& child, | 302 const FilePath::StringType& child, |
304 bool created) { | 303 bool created) { |
305 if (!message_loop()->BelongsToCurrentThread()) { | 304 if (!message_loop()->BelongsToCurrentThread()) { |
306 // Switch to message_loop_ to access watches_ safely. | 305 // Switch to message_loop_ to access watches_ safely. |
307 message_loop()->PostTask(FROM_HERE, | 306 message_loop()->PostTask(FROM_HERE, |
308 base::Bind(&FilePathWatcherImpl::OnFilePathChanged, | 307 base::Bind(&FilePathWatcherImpl::OnFilePathChanged, |
309 this, | 308 this, |
(...skipping 22 matching lines...) Expand all Loading... |
332 (watch_entry->subdir_.empty() && watch_entry->linkname_.empty()) || | 331 (watch_entry->subdir_.empty() && watch_entry->linkname_.empty()) || |
333 (watch_entry->subdir_ == child && (watch_entry + 1)->subdir_.empty()); | 332 (watch_entry->subdir_ == child && (watch_entry + 1)->subdir_.empty()); |
334 | 333 |
335 // Update watches if a directory component of the |target_| path | 334 // Update watches if a directory component of the |target_| path |
336 // (dis)appears. Note that we don't add the additional restriction | 335 // (dis)appears. Note that we don't add the additional restriction |
337 // of checking the event mask to see if it is for a directory here | 336 // of checking the event mask to see if it is for a directory here |
338 // as changes to symlinks on the target path will not have | 337 // as changes to symlinks on the target path will not have |
339 // IN_ISDIR set in the event masks. As a result we may sometimes | 338 // IN_ISDIR set in the event masks. As a result we may sometimes |
340 // call UpdateWatches() unnecessarily. | 339 // call UpdateWatches() unnecessarily. |
341 if (change_on_target_path && !UpdateWatches()) { | 340 if (change_on_target_path && !UpdateWatches()) { |
342 delegate_->OnFilePathError(target_); | 341 callback_.Run(target_, true /* error */); |
343 return; | 342 return; |
344 } | 343 } |
345 | 344 |
346 // Report the following events: | 345 // Report the following events: |
347 // - The target or a direct child of the target got changed (in case the | 346 // - The target or a direct child of the target got changed (in case the |
348 // watched path refers to a directory). | 347 // watched path refers to a directory). |
349 // - One of the parent directories got moved or deleted, since the target | 348 // - One of the parent directories got moved or deleted, since the target |
350 // disappears in this case. | 349 // disappears in this case. |
351 // - One of the parent directories appears. The event corresponding to | 350 // - One of the parent directories appears. The event corresponding to |
352 // the target appearing might have been missed in this case, so | 351 // the target appearing might have been missed in this case, so |
353 // recheck. | 352 // recheck. |
354 if (target_changed || | 353 if (target_changed || |
355 (change_on_target_path && !created) || | 354 (change_on_target_path && !created) || |
356 (change_on_target_path && file_util::PathExists(target_))) { | 355 (change_on_target_path && file_util::PathExists(target_))) { |
357 delegate_->OnFilePathChanged(target_); | 356 callback_.Run(target_, false); |
358 return; | 357 return; |
359 } | 358 } |
360 } | 359 } |
361 } | 360 } |
362 } | 361 } |
363 | 362 |
364 bool FilePathWatcherImpl::Watch(const FilePath& path, | 363 bool FilePathWatcherImpl::Watch(const FilePath& path, |
365 bool recursive, | 364 bool recursive, |
366 FilePathWatcher::Delegate* delegate) { | 365 const FilePathWatcher::Callback& callback) { |
367 DCHECK(target_.empty()); | 366 DCHECK(target_.empty()); |
368 DCHECK(MessageLoopForIO::current()); | 367 DCHECK(MessageLoopForIO::current()); |
369 if (recursive) { | 368 if (recursive) { |
370 // Recursive watch is not supported on this platform. | 369 // Recursive watch is not supported on this platform. |
371 NOTIMPLEMENTED(); | 370 NOTIMPLEMENTED(); |
372 return false; | 371 return false; |
373 } | 372 } |
374 | 373 |
375 set_message_loop(base::MessageLoopProxy::current()); | 374 set_message_loop(base::MessageLoopProxy::current()); |
376 delegate_ = delegate; | 375 callback_ = callback; |
377 target_ = path; | 376 target_ = path; |
378 MessageLoop::current()->AddDestructionObserver(this); | 377 MessageLoop::current()->AddDestructionObserver(this); |
379 | 378 |
380 std::vector<FilePath::StringType> comps; | 379 std::vector<FilePath::StringType> comps; |
381 target_.GetComponents(&comps); | 380 target_.GetComponents(&comps); |
382 DCHECK(!comps.empty()); | 381 DCHECK(!comps.empty()); |
383 std::vector<FilePath::StringType>::const_iterator comp = comps.begin(); | 382 std::vector<FilePath::StringType>::const_iterator comp = comps.begin(); |
384 for (++comp; comp != comps.end(); ++comp) | 383 for (++comp; comp != comps.end(); ++comp) |
385 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, *comp)); | 384 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, *comp)); |
386 | 385 |
387 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, | 386 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, |
388 FilePath::StringType())); | 387 FilePath::StringType())); |
389 return UpdateWatches(); | 388 return UpdateWatches(); |
390 } | 389 } |
391 | 390 |
392 void FilePathWatcherImpl::Cancel() { | 391 void FilePathWatcherImpl::Cancel() { |
393 if (!delegate_) { | 392 if (callback_.is_null()) { |
394 // Watch was never called, or the |message_loop_| thread is already gone. | 393 // Watch was never called, or the |message_loop_| thread is already gone. |
395 set_cancelled(); | 394 set_cancelled(); |
396 return; | 395 return; |
397 } | 396 } |
398 | 397 |
399 // Switch to the message_loop_ if necessary so we can access |watches_|. | 398 // Switch to the message_loop_ if necessary so we can access |watches_|. |
400 if (!message_loop()->BelongsToCurrentThread()) { | 399 if (!message_loop()->BelongsToCurrentThread()) { |
401 message_loop()->PostTask(FROM_HERE, | 400 message_loop()->PostTask(FROM_HERE, |
402 base::Bind(&FilePathWatcher::CancelWatch, | 401 base::Bind(&FilePathWatcher::CancelWatch, |
403 make_scoped_refptr(this))); | 402 make_scoped_refptr(this))); |
404 } else { | 403 } else { |
405 CancelOnMessageLoopThread(); | 404 CancelOnMessageLoopThread(); |
406 } | 405 } |
407 } | 406 } |
408 | 407 |
409 void FilePathWatcherImpl::CancelOnMessageLoopThread() { | 408 void FilePathWatcherImpl::CancelOnMessageLoopThread() { |
410 if (!is_cancelled()) | 409 if (!is_cancelled()) |
411 set_cancelled(); | 410 set_cancelled(); |
412 | 411 |
413 if (delegate_) { | 412 if (!callback_.is_null()) { |
414 MessageLoop::current()->RemoveDestructionObserver(this); | 413 MessageLoop::current()->RemoveDestructionObserver(this); |
415 delegate_ = NULL; | 414 callback_.Reset(); |
416 } | 415 } |
417 | 416 |
418 for (WatchVector::iterator watch_entry(watches_.begin()); | 417 for (WatchVector::iterator watch_entry(watches_.begin()); |
419 watch_entry != watches_.end(); ++watch_entry) { | 418 watch_entry != watches_.end(); ++watch_entry) { |
420 if (watch_entry->watch_ != InotifyReader::kInvalidWatch) | 419 if (watch_entry->watch_ != InotifyReader::kInvalidWatch) |
421 g_inotify_reader.Get().RemoveWatch(watch_entry->watch_, this); | 420 g_inotify_reader.Get().RemoveWatch(watch_entry->watch_, this); |
422 } | 421 } |
423 watches_.clear(); | 422 watches_.clear(); |
424 target_.clear(); | 423 target_.clear(); |
425 } | 424 } |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
481 } | 480 } |
482 | 481 |
483 } // namespace | 482 } // namespace |
484 | 483 |
485 FilePathWatcher::FilePathWatcher() { | 484 FilePathWatcher::FilePathWatcher() { |
486 impl_ = new FilePathWatcherImpl(); | 485 impl_ = new FilePathWatcherImpl(); |
487 } | 486 } |
488 | 487 |
489 } // namespace files | 488 } // namespace files |
490 } // namespace base | 489 } // namespace base |
OLD | NEW |