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 29using WebKit::WebFileInfo; 30using WebKit::WebFileSystemCallbacks; 31using WebKit::WebFileSystemEntry; 32using WebKit::WebString; 33using WebKit::WebURL; 34using WebKit::WebVector; 35using webkit_glue::WorkerTaskRunner; 36 37namespace content { 38 39namespace { 40 41class CallbacksMap; 42 43base::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. 48class 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 98class 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 127void DidReceiveSnapshotFile(int request_id) { 128 if (ChildThread::current()) 129 ChildThread::current()->Send( 130 new FileSystemHostMsg_DidReceiveSnapshotFile(request_id)); 131} 132 133int CurrentWorkerId() { 134 return WorkerTaskRunner::Instance()->CurrentWorkerId(); 135} 136 137template <typename Method, typename Params> 138void 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|. 162template <typename Method, typename Params> 163void 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 172void 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 186template <typename Method, typename Params> 187void 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 196void 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 211void 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 222void 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 238void 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 262void 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 275void 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 299void 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 315WebFileSystemImpl::~WebFileSystemImpl() { 316} 317 318WebFileSystemImpl::WebFileSystemImpl(base::MessageLoopProxy* main_thread_loop) 319 : main_thread_loop_(main_thread_loop) { 320} 321 322void 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 339void 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 356void 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 372void 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 388void 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 407void 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 424void 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 441void 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, 453base::Unretained(waitable_results))), 454 make_scoped_ptr(waitable_results)); 455} 456 457void 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 473void 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, 488base::Unretained(waitable_results))), 489 make_scoped_ptr(waitable_results)); 490} 491 492WebKit::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 499void 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 520void 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