OLD | NEW |
| (Empty) |
1 // Copyright 2014 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/extensions/api/file_system/entry_watcher_service.h" | |
6 | |
7 #include "base/thread_task_runner_handle.h" | |
8 #include "chrome/browser/extensions/extension_util.h" | |
9 #include "chrome/browser/profiles/profile.h" | |
10 #include "chrome/common/extensions/api/file_system.h" | |
11 #include "components/keyed_service/content/browser_context_dependency_manager.h" | |
12 #include "content/public/browser/browser_context.h" | |
13 #include "content/public/browser/browser_thread.h" | |
14 #include "content/public/browser/storage_partition.h" | |
15 #include "extensions/browser/event_router.h" | |
16 #include "storage/browser/fileapi/file_system_context.h" | |
17 | |
18 namespace extensions { | |
19 namespace { | |
20 | |
21 // Default implementation for dispatching an event. Can be replaced for unit | |
22 // tests by EntryWatcherService::SetDispatchEventImplForTesting(). | |
23 void DispatchEventImpl(EventRouter* event_router, | |
24 const std::string& extension_id, | |
25 scoped_ptr<Event> event) { | |
26 event_router->DispatchEventToExtension(extension_id, event.Pass()); | |
27 } | |
28 | |
29 // Default implementation for acquiring a file system context for a specific | |
30 // |extension_id| and |context|. | |
31 storage::FileSystemContext* GetFileSystemContextImpl( | |
32 const std::string& extension_id, | |
33 content::BrowserContext* context) { | |
34 const GURL site = util::GetSiteForExtensionId(extension_id, context); | |
35 return content::BrowserContext::GetStoragePartitionForSite(context, site) | |
36 ->GetFileSystemContext(); | |
37 } | |
38 | |
39 } // namespace | |
40 | |
41 EntryWatcherService::EntryWatcherService(content::BrowserContext* context) | |
42 : context_(context), | |
43 dispatch_event_impl_( | |
44 base::Bind(&DispatchEventImpl, EventRouter::Get(context))), | |
45 get_file_system_context_impl_(base::Bind(&GetFileSystemContextImpl)), | |
46 observing_(this), | |
47 weak_ptr_factory_(this) { | |
48 // TODO(mtomasz): Restore persistent watchers. | |
49 } | |
50 | |
51 EntryWatcherService::~EntryWatcherService() { | |
52 } | |
53 | |
54 void EntryWatcherService::SetDispatchEventImplForTesting( | |
55 const DispatchEventImplCallback& callback) { | |
56 dispatch_event_impl_ = callback; | |
57 } | |
58 | |
59 void EntryWatcherService::SetGetFileSystemContextImplForTesting( | |
60 const GetFileSystemContextImplCallback& callback) { | |
61 get_file_system_context_impl_ = callback; | |
62 } | |
63 | |
64 void EntryWatcherService::WatchDirectory( | |
65 const std::string& extension_id, | |
66 const storage::FileSystemURL& url, | |
67 bool recursive, | |
68 const storage::WatcherManager::StatusCallback& callback) { | |
69 // TODO(mtomasz): Add support for recursive watchers. | |
70 if (recursive) { | |
71 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
72 FROM_HERE, | |
73 base::Bind(callback, base::File::FILE_ERROR_INVALID_OPERATION)); | |
74 return; | |
75 } | |
76 | |
77 storage::FileSystemContext* const context = | |
78 get_file_system_context_impl_.Run(extension_id, context_); | |
79 DCHECK(context); | |
80 | |
81 storage::WatcherManager* const watcher_manager = | |
82 context->GetWatcherManager(url.type()); | |
83 if (!watcher_manager) { | |
84 // Post a task instead of calling the callback directly, since the caller | |
85 // may expect the callback to be called asynchronously. | |
86 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
87 FROM_HERE, | |
88 base::Bind(callback, base::File::FILE_ERROR_INVALID_OPERATION)); | |
89 return; | |
90 } | |
91 | |
92 // Passing a pointer to WatcherManager is safe, since the pointer is required | |
93 // to be valid until shutdown. | |
94 context->default_file_task_runner()->PostNonNestableTask( | |
95 FROM_HERE, | |
96 base::Bind(&storage::WatcherManager::WatchDirectory, | |
97 base::Unretained(watcher_manager), // Outlives the service. | |
98 url, | |
99 recursive, | |
100 base::Bind(&EntryWatcherService::OnWatchDirectoryCompleted, | |
101 weak_ptr_factory_.GetWeakPtr(), | |
102 watcher_manager, // Outlives the service. | |
103 extension_id, | |
104 url, | |
105 recursive, | |
106 callback))); | |
107 } | |
108 | |
109 void EntryWatcherService::UnwatchEntry( | |
110 const std::string& extension_id, | |
111 const storage::FileSystemURL& url, | |
112 const storage::WatcherManager::StatusCallback& callback) { | |
113 storage::FileSystemContext* const context = | |
114 get_file_system_context_impl_.Run(extension_id, context_); | |
115 DCHECK(context); | |
116 | |
117 storage::WatcherManager* const watcher_manager = | |
118 context->GetWatcherManager(url.type()); | |
119 if (!watcher_manager) { | |
120 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
121 FROM_HERE, | |
122 base::Bind(callback, base::File::FILE_ERROR_INVALID_OPERATION)); | |
123 return; | |
124 } | |
125 | |
126 // Passing a pointer to WatcherManager is safe, since the pointer is required | |
127 // to be valid until shutdown. | |
128 context->default_file_task_runner()->PostNonNestableTask( | |
129 FROM_HERE, | |
130 base::Bind(&storage::WatcherManager::UnwatchEntry, | |
131 base::Unretained(watcher_manager), // Outlives the service. | |
132 url, | |
133 base::Bind(&EntryWatcherService::OnUnwatchEntryCompleted, | |
134 weak_ptr_factory_.GetWeakPtr(), | |
135 extension_id, | |
136 url, | |
137 callback))); | |
138 } | |
139 | |
140 std::vector<storage::FileSystemURL> EntryWatcherService::GetWatchedEntries( | |
141 const std::string& extension_id) { | |
142 std::vector<storage::FileSystemURL> result; | |
143 for (WatcherMap::const_iterator it = watchers_.begin(); it != watchers_.end(); | |
144 ++it) { | |
145 const std::map<std::string, EntryWatcher>::const_iterator watcher_it = | |
146 it->second.find(extension_id); | |
147 if (watcher_it != it->second.end()) | |
148 result.push_back(watcher_it->second.url); | |
149 } | |
150 | |
151 return result; | |
152 } | |
153 | |
154 void EntryWatcherService::OnEntryChanged(const storage::FileSystemURL& url) { | |
155 const WatcherMap::const_iterator it = watchers_.find(url); | |
156 DCHECK(it != watchers_.end()); | |
157 for (std::map<std::string, EntryWatcher>::const_iterator watcher_it = | |
158 it->second.begin(); | |
159 watcher_it != it->second.end(); | |
160 ++watcher_it) { | |
161 const std::string& extension_id = watcher_it->first; | |
162 api::file_system::EntryChangedEvent event; | |
163 dispatch_event_impl_.Run( | |
164 extension_id, | |
165 make_scoped_ptr( | |
166 new Event(api::file_system::OnEntryChanged::kEventName, | |
167 api::file_system::OnEntryChanged::Create(event)))); | |
168 } | |
169 } | |
170 | |
171 void EntryWatcherService::OnEntryRemoved(const storage::FileSystemURL& url) { | |
172 WatcherMap::const_iterator it = watchers_.find(url); | |
173 DCHECK(it != watchers_.end()); | |
174 for (std::map<std::string, EntryWatcher>::const_iterator watcher_it = | |
175 it->second.begin(); | |
176 watcher_it != it->second.end(); | |
177 ++watcher_it) { | |
178 const std::string& extension_id = watcher_it->first; | |
179 api::file_system::EntryRemovedEvent event; | |
180 dispatch_event_impl_.Run( | |
181 extension_id, | |
182 make_scoped_ptr( | |
183 new Event(api::file_system::OnEntryRemoved::kEventName, | |
184 api::file_system::OnEntryRemoved::Create(event)))); | |
185 } | |
186 } | |
187 | |
188 void EntryWatcherService::OnWatchDirectoryCompleted( | |
189 storage::WatcherManager* watcher_manager, | |
190 const std::string& extension_id, | |
191 const storage::FileSystemURL& url, | |
192 bool recursive, | |
193 const storage::WatcherManager::StatusCallback& callback, | |
194 base::File::Error result) { | |
195 if (result != base::File::FILE_OK) { | |
196 callback.Run(result); | |
197 return; | |
198 } | |
199 | |
200 storage::FileSystemContext* const context = | |
201 get_file_system_context_impl_.Run(extension_id, context_); | |
202 DCHECK(context); | |
203 | |
204 // Observe the manager if not observed yet. | |
205 if (!observing_.IsObserving(watcher_manager)) | |
206 observing_.Add(watcher_manager); | |
207 | |
208 watchers_[url][extension_id] = | |
209 EntryWatcher(url, true /* directory */, recursive); | |
210 | |
211 // TODO(mtomasz): Save in preferences. | |
212 | |
213 callback.Run(base::File::FILE_OK); | |
214 } | |
215 | |
216 void EntryWatcherService::OnUnwatchEntryCompleted( | |
217 const std::string& extension_id, | |
218 const storage::FileSystemURL& url, | |
219 const storage::WatcherManager::StatusCallback& callback, | |
220 base::File::Error result) { | |
221 if (result != base::File::FILE_OK) { | |
222 callback.Run(result); | |
223 return; | |
224 } | |
225 | |
226 if (watchers_[url].erase(extension_id) == 0) { | |
227 callback.Run(base::File::FILE_ERROR_NOT_FOUND); | |
228 return; | |
229 } | |
230 | |
231 if (watchers_[url].empty()) | |
232 watchers_.erase(url); | |
233 | |
234 // TODO(mtomasz): Save in preferences. | |
235 | |
236 callback.Run(base::File::FILE_OK); | |
237 } | |
238 | |
239 EntryWatcherService::EntryWatcher::EntryWatcher() | |
240 : directory(false), recursive(false) { | |
241 } | |
242 | |
243 EntryWatcherService::EntryWatcher::EntryWatcher( | |
244 const storage::FileSystemURL& url, | |
245 bool directory, | |
246 bool recursive) | |
247 : url(url), directory(directory), recursive(recursive) { | |
248 } | |
249 | |
250 EntryWatcherService::EntryWatcher::~EntryWatcher() { | |
251 } | |
252 | |
253 } // namespace extensions | |
OLD | NEW |