Home | History | Annotate | Download | only in fileapi
      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/id_map.h"
      9 #include "base/lazy_instance.h"
     10 #include "base/logging.h"
     11 #include "base/message_loop/message_loop_proxy.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/webfilesystem_callback_adapters.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/WebString.h"
     21 #include "third_party/WebKit/public/platform/WebURL.h"
     22 #include "third_party/WebKit/public/web/WebFileSystemCallbacks.h"
     23 #include "url/gurl.h"
     24 #include "webkit/child/worker_task_runner.h"
     25 #include "webkit/common/fileapi/directory_entry.h"
     26 #include "webkit/common/fileapi/file_system_util.h"
     27 #include "webkit/glue/webkit_glue.h"
     28 
     29 using WebKit::WebFileInfo;
     30 using WebKit::WebFileSystemCallbacks;
     31 using WebKit::WebFileSystemEntry;
     32 using WebKit::WebString;
     33 using WebKit::WebURL;
     34 using WebKit::WebVector;
     35 using webkit_glue::WorkerTaskRunner;
     36 
     37 namespace content {
     38 
     39 namespace {
     40 
     41 class CallbacksMap;
     42 
     43 base::LazyInstance<base::ThreadLocalPointer<CallbacksMap> >::Leaky
     44     g_callbacks_map_tls = LAZY_INSTANCE_INITIALIZER;
     45 
     46 // TODO(kinuko): Integrate this into WebFileSystemImpl when blink side
     47 // becomes ready to make WebFileSystemImpl thread-local.
     48 class CallbacksMap : public WorkerTaskRunner::Observer {
     49  public:
     50   static CallbacksMap* Get() {
     51     return g_callbacks_map_tls.Pointer()->Get();
     52   }
     53 
     54   static CallbacksMap* GetOrCreate() {
     55     if (g_callbacks_map_tls.Pointer()->Get())
     56       return g_callbacks_map_tls.Pointer()->Get();
     57     CallbacksMap* map = new CallbacksMap;
     58     if (WorkerTaskRunner::Instance()->CurrentWorkerId())
     59       WorkerTaskRunner::Instance()->AddStopObserver(map);
     60     return map;
     61   }
     62 
     63   virtual ~CallbacksMap() {
     64     IDMap<WebFileSystemCallbacks>::iterator iter(&callbacks_);
     65     while (!iter.IsAtEnd()) {
     66       iter.GetCurrentValue()->didFail(WebKit::WebFileErrorAbort);
     67       iter.Advance();
     68     }
     69     g_callbacks_map_tls.Pointer()->Set(NULL);
     70   }
     71 
     72   // webkit_glue::WorkerTaskRunner::Observer implementation.
     73   virtual void OnWorkerRunLoopStopped() OVERRIDE {
     74     delete this;
     75   }
     76 
     77   int RegisterCallbacks(WebFileSystemCallbacks* callbacks) {
     78     return callbacks_.Add(callbacks);
     79   }
     80 
     81   WebFileSystemCallbacks* GetAndUnregisterCallbacks(
     82       int callbacks_id) {
     83     WebFileSystemCallbacks* callbacks = callbacks_.Lookup(callbacks_id);
     84     callbacks_.Remove(callbacks_id);
     85     return callbacks;
     86   }
     87 
     88  private:
     89   CallbacksMap() {
     90     g_callbacks_map_tls.Pointer()->Set(this);
     91   }
     92 
     93   IDMap<WebFileSystemCallbacks> callbacks_;
     94 
     95   DISALLOW_COPY_AND_ASSIGN(CallbacksMap);
     96 };
     97 
     98 class WaitableCallbackResults {
     99  public:
    100   static WaitableCallbackResults* MaybeCreate(
    101       WebKit::WebFileSystemCallbacks* callbacks) {
    102     if (callbacks->shouldBlockUntilCompletion())
    103       return new WaitableCallbackResults;
    104     return NULL;
    105   }
    106   ~WaitableCallbackResults() {}
    107 
    108   void SetResultsAndSignal(const base::Closure& results_closure) {
    109     results_closure_ = results_closure;
    110     event_->Signal();
    111   }
    112 
    113   void WaitAndRun() {
    114     event_->Wait();
    115     DCHECK(!results_closure_.is_null());
    116     results_closure_.Run();
    117   }
    118 
    119  private:
    120   WaitableCallbackResults() : event_(new base::WaitableEvent(true, false)) {}
    121 
    122   base::WaitableEvent* event_;
    123   base::Closure results_closure_;
    124   DISALLOW_COPY_AND_ASSIGN(WaitableCallbackResults);
    125 };
    126 
    127 void DidReceiveSnapshotFile(int request_id) {
    128   if (ChildThread::current())
    129     ChildThread::current()->Send(
    130         new FileSystemHostMsg_DidReceiveSnapshotFile(request_id));
    131 }
    132 
    133 int CurrentWorkerId() {
    134   return WorkerTaskRunner::Instance()->CurrentWorkerId();
    135 }
    136 
    137 template <typename Method, typename Params>
    138 void CallDispatcherOnMainThread(
    139     base::MessageLoopProxy* loop,
    140     Method method, const Params& params,
    141     scoped_ptr<WaitableCallbackResults> waitable_results) {
    142   scoped_ptr<WaitableCallbackResults> null_waitable;
    143   if (!loop->RunsTasksOnCurrentThread()) {
    144     loop->PostTask(FROM_HERE,
    145                    base::Bind(&CallDispatcherOnMainThread<Method, Params>,
    146                               make_scoped_refptr(loop), method, params,
    147                               base::Passed(&null_waitable)));
    148     if (!waitable_results)
    149       return;
    150     waitable_results->WaitAndRun();
    151   }
    152   if (!ChildThread::current() ||
    153       !ChildThread::current()->file_system_dispatcher())
    154     return;
    155 
    156   DCHECK(!waitable_results);
    157   DispatchToMethod(ChildThread::current()->file_system_dispatcher(),
    158                    method, params);
    159 }
    160 
    161 // Run WebFileSystemCallbacks's |method| with |params|.
    162 template <typename Method, typename Params>
    163 void RunCallbacks(int callbacks_id, Method method, const Params& params) {
    164   if (!CallbacksMap::Get())
    165     return;
    166   WebFileSystemCallbacks* callbacks =
    167       CallbacksMap::Get()->GetAndUnregisterCallbacks(callbacks_id);
    168   DCHECK(callbacks);
    169   DispatchToMethod(callbacks, method, params);
    170 }
    171 
    172 void DispatchResultsClosure(int thread_id, int callbacks_id,
    173                             WaitableCallbackResults* waitable_results,
    174                             const base::Closure& results_closure) {
    175   if (thread_id != CurrentWorkerId()) {
    176     if (waitable_results) {
    177       waitable_results->SetResultsAndSignal(results_closure);
    178       return;
    179     }
    180     WorkerTaskRunner::Instance()->PostTask(thread_id, results_closure);
    181     return;
    182   }
    183   results_closure.Run();
    184 }
    185 
    186 template <typename Method, typename Params>
    187 void CallbackFileSystemCallbacks(
    188     int thread_id, int callbacks_id,
    189     WaitableCallbackResults* waitable_results,
    190     Method method, const Params& params) {
    191   DispatchResultsClosure(
    192       thread_id, callbacks_id, waitable_results,
    193       base::Bind(&RunCallbacks<Method, Params>, callbacks_id, method, params));
    194 }
    195 
    196 void StatusCallbackAdapter(int thread_id, int callbacks_id,
    197                            WaitableCallbackResults* waitable_results,
    198                            base::PlatformFileError error) {
    199   if (error == base::PLATFORM_FILE_OK) {
    200     CallbackFileSystemCallbacks(
    201         thread_id, callbacks_id, waitable_results,
    202         &WebFileSystemCallbacks::didSucceed, MakeTuple());
    203   } else {
    204     CallbackFileSystemCallbacks(
    205         thread_id, callbacks_id, waitable_results,
    206         &WebFileSystemCallbacks::didFail,
    207         MakeTuple(fileapi::PlatformFileErrorToWebFileError(error)));
    208   }
    209 }
    210 
    211 void ReadMetadataCallbackAdapter(int thread_id, int callbacks_id,
    212                                  WaitableCallbackResults* waitable_results,
    213                                  const base::PlatformFileInfo& file_info) {
    214   WebFileInfo web_file_info;
    215   webkit_glue::PlatformFileInfoToWebFileInfo(file_info, &web_file_info);
    216   CallbackFileSystemCallbacks(
    217       thread_id, callbacks_id, waitable_results,
    218       &WebFileSystemCallbacks::didReadMetadata,
    219       MakeTuple(web_file_info));
    220 }
    221 
    222 void ReadDirectoryCallbackAdapater(
    223     int thread_id, int callbacks_id, WaitableCallbackResults* waitable_results,
    224     const std::vector<fileapi::DirectoryEntry>& entries,
    225     bool has_more) {
    226   WebVector<WebFileSystemEntry> file_system_entries(entries.size());
    227   for (size_t i = 0; i < entries.size(); i++) {
    228     file_system_entries[i].name =
    229         base::FilePath(entries[i].name).AsUTF16Unsafe();
    230     file_system_entries[i].isDirectory = entries[i].is_directory;
    231   }
    232   CallbackFileSystemCallbacks(
    233       thread_id, callbacks_id, waitable_results,
    234       &WebFileSystemCallbacks::didReadDirectory,
    235       MakeTuple(file_system_entries, has_more));
    236 }
    237 
    238 void DidCreateFileWriter(
    239     int callbacks_id,
    240     const GURL& path,
    241     WebKit::WebFileWriterClient* client,
    242     base::MessageLoopProxy* main_thread_loop,
    243     const base::PlatformFileInfo& file_info) {
    244   if (!CallbacksMap::Get())
    245     return;
    246 
    247   WebFileSystemCallbacks* callbacks =
    248       CallbacksMap::Get()->GetAndUnregisterCallbacks(callbacks_id);
    249   DCHECK(callbacks);
    250 
    251   if (file_info.is_directory || file_info.size < 0) {
    252     callbacks->didFail(WebKit::WebFileErrorInvalidState);
    253     return;
    254   }
    255   WebFileWriterImpl::Type type = callbacks->shouldBlockUntilCompletion() ?
    256       WebFileWriterImpl::TYPE_SYNC : WebFileWriterImpl::TYPE_ASYNC;
    257   callbacks->didCreateFileWriter(
    258       new WebFileWriterImpl(path, client, type, main_thread_loop),
    259       file_info.size);
    260 }
    261 
    262 void CreateFileWriterCallbackAdapter(
    263     int thread_id, int callbacks_id,
    264     WaitableCallbackResults* waitable_results,
    265     base::MessageLoopProxy* main_thread_loop,
    266     const GURL& path,
    267     WebKit::WebFileWriterClient* client,
    268     const base::PlatformFileInfo& file_info) {
    269   DispatchResultsClosure(
    270       thread_id, callbacks_id, waitable_results,
    271       base::Bind(&DidCreateFileWriter, callbacks_id, path, client,
    272                  make_scoped_refptr(main_thread_loop), file_info));
    273 }
    274 
    275 void DidCreateSnapshotFile(
    276     int callbacks_id,
    277     base::MessageLoopProxy* main_thread_loop,
    278     const base::PlatformFileInfo& file_info,
    279     const base::FilePath& platform_path,
    280     int request_id) {
    281   if (!CallbacksMap::Get())
    282     return;
    283 
    284   WebFileSystemCallbacks* callbacks =
    285       CallbacksMap::Get()->GetAndUnregisterCallbacks(callbacks_id);
    286   DCHECK(callbacks);
    287 
    288   WebFileInfo web_file_info;
    289   webkit_glue::PlatformFileInfoToWebFileInfo(file_info, &web_file_info);
    290   web_file_info.platformPath = platform_path.AsUTF16Unsafe();
    291   callbacks->didCreateSnapshotFile(web_file_info);
    292 
    293   // TODO(michaeln,kinuko): Use ThreadSafeSender when Blob becomes
    294   // non-bridge model.
    295   main_thread_loop->PostTask(
    296       FROM_HERE, base::Bind(&DidReceiveSnapshotFile, request_id));
    297 }
    298 
    299 void CreateSnapshotFileCallbackAdapter(
    300     int thread_id, int callbacks_id,
    301     WaitableCallbackResults* waitable_results,
    302     base::MessageLoopProxy* main_thread_loop,
    303     const base::PlatformFileInfo& file_info,
    304     const base::FilePath& platform_path,
    305     int request_id) {
    306   DispatchResultsClosure(
    307       thread_id, callbacks_id, waitable_results,
    308       base::Bind(&DidCreateSnapshotFile, callbacks_id,
    309                  make_scoped_refptr(main_thread_loop),
    310                  file_info, platform_path, request_id));
    311 }
    312 
    313 }  // namespace
    314 
    315 WebFileSystemImpl::~WebFileSystemImpl() {
    316 }
    317 
    318 WebFileSystemImpl::WebFileSystemImpl(base::MessageLoopProxy* main_thread_loop)
    319     : main_thread_loop_(main_thread_loop) {
    320 }
    321 
    322 void WebFileSystemImpl::move(
    323     const WebKit::WebURL& src_path,
    324     const WebKit::WebURL& dest_path,
    325     WebKit::WebFileSystemCallbacks* callbacks) {
    326   int callbacks_id = CallbacksMap::GetOrCreate()->RegisterCallbacks(callbacks);
    327   WaitableCallbackResults* waitable_results =
    328       WaitableCallbackResults::MaybeCreate(callbacks);
    329   CallDispatcherOnMainThread(
    330       main_thread_loop_.get(),
    331       &FileSystemDispatcher::Move,
    332       MakeTuple(GURL(src_path), GURL(dest_path),
    333                 base::Bind(&StatusCallbackAdapter,
    334                            CurrentWorkerId(), callbacks_id,
    335                            base::Unretained(waitable_results))),
    336       make_scoped_ptr(waitable_results));
    337 }
    338 
    339 void WebFileSystemImpl::copy(
    340     const WebKit::WebURL& src_path,
    341     const WebKit::WebURL& dest_path,
    342     WebKit::WebFileSystemCallbacks* callbacks) {
    343   int callbacks_id = CallbacksMap::GetOrCreate()->RegisterCallbacks(callbacks);
    344   WaitableCallbackResults* waitable_results =
    345       WaitableCallbackResults::MaybeCreate(callbacks);
    346   CallDispatcherOnMainThread(
    347       main_thread_loop_.get(),
    348       &FileSystemDispatcher::Copy,
    349       MakeTuple(GURL(src_path), GURL(dest_path),
    350                 base::Bind(&StatusCallbackAdapter,
    351                            CurrentWorkerId(), callbacks_id,
    352                            base::Unretained(waitable_results))),
    353       make_scoped_ptr(waitable_results));
    354 }
    355 
    356 void WebFileSystemImpl::remove(
    357     const WebKit::WebURL& path,
    358     WebKit::WebFileSystemCallbacks* callbacks) {
    359   int callbacks_id = CallbacksMap::GetOrCreate()->RegisterCallbacks(callbacks);
    360   WaitableCallbackResults* waitable_results =
    361       WaitableCallbackResults::MaybeCreate(callbacks);
    362   CallDispatcherOnMainThread(
    363       main_thread_loop_.get(),
    364       &FileSystemDispatcher::Remove,
    365       MakeTuple(GURL(path), false /* recursive */,
    366                 base::Bind(&StatusCallbackAdapter,
    367                            CurrentWorkerId(), callbacks_id,
    368                            base::Unretained(waitable_results))),
    369       make_scoped_ptr(waitable_results));
    370 }
    371 
    372 void WebFileSystemImpl::removeRecursively(
    373     const WebKit::WebURL& path,
    374     WebKit::WebFileSystemCallbacks* callbacks) {
    375   int callbacks_id = CallbacksMap::GetOrCreate()->RegisterCallbacks(callbacks);
    376   WaitableCallbackResults* waitable_results =
    377       WaitableCallbackResults::MaybeCreate(callbacks);
    378   CallDispatcherOnMainThread(
    379       main_thread_loop_.get(),
    380       &FileSystemDispatcher::Remove,
    381       MakeTuple(GURL(path), true /* recursive */,
    382                 base::Bind(&StatusCallbackAdapter,
    383                            CurrentWorkerId(), callbacks_id,
    384                            base::Unretained(waitable_results))),
    385       make_scoped_ptr(waitable_results));
    386 }
    387 
    388 void WebFileSystemImpl::readMetadata(
    389     const WebKit::WebURL& path,
    390     WebKit::WebFileSystemCallbacks* callbacks) {
    391   int callbacks_id = CallbacksMap::GetOrCreate()->RegisterCallbacks(callbacks);
    392   WaitableCallbackResults* waitable_results =
    393       WaitableCallbackResults::MaybeCreate(callbacks);
    394   CallDispatcherOnMainThread(
    395       main_thread_loop_.get(),
    396       &FileSystemDispatcher::ReadMetadata,
    397       MakeTuple(GURL(path),
    398                 base::Bind(&ReadMetadataCallbackAdapter,
    399                            CurrentWorkerId(), callbacks_id,
    400                            base::Unretained(waitable_results)),
    401                 base::Bind(&StatusCallbackAdapter,
    402                            CurrentWorkerId(), callbacks_id,
    403                            base::Unretained(waitable_results))),
    404       make_scoped_ptr(waitable_results));
    405 }
    406 
    407 void WebFileSystemImpl::createFile(
    408     const WebKit::WebURL& path,
    409     bool exclusive,
    410     WebKit::WebFileSystemCallbacks* callbacks) {
    411   int callbacks_id = CallbacksMap::GetOrCreate()->RegisterCallbacks(callbacks);
    412   WaitableCallbackResults* waitable_results =
    413       WaitableCallbackResults::MaybeCreate(callbacks);
    414   CallDispatcherOnMainThread(
    415       main_thread_loop_.get(),
    416       &FileSystemDispatcher::CreateFile,
    417       MakeTuple(GURL(path), exclusive,
    418                 base::Bind(&StatusCallbackAdapter,
    419                            CurrentWorkerId(), callbacks_id,
    420                            base::Unretained(waitable_results))),
    421       make_scoped_ptr(waitable_results));
    422 }
    423 
    424 void WebFileSystemImpl::createDirectory(
    425     const WebKit::WebURL& path,
    426     bool exclusive,
    427     WebKit::WebFileSystemCallbacks* callbacks) {
    428   int callbacks_id = CallbacksMap::GetOrCreate()->RegisterCallbacks(callbacks);
    429   WaitableCallbackResults* waitable_results =
    430       WaitableCallbackResults::MaybeCreate(callbacks);
    431   CallDispatcherOnMainThread(
    432       main_thread_loop_.get(),
    433       &FileSystemDispatcher::CreateDirectory,
    434       MakeTuple(GURL(path), exclusive, false /* recursive */,
    435                 base::Bind(&StatusCallbackAdapter,
    436                            CurrentWorkerId(), callbacks_id,
    437                            base::Unretained(waitable_results))),
    438       make_scoped_ptr(waitable_results));
    439 }
    440 
    441 void WebFileSystemImpl::fileExists(
    442     const WebKit::WebURL& path,
    443     WebKit::WebFileSystemCallbacks* callbacks) {
    444   int callbacks_id = CallbacksMap::GetOrCreate()->RegisterCallbacks(callbacks);
    445   WaitableCallbackResults* waitable_results =
    446       WaitableCallbackResults::MaybeCreate(callbacks);
    447   CallDispatcherOnMainThread(
    448       main_thread_loop_.get(),
    449       &FileSystemDispatcher::Exists,
    450       MakeTuple(GURL(path), false /* directory */,
    451                 base::Bind(&StatusCallbackAdapter,
    452                            CurrentWorkerId(), callbacks_id,
    453 base::Unretained(waitable_results))),
    454       make_scoped_ptr(waitable_results));
    455 }
    456 
    457 void WebFileSystemImpl::directoryExists(
    458     const WebKit::WebURL& path,
    459     WebKit::WebFileSystemCallbacks* callbacks) {
    460   int callbacks_id = CallbacksMap::GetOrCreate()->RegisterCallbacks(callbacks);
    461   WaitableCallbackResults* waitable_results =
    462       WaitableCallbackResults::MaybeCreate(callbacks);
    463   CallDispatcherOnMainThread(
    464       main_thread_loop_.get(),
    465       &FileSystemDispatcher::Exists,
    466       MakeTuple(GURL(path), true /* directory */,
    467                 base::Bind(&StatusCallbackAdapter,
    468                            CurrentWorkerId(), callbacks_id,
    469                            base::Unretained(waitable_results))),
    470       make_scoped_ptr(waitable_results));
    471 }
    472 
    473 void WebFileSystemImpl::readDirectory(
    474     const WebKit::WebURL& path,
    475     WebKit::WebFileSystemCallbacks* callbacks) {
    476   int callbacks_id = CallbacksMap::GetOrCreate()->RegisterCallbacks(callbacks);
    477   WaitableCallbackResults* waitable_results =
    478       WaitableCallbackResults::MaybeCreate(callbacks);
    479   CallDispatcherOnMainThread(
    480       main_thread_loop_.get(),
    481       &FileSystemDispatcher::ReadDirectory,
    482       MakeTuple(GURL(path),
    483                 base::Bind(&ReadDirectoryCallbackAdapater,
    484                            CurrentWorkerId(), callbacks_id,
    485                            base::Unretained(waitable_results)),
    486                 base::Bind(&StatusCallbackAdapter,
    487                            CurrentWorkerId(), callbacks_id,
    488 base::Unretained(waitable_results))),
    489       make_scoped_ptr(waitable_results));
    490 }
    491 
    492 WebKit::WebFileWriter* WebFileSystemImpl::createFileWriter(
    493     const WebURL& path, WebKit::WebFileWriterClient* client) {
    494   return new WebFileWriterImpl(GURL(path), client,
    495                                WebFileWriterImpl::TYPE_ASYNC,
    496                                main_thread_loop_.get());
    497 }
    498 
    499 void WebFileSystemImpl::createFileWriter(
    500     const WebURL& path,
    501     WebKit::WebFileWriterClient* client,
    502     WebKit::WebFileSystemCallbacks* callbacks) {
    503   int callbacks_id = CallbacksMap::GetOrCreate()->RegisterCallbacks(callbacks);
    504   WaitableCallbackResults* waitable_results =
    505       WaitableCallbackResults::MaybeCreate(callbacks);
    506   CallDispatcherOnMainThread(
    507       main_thread_loop_.get(),
    508       &FileSystemDispatcher::ReadMetadata,
    509       MakeTuple(GURL(path),
    510                 base::Bind(&CreateFileWriterCallbackAdapter,
    511                            CurrentWorkerId(), callbacks_id,
    512                            base::Unretained(waitable_results),
    513                            main_thread_loop_, GURL(path), client),
    514                 base::Bind(&StatusCallbackAdapter,
    515                            CurrentWorkerId(), callbacks_id,
    516                            base::Unretained(waitable_results))),
    517       make_scoped_ptr(waitable_results));
    518 }
    519 
    520 void WebFileSystemImpl::createSnapshotFileAndReadMetadata(
    521     const WebKit::WebURL& path,
    522     WebKit::WebFileSystemCallbacks* callbacks) {
    523   int callbacks_id = CallbacksMap::GetOrCreate()->RegisterCallbacks(callbacks);
    524   WaitableCallbackResults* waitable_results =
    525       WaitableCallbackResults::MaybeCreate(callbacks);
    526   CallDispatcherOnMainThread(
    527       main_thread_loop_.get(),
    528       &FileSystemDispatcher::CreateSnapshotFile,
    529       MakeTuple(GURL(path),
    530                 base::Bind(&CreateSnapshotFileCallbackAdapter,
    531                            CurrentWorkerId(), callbacks_id,
    532                            base::Unretained(waitable_results),
    533                            main_thread_loop_),
    534                 base::Bind(&StatusCallbackAdapter,
    535                            CurrentWorkerId(), callbacks_id,
    536                            base::Unretained(waitable_results))),
    537       make_scoped_ptr(waitable_results));
    538 }
    539 
    540 }  // namespace content
    541