OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/devtools/devtools_file_watcher.h" |
| 6 |
| 7 #include <map> |
| 8 #include <set> |
| 9 |
| 10 #include "base/bind.h" |
| 11 #include "base/files/file_enumerator.h" |
| 12 #include "base/files/file_path.h" |
| 13 #include "base/files/file_path_watcher.h" |
| 14 #include "base/memory/ref_counted.h" |
| 15 #include "content/public/browser/browser_thread.h" |
| 16 |
| 17 using content::BrowserThread; |
| 18 |
| 19 static int kFirstThrottleTimeout = 10; |
| 20 static int kDefaultThrottleTimeout = 200; |
| 21 |
| 22 // DevToolsFileWatcher::SharedFileWatcher -------------------------------------- |
| 23 |
| 24 class DevToolsFileWatcher::SharedFileWatcher : |
| 25 public base::RefCounted<SharedFileWatcher> { |
| 26 public: |
| 27 SharedFileWatcher(); |
| 28 |
| 29 void AddListener(DevToolsFileWatcher* watcher); |
| 30 void RemoveListener(DevToolsFileWatcher* watcher); |
| 31 void AddWatch(const base::FilePath& path); |
| 32 void RemoveWatch(const base::FilePath& path); |
| 33 |
| 34 private: |
| 35 friend class base::RefCounted< |
| 36 DevToolsFileWatcher::SharedFileWatcher>; |
| 37 ~SharedFileWatcher(); |
| 38 |
| 39 void DirectoryChanged(const base::FilePath& path, bool error); |
| 40 void DispatchNotifications(); |
| 41 |
| 42 std::vector<DevToolsFileWatcher*> listeners_; |
| 43 std::map<base::FilePath, scoped_ptr<base::FilePathWatcher>> watchers_; |
| 44 using FilePathTimesMap = std::map<base::FilePath, base::Time>; |
| 45 FilePathTimesMap file_path_times_; |
| 46 std::set<base::FilePath> pending_paths_; |
| 47 base::Time last_event_time_; |
| 48 base::TimeDelta last_dispatch_cost_; |
| 49 }; |
| 50 |
| 51 DevToolsFileWatcher::SharedFileWatcher::SharedFileWatcher() |
| 52 : last_dispatch_cost_( |
| 53 base::TimeDelta::FromMilliseconds(kDefaultThrottleTimeout)) { |
| 54 DevToolsFileWatcher::s_shared_watcher_ = this; |
| 55 } |
| 56 |
| 57 DevToolsFileWatcher::SharedFileWatcher::~SharedFileWatcher() { |
| 58 DevToolsFileWatcher::s_shared_watcher_ = nullptr; |
| 59 } |
| 60 |
| 61 void DevToolsFileWatcher::SharedFileWatcher::AddListener( |
| 62 DevToolsFileWatcher* watcher) { |
| 63 listeners_.push_back(watcher); |
| 64 } |
| 65 |
| 66 void DevToolsFileWatcher::SharedFileWatcher::RemoveListener( |
| 67 DevToolsFileWatcher* watcher) { |
| 68 auto it = std::find(listeners_.begin(), listeners_.end(), watcher); |
| 69 listeners_.erase(it); |
| 70 } |
| 71 |
| 72 void DevToolsFileWatcher::SharedFileWatcher::AddWatch( |
| 73 const base::FilePath& path) { |
| 74 if (watchers_.find(path) != watchers_.end()) |
| 75 return; |
| 76 if (!base::FilePathWatcher::RecursiveWatchAvailable()) |
| 77 return; |
| 78 watchers_[path].reset(new base::FilePathWatcher()); |
| 79 bool success = watchers_[path]->Watch( |
| 80 path, true, |
| 81 base::Bind(&SharedFileWatcher::DirectoryChanged, base::Unretained(this))); |
| 82 if (!success) |
| 83 return; |
| 84 |
| 85 base::FileEnumerator enumerator(path, true, base::FileEnumerator::FILES); |
| 86 base::FilePath file_path = enumerator.Next(); |
| 87 while (!file_path.empty()) { |
| 88 base::FileEnumerator::FileInfo file_info = enumerator.GetInfo(); |
| 89 file_path_times_[file_path] = file_info.GetLastModifiedTime(); |
| 90 file_path = enumerator.Next(); |
| 91 } |
| 92 } |
| 93 |
| 94 void DevToolsFileWatcher::SharedFileWatcher::RemoveWatch( |
| 95 const base::FilePath& path) { |
| 96 watchers_.erase(path); |
| 97 } |
| 98 |
| 99 void DevToolsFileWatcher::SharedFileWatcher::DirectoryChanged( |
| 100 const base::FilePath& path, |
| 101 bool error) { |
| 102 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 103 pending_paths_.insert(path); |
| 104 if (pending_paths_.size() > 1) |
| 105 return; // PostDelayedTask is already pending. |
| 106 |
| 107 base::Time now = base::Time::Now(); |
| 108 // Quickly dispatch first chunk. |
| 109 base::TimeDelta shedule_for = |
| 110 now - last_event_time_ > last_dispatch_cost_ ? |
| 111 base::TimeDelta::FromMilliseconds(kFirstThrottleTimeout) : |
| 112 last_dispatch_cost_ * 2; |
| 113 |
| 114 BrowserThread::PostDelayedTask( |
| 115 BrowserThread::FILE, FROM_HERE, |
| 116 base::Bind(&DevToolsFileWatcher::SharedFileWatcher::DispatchNotifications, |
| 117 this), shedule_for); |
| 118 last_event_time_ = now; |
| 119 } |
| 120 |
| 121 void DevToolsFileWatcher::SharedFileWatcher::DispatchNotifications() { |
| 122 base::Time start = base::Time::Now(); |
| 123 std::vector<std::string> changed_paths; |
| 124 for (auto path : pending_paths_) { |
| 125 base::FileEnumerator enumerator(path, true, base::FileEnumerator::FILES); |
| 126 base::FilePath file_path = enumerator.Next(); |
| 127 while (!file_path.empty()) { |
| 128 base::FileEnumerator::FileInfo file_info = enumerator.GetInfo(); |
| 129 base::Time new_time = file_info.GetLastModifiedTime(); |
| 130 if (file_path_times_[file_path] != new_time) { |
| 131 file_path_times_[file_path] = new_time; |
| 132 changed_paths.push_back(file_path.AsUTF8Unsafe()); |
| 133 } |
| 134 file_path = enumerator.Next(); |
| 135 } |
| 136 } |
| 137 pending_paths_.clear(); |
| 138 |
| 139 for (auto watcher : listeners_) { |
| 140 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| 141 base::Bind(watcher->callback_, changed_paths)); |
| 142 } |
| 143 last_dispatch_cost_ = base::Time::Now() - start; |
| 144 } |
| 145 |
| 146 // static |
| 147 DevToolsFileWatcher::SharedFileWatcher* |
| 148 DevToolsFileWatcher::s_shared_watcher_ = nullptr; |
| 149 |
| 150 // DevToolsFileWatcher --------------------------------------------------------- |
| 151 |
| 152 DevToolsFileWatcher::DevToolsFileWatcher(const WatchCallback& callback) |
| 153 : callback_(callback) { |
| 154 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 155 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
| 156 base::Bind(&DevToolsFileWatcher::InitSharedWatcher, |
| 157 base::Unretained(this))); |
| 158 } |
| 159 |
| 160 DevToolsFileWatcher::~DevToolsFileWatcher() { |
| 161 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 162 shared_watcher_->RemoveListener(this); |
| 163 } |
| 164 |
| 165 void DevToolsFileWatcher::InitSharedWatcher() { |
| 166 if (!DevToolsFileWatcher::s_shared_watcher_) |
| 167 new SharedFileWatcher(); |
| 168 shared_watcher_ = DevToolsFileWatcher::s_shared_watcher_; |
| 169 shared_watcher_->AddListener(this); |
| 170 } |
| 171 |
| 172 void DevToolsFileWatcher::AddWatch(const base::FilePath& path) { |
| 173 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 174 shared_watcher_->AddWatch(path); |
| 175 } |
| 176 |
| 177 void DevToolsFileWatcher::RemoveWatch(const base::FilePath& path) { |
| 178 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 179 shared_watcher_->RemoveWatch(path); |
| 180 } |
OLD | NEW |