Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / chromeos / drive / file_write_watcher.cc
blob8c658d6c8b7d345ebf7bfb0b1b9c8fcba8166c33
1 // Copyright 2013 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.
5 #include "chrome/browser/chromeos/drive/file_write_watcher.h"
7 #include <map>
8 #include <vector>
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/files/file_path_watcher.h"
13 #include "base/stl_util.h"
14 #include "base/timer/timer.h"
15 #include "chrome/browser/chromeos/drive/logging.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "google_apis/drive/task_util.h"
19 using content::BrowserThread;
21 namespace drive {
22 namespace internal {
24 namespace {
25 const int64 kWriteEventDelayInSeconds = 5;
26 } // namespace
28 // base::FileWatcher needs to live in a thread that is allowed to do File IO
29 // and has a TYPE_IO message loop: that is, FILE thread. This class bridges the
30 // UI thread and FILE thread, and does all the main tasks in the FILE thread.
31 class FileWriteWatcher::FileWriteWatcherImpl {
32 public:
33 FileWriteWatcherImpl();
35 // Forwards the call to DestoryOnFileThread(). This method must be used to
36 // destruct the instance.
37 void Destroy();
39 // Forwards the call to StartWatchOnFileThread(). |on_start_callback| is
40 // called back on the caller (UI) thread when the watch has started.
41 // |on_write_callback| is called when a write has happened to the path.
42 void StartWatch(const base::FilePath& path,
43 const StartWatchCallback& on_start_callback,
44 const base::Closure& on_write_callback);
46 void set_delay(base::TimeDelta delay) { delay_ = delay; }
48 private:
49 ~FileWriteWatcherImpl();
51 void DestroyOnFileThread();
53 void StartWatchOnFileThread(const base::FilePath& path,
54 const StartWatchCallback& on_start_callback,
55 const base::Closure& on_write_callback);
57 void OnWriteEvent(const base::FilePath& path, bool error);
59 void InvokeCallback(const base::FilePath& path);
61 struct PathWatchInfo {
62 std::vector<base::Closure> on_write_callbacks;
63 base::FilePathWatcher watcher;
64 base::Timer timer;
66 explicit PathWatchInfo(const base::Closure& on_write_callback)
67 : on_write_callbacks(1, on_write_callback),
68 timer(false /* retain_closure_on_reset */, false /* is_repeating */) {
72 base::TimeDelta delay_;
73 std::map<base::FilePath, PathWatchInfo*> watchers_;
75 // Note: This should remain the last member so it'll be destroyed and
76 // invalidate its weak pointers before any other members are destroyed.
77 base::WeakPtrFactory<FileWriteWatcherImpl> weak_ptr_factory_;
78 DISALLOW_COPY_AND_ASSIGN(FileWriteWatcherImpl);
81 FileWriteWatcher::FileWriteWatcherImpl::FileWriteWatcherImpl()
82 : delay_(base::TimeDelta::FromSeconds(kWriteEventDelayInSeconds)),
83 weak_ptr_factory_(this) {
84 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
87 void FileWriteWatcher::FileWriteWatcherImpl::Destroy() {
88 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
90 // Just forwarding the call to FILE thread.
91 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)->PostTask(
92 FROM_HERE,
93 base::Bind(&FileWriteWatcherImpl::DestroyOnFileThread,
94 base::Unretained(this)));
97 void FileWriteWatcher::FileWriteWatcherImpl::StartWatch(
98 const base::FilePath& path,
99 const StartWatchCallback& on_start_callback,
100 const base::Closure& on_write_callback) {
101 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
103 // Forwarding the call to FILE thread and relaying the |callback|.
104 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)->PostTask(
105 FROM_HERE,
106 base::Bind(&FileWriteWatcherImpl::StartWatchOnFileThread,
107 base::Unretained(this),
108 path,
109 google_apis::CreateRelayCallback(on_start_callback),
110 google_apis::CreateRelayCallback(on_write_callback)));
113 FileWriteWatcher::FileWriteWatcherImpl::~FileWriteWatcherImpl() {
114 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
116 STLDeleteContainerPairSecondPointers(watchers_.begin(), watchers_.end());
119 void FileWriteWatcher::FileWriteWatcherImpl::DestroyOnFileThread() {
120 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
122 delete this;
125 void FileWriteWatcher::FileWriteWatcherImpl::StartWatchOnFileThread(
126 const base::FilePath& path,
127 const StartWatchCallback& on_start_callback,
128 const base::Closure& on_write_callback) {
129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
130 util::Log(logging::LOG_INFO, "Started watching modification to %s.",
131 path.AsUTF8Unsafe().c_str());
133 std::map<base::FilePath, PathWatchInfo*>::iterator it = watchers_.find(path);
134 if (it != watchers_.end()) {
135 // We are already watching the path.
136 on_start_callback.Run(true);
137 it->second->on_write_callbacks.push_back(on_write_callback);
138 return;
141 // Start watching |path|.
142 scoped_ptr<PathWatchInfo> info(new PathWatchInfo(on_write_callback));
143 bool ok = info->watcher.Watch(
144 path,
145 false, // recursive
146 base::Bind(&FileWriteWatcherImpl::OnWriteEvent,
147 weak_ptr_factory_.GetWeakPtr()));
148 watchers_[path] = info.release();
149 on_start_callback.Run(ok);
152 void FileWriteWatcher::FileWriteWatcherImpl::OnWriteEvent(
153 const base::FilePath& path,
154 bool error) {
155 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
156 util::Log(logging::LOG_INFO, "Detected modification to %s.",
157 path.AsUTF8Unsafe().c_str());
159 if (error)
160 return;
162 std::map<base::FilePath, PathWatchInfo*>::iterator it = watchers_.find(path);
163 DCHECK(it != watchers_.end());
165 // Heuristics for detecting the end of successive write operations.
166 // Delay running on_write_event_callback by |delay_| time, and if OnWriteEvent
167 // is called again in the period, the timer is reset. In other words, we
168 // invoke callback when |delay_| has passed after the last OnWriteEvent().
169 it->second->timer.Start(FROM_HERE,
170 delay_,
171 base::Bind(&FileWriteWatcherImpl::InvokeCallback,
172 weak_ptr_factory_.GetWeakPtr(),
173 path));
176 void FileWriteWatcher::FileWriteWatcherImpl::InvokeCallback(
177 const base::FilePath& path) {
178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
179 util::Log(logging::LOG_INFO, "Finished watching modification to %s.",
180 path.AsUTF8Unsafe().c_str());
182 std::map<base::FilePath, PathWatchInfo*>::iterator it = watchers_.find(path);
183 DCHECK(it != watchers_.end());
185 std::vector<base::Closure> callbacks;
186 callbacks.swap(it->second->on_write_callbacks);
187 delete it->second;
188 watchers_.erase(it);
190 for (size_t i = 0; i < callbacks.size(); ++i)
191 callbacks[i].Run();
194 FileWriteWatcher::FileWriteWatcher()
195 : watcher_impl_(new FileWriteWatcherImpl) {
196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
199 FileWriteWatcher::~FileWriteWatcher() {
200 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
203 void FileWriteWatcher::StartWatch(const base::FilePath& file_path,
204 const StartWatchCallback& on_start_callback,
205 const base::Closure& on_write_callback) {
206 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
207 watcher_impl_->StartWatch(file_path, on_start_callback, on_write_callback);
210 void FileWriteWatcher::DisableDelayForTesting() {
211 watcher_impl_->set_delay(base::TimeDelta());
214 } // namespace internal
215 } // namespace drive