| OLD | NEW |
| 1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2015 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 "chrome/browser/devtools/devtools_file_watcher.h" | 5 #include "chrome/browser/devtools/devtools_file_watcher.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <map> | 8 #include <map> |
| 9 #include <memory> | 9 #include <memory> |
| 10 #include <set> | 10 #include <set> |
| 11 | 11 |
| 12 #include "base/bind.h" | 12 #include "base/bind.h" |
| 13 #include "base/files/file_enumerator.h" | 13 #include "base/files/file_enumerator.h" |
| 14 #include "base/files/file_path.h" | 14 #include "base/files/file_path.h" |
| 15 #include "base/files/file_path_watcher.h" | 15 #include "base/files/file_path_watcher.h" |
| 16 #include "base/files/file_util.h" |
| 16 #include "base/memory/ref_counted.h" | 17 #include "base/memory/ref_counted.h" |
| 17 #include "content/public/browser/browser_thread.h" | 18 #include "content/public/browser/browser_thread.h" |
| 18 | 19 |
| 19 using content::BrowserThread; | 20 using content::BrowserThread; |
| 20 | 21 |
| 21 static int kFirstThrottleTimeout = 10; | 22 static int kFirstThrottleTimeout = 10; |
| 22 static int kDefaultThrottleTimeout = 200; | 23 static int kDefaultThrottleTimeout = 200; |
| 23 | 24 |
| 24 // DevToolsFileWatcher::SharedFileWatcher -------------------------------------- | 25 // DevToolsFileWatcher::SharedFileWatcher -------------------------------------- |
| 25 | 26 |
| 26 class DevToolsFileWatcher::SharedFileWatcher : | 27 class DevToolsFileWatcher::SharedFileWatcher : |
| 27 public base::RefCounted<SharedFileWatcher> { | 28 public base::RefCounted<SharedFileWatcher> { |
| 28 public: | 29 public: |
| 29 SharedFileWatcher(); | 30 SharedFileWatcher(); |
| 30 | 31 |
| 31 void AddListener(DevToolsFileWatcher* watcher); | 32 void AddListener(DevToolsFileWatcher* watcher); |
| 32 void RemoveListener(DevToolsFileWatcher* watcher); | 33 void RemoveListener(DevToolsFileWatcher* watcher); |
| 33 void AddWatch(const base::FilePath& path); | 34 void AddWatch(const base::FilePath& path); |
| 34 void RemoveWatch(const base::FilePath& path); | 35 void RemoveWatch(const base::FilePath& path); |
| 35 | 36 |
| 36 private: | 37 private: |
| 37 friend class base::RefCounted< | 38 friend class base::RefCounted< |
| 38 DevToolsFileWatcher::SharedFileWatcher>; | 39 DevToolsFileWatcher::SharedFileWatcher>; |
| 39 ~SharedFileWatcher(); | 40 ~SharedFileWatcher(); |
| 40 | 41 |
| 42 using FilePathTimesMap = std::map<base::FilePath, base::Time>; |
| 43 void GetModificationTimes(const base::FilePath& path, |
| 44 FilePathTimesMap* file_path_times); |
| 41 void DirectoryChanged(const base::FilePath& path, bool error); | 45 void DirectoryChanged(const base::FilePath& path, bool error); |
| 42 void DispatchNotifications(); | 46 void DispatchNotifications(); |
| 43 | 47 |
| 44 std::vector<DevToolsFileWatcher*> listeners_; | 48 std::vector<DevToolsFileWatcher*> listeners_; |
| 45 std::map<base::FilePath, std::unique_ptr<base::FilePathWatcher>> watchers_; | 49 std::map<base::FilePath, std::unique_ptr<base::FilePathWatcher>> watchers_; |
| 46 using FilePathTimesMap = std::map<base::FilePath, base::Time>; | 50 std::map<base::FilePath, FilePathTimesMap> file_path_times_; |
| 47 FilePathTimesMap file_path_times_; | |
| 48 std::set<base::FilePath> pending_paths_; | 51 std::set<base::FilePath> pending_paths_; |
| 49 base::Time last_event_time_; | 52 base::Time last_event_time_; |
| 50 base::TimeDelta last_dispatch_cost_; | 53 base::TimeDelta last_dispatch_cost_; |
| 51 }; | 54 }; |
| 52 | 55 |
| 53 DevToolsFileWatcher::SharedFileWatcher::SharedFileWatcher() | 56 DevToolsFileWatcher::SharedFileWatcher::SharedFileWatcher() |
| 54 : last_dispatch_cost_( | 57 : last_dispatch_cost_( |
| 55 base::TimeDelta::FromMilliseconds(kDefaultThrottleTimeout)) { | 58 base::TimeDelta::FromMilliseconds(kDefaultThrottleTimeout)) { |
| 56 DevToolsFileWatcher::s_shared_watcher_ = this; | 59 DevToolsFileWatcher::s_shared_watcher_ = this; |
| 57 } | 60 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 77 return; | 80 return; |
| 78 if (!base::FilePathWatcher::RecursiveWatchAvailable()) | 81 if (!base::FilePathWatcher::RecursiveWatchAvailable()) |
| 79 return; | 82 return; |
| 80 watchers_[path].reset(new base::FilePathWatcher()); | 83 watchers_[path].reset(new base::FilePathWatcher()); |
| 81 bool success = watchers_[path]->Watch( | 84 bool success = watchers_[path]->Watch( |
| 82 path, true, | 85 path, true, |
| 83 base::Bind(&SharedFileWatcher::DirectoryChanged, base::Unretained(this))); | 86 base::Bind(&SharedFileWatcher::DirectoryChanged, base::Unretained(this))); |
| 84 if (!success) | 87 if (!success) |
| 85 return; | 88 return; |
| 86 | 89 |
| 90 GetModificationTimes(path, &file_path_times_[path]); |
| 91 } |
| 92 |
| 93 void DevToolsFileWatcher::SharedFileWatcher::GetModificationTimes( |
| 94 const base::FilePath& path, |
| 95 FilePathTimesMap* times_map) { |
| 87 base::FileEnumerator enumerator(path, true, base::FileEnumerator::FILES); | 96 base::FileEnumerator enumerator(path, true, base::FileEnumerator::FILES); |
| 88 base::FilePath file_path = enumerator.Next(); | 97 base::FilePath file_path = enumerator.Next(); |
| 89 while (!file_path.empty()) { | 98 while (!file_path.empty()) { |
| 90 base::FileEnumerator::FileInfo file_info = enumerator.GetInfo(); | 99 base::FileEnumerator::FileInfo file_info = enumerator.GetInfo(); |
| 91 file_path_times_[file_path] = file_info.GetLastModifiedTime(); | 100 (*times_map)[file_path] = file_info.GetLastModifiedTime(); |
| 92 file_path = enumerator.Next(); | 101 file_path = enumerator.Next(); |
| 93 } | 102 } |
| 94 } | 103 } |
| 95 | 104 |
| 96 void DevToolsFileWatcher::SharedFileWatcher::RemoveWatch( | 105 void DevToolsFileWatcher::SharedFileWatcher::RemoveWatch( |
| 97 const base::FilePath& path) { | 106 const base::FilePath& path) { |
| 98 watchers_.erase(path); | 107 watchers_.erase(path); |
| 99 } | 108 } |
| 100 | 109 |
| 101 void DevToolsFileWatcher::SharedFileWatcher::DirectoryChanged( | 110 void DevToolsFileWatcher::SharedFileWatcher::DirectoryChanged( |
| (...skipping 12 matching lines...) Expand all Loading... |
| 114 last_dispatch_cost_ * 2; | 123 last_dispatch_cost_ * 2; |
| 115 | 124 |
| 116 BrowserThread::PostDelayedTask( | 125 BrowserThread::PostDelayedTask( |
| 117 BrowserThread::FILE, FROM_HERE, | 126 BrowserThread::FILE, FROM_HERE, |
| 118 base::Bind(&DevToolsFileWatcher::SharedFileWatcher::DispatchNotifications, | 127 base::Bind(&DevToolsFileWatcher::SharedFileWatcher::DispatchNotifications, |
| 119 this), shedule_for); | 128 this), shedule_for); |
| 120 last_event_time_ = now; | 129 last_event_time_ = now; |
| 121 } | 130 } |
| 122 | 131 |
| 123 void DevToolsFileWatcher::SharedFileWatcher::DispatchNotifications() { | 132 void DevToolsFileWatcher::SharedFileWatcher::DispatchNotifications() { |
| 133 if (!pending_paths_.size()) |
| 134 return; |
| 124 base::Time start = base::Time::Now(); | 135 base::Time start = base::Time::Now(); |
| 136 std::vector<std::string> added_paths; |
| 137 std::vector<std::string> removed_paths; |
| 125 std::vector<std::string> changed_paths; | 138 std::vector<std::string> changed_paths; |
| 126 for (auto path : pending_paths_) { | 139 |
| 127 base::FileEnumerator enumerator(path, true, base::FileEnumerator::FILES); | 140 for (const auto& path : pending_paths_) { |
| 128 base::FilePath file_path = enumerator.Next(); | 141 FilePathTimesMap& old_times = file_path_times_[path]; |
| 129 while (!file_path.empty()) { | 142 FilePathTimesMap current_times; |
| 130 base::FileEnumerator::FileInfo file_info = enumerator.GetInfo(); | 143 GetModificationTimes(path, ¤t_times); |
| 131 base::Time new_time = file_info.GetLastModifiedTime(); | 144 for (const auto& path_time : current_times) { |
| 132 if (file_path_times_[file_path] != new_time) { | 145 const base::FilePath& path = path_time.first; |
| 133 file_path_times_[file_path] = new_time; | 146 auto old_timestamp = old_times.find(path); |
| 134 changed_paths.push_back(file_path.AsUTF8Unsafe()); | 147 if (old_timestamp == old_times.end()) |
| 135 } | 148 added_paths.push_back(path.AsUTF8Unsafe()); |
| 136 file_path = enumerator.Next(); | 149 else if (old_timestamp->second != path_time.second) |
| 150 changed_paths.push_back(path.AsUTF8Unsafe()); |
| 137 } | 151 } |
| 152 for (const auto& path_time : old_times) { |
| 153 const base::FilePath& path = path_time.first; |
| 154 if (current_times.find(path) == current_times.end()) |
| 155 removed_paths.push_back(path.AsUTF8Unsafe()); |
| 156 } |
| 157 old_times.swap(current_times); |
| 138 } | 158 } |
| 139 pending_paths_.clear(); | 159 pending_paths_.clear(); |
| 140 | 160 |
| 141 for (auto* watcher : listeners_) { | 161 for (auto* watcher : listeners_) { |
| 142 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 162 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| 143 base::Bind(watcher->callback_, changed_paths)); | 163 base::Bind(watcher->callback_, changed_paths, |
| 164 added_paths, removed_paths)); |
| 144 } | 165 } |
| 145 last_dispatch_cost_ = base::Time::Now() - start; | 166 last_dispatch_cost_ = base::Time::Now() - start; |
| 146 } | 167 } |
| 147 | 168 |
| 148 // static | 169 // static |
| 149 DevToolsFileWatcher::SharedFileWatcher* | 170 DevToolsFileWatcher::SharedFileWatcher* |
| 150 DevToolsFileWatcher::s_shared_watcher_ = nullptr; | 171 DevToolsFileWatcher::s_shared_watcher_ = nullptr; |
| 151 | 172 |
| 152 // DevToolsFileWatcher --------------------------------------------------------- | 173 // DevToolsFileWatcher --------------------------------------------------------- |
| 153 | 174 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 173 | 194 |
| 174 void DevToolsFileWatcher::AddWatch(const base::FilePath& path) { | 195 void DevToolsFileWatcher::AddWatch(const base::FilePath& path) { |
| 175 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 196 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 176 shared_watcher_->AddWatch(path); | 197 shared_watcher_->AddWatch(path); |
| 177 } | 198 } |
| 178 | 199 |
| 179 void DevToolsFileWatcher::RemoveWatch(const base::FilePath& path) { | 200 void DevToolsFileWatcher::RemoveWatch(const base::FilePath& path) { |
| 180 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 201 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 181 shared_watcher_->RemoveWatch(path); | 202 shared_watcher_->RemoveWatch(path); |
| 182 } | 203 } |
| OLD | NEW |