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_) { |
155 last_modified_ != file_info.last_modified)) { | 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() || |
| 169 last_modified_ != file_info.last_modified)) { |
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 |