Chromium Code Reviews| 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 FilePathTimesMap times_map; | |
| 91 GetModificationTimes(path, ×_map); | |
|
dgozman
2016/10/04 02:26:41
One line, avoids copying:
GetModificationTimes(pa
lushnikov
2016/10/04 02:51:59
Done.
| |
| 92 file_path_times_[path] = times_map; | |
| 93 } | |
| 94 | |
| 95 void DevToolsFileWatcher::SharedFileWatcher::GetModificationTimes( | |
| 96 const base::FilePath& path, | |
| 97 FilePathTimesMap* times_map) { | |
| 87 base::FileEnumerator enumerator(path, true, base::FileEnumerator::FILES); | 98 base::FileEnumerator enumerator(path, true, base::FileEnumerator::FILES); |
| 88 base::FilePath file_path = enumerator.Next(); | 99 base::FilePath file_path = enumerator.Next(); |
| 89 while (!file_path.empty()) { | 100 while (!file_path.empty()) { |
| 90 base::FileEnumerator::FileInfo file_info = enumerator.GetInfo(); | 101 base::FileEnumerator::FileInfo file_info = enumerator.GetInfo(); |
| 91 file_path_times_[file_path] = file_info.GetLastModifiedTime(); | 102 (*times_map)[file_path] = file_info.GetLastModifiedTime(); |
| 92 file_path = enumerator.Next(); | 103 file_path = enumerator.Next(); |
| 93 } | 104 } |
| 94 } | 105 } |
| 95 | 106 |
| 96 void DevToolsFileWatcher::SharedFileWatcher::RemoveWatch( | 107 void DevToolsFileWatcher::SharedFileWatcher::RemoveWatch( |
| 97 const base::FilePath& path) { | 108 const base::FilePath& path) { |
| 98 watchers_.erase(path); | 109 watchers_.erase(path); |
| 99 } | 110 } |
| 100 | 111 |
| 101 void DevToolsFileWatcher::SharedFileWatcher::DirectoryChanged( | 112 void DevToolsFileWatcher::SharedFileWatcher::DirectoryChanged( |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 114 last_dispatch_cost_ * 2; | 125 last_dispatch_cost_ * 2; |
| 115 | 126 |
| 116 BrowserThread::PostDelayedTask( | 127 BrowserThread::PostDelayedTask( |
| 117 BrowserThread::FILE, FROM_HERE, | 128 BrowserThread::FILE, FROM_HERE, |
| 118 base::Bind(&DevToolsFileWatcher::SharedFileWatcher::DispatchNotifications, | 129 base::Bind(&DevToolsFileWatcher::SharedFileWatcher::DispatchNotifications, |
| 119 this), shedule_for); | 130 this), shedule_for); |
| 120 last_event_time_ = now; | 131 last_event_time_ = now; |
| 121 } | 132 } |
| 122 | 133 |
| 123 void DevToolsFileWatcher::SharedFileWatcher::DispatchNotifications() { | 134 void DevToolsFileWatcher::SharedFileWatcher::DispatchNotifications() { |
| 135 if (!pending_paths_.size()) | |
| 136 return; | |
| 124 base::Time start = base::Time::Now(); | 137 base::Time start = base::Time::Now(); |
| 138 std::vector<std::string> added_paths; | |
| 139 std::vector<std::string> removed_paths; | |
| 125 std::vector<std::string> changed_paths; | 140 std::vector<std::string> changed_paths; |
| 126 for (auto path : pending_paths_) { | 141 |
| 127 base::FileEnumerator enumerator(path, true, base::FileEnumerator::FILES); | 142 for (const auto& path : pending_paths_) { |
| 128 base::FilePath file_path = enumerator.Next(); | 143 FilePathTimesMap& old_times = file_path_times_[path]; |
| 129 while (!file_path.empty()) { | 144 FilePathTimesMap current_times; |
| 130 base::FileEnumerator::FileInfo file_info = enumerator.GetInfo(); | 145 GetModificationTimes(path, ¤t_times); |
| 131 base::Time new_time = file_info.GetLastModifiedTime(); | 146 for (auto it = current_times.begin(); it != current_times.end(); ++it) { |
|
dgozman
2016/10/04 02:26:41
for (const auto& pair : current_times)
lushnikov
2016/10/04 02:51:59
Done.
| |
| 132 if (file_path_times_[file_path] != new_time) { | 147 auto old_timestamp = old_times.find(it->first); |
| 133 file_path_times_[file_path] = new_time; | 148 if (old_timestamp == old_times.end()) { |
| 134 changed_paths.push_back(file_path.AsUTF8Unsafe()); | 149 old_times[it->first] = it->second; |
| 150 added_paths.push_back(it->first.AsUTF8Unsafe()); | |
| 151 } else if (old_timestamp->second != it->second) { | |
| 152 old_timestamp->second = it->second; | |
| 153 changed_paths.push_back(it->first.AsUTF8Unsafe()); | |
| 135 } | 154 } |
| 136 file_path = enumerator.Next(); | 155 } |
| 156 for (auto it = old_times.begin(); it != old_times.end();) { | |
|
dgozman
2016/10/04 02:26:41
ditto
lushnikov
2016/10/04 02:51:59
Done.
| |
| 157 auto new_timestamp = current_times.find(it->first); | |
| 158 if (new_timestamp == current_times.end()) { | |
| 159 removed_paths.push_back(it->first.AsUTF8Unsafe()); | |
| 160 old_times.erase(it++); | |
|
dgozman
2016/10/04 02:26:41
Don't do this. Just |old_times.swap(current_times)
lushnikov
2016/10/04 02:51:59
Done.
| |
| 161 } else { | |
| 162 ++it; | |
| 163 } | |
| 137 } | 164 } |
| 138 } | 165 } |
| 139 pending_paths_.clear(); | 166 pending_paths_.clear(); |
| 140 | 167 |
| 141 for (auto* watcher : listeners_) { | 168 for (auto* watcher : listeners_) { |
| 142 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 169 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| 143 base::Bind(watcher->callback_, changed_paths)); | 170 base::Bind(watcher->callback_, changed_paths, |
| 171 added_paths, removed_paths)); | |
| 144 } | 172 } |
| 145 last_dispatch_cost_ = base::Time::Now() - start; | 173 last_dispatch_cost_ = base::Time::Now() - start; |
| 146 } | 174 } |
| 147 | 175 |
| 148 // static | 176 // static |
| 149 DevToolsFileWatcher::SharedFileWatcher* | 177 DevToolsFileWatcher::SharedFileWatcher* |
| 150 DevToolsFileWatcher::s_shared_watcher_ = nullptr; | 178 DevToolsFileWatcher::s_shared_watcher_ = nullptr; |
| 151 | 179 |
| 152 // DevToolsFileWatcher --------------------------------------------------------- | 180 // DevToolsFileWatcher --------------------------------------------------------- |
| 153 | 181 |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 173 | 201 |
| 174 void DevToolsFileWatcher::AddWatch(const base::FilePath& path) { | 202 void DevToolsFileWatcher::AddWatch(const base::FilePath& path) { |
| 175 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 203 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 176 shared_watcher_->AddWatch(path); | 204 shared_watcher_->AddWatch(path); |
| 177 } | 205 } |
| 178 | 206 |
| 179 void DevToolsFileWatcher::RemoveWatch(const base::FilePath& path) { | 207 void DevToolsFileWatcher::RemoveWatch(const base::FilePath& path) { |
| 180 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 208 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 181 shared_watcher_->RemoveWatch(path); | 209 shared_watcher_->RemoveWatch(path); |
| 182 } | 210 } |
| OLD | NEW |