Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/file_util.h" | 8 #include "base/file_util.h" |
| 9 #include "base/files/file.h" | 9 #include "base/files/file.h" |
| 10 #include "base/files/file_path.h" | 10 #include "base/files/file_path.h" |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 90 bool recursive, | 90 bool recursive, |
| 91 const FilePathWatcher::Callback& callback) { | 91 const FilePathWatcher::Callback& callback) { |
| 92 DCHECK(target_.value().empty()); // Can only watch one path. | 92 DCHECK(target_.value().empty()); // Can only watch one path. |
| 93 | 93 |
| 94 set_message_loop(MessageLoopProxy::current()); | 94 set_message_loop(MessageLoopProxy::current()); |
| 95 callback_ = callback; | 95 callback_ = callback; |
| 96 target_ = path; | 96 target_ = path; |
| 97 recursive_watch_ = recursive; | 97 recursive_watch_ = recursive; |
| 98 MessageLoop::current()->AddDestructionObserver(this); | 98 MessageLoop::current()->AddDestructionObserver(this); |
| 99 | 99 |
| 100 File::Info file_info; | |
| 101 if (GetFileInfo(target_, &file_info)) { | |
| 102 last_modified_ = file_info.last_modified; | |
| 103 first_notification_ = Time::Now(); | |
| 104 } | |
| 105 | |
| 100 if (!UpdateWatch()) | 106 if (!UpdateWatch()) |
| 101 return false; | 107 return false; |
| 102 | 108 |
| 103 watcher_.StartWatching(handle_, this); | 109 watcher_.StartWatching(handle_, this); |
| 104 | 110 |
| 105 return true; | 111 return true; |
| 106 } | 112 } |
| 107 | 113 |
| 108 void FilePathWatcherImpl::Cancel() { | 114 void FilePathWatcherImpl::Cancel() { |
| 109 if (callback_.is_null()) { | 115 if (callback_.is_null()) { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 144 scoped_refptr<FilePathWatcherImpl> keep_alive(this); | 150 scoped_refptr<FilePathWatcherImpl> keep_alive(this); |
| 145 | 151 |
| 146 if (!UpdateWatch()) { | 152 if (!UpdateWatch()) { |
| 147 callback_.Run(target_, true /* error */); | 153 callback_.Run(target_, true /* error */); |
| 148 return; | 154 return; |
| 149 } | 155 } |
| 150 | 156 |
| 151 // Check whether the event applies to |target_| and notify the callback. | 157 // Check whether the event applies to |target_| and notify the callback. |
| 152 File::Info file_info; | 158 File::Info file_info; |
| 153 bool file_exists = GetFileInfo(target_, &file_info); | 159 bool file_exists = GetFileInfo(target_, &file_info); |
| 154 if (file_exists && (last_modified_.is_null() || | 160 if (recursive_watch_) { |
| 161 // Only the mtime of |target_| is tracked but in a recursive watch, | |
| 162 // some other file or directory may have changed so all notifications | |
| 163 // are passed through. It is possible to figure out which file changed | |
| 164 // using ReadDirectoryChangesW() instead of FindFirstChangeNotification(), | |
| 165 // but that function is quite complicated: | |
| 166 // http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw. html | |
| 167 callback_.Run(target_, false); | |
| 168 } else if (file_exists && (last_modified_.is_null() || | |
| 155 last_modified_ != file_info.last_modified)) { | 169 last_modified_ != file_info.last_modified)) { |
|
Mattias Nissler (ping if slow)
2014/05/14 10:27:52
nit: align at ( for readability
vandebo (ex-Chrome)
2014/05/14 16:55:41
Oops, fixed.
| |
| 156 last_modified_ = file_info.last_modified; | 170 last_modified_ = file_info.last_modified; |
| 157 first_notification_ = Time::Now(); | 171 first_notification_ = Time::Now(); |
| 158 callback_.Run(target_, false); | 172 callback_.Run(target_, false); |
| 159 } else if (file_exists && !first_notification_.is_null()) { | 173 } else if (file_exists && last_modified_ == file_info.last_modified && |
| 174 !first_notification_.is_null()) { | |
| 160 // The target's last modification time is equal to what's on record. This | 175 // The target's last modification time is equal to what's on record. This |
| 161 // means that either an unrelated event occurred, or the target changed | 176 // means that either an unrelated event occurred, or the target changed |
| 162 // again (file modification times only have a resolution of 1s). Comparing | 177 // again (file modification times only have a resolution of 1s). Comparing |
| 163 // file modification times against the wall clock is not reliable to find | 178 // file modification times against the wall clock is not reliable to find |
| 164 // out whether the change is recent, since this code might just run too | 179 // out whether the change is recent, since this code might just run too |
| 165 // late. Moreover, there's no guarantee that file modification time and wall | 180 // late. Moreover, there's no guarantee that file modification time and wall |
| 166 // clock times come from the same source. | 181 // clock times come from the same source. |
| 167 // | 182 // |
| 168 // Instead, the time at which the first notification carrying the current | 183 // Instead, the time at which the first notification carrying the current |
| 169 // |last_notified_| time stamp is recorded. Later notifications that find | 184 // |last_notified_| time stamp is recorded. Later notifications that find |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 223 return false; | 238 return false; |
| 224 } | 239 } |
| 225 | 240 |
| 226 return true; | 241 return true; |
| 227 } | 242 } |
| 228 | 243 |
| 229 bool FilePathWatcherImpl::UpdateWatch() { | 244 bool FilePathWatcherImpl::UpdateWatch() { |
| 230 if (handle_ != INVALID_HANDLE_VALUE) | 245 if (handle_ != INVALID_HANDLE_VALUE) |
| 231 DestroyWatch(); | 246 DestroyWatch(); |
| 232 | 247 |
| 233 File::Info file_info; | |
| 234 if (GetFileInfo(target_, &file_info)) { | |
| 235 last_modified_ = file_info.last_modified; | |
| 236 first_notification_ = Time::Now(); | |
| 237 } | |
| 238 | |
| 239 // Start at the target and walk up the directory chain until we succesfully | 248 // Start at the target and walk up the directory chain until we succesfully |
| 240 // create a watch handle in |handle_|. |child_dirs| keeps a stack of child | 249 // create a watch handle in |handle_|. |child_dirs| keeps a stack of child |
| 241 // directories stripped from target, in reverse order. | 250 // directories stripped from target, in reverse order. |
| 242 std::vector<FilePath> child_dirs; | 251 std::vector<FilePath> child_dirs; |
| 243 FilePath watched_path(target_); | 252 FilePath watched_path(target_); |
| 244 while (true) { | 253 while (true) { |
| 245 if (!SetupWatchHandle(watched_path, recursive_watch_, &handle_)) | 254 if (!SetupWatchHandle(watched_path, recursive_watch_, &handle_)) |
| 246 return false; | 255 return false; |
| 247 | 256 |
| 248 // Break if a valid handle is returned. Try the parent directory otherwise. | 257 // Break if a valid handle is returned. Try the parent directory otherwise. |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 283 handle_ = INVALID_HANDLE_VALUE; | 292 handle_ = INVALID_HANDLE_VALUE; |
| 284 } | 293 } |
| 285 | 294 |
| 286 } // namespace | 295 } // namespace |
| 287 | 296 |
| 288 FilePathWatcher::FilePathWatcher() { | 297 FilePathWatcher::FilePathWatcher() { |
| 289 impl_ = new FilePathWatcherImpl(); | 298 impl_ = new FilePathWatcherImpl(); |
| 290 } | 299 } |
| 291 | 300 |
| 292 } // namespace base | 301 } // namespace base |
| OLD | NEW |