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/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/storage_partition.h" | |
14 #include "extensions/browser/event_router.h" | |
15 #include "webkit/browser/fileapi/file_system_context.h" | |
16 | |
17 namespace extensions { | |
18 namespace { | |
19 | |
20 // Default implementation for dispatching an event. Can be replaced for unit | |
21 // tests by EntryWatcherService::SetDispatchEventImplForTesting(). | |
22 bool DispatchEventImpl(extensions::EventRouter* event_router, | |
23 const std::string& extension_id, | |
24 scoped_ptr<extensions::Event> event) { | |
25 if (!event_router->ExtensionHasEventListener(extension_id, event->event_name)) | |
benwells
2014/08/13 05:50:47
Is it necessary to check this and return a bool?
mtomasz
2014/08/13 07:02:08
Right. Removed.
| |
26 return false; | |
27 | |
28 event_router->DispatchEventToExtension(extension_id, event.Pass()); | |
29 return true; | |
30 } | |
31 | |
32 // Default implementation for acquiring a file system context for a specific | |
33 // |extension_id| and |context|. | |
34 fileapi::FileSystemContext* GetFileSystemContextImpl( | |
35 const std::string& extension_id, | |
36 content::BrowserContext* context) { | |
37 const GURL site = | |
38 extensions::util::GetSiteForExtensionId(extension_id, context); | |
39 return content::BrowserContext::GetStoragePartitionForSite(context, site) | |
40 ->GetFileSystemContext(); | |
41 } | |
42 | |
43 } // namespace | |
44 | |
45 EntryWatcherService::EntryWatcherService(content::BrowserContext* context) | |
46 : context_(context), | |
47 dispatch_event_impl_(base::Bind(&DispatchEventImpl, | |
48 extensions::EventRouter::Get(context))), | |
49 get_file_system_context_impl_(base::Bind(&GetFileSystemContextImpl)), | |
50 observing_(this), | |
51 weak_ptr_factory_(this) { | |
52 // TODO(mtomasz): Restore persistent watchers. | |
53 } | |
54 | |
55 EntryWatcherService::~EntryWatcherService() { | |
56 } | |
57 | |
58 void EntryWatcherService::SetDispatchEventImplForTesting( | |
59 const DispatchEventImplCallback& callback) { | |
60 dispatch_event_impl_ = callback; | |
61 } | |
62 | |
63 void EntryWatcherService::SetGetFileSystemContextImplForTesting( | |
64 const GetFileSystemContextImplCallback& callback) { | |
65 get_file_system_context_impl_ = callback; | |
66 } | |
67 | |
68 void EntryWatcherService::WatchDirectory( | |
69 const std::string& extension_id, | |
70 const fileapi::FileSystemURL& url, | |
71 bool recursive, | |
72 const fileapi::WatcherManager::StatusCallback& callback) { | |
73 // TODO(mtomasz): Add support for recursive watchers. | |
74 if (recursive) { | |
75 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
76 FROM_HERE, | |
77 base::Bind(callback, base::File::FILE_ERROR_INVALID_OPERATION)); | |
78 return; | |
79 } | |
80 | |
81 fileapi::FileSystemContext* const context = | |
82 get_file_system_context_impl_.Run(extension_id, context_); | |
83 DCHECK(context); | |
84 | |
85 fileapi::WatcherManager* const watcher_manager = | |
86 context->GetWatcherManager(url.type()); | |
87 if (!watcher_manager) { | |
88 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
benwells
2014/08/13 05:50:47
Why do you post this and not just run it immediate
mtomasz
2014/08/13 07:02:09
Several times during reviews I was asked to post c
benwells
2014/08/14 05:16:32
OK, makes sense. Could you add a comment explainin
mtomasz
2014/08/15 05:35:57
Done.
| |
89 FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_SECURITY)); | |
benwells
2014/08/13 05:50:47
Why is this a security error?
mtomasz
2014/08/13 07:02:09
Changed to base::File::FILE_ERROR_INVALID_OPERATIO
| |
90 return; | |
91 } | |
92 | |
93 watcher_manager->WatchDirectory( | |
94 url, | |
95 recursive, | |
96 base::Bind(&EntryWatcherService::OnWatchDirectoryCompleted, | |
97 weak_ptr_factory_.GetWeakPtr(), | |
98 extension_id, | |
99 url, | |
100 recursive, | |
101 callback)); | |
102 } | |
103 | |
104 void EntryWatcherService::UnwatchEntry( | |
105 const std::string& extension_id, | |
106 const fileapi::FileSystemURL& url, | |
107 const fileapi::WatcherManager::StatusCallback& callback) { | |
108 fileapi::FileSystemContext* const context = | |
109 get_file_system_context_impl_.Run(extension_id, context_); | |
110 DCHECK(context); | |
111 | |
112 fileapi::WatcherManager* const watcher_manager = | |
113 context->GetWatcherManager(url.type()); | |
114 if (!watcher_manager) { | |
115 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
116 FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_SECURITY)); | |
117 return; | |
118 } | |
119 | |
120 watcher_manager->UnwatchEntry( | |
121 url, | |
122 base::Bind(&EntryWatcherService::OnUnwatchEntryCompleted, | |
123 weak_ptr_factory_.GetWeakPtr(), | |
124 extension_id, | |
125 url, | |
126 callback)); | |
127 } | |
128 | |
129 std::vector<fileapi::FileSystemURL> EntryWatcherService::GetWatchedEntries( | |
130 const std::string& extension_id) { | |
131 std::vector<fileapi::FileSystemURL> result; | |
132 for (WatcherMap::const_iterator it = watchers_.begin(); it != watchers_.end(); | |
133 ++it) { | |
134 const std::map<std::string, EntryWatcher>::const_iterator watcher_it = | |
135 it->second.find(extension_id); | |
136 if (watcher_it != it->second.end()) | |
137 result.push_back(watcher_it->second.url); | |
138 } | |
139 | |
140 return result; | |
141 } | |
142 | |
143 void EntryWatcherService::OnEntryChanged(const fileapi::FileSystemURL& url) { | |
144 const WatcherMap::const_iterator it = watchers_.find(url.ToGURL()); | |
145 DCHECK(it != watchers_.end()); | |
146 for (std::map<std::string, EntryWatcher>::const_iterator watcher_it = | |
147 it->second.begin(); | |
148 watcher_it != it->second.end(); | |
149 ++watcher_it) { | |
150 const std::string& extension_id = watcher_it->first; | |
151 api::file_system::EntryChangedEvent event; | |
152 dispatch_event_impl_.Run( | |
153 extension_id, | |
154 make_scoped_ptr( | |
155 new Event(api::file_system::OnEntryChanged::kEventName, | |
156 api::file_system::OnEntryChanged::Create(event)))); | |
157 } | |
158 } | |
159 | |
160 void EntryWatcherService::OnEntryRemoved(const fileapi::FileSystemURL& url) { | |
161 WatcherMap::const_iterator it = watchers_.find(url.ToGURL()); | |
162 DCHECK(it != watchers_.end()); | |
163 for (std::map<std::string, EntryWatcher>::const_iterator watcher_it = | |
164 it->second.begin(); | |
165 watcher_it != it->second.end(); | |
166 ++watcher_it) { | |
167 const std::string& extension_id = watcher_it->first; | |
168 api::file_system::EntryRemovedEvent event; | |
169 dispatch_event_impl_.Run( | |
170 extension_id, | |
171 make_scoped_ptr( | |
172 new Event(api::file_system::OnEntryRemoved::kEventName, | |
173 api::file_system::OnEntryRemoved::Create(event)))); | |
174 } | |
175 } | |
176 | |
177 void EntryWatcherService::OnWatchDirectoryCompleted( | |
178 const std::string& extension_id, | |
179 const fileapi::FileSystemURL& url, | |
180 bool recursive, | |
181 const fileapi::WatcherManager::StatusCallback& callback, | |
182 base::File::Error result) { | |
183 if (result != base::File::FILE_OK) { | |
184 callback.Run(result); | |
185 return; | |
186 } | |
187 | |
188 DCHECK_EQ(base::File::FILE_OK, result); | |
benwells
2014/08/13 05:50:47
Is this DCHECK necessary?
mtomasz
2014/08/13 07:02:09
Removed.
| |
189 | |
190 fileapi::FileSystemContext* const context = | |
191 get_file_system_context_impl_.Run(extension_id, context_); | |
192 DCHECK(context); | |
193 | |
194 fileapi::WatcherManager* const watcher_manager = | |
195 context->GetWatcherManager(url.type()); | |
196 if (!watcher_manager) { | |
197 callback.Run(base::File::FILE_ERROR_SECURITY); | |
benwells
2014/08/13 05:50:47
Given that you should have already successfully go
mtomasz
2014/08/13 07:02:09
GetWatcherManager() may return NULL at any point.
benwells
2014/08/14 05:16:32
That seems odd, are you thinking about the cases w
mtomasz
2014/08/15 05:35:56
I don't think it's weird. I can imagine a backend
mtomasz
2014/08/15 05:35:57
I reorganized the code. How about now?
benwells
2014/08/18 22:58:32
I think that is better, but the lifetime issues ar
mtomasz
2014/08/19 06:50:36
Done.
| |
198 return; | |
199 } | |
200 | |
201 // Observe the manager if not observed yet. | |
202 if (!observing_.IsObserving(watcher_manager)) | |
203 observing_.Add(watcher_manager); | |
204 | |
205 const GURL gurl = url.ToGURL(); | |
206 watchers_[gurl][extension_id] = | |
207 EntryWatcher(url, true /* directory */, recursive); | |
208 | |
209 // TODO(mtomasz): Save in preferences. | |
210 | |
211 callback.Run(base::File::FILE_OK); | |
212 } | |
213 | |
214 void EntryWatcherService::OnUnwatchEntryCompleted( | |
215 const std::string& extension_id, | |
216 const fileapi::FileSystemURL& url, | |
217 const fileapi::WatcherManager::StatusCallback& callback, | |
218 base::File::Error result) { | |
219 if (result != base::File::FILE_OK) { | |
220 callback.Run(result); | |
221 return; | |
222 } | |
223 | |
224 DCHECK_EQ(base::File::FILE_OK, result); | |
225 | |
226 const GURL gurl = url.ToGURL(); | |
227 if (watchers_[gurl].erase(extension_id) == 0) { | |
228 callback.Run(base::File::FILE_ERROR_NOT_FOUND); | |
229 return; | |
230 } | |
231 | |
232 if (watchers_[gurl].empty()) | |
233 watchers_.erase(gurl); | |
234 | |
235 fileapi::FileSystemContext* const context = | |
236 get_file_system_context_impl_.Run(extension_id, context_); | |
237 DCHECK(context); | |
238 | |
239 fileapi::WatcherManager* const watcher_manager = | |
benwells
2014/08/13 05:50:47
Why do you get the watcher manager here?
mtomasz
2014/08/13 07:02:09
Not needed. Removed.
| |
240 context->GetWatcherManager(url.type()); | |
241 if (!watcher_manager) { | |
242 callback.Run(base::File::FILE_ERROR_SECURITY); | |
243 return; | |
244 } | |
245 | |
246 // TODO(mtomasz): Save in preferences. | |
247 | |
248 callback.Run(base::File::FILE_OK); | |
249 } | |
250 | |
251 EntryWatcherService::EntryWatcher::EntryWatcher() | |
252 : directory(false), recursive(false) { | |
253 } | |
254 | |
255 EntryWatcherService::EntryWatcher::EntryWatcher( | |
256 const fileapi::FileSystemURL& url, | |
257 bool directory, | |
258 bool recursive) | |
259 : url(url), directory(directory), recursive(recursive) { | |
260 } | |
261 | |
262 EntryWatcherService::EntryWatcher::~EntryWatcher() { | |
263 } | |
264 | |
265 // static | |
266 EntryWatcherService* EntryWatcherServiceFactory::Get( | |
267 content::BrowserContext* context) { | |
268 return static_cast<EntryWatcherService*>( | |
269 GetInstance()->GetServiceForBrowserContext(context, true)); | |
270 } | |
271 | |
272 // static | |
273 EntryWatcherService* EntryWatcherServiceFactory::FindExisting( | |
274 content::BrowserContext* context) { | |
275 return static_cast<EntryWatcherService*>( | |
276 GetInstance()->GetServiceForBrowserContext(context, false)); | |
277 } | |
278 | |
279 EntryWatcherServiceFactory* EntryWatcherServiceFactory::GetInstance() { | |
280 return Singleton<EntryWatcherServiceFactory>::get(); | |
281 } | |
282 | |
283 EntryWatcherServiceFactory::EntryWatcherServiceFactory() | |
284 : BrowserContextKeyedServiceFactory( | |
285 "EntryWatcherService", | |
286 BrowserContextDependencyManager::GetInstance()) { | |
287 } | |
288 | |
289 EntryWatcherServiceFactory::~EntryWatcherServiceFactory() { | |
290 } | |
291 | |
292 KeyedService* EntryWatcherServiceFactory::BuildServiceInstanceFor( | |
293 content::BrowserContext* context) const { | |
294 return new EntryWatcherService(Profile::FromBrowserContext(context)); | |
295 } | |
296 | |
297 bool EntryWatcherServiceFactory::ServiceIsCreatedWithBrowserContext() const { | |
298 return true; | |
benwells
2014/08/13 05:50:47
Could you add a comment about why this is needed?
mtomasz
2014/08/13 07:02:09
Done.
| |
299 } | |
300 | |
301 } // namespace extensions | |
OLD | NEW |