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.
4
5#include "content/child/fileapi/webfilesystem_impl.h"
6
7#include "base/bind.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/file_info_util.h"
16#include "content/child/fileapi/file_system_dispatcher.h"
17#include "content/child/fileapi/webfilewriter_impl.h"
18#include "content/child/worker_task_runner.h"
19#include "content/common/fileapi/file_system_messages.h"
20#include "storage/common/fileapi/directory_entry.h"
21#include "storage/common/fileapi/file_system_util.h"
22#include "third_party/WebKit/public/platform/WebFileInfo.h"
23#include "third_party/WebKit/public/platform/WebFileSystemCallbacks.h"
24#include "third_party/WebKit/public/platform/WebString.h"
25#include "third_party/WebKit/public/platform/WebURL.h"
26#include "third_party/WebKit/public/web/WebHeap.h"
27#include "url/gurl.h"
28
29using blink::WebFileInfo;
30using blink::WebFileSystemCallbacks;
31using blink::WebFileSystemEntry;
32using blink::WebString;
33using blink::WebURL;
34using blink::WebVector;
35
36namespace content {
37
38class WebFileSystemImpl::WaitableCallbackResults
39    : public base::RefCountedThreadSafe<WaitableCallbackResults> {
40 public:
41  WaitableCallbackResults()
42      : results_available_event_(true /* manual_reset */,
43                                 false /* initially_signaled */) {}
44
45  void AddResultsAndSignal(const base::Closure& results_closure) {
46    base::AutoLock lock(lock_);
47    results_closures_.push_back(results_closure);
48    results_available_event_.Signal();
49  }
50
51  void WaitAndRun() {
52    {
53      blink::WebHeap::SafePointScope safe_point;
54      results_available_event_.Wait();
55    }
56    Run();
57  }
58
59  void Run() {
60    std::vector<base::Closure> closures;
61    {
62      base::AutoLock lock(lock_);
63      results_closures_.swap(closures);
64      results_available_event_.Reset();
65    }
66    for (size_t i = 0; i < closures.size(); ++i)
67      closures[i].Run();
68  }
69
70 private:
71  friend class base::RefCountedThreadSafe<WaitableCallbackResults>;
72
73  ~WaitableCallbackResults() {}
74
75  base::Lock lock_;
76  base::WaitableEvent results_available_event_;
77  std::vector<base::Closure> results_closures_;
78  DISALLOW_COPY_AND_ASSIGN(WaitableCallbackResults);
79};
80
81namespace {
82
83typedef WebFileSystemImpl::WaitableCallbackResults WaitableCallbackResults;
84
85base::LazyInstance<base::ThreadLocalPointer<WebFileSystemImpl> >::Leaky
86    g_webfilesystem_tls = LAZY_INSTANCE_INITIALIZER;
87
88void DidReceiveSnapshotFile(int request_id) {
89  if (ChildThread::current())
90    ChildThread::current()->Send(
91        new FileSystemHostMsg_DidReceiveSnapshotFile(request_id));
92}
93
94int CurrentWorkerId() {
95  return WorkerTaskRunner::Instance()->CurrentWorkerId();
96}
97
98template <typename Method, typename Params>
99void CallDispatcherOnMainThread(
100    base::MessageLoopProxy* loop,
101    Method method, const Params& params,
102    WaitableCallbackResults* waitable_results) {
103  if (!loop->RunsTasksOnCurrentThread()) {
104    loop->PostTask(FROM_HERE,
105                   base::Bind(&CallDispatcherOnMainThread<Method, Params>,
106                              make_scoped_refptr(loop), method, params,
107                              scoped_refptr<WaitableCallbackResults>()));
108    if (!waitable_results)
109      return;
110    waitable_results->WaitAndRun();
111  }
112  if (!ChildThread::current() ||
113      !ChildThread::current()->file_system_dispatcher())
114    return;
115
116  DCHECK(!waitable_results);
117  DispatchToMethod(ChildThread::current()->file_system_dispatcher(),
118                   method, params);
119}
120
121enum CallbacksUnregisterMode {
122  UNREGISTER_CALLBACKS,
123  DO_NOT_UNREGISTER_CALLBACKS,
124};
125
126// Bridging functions that convert the arguments into Blink objects
127// (e.g. WebFileInfo, WebString, WebVector<WebFileSystemEntry>)
128// and call WebFileSystemCallbacks's methods.
129// These are called by RunCallbacks after crossing threads to ensure
130// thread safety, because the Blink objects cannot be passed across
131// threads by base::Bind().
132void DidSucceed(WebFileSystemCallbacks* callbacks) {
133  callbacks->didSucceed();
134}
135
136void DidReadMetadata(const base::File::Info& file_info,
137                     WebFileSystemCallbacks* callbacks) {
138  WebFileInfo web_file_info;
139  FileInfoToWebFileInfo(file_info, &web_file_info);
140  callbacks->didReadMetadata(web_file_info);
141}
142
143void DidReadDirectory(const std::vector<storage::DirectoryEntry>& entries,
144                      bool has_more,
145                      WebFileSystemCallbacks* callbacks) {
146  WebVector<WebFileSystemEntry> file_system_entries(entries.size());
147  for (size_t i = 0; i < entries.size(); ++i) {
148    file_system_entries[i].name =
149        base::FilePath(entries[i].name).AsUTF16Unsafe();
150    file_system_entries[i].isDirectory = entries[i].is_directory;
151  }
152  callbacks->didReadDirectory(file_system_entries, has_more);
153}
154
155void DidOpenFileSystem(const base::string16& name, const GURL& root,
156                       WebFileSystemCallbacks* callbacks) {
157  callbacks->didOpenFileSystem(name, root);
158}
159
160void DidResolveURL(const base::string16& name,
161                   const GURL& root_url,
162                   storage::FileSystemType mount_type,
163                   const base::string16& file_path,
164                   bool is_directory,
165                   WebFileSystemCallbacks* callbacks) {
166  callbacks->didResolveURL(
167      name,
168      root_url,
169      static_cast<blink::WebFileSystemType>(mount_type),
170      file_path,
171      is_directory);
172}
173
174void DidFail(base::File::Error error, WebFileSystemCallbacks* callbacks) {
175  callbacks->didFail(storage::FileErrorToWebFileError(error));
176}
177
178// Run WebFileSystemCallbacks's |method| with |params|.
179void RunCallbacks(
180    int callbacks_id,
181    const base::Callback<void(WebFileSystemCallbacks*)>& callback,
182    CallbacksUnregisterMode callbacks_unregister_mode) {
183  WebFileSystemImpl* filesystem =
184      WebFileSystemImpl::ThreadSpecificInstance(NULL);
185  if (!filesystem)
186    return;
187  WebFileSystemCallbacks callbacks = filesystem->GetCallbacks(callbacks_id);
188  if (callbacks_unregister_mode == UNREGISTER_CALLBACKS)
189    filesystem->UnregisterCallbacks(callbacks_id);
190  callback.Run(&callbacks);
191}
192
193void DispatchResultsClosure(int thread_id, int callbacks_id,
194                            WaitableCallbackResults* waitable_results,
195                            const base::Closure& results_closure) {
196  if (thread_id != CurrentWorkerId()) {
197    if (waitable_results) {
198      // If someone is waiting, this should result in running the closure.
199      waitable_results->AddResultsAndSignal(results_closure);
200      // In case no one is waiting, post a task to run the closure.
201      WorkerTaskRunner::Instance()->PostTask(
202          thread_id,
203          base::Bind(&WaitableCallbackResults::Run,
204                     make_scoped_refptr(waitable_results)));
205      return;
206    }
207    WorkerTaskRunner::Instance()->PostTask(thread_id, results_closure);
208    return;
209  }
210  results_closure.Run();
211}
212
213void CallbackFileSystemCallbacks(
214    int thread_id, int callbacks_id,
215    WaitableCallbackResults* waitable_results,
216    const base::Callback<void(WebFileSystemCallbacks*)>& callback,
217    CallbacksUnregisterMode callbacksunregister_mode) {
218  DispatchResultsClosure(
219      thread_id, callbacks_id, waitable_results,
220      base::Bind(&RunCallbacks, callbacks_id, callback,
221                 callbacksunregister_mode));
222}
223
224//-----------------------------------------------------------------------------
225// Callback adapters. Callbacks must be called on the original calling thread,
226// so these callback adapters relay back the results to the calling thread
227// if necessary.
228
229void OpenFileSystemCallbackAdapter(
230    int thread_id, int callbacks_id,
231    WaitableCallbackResults* waitable_results,
232    const std::string& name, const GURL& root) {
233  CallbackFileSystemCallbacks(
234      thread_id, callbacks_id, waitable_results,
235      base::Bind(&DidOpenFileSystem, base::UTF8ToUTF16(name), root),
236      UNREGISTER_CALLBACKS);
237}
238
239void ResolveURLCallbackAdapter(int thread_id,
240                               int callbacks_id,
241                               WaitableCallbackResults* waitable_results,
242                               const storage::FileSystemInfo& info,
243                               const base::FilePath& file_path,
244                               bool is_directory) {
245  base::FilePath normalized_path(
246      storage::VirtualPath::GetNormalizedFilePath(file_path));
247  CallbackFileSystemCallbacks(
248      thread_id, callbacks_id, waitable_results,
249      base::Bind(&DidResolveURL, base::UTF8ToUTF16(info.name), info.root_url,
250                 info.mount_type,
251                 normalized_path.AsUTF16Unsafe(), is_directory),
252      UNREGISTER_CALLBACKS);
253}
254
255void StatusCallbackAdapter(int thread_id, int callbacks_id,
256                           WaitableCallbackResults* waitable_results,
257                           base::File::Error error) {
258  if (error == base::File::FILE_OK) {
259    CallbackFileSystemCallbacks(
260        thread_id, callbacks_id, waitable_results,
261        base::Bind(&DidSucceed),
262        UNREGISTER_CALLBACKS);
263  } else {
264    CallbackFileSystemCallbacks(
265        thread_id, callbacks_id, waitable_results,
266        base::Bind(&DidFail, error),
267        UNREGISTER_CALLBACKS);
268  }
269}
270
271void ReadMetadataCallbackAdapter(int thread_id, int callbacks_id,
272                                 WaitableCallbackResults* waitable_results,
273                                 const base::File::Info& file_info) {
274  CallbackFileSystemCallbacks(
275      thread_id, callbacks_id, waitable_results,
276      base::Bind(&DidReadMetadata, file_info),
277      UNREGISTER_CALLBACKS);
278}
279
280void ReadDirectoryCallbackAdapter(
281    int thread_id,
282    int callbacks_id,
283    WaitableCallbackResults* waitable_results,
284    const std::vector<storage::DirectoryEntry>& entries,
285    bool has_more) {
286  CallbackFileSystemCallbacks(
287      thread_id, callbacks_id, waitable_results,
288      base::Bind(&DidReadDirectory, entries, has_more),
289      has_more ? DO_NOT_UNREGISTER_CALLBACKS : UNREGISTER_CALLBACKS);
290}
291
292void DidCreateFileWriter(
293    int callbacks_id,
294    const GURL& path,
295    blink::WebFileWriterClient* client,
296    base::MessageLoopProxy* main_thread_loop,
297    const base::File::Info& file_info) {
298  WebFileSystemImpl* filesystem =
299      WebFileSystemImpl::ThreadSpecificInstance(NULL);
300  if (!filesystem)
301    return;
302
303  WebFileSystemCallbacks callbacks = filesystem->GetCallbacks(callbacks_id);
304  filesystem->UnregisterCallbacks(callbacks_id);
305
306  if (file_info.is_directory || file_info.size < 0) {
307    callbacks.didFail(blink::WebFileErrorInvalidState);
308    return;
309  }
310  WebFileWriterImpl::Type type =
311      callbacks.shouldBlockUntilCompletion() ?
312          WebFileWriterImpl::TYPE_SYNC : WebFileWriterImpl::TYPE_ASYNC;
313  callbacks.didCreateFileWriter(
314      new WebFileWriterImpl(path, client, type, main_thread_loop),
315      file_info.size);
316}
317
318void CreateFileWriterCallbackAdapter(
319    int thread_id, int callbacks_id,
320    WaitableCallbackResults* waitable_results,
321    base::MessageLoopProxy* main_thread_loop,
322    const GURL& path,
323    blink::WebFileWriterClient* client,
324    const base::File::Info& file_info) {
325  DispatchResultsClosure(
326      thread_id, callbacks_id, waitable_results,
327      base::Bind(&DidCreateFileWriter, callbacks_id, path, client,
328                 make_scoped_refptr(main_thread_loop), file_info));
329}
330
331void DidCreateSnapshotFile(
332    int callbacks_id,
333    base::MessageLoopProxy* main_thread_loop,
334    const base::File::Info& file_info,
335    const base::FilePath& platform_path,
336    int request_id) {
337  WebFileSystemImpl* filesystem =
338      WebFileSystemImpl::ThreadSpecificInstance(NULL);
339  if (!filesystem)
340    return;
341
342  WebFileSystemCallbacks callbacks = filesystem->GetCallbacks(callbacks_id);
343  filesystem->UnregisterCallbacks(callbacks_id);
344
345  WebFileInfo web_file_info;
346  FileInfoToWebFileInfo(file_info, &web_file_info);
347  web_file_info.platformPath = platform_path.AsUTF16Unsafe();
348  callbacks.didCreateSnapshotFile(web_file_info);
349
350  // TODO(michaeln,kinuko): Use ThreadSafeSender when Blob becomes
351  // non-bridge model.
352  main_thread_loop->PostTask(
353      FROM_HERE, base::Bind(&DidReceiveSnapshotFile, request_id));
354}
355
356void CreateSnapshotFileCallbackAdapter(
357    int thread_id, int callbacks_id,
358    WaitableCallbackResults* waitable_results,
359    base::MessageLoopProxy* main_thread_loop,
360    const base::File::Info& file_info,
361    const base::FilePath& platform_path,
362    int request_id) {
363  DispatchResultsClosure(
364      thread_id, callbacks_id, waitable_results,
365      base::Bind(&DidCreateSnapshotFile, callbacks_id,
366                 make_scoped_refptr(main_thread_loop),
367                 file_info, platform_path, request_id));
368}
369
370}  // namespace
371
372//-----------------------------------------------------------------------------
373// WebFileSystemImpl
374
375WebFileSystemImpl* WebFileSystemImpl::ThreadSpecificInstance(
376    base::MessageLoopProxy* main_thread_loop) {
377  if (g_webfilesystem_tls.Pointer()->Get() || !main_thread_loop)
378    return g_webfilesystem_tls.Pointer()->Get();
379  WebFileSystemImpl* filesystem = new WebFileSystemImpl(main_thread_loop);
380  if (WorkerTaskRunner::Instance()->CurrentWorkerId())
381    WorkerTaskRunner::Instance()->AddStopObserver(filesystem);
382  return filesystem;
383}
384
385void WebFileSystemImpl::DeleteThreadSpecificInstance() {
386  DCHECK(!WorkerTaskRunner::Instance()->CurrentWorkerId());
387  if (g_webfilesystem_tls.Pointer()->Get())
388    delete g_webfilesystem_tls.Pointer()->Get();
389}
390
391WebFileSystemImpl::WebFileSystemImpl(base::MessageLoopProxy* main_thread_loop)
392    : main_thread_loop_(main_thread_loop),
393      next_callbacks_id_(1) {
394  g_webfilesystem_tls.Pointer()->Set(this);
395}
396
397WebFileSystemImpl::~WebFileSystemImpl() {
398  g_webfilesystem_tls.Pointer()->Set(NULL);
399}
400
401void WebFileSystemImpl::OnWorkerRunLoopStopped() {
402  delete this;
403}
404
405void WebFileSystemImpl::openFileSystem(
406    const blink::WebURL& storage_partition,
407    blink::WebFileSystemType type,
408    WebFileSystemCallbacks callbacks) {
409  int callbacks_id = RegisterCallbacks(callbacks);
410  scoped_refptr<WaitableCallbackResults> waitable_results =
411      MaybeCreateWaitableResults(callbacks, callbacks_id);
412  CallDispatcherOnMainThread(
413      main_thread_loop_.get(),
414      &FileSystemDispatcher::OpenFileSystem,
415      MakeTuple(GURL(storage_partition),
416                static_cast<storage::FileSystemType>(type),
417                base::Bind(&OpenFileSystemCallbackAdapter,
418                           CurrentWorkerId(),
419                           callbacks_id,
420                           waitable_results),
421                base::Bind(&StatusCallbackAdapter,
422                           CurrentWorkerId(),
423                           callbacks_id,
424                           waitable_results)),
425      waitable_results.get());
426}
427
428void WebFileSystemImpl::resolveURL(
429    const blink::WebURL& filesystem_url,
430    WebFileSystemCallbacks callbacks) {
431  int callbacks_id = RegisterCallbacks(callbacks);
432  scoped_refptr<WaitableCallbackResults> waitable_results =
433      MaybeCreateWaitableResults(callbacks, callbacks_id);
434  CallDispatcherOnMainThread(
435      main_thread_loop_.get(),
436      &FileSystemDispatcher::ResolveURL,
437      MakeTuple(GURL(filesystem_url),
438                base::Bind(&ResolveURLCallbackAdapter,
439                           CurrentWorkerId(), callbacks_id, waitable_results),
440                base::Bind(&StatusCallbackAdapter,
441                           CurrentWorkerId(), callbacks_id, waitable_results)),
442      waitable_results.get());
443}
444
445void WebFileSystemImpl::deleteFileSystem(
446    const blink::WebURL& storage_partition,
447    blink::WebFileSystemType type,
448    WebFileSystemCallbacks callbacks) {
449  int callbacks_id = RegisterCallbacks(callbacks);
450  scoped_refptr<WaitableCallbackResults> waitable_results =
451      MaybeCreateWaitableResults(callbacks, callbacks_id);
452  CallDispatcherOnMainThread(
453      main_thread_loop_.get(),
454      &FileSystemDispatcher::DeleteFileSystem,
455      MakeTuple(GURL(storage_partition),
456                static_cast<storage::FileSystemType>(type),
457                base::Bind(&StatusCallbackAdapter,
458                           CurrentWorkerId(),
459                           callbacks_id,
460                           waitable_results)),
461      waitable_results.get());
462}
463
464void WebFileSystemImpl::move(
465    const blink::WebURL& src_path,
466    const blink::WebURL& dest_path,
467    WebFileSystemCallbacks callbacks) {
468  int callbacks_id = RegisterCallbacks(callbacks);
469  scoped_refptr<WaitableCallbackResults> waitable_results =
470      MaybeCreateWaitableResults(callbacks, callbacks_id);
471  CallDispatcherOnMainThread(
472      main_thread_loop_.get(),
473      &FileSystemDispatcher::Move,
474      MakeTuple(GURL(src_path), GURL(dest_path),
475                base::Bind(&StatusCallbackAdapter,
476                           CurrentWorkerId(), callbacks_id, waitable_results)),
477      waitable_results.get());
478}
479
480void WebFileSystemImpl::copy(
481    const blink::WebURL& src_path,
482    const blink::WebURL& dest_path,
483    WebFileSystemCallbacks callbacks) {
484  int callbacks_id = RegisterCallbacks(callbacks);
485  scoped_refptr<WaitableCallbackResults> waitable_results =
486      MaybeCreateWaitableResults(callbacks, callbacks_id);
487  CallDispatcherOnMainThread(
488      main_thread_loop_.get(),
489      &FileSystemDispatcher::Copy,
490      MakeTuple(GURL(src_path), GURL(dest_path),
491                base::Bind(&StatusCallbackAdapter,
492                           CurrentWorkerId(), callbacks_id, waitable_results)),
493      waitable_results.get());
494}
495
496void WebFileSystemImpl::remove(
497    const blink::WebURL& path,
498    WebFileSystemCallbacks callbacks) {
499  int callbacks_id = RegisterCallbacks(callbacks);
500  scoped_refptr<WaitableCallbackResults> waitable_results =
501      MaybeCreateWaitableResults(callbacks, callbacks_id);
502  CallDispatcherOnMainThread(
503      main_thread_loop_.get(),
504      &FileSystemDispatcher::Remove,
505      MakeTuple(GURL(path), false /* recursive */,
506                base::Bind(&StatusCallbackAdapter,
507                           CurrentWorkerId(), callbacks_id, waitable_results)),
508      waitable_results.get());
509}
510
511void WebFileSystemImpl::removeRecursively(
512    const blink::WebURL& path,
513    WebFileSystemCallbacks callbacks) {
514  int callbacks_id = RegisterCallbacks(callbacks);
515  scoped_refptr<WaitableCallbackResults> waitable_results =
516      MaybeCreateWaitableResults(callbacks, callbacks_id);
517  CallDispatcherOnMainThread(
518      main_thread_loop_.get(),
519      &FileSystemDispatcher::Remove,
520      MakeTuple(GURL(path), true /* recursive */,
521                base::Bind(&StatusCallbackAdapter,
522                           CurrentWorkerId(), callbacks_id, waitable_results)),
523      waitable_results.get());
524}
525
526void WebFileSystemImpl::readMetadata(
527    const blink::WebURL& path,
528    WebFileSystemCallbacks callbacks) {
529  int callbacks_id = RegisterCallbacks(callbacks);
530  scoped_refptr<WaitableCallbackResults> waitable_results =
531      MaybeCreateWaitableResults(callbacks, callbacks_id);
532  CallDispatcherOnMainThread(
533      main_thread_loop_.get(),
534      &FileSystemDispatcher::ReadMetadata,
535      MakeTuple(GURL(path),
536                base::Bind(&ReadMetadataCallbackAdapter,
537                           CurrentWorkerId(), callbacks_id, waitable_results),
538                base::Bind(&StatusCallbackAdapter,
539                           CurrentWorkerId(), callbacks_id, waitable_results)),
540      waitable_results.get());
541}
542
543void WebFileSystemImpl::createFile(
544    const blink::WebURL& path,
545    bool exclusive,
546    WebFileSystemCallbacks callbacks) {
547  int callbacks_id = RegisterCallbacks(callbacks);
548  scoped_refptr<WaitableCallbackResults> waitable_results =
549      MaybeCreateWaitableResults(callbacks, callbacks_id);
550  CallDispatcherOnMainThread(
551      main_thread_loop_.get(),
552      &FileSystemDispatcher::CreateFile,
553      MakeTuple(GURL(path), exclusive,
554                base::Bind(&StatusCallbackAdapter,
555                           CurrentWorkerId(), callbacks_id, waitable_results)),
556      waitable_results.get());
557}
558
559void WebFileSystemImpl::createDirectory(
560    const blink::WebURL& path,
561    bool exclusive,
562    WebFileSystemCallbacks callbacks) {
563  int callbacks_id = RegisterCallbacks(callbacks);
564  scoped_refptr<WaitableCallbackResults> waitable_results =
565      MaybeCreateWaitableResults(callbacks, callbacks_id);
566  CallDispatcherOnMainThread(
567      main_thread_loop_.get(),
568      &FileSystemDispatcher::CreateDirectory,
569      MakeTuple(GURL(path), exclusive, false /* recursive */,
570                base::Bind(&StatusCallbackAdapter,
571                           CurrentWorkerId(), callbacks_id, waitable_results)),
572      waitable_results.get());
573}
574
575void WebFileSystemImpl::fileExists(
576    const blink::WebURL& path,
577    WebFileSystemCallbacks callbacks) {
578  int callbacks_id = RegisterCallbacks(callbacks);
579  scoped_refptr<WaitableCallbackResults> waitable_results =
580      MaybeCreateWaitableResults(callbacks, callbacks_id);
581  CallDispatcherOnMainThread(
582      main_thread_loop_.get(),
583      &FileSystemDispatcher::Exists,
584      MakeTuple(GURL(path), false /* directory */,
585                base::Bind(&StatusCallbackAdapter,
586                           CurrentWorkerId(), callbacks_id, waitable_results)),
587      waitable_results.get());
588}
589
590void WebFileSystemImpl::directoryExists(
591    const blink::WebURL& path,
592    WebFileSystemCallbacks callbacks) {
593  int callbacks_id = RegisterCallbacks(callbacks);
594  scoped_refptr<WaitableCallbackResults> waitable_results =
595      MaybeCreateWaitableResults(callbacks, callbacks_id);
596  CallDispatcherOnMainThread(
597      main_thread_loop_.get(),
598      &FileSystemDispatcher::Exists,
599      MakeTuple(GURL(path), true /* directory */,
600                base::Bind(&StatusCallbackAdapter,
601                           CurrentWorkerId(), callbacks_id, waitable_results)),
602      waitable_results.get());
603}
604
605int WebFileSystemImpl::readDirectory(
606    const blink::WebURL& path,
607    WebFileSystemCallbacks callbacks) {
608  int callbacks_id = RegisterCallbacks(callbacks);
609  scoped_refptr<WaitableCallbackResults> waitable_results =
610      MaybeCreateWaitableResults(callbacks, callbacks_id);
611  CallDispatcherOnMainThread(
612      main_thread_loop_.get(),
613      &FileSystemDispatcher::ReadDirectory,
614      MakeTuple(GURL(path),
615                base::Bind(&ReadDirectoryCallbackAdapter,
616                           CurrentWorkerId(), callbacks_id, waitable_results),
617                base::Bind(&StatusCallbackAdapter,
618                           CurrentWorkerId(), callbacks_id, waitable_results)),
619      waitable_results.get());
620  return callbacks_id;
621}
622
623void WebFileSystemImpl::createFileWriter(
624    const WebURL& path,
625    blink::WebFileWriterClient* client,
626    WebFileSystemCallbacks callbacks) {
627  int callbacks_id = RegisterCallbacks(callbacks);
628  scoped_refptr<WaitableCallbackResults> waitable_results =
629      MaybeCreateWaitableResults(callbacks, callbacks_id);
630  CallDispatcherOnMainThread(
631      main_thread_loop_.get(),
632      &FileSystemDispatcher::ReadMetadata,
633      MakeTuple(GURL(path),
634                base::Bind(&CreateFileWriterCallbackAdapter,
635                           CurrentWorkerId(), callbacks_id, waitable_results,
636                           main_thread_loop_, GURL(path), client),
637                base::Bind(&StatusCallbackAdapter,
638                           CurrentWorkerId(), callbacks_id, waitable_results)),
639      waitable_results.get());
640}
641
642void WebFileSystemImpl::createSnapshotFileAndReadMetadata(
643    const blink::WebURL& path,
644    WebFileSystemCallbacks callbacks) {
645  int callbacks_id = RegisterCallbacks(callbacks);
646  scoped_refptr<WaitableCallbackResults> waitable_results =
647      MaybeCreateWaitableResults(callbacks, callbacks_id);
648  CallDispatcherOnMainThread(
649      main_thread_loop_.get(),
650      &FileSystemDispatcher::CreateSnapshotFile,
651      MakeTuple(GURL(path),
652                base::Bind(&CreateSnapshotFileCallbackAdapter,
653                           CurrentWorkerId(), callbacks_id, waitable_results,
654                           main_thread_loop_),
655                base::Bind(&StatusCallbackAdapter,
656                           CurrentWorkerId(), callbacks_id, waitable_results)),
657      waitable_results.get());
658}
659
660bool WebFileSystemImpl::waitForAdditionalResult(int callbacksId) {
661  WaitableCallbackResultsMap::iterator found =
662      waitable_results_.find(callbacksId);
663  if (found == waitable_results_.end())
664    return false;
665
666  found->second->WaitAndRun();
667  return true;
668}
669
670int WebFileSystemImpl::RegisterCallbacks(
671    const WebFileSystemCallbacks& callbacks) {
672  DCHECK(CalledOnValidThread());
673  int id = next_callbacks_id_++;
674  callbacks_[id] = callbacks;
675  return id;
676}
677
678WebFileSystemCallbacks WebFileSystemImpl::GetCallbacks(int callbacks_id) {
679  DCHECK(CalledOnValidThread());
680  CallbacksMap::iterator found = callbacks_.find(callbacks_id);
681  DCHECK(found != callbacks_.end());
682  return found->second;
683}
684
685void WebFileSystemImpl::UnregisterCallbacks(int callbacks_id) {
686  DCHECK(CalledOnValidThread());
687  CallbacksMap::iterator found = callbacks_.find(callbacks_id);
688  DCHECK(found != callbacks_.end());
689  callbacks_.erase(found);
690
691  waitable_results_.erase(callbacks_id);
692}
693
694WaitableCallbackResults* WebFileSystemImpl::MaybeCreateWaitableResults(
695    const WebFileSystemCallbacks& callbacks, int callbacks_id) {
696  if (!callbacks.shouldBlockUntilCompletion())
697    return NULL;
698  WaitableCallbackResults* results = new WaitableCallbackResults();
699  waitable_results_[callbacks_id] = results;
700  return results;
701}
702
703}  // namespace content
704