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/blink_glue.h"
15 #include "content/child/child_thread.h"
16 #include "content/child/fileapi/file_system_dispatcher.h"
17 #include "content/child/fileapi/webfilewriter_impl.h"
18 #include "content/common/fileapi/file_system_messages.h"
19 #include "third_party/WebKit/public/platform/WebFileInfo.h"
20 #include "third_party/WebKit/public/platform/WebFileSystemCallbacks.h"
21 #include "third_party/WebKit/public/platform/WebString.h"
22 #include "third_party/WebKit/public/platform/WebURL.h"
23 #include "third_party/WebKit/public/web/WebHeap.h"
25 #include "webkit/child/worker_task_runner.h"
26 #include "webkit/common/fileapi/directory_entry.h"
27 #include "webkit/common/fileapi/file_system_util.h"
29 using blink::WebFileInfo;
30 using blink::WebFileSystemCallbacks;
31 using blink::WebFileSystemEntry;
32 using blink::WebString;
34 using blink::WebVector;
35 using webkit_glue::WorkerTaskRunner;
41 base::LazyInstance<base::ThreadLocalPointer<WebFileSystemImpl> >::Leaky
42 g_webfilesystem_tls = LAZY_INSTANCE_INITIALIZER;
44 class WaitableCallbackResults {
46 static WaitableCallbackResults* MaybeCreate(
47 const WebFileSystemCallbacks& callbacks) {
48 if (callbacks.shouldBlockUntilCompletion())
49 return new WaitableCallbackResults;
52 ~WaitableCallbackResults() {}
54 void SetResultsAndSignal(const base::Closure& results_closure) {
55 results_closure_ = results_closure;
61 blink::WebHeap::SafePointScope safe_point;
64 DCHECK(!results_closure_.is_null());
65 results_closure_.Run();
69 WaitableCallbackResults() : event_(new base::WaitableEvent(true, false)) {}
71 base::WaitableEvent* event_;
72 base::Closure results_closure_;
73 DISALLOW_COPY_AND_ASSIGN(WaitableCallbackResults);
76 void DidReceiveSnapshotFile(int request_id) {
77 if (ChildThread::current())
78 ChildThread::current()->Send(
79 new FileSystemHostMsg_DidReceiveSnapshotFile(request_id));
82 int CurrentWorkerId() {
83 return WorkerTaskRunner::Instance()->CurrentWorkerId();
86 template <typename Method, typename Params>
87 void CallDispatcherOnMainThread(
88 base::MessageLoopProxy* loop,
89 Method method, const Params& params,
90 scoped_ptr<WaitableCallbackResults> waitable_results) {
91 scoped_ptr<WaitableCallbackResults> null_waitable;
92 if (!loop->RunsTasksOnCurrentThread()) {
93 loop->PostTask(FROM_HERE,
94 base::Bind(&CallDispatcherOnMainThread<Method, Params>,
95 make_scoped_refptr(loop), method, params,
96 base::Passed(&null_waitable)));
97 if (!waitable_results)
99 waitable_results->WaitAndRun();
101 if (!ChildThread::current() ||
102 !ChildThread::current()->file_system_dispatcher())
105 DCHECK(!waitable_results);
106 DispatchToMethod(ChildThread::current()->file_system_dispatcher(),
110 // Run WebFileSystemCallbacks's |method| with |params|.
111 template <typename Method, typename Params>
112 void RunCallbacks(int callbacks_id, Method method, const Params& params) {
113 WebFileSystemImpl* filesystem =
114 WebFileSystemImpl::ThreadSpecificInstance(NULL);
117 WebFileSystemCallbacks callbacks =
118 filesystem->GetAndUnregisterCallbacks(callbacks_id);
119 DispatchToMethod(&callbacks, method, params);
122 void DispatchResultsClosure(int thread_id, int callbacks_id,
123 WaitableCallbackResults* waitable_results,
124 const base::Closure& results_closure) {
125 if (thread_id != CurrentWorkerId()) {
126 if (waitable_results) {
127 waitable_results->SetResultsAndSignal(results_closure);
130 WorkerTaskRunner::Instance()->PostTask(thread_id, results_closure);
133 results_closure.Run();
136 template <typename Method, typename Params>
137 void CallbackFileSystemCallbacks(
138 int thread_id, int callbacks_id,
139 WaitableCallbackResults* waitable_results,
140 Method method, const Params& params) {
141 DispatchResultsClosure(
142 thread_id, callbacks_id, waitable_results,
143 base::Bind(&RunCallbacks<Method, Params>, callbacks_id, method, params));
146 //-----------------------------------------------------------------------------
147 // Callback adapters. Callbacks must be called on the original calling thread,
148 // so these callback adapters relay back the results to the calling thread
151 void OpenFileSystemCallbackAdapter(
152 int thread_id, int callbacks_id,
153 WaitableCallbackResults* waitable_results,
154 const std::string& name, const GURL& root) {
155 CallbackFileSystemCallbacks(
156 thread_id, callbacks_id, waitable_results,
157 &WebFileSystemCallbacks::didOpenFileSystem,
158 MakeTuple(base::UTF8ToUTF16(name), root));
161 void ResolveURLCallbackAdapter(
162 int thread_id, int callbacks_id,
163 WaitableCallbackResults* waitable_results,
164 const fileapi::FileSystemInfo& info,
165 const base::FilePath& file_path, bool is_directory) {
166 base::FilePath normalized_path(
167 fileapi::VirtualPath::GetNormalizedFilePath(file_path));
168 CallbackFileSystemCallbacks(
169 thread_id, callbacks_id, waitable_results,
170 &WebFileSystemCallbacks::didResolveURL,
171 MakeTuple(base::UTF8ToUTF16(info.name), info.root_url,
172 static_cast<blink::WebFileSystemType>(info.mount_type),
173 normalized_path.AsUTF16Unsafe(), is_directory));
176 void StatusCallbackAdapter(int thread_id, int callbacks_id,
177 WaitableCallbackResults* waitable_results,
178 base::File::Error error) {
179 if (error == base::File::FILE_OK) {
180 CallbackFileSystemCallbacks(
181 thread_id, callbacks_id, waitable_results,
182 &WebFileSystemCallbacks::didSucceed, MakeTuple());
184 CallbackFileSystemCallbacks(
185 thread_id, callbacks_id, waitable_results,
186 &WebFileSystemCallbacks::didFail,
187 MakeTuple(fileapi::FileErrorToWebFileError(error)));
191 void ReadMetadataCallbackAdapter(int thread_id, int callbacks_id,
192 WaitableCallbackResults* waitable_results,
193 const base::File::Info& file_info) {
194 WebFileInfo web_file_info;
195 FileInfoToWebFileInfo(file_info, &web_file_info);
196 CallbackFileSystemCallbacks(
197 thread_id, callbacks_id, waitable_results,
198 &WebFileSystemCallbacks::didReadMetadata,
199 MakeTuple(web_file_info));
202 void ReadDirectoryCallbackAdapater(
203 int thread_id, int callbacks_id, WaitableCallbackResults* waitable_results,
204 const std::vector<fileapi::DirectoryEntry>& entries,
206 WebVector<WebFileSystemEntry> file_system_entries(entries.size());
207 for (size_t i = 0; i < entries.size(); i++) {
208 file_system_entries[i].name =
209 base::FilePath(entries[i].name).AsUTF16Unsafe();
210 file_system_entries[i].isDirectory = entries[i].is_directory;
212 CallbackFileSystemCallbacks(
213 thread_id, callbacks_id, waitable_results,
214 &WebFileSystemCallbacks::didReadDirectory,
215 MakeTuple(file_system_entries, has_more));
218 void DidCreateFileWriter(
221 blink::WebFileWriterClient* client,
222 base::MessageLoopProxy* main_thread_loop,
223 const base::File::Info& file_info) {
224 WebFileSystemImpl* filesystem =
225 WebFileSystemImpl::ThreadSpecificInstance(NULL);
229 WebFileSystemCallbacks callbacks =
230 filesystem->GetAndUnregisterCallbacks(callbacks_id);
232 if (file_info.is_directory || file_info.size < 0) {
233 callbacks.didFail(blink::WebFileErrorInvalidState);
236 WebFileWriterImpl::Type type =
237 callbacks.shouldBlockUntilCompletion() ?
238 WebFileWriterImpl::TYPE_SYNC : WebFileWriterImpl::TYPE_ASYNC;
239 callbacks.didCreateFileWriter(
240 new WebFileWriterImpl(path, client, type, main_thread_loop),
244 void CreateFileWriterCallbackAdapter(
245 int thread_id, int callbacks_id,
246 WaitableCallbackResults* waitable_results,
247 base::MessageLoopProxy* main_thread_loop,
249 blink::WebFileWriterClient* client,
250 const base::File::Info& file_info) {
251 DispatchResultsClosure(
252 thread_id, callbacks_id, waitable_results,
253 base::Bind(&DidCreateFileWriter, callbacks_id, path, client,
254 make_scoped_refptr(main_thread_loop), file_info));
257 void DidCreateSnapshotFile(
259 base::MessageLoopProxy* main_thread_loop,
260 const base::File::Info& file_info,
261 const base::FilePath& platform_path,
263 WebFileSystemImpl* filesystem =
264 WebFileSystemImpl::ThreadSpecificInstance(NULL);
268 WebFileSystemCallbacks callbacks =
269 filesystem->GetAndUnregisterCallbacks(callbacks_id);
271 WebFileInfo web_file_info;
272 FileInfoToWebFileInfo(file_info, &web_file_info);
273 web_file_info.platformPath = platform_path.AsUTF16Unsafe();
274 callbacks.didCreateSnapshotFile(web_file_info);
276 // TODO(michaeln,kinuko): Use ThreadSafeSender when Blob becomes
278 main_thread_loop->PostTask(
279 FROM_HERE, base::Bind(&DidReceiveSnapshotFile, request_id));
282 void CreateSnapshotFileCallbackAdapter(
283 int thread_id, int callbacks_id,
284 WaitableCallbackResults* waitable_results,
285 base::MessageLoopProxy* main_thread_loop,
286 const base::File::Info& file_info,
287 const base::FilePath& platform_path,
289 DispatchResultsClosure(
290 thread_id, callbacks_id, waitable_results,
291 base::Bind(&DidCreateSnapshotFile, callbacks_id,
292 make_scoped_refptr(main_thread_loop),
293 file_info, platform_path, request_id));
298 //-----------------------------------------------------------------------------
301 WebFileSystemImpl* WebFileSystemImpl::ThreadSpecificInstance(
302 base::MessageLoopProxy* main_thread_loop) {
303 if (g_webfilesystem_tls.Pointer()->Get() || !main_thread_loop)
304 return g_webfilesystem_tls.Pointer()->Get();
305 WebFileSystemImpl* filesystem = new WebFileSystemImpl(main_thread_loop);
306 if (WorkerTaskRunner::Instance()->CurrentWorkerId())
307 WorkerTaskRunner::Instance()->AddStopObserver(filesystem);
311 void WebFileSystemImpl::DeleteThreadSpecificInstance() {
312 DCHECK(!WorkerTaskRunner::Instance()->CurrentWorkerId());
313 if (g_webfilesystem_tls.Pointer()->Get())
314 delete g_webfilesystem_tls.Pointer()->Get();
317 WebFileSystemImpl::WebFileSystemImpl(base::MessageLoopProxy* main_thread_loop)
318 : main_thread_loop_(main_thread_loop),
319 next_callbacks_id_(0) {
320 g_webfilesystem_tls.Pointer()->Set(this);
323 WebFileSystemImpl::~WebFileSystemImpl() {
324 g_webfilesystem_tls.Pointer()->Set(NULL);
327 void WebFileSystemImpl::OnWorkerRunLoopStopped() {
331 void WebFileSystemImpl::openFileSystem(
332 const blink::WebURL& storage_partition,
333 blink::WebFileSystemType type,
334 WebFileSystemCallbacks callbacks) {
335 int callbacks_id = RegisterCallbacks(callbacks);
336 WaitableCallbackResults* waitable_results =
337 WaitableCallbackResults::MaybeCreate(callbacks);
338 CallDispatcherOnMainThread(
339 main_thread_loop_.get(),
340 &FileSystemDispatcher::OpenFileSystem,
341 MakeTuple(GURL(storage_partition),
342 static_cast<fileapi::FileSystemType>(type),
343 base::Bind(&OpenFileSystemCallbackAdapter,
344 CurrentWorkerId(), callbacks_id,
345 base::Unretained(waitable_results)),
346 base::Bind(&StatusCallbackAdapter,
347 CurrentWorkerId(), callbacks_id,
348 base::Unretained(waitable_results))),
349 make_scoped_ptr(waitable_results));
352 void WebFileSystemImpl::resolveURL(
353 const blink::WebURL& filesystem_url,
354 WebFileSystemCallbacks callbacks) {
355 int callbacks_id = RegisterCallbacks(callbacks);
356 WaitableCallbackResults* waitable_results =
357 WaitableCallbackResults::MaybeCreate(callbacks);
358 CallDispatcherOnMainThread(
359 main_thread_loop_.get(),
360 &FileSystemDispatcher::ResolveURL,
361 MakeTuple(GURL(filesystem_url),
362 base::Bind(&ResolveURLCallbackAdapter,
363 CurrentWorkerId(), callbacks_id,
364 base::Unretained(waitable_results)),
365 base::Bind(&StatusCallbackAdapter,
366 CurrentWorkerId(), callbacks_id,
367 base::Unretained(waitable_results))),
368 make_scoped_ptr(waitable_results));
371 void WebFileSystemImpl::deleteFileSystem(
372 const blink::WebURL& storage_partition,
373 blink::WebFileSystemType type,
374 WebFileSystemCallbacks callbacks) {
375 int callbacks_id = RegisterCallbacks(callbacks);
376 WaitableCallbackResults* waitable_results =
377 WaitableCallbackResults::MaybeCreate(callbacks);
378 CallDispatcherOnMainThread(
379 main_thread_loop_.get(),
380 &FileSystemDispatcher::DeleteFileSystem,
381 MakeTuple(GURL(storage_partition),
382 static_cast<fileapi::FileSystemType>(type),
383 base::Bind(&StatusCallbackAdapter,
384 CurrentWorkerId(), callbacks_id,
385 base::Unretained(waitable_results))),
386 make_scoped_ptr(waitable_results));
389 void WebFileSystemImpl::move(
390 const blink::WebURL& src_path,
391 const blink::WebURL& dest_path,
392 WebFileSystemCallbacks callbacks) {
393 int callbacks_id = RegisterCallbacks(callbacks);
394 WaitableCallbackResults* waitable_results =
395 WaitableCallbackResults::MaybeCreate(callbacks);
396 CallDispatcherOnMainThread(
397 main_thread_loop_.get(),
398 &FileSystemDispatcher::Move,
399 MakeTuple(GURL(src_path), GURL(dest_path),
400 base::Bind(&StatusCallbackAdapter,
401 CurrentWorkerId(), callbacks_id,
402 base::Unretained(waitable_results))),
403 make_scoped_ptr(waitable_results));
406 void WebFileSystemImpl::copy(
407 const blink::WebURL& src_path,
408 const blink::WebURL& dest_path,
409 WebFileSystemCallbacks callbacks) {
410 int callbacks_id = RegisterCallbacks(callbacks);
411 WaitableCallbackResults* waitable_results =
412 WaitableCallbackResults::MaybeCreate(callbacks);
413 CallDispatcherOnMainThread(
414 main_thread_loop_.get(),
415 &FileSystemDispatcher::Copy,
416 MakeTuple(GURL(src_path), GURL(dest_path),
417 base::Bind(&StatusCallbackAdapter,
418 CurrentWorkerId(), callbacks_id,
419 base::Unretained(waitable_results))),
420 make_scoped_ptr(waitable_results));
423 void WebFileSystemImpl::remove(
424 const blink::WebURL& path,
425 WebFileSystemCallbacks callbacks) {
426 int callbacks_id = RegisterCallbacks(callbacks);
427 WaitableCallbackResults* waitable_results =
428 WaitableCallbackResults::MaybeCreate(callbacks);
429 CallDispatcherOnMainThread(
430 main_thread_loop_.get(),
431 &FileSystemDispatcher::Remove,
432 MakeTuple(GURL(path), false /* recursive */,
433 base::Bind(&StatusCallbackAdapter,
434 CurrentWorkerId(), callbacks_id,
435 base::Unretained(waitable_results))),
436 make_scoped_ptr(waitable_results));
439 void WebFileSystemImpl::removeRecursively(
440 const blink::WebURL& path,
441 WebFileSystemCallbacks callbacks) {
442 int callbacks_id = RegisterCallbacks(callbacks);
443 WaitableCallbackResults* waitable_results =
444 WaitableCallbackResults::MaybeCreate(callbacks);
445 CallDispatcherOnMainThread(
446 main_thread_loop_.get(),
447 &FileSystemDispatcher::Remove,
448 MakeTuple(GURL(path), true /* recursive */,
449 base::Bind(&StatusCallbackAdapter,
450 CurrentWorkerId(), callbacks_id,
451 base::Unretained(waitable_results))),
452 make_scoped_ptr(waitable_results));
455 void WebFileSystemImpl::readMetadata(
456 const blink::WebURL& path,
457 WebFileSystemCallbacks callbacks) {
458 int callbacks_id = RegisterCallbacks(callbacks);
459 WaitableCallbackResults* waitable_results =
460 WaitableCallbackResults::MaybeCreate(callbacks);
461 CallDispatcherOnMainThread(
462 main_thread_loop_.get(),
463 &FileSystemDispatcher::ReadMetadata,
464 MakeTuple(GURL(path),
465 base::Bind(&ReadMetadataCallbackAdapter,
466 CurrentWorkerId(), callbacks_id,
467 base::Unretained(waitable_results)),
468 base::Bind(&StatusCallbackAdapter,
469 CurrentWorkerId(), callbacks_id,
470 base::Unretained(waitable_results))),
471 make_scoped_ptr(waitable_results));
474 void WebFileSystemImpl::createFile(
475 const blink::WebURL& path,
477 WebFileSystemCallbacks callbacks) {
478 int callbacks_id = RegisterCallbacks(callbacks);
479 WaitableCallbackResults* waitable_results =
480 WaitableCallbackResults::MaybeCreate(callbacks);
481 CallDispatcherOnMainThread(
482 main_thread_loop_.get(),
483 &FileSystemDispatcher::CreateFile,
484 MakeTuple(GURL(path), exclusive,
485 base::Bind(&StatusCallbackAdapter,
486 CurrentWorkerId(), callbacks_id,
487 base::Unretained(waitable_results))),
488 make_scoped_ptr(waitable_results));
491 void WebFileSystemImpl::createDirectory(
492 const blink::WebURL& path,
494 WebFileSystemCallbacks callbacks) {
495 int callbacks_id = RegisterCallbacks(callbacks);
496 WaitableCallbackResults* waitable_results =
497 WaitableCallbackResults::MaybeCreate(callbacks);
498 CallDispatcherOnMainThread(
499 main_thread_loop_.get(),
500 &FileSystemDispatcher::CreateDirectory,
501 MakeTuple(GURL(path), exclusive, false /* recursive */,
502 base::Bind(&StatusCallbackAdapter,
503 CurrentWorkerId(), callbacks_id,
504 base::Unretained(waitable_results))),
505 make_scoped_ptr(waitable_results));
508 void WebFileSystemImpl::fileExists(
509 const blink::WebURL& path,
510 WebFileSystemCallbacks callbacks) {
511 int callbacks_id = RegisterCallbacks(callbacks);
512 WaitableCallbackResults* waitable_results =
513 WaitableCallbackResults::MaybeCreate(callbacks);
514 CallDispatcherOnMainThread(
515 main_thread_loop_.get(),
516 &FileSystemDispatcher::Exists,
517 MakeTuple(GURL(path), false /* directory */,
518 base::Bind(&StatusCallbackAdapter,
519 CurrentWorkerId(), callbacks_id,
520 base::Unretained(waitable_results))),
521 make_scoped_ptr(waitable_results));
524 void WebFileSystemImpl::directoryExists(
525 const blink::WebURL& path,
526 WebFileSystemCallbacks callbacks) {
527 int callbacks_id = RegisterCallbacks(callbacks);
528 WaitableCallbackResults* waitable_results =
529 WaitableCallbackResults::MaybeCreate(callbacks);
530 CallDispatcherOnMainThread(
531 main_thread_loop_.get(),
532 &FileSystemDispatcher::Exists,
533 MakeTuple(GURL(path), true /* directory */,
534 base::Bind(&StatusCallbackAdapter,
535 CurrentWorkerId(), callbacks_id,
536 base::Unretained(waitable_results))),
537 make_scoped_ptr(waitable_results));
540 void WebFileSystemImpl::readDirectory(
541 const blink::WebURL& path,
542 WebFileSystemCallbacks callbacks) {
543 int callbacks_id = RegisterCallbacks(callbacks);
544 WaitableCallbackResults* waitable_results =
545 WaitableCallbackResults::MaybeCreate(callbacks);
546 CallDispatcherOnMainThread(
547 main_thread_loop_.get(),
548 &FileSystemDispatcher::ReadDirectory,
549 MakeTuple(GURL(path),
550 base::Bind(&ReadDirectoryCallbackAdapater,
551 CurrentWorkerId(), callbacks_id,
552 base::Unretained(waitable_results)),
553 base::Bind(&StatusCallbackAdapter,
554 CurrentWorkerId(), callbacks_id,
555 base::Unretained(waitable_results))),
556 make_scoped_ptr(waitable_results));
559 void WebFileSystemImpl::createFileWriter(
561 blink::WebFileWriterClient* client,
562 WebFileSystemCallbacks callbacks) {
563 int callbacks_id = RegisterCallbacks(callbacks);
564 WaitableCallbackResults* waitable_results =
565 WaitableCallbackResults::MaybeCreate(callbacks);
566 CallDispatcherOnMainThread(
567 main_thread_loop_.get(),
568 &FileSystemDispatcher::ReadMetadata,
569 MakeTuple(GURL(path),
570 base::Bind(&CreateFileWriterCallbackAdapter,
571 CurrentWorkerId(), callbacks_id,
572 base::Unretained(waitable_results),
573 main_thread_loop_, GURL(path), client),
574 base::Bind(&StatusCallbackAdapter,
575 CurrentWorkerId(), callbacks_id,
576 base::Unretained(waitable_results))),
577 make_scoped_ptr(waitable_results));
580 void WebFileSystemImpl::createSnapshotFileAndReadMetadata(
581 const blink::WebURL& path,
582 WebFileSystemCallbacks callbacks) {
583 int callbacks_id = RegisterCallbacks(callbacks);
584 WaitableCallbackResults* waitable_results =
585 WaitableCallbackResults::MaybeCreate(callbacks);
586 CallDispatcherOnMainThread(
587 main_thread_loop_.get(),
588 &FileSystemDispatcher::CreateSnapshotFile,
589 MakeTuple(GURL(path),
590 base::Bind(&CreateSnapshotFileCallbackAdapter,
591 CurrentWorkerId(), callbacks_id,
592 base::Unretained(waitable_results),
594 base::Bind(&StatusCallbackAdapter,
595 CurrentWorkerId(), callbacks_id,
596 base::Unretained(waitable_results))),
597 make_scoped_ptr(waitable_results));
600 int WebFileSystemImpl::RegisterCallbacks(
601 const WebFileSystemCallbacks& callbacks) {
602 DCHECK(CalledOnValidThread());
603 int id = next_callbacks_id_++;
604 callbacks_[id] = callbacks;
608 WebFileSystemCallbacks WebFileSystemImpl::GetAndUnregisterCallbacks(
610 DCHECK(CalledOnValidThread());
611 CallbacksMap::iterator found = callbacks_.find(callbacks_id);
612 DCHECK(found != callbacks_.end());
613 WebFileSystemCallbacks callbacks = found->second;
614 callbacks_.erase(found);
618 } // namespace content