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 "content/child/fileapi/webfilesystem_impl.h"
8 #include "base/lazy_instance.h"
9 #include "base/logging.h"
10 #include "base/message_loop/message_loop_proxy.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/synchronization/waitable_event.h"
13 #include "base/threading/thread_local.h"
14 #include "content/child/child_thread.h"
15 #include "content/child/fileapi/file_system_dispatcher.h"
16 #include "content/child/fileapi/webfilewriter_impl.h"
17 #include "content/common/fileapi/file_system_messages.h"
18 #include "third_party/WebKit/public/platform/WebFileInfo.h"
19 #include "third_party/WebKit/public/platform/WebFileSystemCallbacks.h"
20 #include "third_party/WebKit/public/platform/WebString.h"
21 #include "third_party/WebKit/public/platform/WebURL.h"
23 #include "webkit/child/worker_task_runner.h"
24 #include "webkit/common/fileapi/directory_entry.h"
25 #include "webkit/common/fileapi/file_system_util.h"
26 #include "webkit/glue/webkit_glue.h"
28 using WebKit::WebFileInfo;
29 using WebKit::WebFileSystemCallbacks;
30 using WebKit::WebFileSystemEntry;
31 using WebKit::WebString;
33 using WebKit::WebVector;
34 using webkit_glue::WorkerTaskRunner;
40 base::LazyInstance<base::ThreadLocalPointer<WebFileSystemImpl> >::Leaky
41 g_webfilesystem_tls = LAZY_INSTANCE_INITIALIZER;
43 class WaitableCallbackResults {
45 static WaitableCallbackResults* MaybeCreate(
46 const WebFileSystemCallbacks& callbacks) {
47 if (callbacks.shouldBlockUntilCompletion())
48 return new WaitableCallbackResults;
51 ~WaitableCallbackResults() {}
53 void SetResultsAndSignal(const base::Closure& results_closure) {
54 results_closure_ = results_closure;
60 DCHECK(!results_closure_.is_null());
61 results_closure_.Run();
65 WaitableCallbackResults() : event_(new base::WaitableEvent(true, false)) {}
67 base::WaitableEvent* event_;
68 base::Closure results_closure_;
69 DISALLOW_COPY_AND_ASSIGN(WaitableCallbackResults);
72 void DidReceiveSnapshotFile(int request_id) {
73 if (ChildThread::current())
74 ChildThread::current()->Send(
75 new FileSystemHostMsg_DidReceiveSnapshotFile(request_id));
78 int CurrentWorkerId() {
79 return WorkerTaskRunner::Instance()->CurrentWorkerId();
82 template <typename Method, typename Params>
83 void CallDispatcherOnMainThread(
84 base::MessageLoopProxy* loop,
85 Method method, const Params& params,
86 scoped_ptr<WaitableCallbackResults> waitable_results) {
87 scoped_ptr<WaitableCallbackResults> null_waitable;
88 if (!loop->RunsTasksOnCurrentThread()) {
89 loop->PostTask(FROM_HERE,
90 base::Bind(&CallDispatcherOnMainThread<Method, Params>,
91 make_scoped_refptr(loop), method, params,
92 base::Passed(&null_waitable)));
93 if (!waitable_results)
95 waitable_results->WaitAndRun();
97 if (!ChildThread::current() ||
98 !ChildThread::current()->file_system_dispatcher())
101 DCHECK(!waitable_results);
102 DispatchToMethod(ChildThread::current()->file_system_dispatcher(),
106 // Run WebFileSystemCallbacks's |method| with |params|.
107 template <typename Method, typename Params>
108 void RunCallbacks(int callbacks_id, Method method, const Params& params) {
109 WebFileSystemImpl* filesystem =
110 WebFileSystemImpl::ThreadSpecificInstance(NULL);
113 WebFileSystemCallbacks callbacks =
114 filesystem->GetAndUnregisterCallbacks(callbacks_id);
115 DispatchToMethod(&callbacks, method, params);
118 void DispatchResultsClosure(int thread_id, int callbacks_id,
119 WaitableCallbackResults* waitable_results,
120 const base::Closure& results_closure) {
121 if (thread_id != CurrentWorkerId()) {
122 if (waitable_results) {
123 waitable_results->SetResultsAndSignal(results_closure);
126 WorkerTaskRunner::Instance()->PostTask(thread_id, results_closure);
129 results_closure.Run();
132 template <typename Method, typename Params>
133 void CallbackFileSystemCallbacks(
134 int thread_id, int callbacks_id,
135 WaitableCallbackResults* waitable_results,
136 Method method, const Params& params) {
137 DispatchResultsClosure(
138 thread_id, callbacks_id, waitable_results,
139 base::Bind(&RunCallbacks<Method, Params>, callbacks_id, method, params));
142 //-----------------------------------------------------------------------------
143 // Callback adapters. Callbacks must be called on the original calling thread,
144 // so these callback adapters relay back the results to the calling thread
147 void OpenFileSystemCallbackAdapter(
148 int thread_id, int callbacks_id,
149 WaitableCallbackResults* waitable_results,
150 const std::string& name, const GURL& root) {
151 CallbackFileSystemCallbacks(
152 thread_id, callbacks_id, waitable_results,
153 &WebFileSystemCallbacks::didOpenFileSystem,
154 MakeTuple(UTF8ToUTF16(name), root));
157 void ResolveURLCallbackAdapter(
158 int thread_id, int callbacks_id,
159 WaitableCallbackResults* waitable_results,
160 const fileapi::FileSystemInfo& info,
161 const base::FilePath& file_path, bool is_directory) {
162 base::FilePath normalized_path(
163 fileapi::VirtualPath::GetNormalizedFilePath(file_path));
164 CallbackFileSystemCallbacks(
165 thread_id, callbacks_id, waitable_results,
166 &WebFileSystemCallbacks::didResolveURL,
167 MakeTuple(UTF8ToUTF16(info.name), info.root_url,
168 static_cast<WebKit::WebFileSystemType>(info.mount_type),
169 normalized_path.AsUTF16Unsafe(), is_directory));
172 void StatusCallbackAdapter(int thread_id, int callbacks_id,
173 WaitableCallbackResults* waitable_results,
174 base::PlatformFileError error) {
175 if (error == base::PLATFORM_FILE_OK) {
176 CallbackFileSystemCallbacks(
177 thread_id, callbacks_id, waitable_results,
178 &WebFileSystemCallbacks::didSucceed, MakeTuple());
180 CallbackFileSystemCallbacks(
181 thread_id, callbacks_id, waitable_results,
182 &WebFileSystemCallbacks::didFail,
183 MakeTuple(fileapi::PlatformFileErrorToWebFileError(error)));
187 void ReadMetadataCallbackAdapter(int thread_id, int callbacks_id,
188 WaitableCallbackResults* waitable_results,
189 const base::PlatformFileInfo& file_info) {
190 WebFileInfo web_file_info;
191 webkit_glue::PlatformFileInfoToWebFileInfo(file_info, &web_file_info);
192 CallbackFileSystemCallbacks(
193 thread_id, callbacks_id, waitable_results,
194 &WebFileSystemCallbacks::didReadMetadata,
195 MakeTuple(web_file_info));
198 void ReadDirectoryCallbackAdapater(
199 int thread_id, int callbacks_id, WaitableCallbackResults* waitable_results,
200 const std::vector<fileapi::DirectoryEntry>& entries,
202 WebVector<WebFileSystemEntry> file_system_entries(entries.size());
203 for (size_t i = 0; i < entries.size(); i++) {
204 file_system_entries[i].name =
205 base::FilePath(entries[i].name).AsUTF16Unsafe();
206 file_system_entries[i].isDirectory = entries[i].is_directory;
208 CallbackFileSystemCallbacks(
209 thread_id, callbacks_id, waitable_results,
210 &WebFileSystemCallbacks::didReadDirectory,
211 MakeTuple(file_system_entries, has_more));
214 void DidCreateFileWriter(
217 WebKit::WebFileWriterClient* client,
218 base::MessageLoopProxy* main_thread_loop,
219 const base::PlatformFileInfo& file_info) {
220 WebFileSystemImpl* filesystem =
221 WebFileSystemImpl::ThreadSpecificInstance(NULL);
225 WebFileSystemCallbacks callbacks =
226 filesystem->GetAndUnregisterCallbacks(callbacks_id);
228 if (file_info.is_directory || file_info.size < 0) {
229 callbacks.didFail(WebKit::WebFileErrorInvalidState);
232 WebFileWriterImpl::Type type =
233 callbacks.shouldBlockUntilCompletion() ?
234 WebFileWriterImpl::TYPE_SYNC : WebFileWriterImpl::TYPE_ASYNC;
235 callbacks.didCreateFileWriter(
236 new WebFileWriterImpl(path, client, type, main_thread_loop),
240 void CreateFileWriterCallbackAdapter(
241 int thread_id, int callbacks_id,
242 WaitableCallbackResults* waitable_results,
243 base::MessageLoopProxy* main_thread_loop,
245 WebKit::WebFileWriterClient* client,
246 const base::PlatformFileInfo& file_info) {
247 DispatchResultsClosure(
248 thread_id, callbacks_id, waitable_results,
249 base::Bind(&DidCreateFileWriter, callbacks_id, path, client,
250 make_scoped_refptr(main_thread_loop), file_info));
253 void DidCreateSnapshotFile(
255 base::MessageLoopProxy* main_thread_loop,
256 const base::PlatformFileInfo& file_info,
257 const base::FilePath& platform_path,
259 WebFileSystemImpl* filesystem =
260 WebFileSystemImpl::ThreadSpecificInstance(NULL);
264 WebFileSystemCallbacks callbacks =
265 filesystem->GetAndUnregisterCallbacks(callbacks_id);
267 WebFileInfo web_file_info;
268 webkit_glue::PlatformFileInfoToWebFileInfo(file_info, &web_file_info);
269 web_file_info.platformPath = platform_path.AsUTF16Unsafe();
270 callbacks.didCreateSnapshotFile(web_file_info);
272 // TODO(michaeln,kinuko): Use ThreadSafeSender when Blob becomes
274 main_thread_loop->PostTask(
275 FROM_HERE, base::Bind(&DidReceiveSnapshotFile, request_id));
278 void CreateSnapshotFileCallbackAdapter(
279 int thread_id, int callbacks_id,
280 WaitableCallbackResults* waitable_results,
281 base::MessageLoopProxy* main_thread_loop,
282 const base::PlatformFileInfo& file_info,
283 const base::FilePath& platform_path,
285 DispatchResultsClosure(
286 thread_id, callbacks_id, waitable_results,
287 base::Bind(&DidCreateSnapshotFile, callbacks_id,
288 make_scoped_refptr(main_thread_loop),
289 file_info, platform_path, request_id));
294 //-----------------------------------------------------------------------------
297 WebFileSystemImpl* WebFileSystemImpl::ThreadSpecificInstance(
298 base::MessageLoopProxy* main_thread_loop) {
299 if (g_webfilesystem_tls.Pointer()->Get() || !main_thread_loop)
300 return g_webfilesystem_tls.Pointer()->Get();
301 WebFileSystemImpl* filesystem = new WebFileSystemImpl(main_thread_loop);
302 if (WorkerTaskRunner::Instance()->CurrentWorkerId())
303 WorkerTaskRunner::Instance()->AddStopObserver(filesystem);
307 void WebFileSystemImpl::DeleteThreadSpecificInstance() {
308 DCHECK(!WorkerTaskRunner::Instance()->CurrentWorkerId());
309 if (g_webfilesystem_tls.Pointer()->Get())
310 delete g_webfilesystem_tls.Pointer()->Get();
313 WebFileSystemImpl::WebFileSystemImpl(base::MessageLoopProxy* main_thread_loop)
314 : main_thread_loop_(main_thread_loop),
315 next_callbacks_id_(0) {
316 g_webfilesystem_tls.Pointer()->Set(this);
319 WebFileSystemImpl::~WebFileSystemImpl() {
320 g_webfilesystem_tls.Pointer()->Set(NULL);
323 void WebFileSystemImpl::OnWorkerRunLoopStopped() {
327 void WebFileSystemImpl::openFileSystem(
328 const WebKit::WebURL& storage_partition,
329 WebKit::WebFileSystemType type,
330 WebFileSystemCallbacks callbacks) {
331 int callbacks_id = RegisterCallbacks(callbacks);
332 WaitableCallbackResults* waitable_results =
333 WaitableCallbackResults::MaybeCreate(callbacks);
334 CallDispatcherOnMainThread(
335 main_thread_loop_.get(),
336 &FileSystemDispatcher::OpenFileSystem,
337 MakeTuple(GURL(storage_partition),
338 static_cast<fileapi::FileSystemType>(type),
339 base::Bind(&OpenFileSystemCallbackAdapter,
340 CurrentWorkerId(), callbacks_id,
341 base::Unretained(waitable_results)),
342 base::Bind(&StatusCallbackAdapter,
343 CurrentWorkerId(), callbacks_id,
344 base::Unretained(waitable_results))),
345 make_scoped_ptr(waitable_results));
348 void WebFileSystemImpl::resolveURL(
349 const WebKit::WebURL& filesystem_url,
350 WebFileSystemCallbacks callbacks) {
351 int callbacks_id = RegisterCallbacks(callbacks);
352 WaitableCallbackResults* waitable_results =
353 WaitableCallbackResults::MaybeCreate(callbacks);
354 CallDispatcherOnMainThread(
355 main_thread_loop_.get(),
356 &FileSystemDispatcher::ResolveURL,
357 MakeTuple(GURL(filesystem_url),
358 base::Bind(&ResolveURLCallbackAdapter,
359 CurrentWorkerId(), callbacks_id,
360 base::Unretained(waitable_results)),
361 base::Bind(&StatusCallbackAdapter,
362 CurrentWorkerId(), callbacks_id,
363 base::Unretained(waitable_results))),
364 make_scoped_ptr(waitable_results));
367 void WebFileSystemImpl::deleteFileSystem(
368 const WebKit::WebURL& storage_partition,
369 WebKit::WebFileSystemType type,
370 WebFileSystemCallbacks callbacks) {
371 int callbacks_id = RegisterCallbacks(callbacks);
372 WaitableCallbackResults* waitable_results =
373 WaitableCallbackResults::MaybeCreate(callbacks);
374 CallDispatcherOnMainThread(
375 main_thread_loop_.get(),
376 &FileSystemDispatcher::DeleteFileSystem,
377 MakeTuple(GURL(storage_partition),
378 static_cast<fileapi::FileSystemType>(type),
379 base::Bind(&StatusCallbackAdapter,
380 CurrentWorkerId(), callbacks_id,
381 base::Unretained(waitable_results))),
382 make_scoped_ptr(waitable_results));
385 void WebFileSystemImpl::move(
386 const WebKit::WebURL& src_path,
387 const WebKit::WebURL& dest_path,
388 WebFileSystemCallbacks callbacks) {
389 int callbacks_id = RegisterCallbacks(callbacks);
390 WaitableCallbackResults* waitable_results =
391 WaitableCallbackResults::MaybeCreate(callbacks);
392 CallDispatcherOnMainThread(
393 main_thread_loop_.get(),
394 &FileSystemDispatcher::Move,
395 MakeTuple(GURL(src_path), GURL(dest_path),
396 base::Bind(&StatusCallbackAdapter,
397 CurrentWorkerId(), callbacks_id,
398 base::Unretained(waitable_results))),
399 make_scoped_ptr(waitable_results));
402 void WebFileSystemImpl::copy(
403 const WebKit::WebURL& src_path,
404 const WebKit::WebURL& dest_path,
405 WebFileSystemCallbacks callbacks) {
406 int callbacks_id = RegisterCallbacks(callbacks);
407 WaitableCallbackResults* waitable_results =
408 WaitableCallbackResults::MaybeCreate(callbacks);
409 CallDispatcherOnMainThread(
410 main_thread_loop_.get(),
411 &FileSystemDispatcher::Copy,
412 MakeTuple(GURL(src_path), GURL(dest_path),
413 base::Bind(&StatusCallbackAdapter,
414 CurrentWorkerId(), callbacks_id,
415 base::Unretained(waitable_results))),
416 make_scoped_ptr(waitable_results));
419 void WebFileSystemImpl::remove(
420 const WebKit::WebURL& path,
421 WebFileSystemCallbacks callbacks) {
422 int callbacks_id = RegisterCallbacks(callbacks);
423 WaitableCallbackResults* waitable_results =
424 WaitableCallbackResults::MaybeCreate(callbacks);
425 CallDispatcherOnMainThread(
426 main_thread_loop_.get(),
427 &FileSystemDispatcher::Remove,
428 MakeTuple(GURL(path), false /* recursive */,
429 base::Bind(&StatusCallbackAdapter,
430 CurrentWorkerId(), callbacks_id,
431 base::Unretained(waitable_results))),
432 make_scoped_ptr(waitable_results));
435 void WebFileSystemImpl::removeRecursively(
436 const WebKit::WebURL& path,
437 WebFileSystemCallbacks callbacks) {
438 int callbacks_id = RegisterCallbacks(callbacks);
439 WaitableCallbackResults* waitable_results =
440 WaitableCallbackResults::MaybeCreate(callbacks);
441 CallDispatcherOnMainThread(
442 main_thread_loop_.get(),
443 &FileSystemDispatcher::Remove,
444 MakeTuple(GURL(path), true /* recursive */,
445 base::Bind(&StatusCallbackAdapter,
446 CurrentWorkerId(), callbacks_id,
447 base::Unretained(waitable_results))),
448 make_scoped_ptr(waitable_results));
451 void WebFileSystemImpl::readMetadata(
452 const WebKit::WebURL& path,
453 WebFileSystemCallbacks callbacks) {
454 int callbacks_id = RegisterCallbacks(callbacks);
455 WaitableCallbackResults* waitable_results =
456 WaitableCallbackResults::MaybeCreate(callbacks);
457 CallDispatcherOnMainThread(
458 main_thread_loop_.get(),
459 &FileSystemDispatcher::ReadMetadata,
460 MakeTuple(GURL(path),
461 base::Bind(&ReadMetadataCallbackAdapter,
462 CurrentWorkerId(), callbacks_id,
463 base::Unretained(waitable_results)),
464 base::Bind(&StatusCallbackAdapter,
465 CurrentWorkerId(), callbacks_id,
466 base::Unretained(waitable_results))),
467 make_scoped_ptr(waitable_results));
470 void WebFileSystemImpl::createFile(
471 const WebKit::WebURL& path,
473 WebFileSystemCallbacks callbacks) {
474 int callbacks_id = RegisterCallbacks(callbacks);
475 WaitableCallbackResults* waitable_results =
476 WaitableCallbackResults::MaybeCreate(callbacks);
477 CallDispatcherOnMainThread(
478 main_thread_loop_.get(),
479 &FileSystemDispatcher::CreateFile,
480 MakeTuple(GURL(path), exclusive,
481 base::Bind(&StatusCallbackAdapter,
482 CurrentWorkerId(), callbacks_id,
483 base::Unretained(waitable_results))),
484 make_scoped_ptr(waitable_results));
487 void WebFileSystemImpl::createDirectory(
488 const WebKit::WebURL& path,
490 WebFileSystemCallbacks callbacks) {
491 int callbacks_id = RegisterCallbacks(callbacks);
492 WaitableCallbackResults* waitable_results =
493 WaitableCallbackResults::MaybeCreate(callbacks);
494 CallDispatcherOnMainThread(
495 main_thread_loop_.get(),
496 &FileSystemDispatcher::CreateDirectory,
497 MakeTuple(GURL(path), exclusive, false /* recursive */,
498 base::Bind(&StatusCallbackAdapter,
499 CurrentWorkerId(), callbacks_id,
500 base::Unretained(waitable_results))),
501 make_scoped_ptr(waitable_results));
504 void WebFileSystemImpl::fileExists(
505 const WebKit::WebURL& path,
506 WebFileSystemCallbacks callbacks) {
507 int callbacks_id = RegisterCallbacks(callbacks);
508 WaitableCallbackResults* waitable_results =
509 WaitableCallbackResults::MaybeCreate(callbacks);
510 CallDispatcherOnMainThread(
511 main_thread_loop_.get(),
512 &FileSystemDispatcher::Exists,
513 MakeTuple(GURL(path), false /* directory */,
514 base::Bind(&StatusCallbackAdapter,
515 CurrentWorkerId(), callbacks_id,
516 base::Unretained(waitable_results))),
517 make_scoped_ptr(waitable_results));
520 void WebFileSystemImpl::directoryExists(
521 const WebKit::WebURL& path,
522 WebFileSystemCallbacks callbacks) {
523 int callbacks_id = RegisterCallbacks(callbacks);
524 WaitableCallbackResults* waitable_results =
525 WaitableCallbackResults::MaybeCreate(callbacks);
526 CallDispatcherOnMainThread(
527 main_thread_loop_.get(),
528 &FileSystemDispatcher::Exists,
529 MakeTuple(GURL(path), true /* directory */,
530 base::Bind(&StatusCallbackAdapter,
531 CurrentWorkerId(), callbacks_id,
532 base::Unretained(waitable_results))),
533 make_scoped_ptr(waitable_results));
536 void WebFileSystemImpl::readDirectory(
537 const WebKit::WebURL& path,
538 WebFileSystemCallbacks callbacks) {
539 int callbacks_id = RegisterCallbacks(callbacks);
540 WaitableCallbackResults* waitable_results =
541 WaitableCallbackResults::MaybeCreate(callbacks);
542 CallDispatcherOnMainThread(
543 main_thread_loop_.get(),
544 &FileSystemDispatcher::ReadDirectory,
545 MakeTuple(GURL(path),
546 base::Bind(&ReadDirectoryCallbackAdapater,
547 CurrentWorkerId(), callbacks_id,
548 base::Unretained(waitable_results)),
549 base::Bind(&StatusCallbackAdapter,
550 CurrentWorkerId(), callbacks_id,
551 base::Unretained(waitable_results))),
552 make_scoped_ptr(waitable_results));
555 void WebFileSystemImpl::createFileWriter(
557 WebKit::WebFileWriterClient* client,
558 WebFileSystemCallbacks callbacks) {
559 int callbacks_id = RegisterCallbacks(callbacks);
560 WaitableCallbackResults* waitable_results =
561 WaitableCallbackResults::MaybeCreate(callbacks);
562 CallDispatcherOnMainThread(
563 main_thread_loop_.get(),
564 &FileSystemDispatcher::ReadMetadata,
565 MakeTuple(GURL(path),
566 base::Bind(&CreateFileWriterCallbackAdapter,
567 CurrentWorkerId(), callbacks_id,
568 base::Unretained(waitable_results),
569 main_thread_loop_, GURL(path), client),
570 base::Bind(&StatusCallbackAdapter,
571 CurrentWorkerId(), callbacks_id,
572 base::Unretained(waitable_results))),
573 make_scoped_ptr(waitable_results));
576 void WebFileSystemImpl::createSnapshotFileAndReadMetadata(
577 const WebKit::WebURL& path,
578 WebFileSystemCallbacks callbacks) {
579 int callbacks_id = RegisterCallbacks(callbacks);
580 WaitableCallbackResults* waitable_results =
581 WaitableCallbackResults::MaybeCreate(callbacks);
582 CallDispatcherOnMainThread(
583 main_thread_loop_.get(),
584 &FileSystemDispatcher::CreateSnapshotFile,
585 MakeTuple(GURL(path),
586 base::Bind(&CreateSnapshotFileCallbackAdapter,
587 CurrentWorkerId(), callbacks_id,
588 base::Unretained(waitable_results),
590 base::Bind(&StatusCallbackAdapter,
591 CurrentWorkerId(), callbacks_id,
592 base::Unretained(waitable_results))),
593 make_scoped_ptr(waitable_results));
596 int WebFileSystemImpl::RegisterCallbacks(
597 const WebFileSystemCallbacks& callbacks) {
598 DCHECK(CalledOnValidThread());
599 int id = next_callbacks_id_++;
600 callbacks_[id] = callbacks;
604 WebFileSystemCallbacks WebFileSystemImpl::GetAndUnregisterCallbacks(
606 DCHECK(CalledOnValidThread());
607 CallbacksMap::iterator found = callbacks_.find(callbacks_id);
608 DCHECK(found != callbacks_.end());
609 WebFileSystemCallbacks callbacks = found->second;
610 callbacks_.erase(found);
614 } // namespace content