blob: 1e60f2affb8297b6020e62abba25a822169f8652 [file] [log] [blame]
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "modules/filesystem/FileSystemCallbacks.h"
#include <memory>
#include "core/dom/ExecutionContext.h"
#include "core/fileapi/BlobCallback.h"
#include "core/fileapi/File.h"
#include "core/fileapi/FileError.h"
#include "core/html/VoidCallback.h"
#include "modules/filesystem/DOMFilePath.h"
#include "modules/filesystem/DOMFileSystem.h"
#include "modules/filesystem/DOMFileSystemBase.h"
#include "modules/filesystem/DirectoryEntry.h"
#include "modules/filesystem/DirectoryReader.h"
#include "modules/filesystem/Entry.h"
#include "modules/filesystem/EntryCallback.h"
#include "modules/filesystem/ErrorCallback.h"
#include "modules/filesystem/FileEntry.h"
#include "modules/filesystem/FileSystemCallback.h"
#include "modules/filesystem/FileWriterBase.h"
#include "modules/filesystem/FileWriterBaseCallback.h"
#include "modules/filesystem/Metadata.h"
#include "modules/filesystem/MetadataCallback.h"
#include "platform/FileMetadata.h"
#include "platform/wtf/PtrUtil.h"
#include "public/platform/WebFileWriter.h"
namespace blink {
FileSystemCallbacksBase::FileSystemCallbacksBase(
ErrorCallbackBase* error_callback,
DOMFileSystemBase* file_system,
ExecutionContext* context)
: error_callback_(error_callback),
file_system_(file_system),
execution_context_(context) {
if (file_system_)
file_system_->AddPendingCallbacks();
}
FileSystemCallbacksBase::~FileSystemCallbacksBase() {
if (file_system_)
file_system_->RemovePendingCallbacks();
}
void FileSystemCallbacksBase::DidFail(int code) {
if (error_callback_)
InvokeOrScheduleCallback(error_callback_.Release(),
static_cast<FileError::ErrorCode>(code));
}
bool FileSystemCallbacksBase::ShouldScheduleCallback() const {
return !ShouldBlockUntilCompletion() && execution_context_ &&
execution_context_->IsContextSuspended();
}
template <typename CB, typename CBArg>
void FileSystemCallbacksBase::InvokeOrScheduleCallback(CB* callback,
CBArg arg) {
DCHECK(callback);
if (callback) {
if (ShouldScheduleCallback()) {
DOMFileSystem::ScheduleCallback(
execution_context_.Get(),
WTF::Bind(&CB::Invoke, WrapPersistent(callback), arg));
} else {
callback->Invoke(arg);
}
}
execution_context_.Clear();
}
template <typename CB, typename CBArg>
void FileSystemCallbacksBase::HandleEventOrScheduleCallback(CB* callback,
CBArg* arg) {
DCHECK(callback);
if (callback) {
if (ShouldScheduleCallback()) {
DOMFileSystem::ScheduleCallback(
execution_context_.Get(),
WTF::Bind(&CB::handleEvent, WrapPersistent(callback),
WrapPersistent(arg)));
} else {
callback->handleEvent(arg);
}
}
execution_context_.Clear();
}
template <typename CB>
void FileSystemCallbacksBase::HandleEventOrScheduleCallback(CB* callback) {
DCHECK(callback);
if (callback) {
if (ShouldScheduleCallback()) {
DOMFileSystem::ScheduleCallback(
execution_context_.Get(),
WTF::Bind(&CB::handleEvent, WrapPersistent(callback)));
} else {
callback->handleEvent();
}
}
execution_context_.Clear();
}
// ScriptErrorCallback --------------------------------------------------------
// static
ScriptErrorCallback* ScriptErrorCallback::Wrap(ErrorCallback* callback) {
// DOMFileSystem operations take an optional (nullable) callback. If a
// script callback was not passed, don't bother creating a dummy wrapper
// and checking during invoke().
if (!callback)
return nullptr;
return new ScriptErrorCallback(callback);
}
DEFINE_TRACE(ScriptErrorCallback) {
ErrorCallbackBase::Trace(visitor);
visitor->Trace(callback_);
}
void ScriptErrorCallback::Invoke(FileError::ErrorCode error) {
callback_->handleEvent(FileError::CreateDOMException(error));
};
ScriptErrorCallback::ScriptErrorCallback(ErrorCallback* callback)
: callback_(callback) {}
// EntryCallbacks -------------------------------------------------------------
std::unique_ptr<AsyncFileSystemCallbacks> EntryCallbacks::Create(
EntryCallback* success_callback,
ErrorCallbackBase* error_callback,
ExecutionContext* context,
DOMFileSystemBase* file_system,
const String& expected_path,
bool is_directory) {
return WTF::WrapUnique(new EntryCallbacks(success_callback, error_callback,
context, file_system, expected_path,
is_directory));
}
EntryCallbacks::EntryCallbacks(EntryCallback* success_callback,
ErrorCallbackBase* error_callback,
ExecutionContext* context,
DOMFileSystemBase* file_system,
const String& expected_path,
bool is_directory)
: FileSystemCallbacksBase(error_callback, file_system, context),
success_callback_(success_callback),
expected_path_(expected_path),
is_directory_(is_directory) {}
void EntryCallbacks::DidSucceed() {
if (success_callback_) {
if (is_directory_)
HandleEventOrScheduleCallback(
success_callback_.Release(),
DirectoryEntry::Create(file_system_, expected_path_));
else
HandleEventOrScheduleCallback(
success_callback_.Release(),
FileEntry::Create(file_system_, expected_path_));
}
}
// EntriesCallbacks -----------------------------------------------------------
std::unique_ptr<AsyncFileSystemCallbacks> EntriesCallbacks::Create(
EntriesCallback* success_callback,
ErrorCallbackBase* error_callback,
ExecutionContext* context,
DirectoryReaderBase* directory_reader,
const String& base_path) {
return WTF::WrapUnique(new EntriesCallbacks(
success_callback, error_callback, context, directory_reader, base_path));
}
EntriesCallbacks::EntriesCallbacks(EntriesCallback* success_callback,
ErrorCallbackBase* error_callback,
ExecutionContext* context,
DirectoryReaderBase* directory_reader,
const String& base_path)
: FileSystemCallbacksBase(error_callback,
directory_reader->Filesystem(),
context),
success_callback_(success_callback),
directory_reader_(directory_reader),
base_path_(base_path) {
DCHECK(directory_reader_);
}
void EntriesCallbacks::DidReadDirectoryEntry(const String& name,
bool is_directory) {
if (is_directory)
entries_.push_back(
DirectoryEntry::Create(directory_reader_->Filesystem(),
DOMFilePath::Append(base_path_, name)));
else
entries_.push_back(
FileEntry::Create(directory_reader_->Filesystem(),
DOMFilePath::Append(base_path_, name)));
}
void EntriesCallbacks::DidReadDirectoryEntries(bool has_more) {
directory_reader_->SetHasMoreEntries(has_more);
EntryHeapVector entries;
entries.Swap(entries_);
// FIXME: delay the callback iff shouldScheduleCallback() is true.
if (success_callback_)
success_callback_->handleEvent(entries);
}
// FileSystemCallbacks --------------------------------------------------------
std::unique_ptr<AsyncFileSystemCallbacks> FileSystemCallbacks::Create(
FileSystemCallback* success_callback,
ErrorCallbackBase* error_callback,
ExecutionContext* context,
FileSystemType type) {
return WTF::WrapUnique(
new FileSystemCallbacks(success_callback, error_callback, context, type));
}
FileSystemCallbacks::FileSystemCallbacks(FileSystemCallback* success_callback,
ErrorCallbackBase* error_callback,
ExecutionContext* context,
FileSystemType type)
: FileSystemCallbacksBase(error_callback, nullptr, context),
success_callback_(success_callback),
type_(type) {}
void FileSystemCallbacks::DidOpenFileSystem(const String& name,
const KURL& root_url) {
if (success_callback_)
HandleEventOrScheduleCallback(
success_callback_.Release(),
DOMFileSystem::Create(execution_context_.Get(), name, type_, root_url));
}
// ResolveURICallbacks --------------------------------------------------------
std::unique_ptr<AsyncFileSystemCallbacks> ResolveURICallbacks::Create(
EntryCallback* success_callback,
ErrorCallbackBase* error_callback,
ExecutionContext* context) {
return WTF::WrapUnique(
new ResolveURICallbacks(success_callback, error_callback, context));
}
ResolveURICallbacks::ResolveURICallbacks(EntryCallback* success_callback,
ErrorCallbackBase* error_callback,
ExecutionContext* context)
: FileSystemCallbacksBase(error_callback, nullptr, context),
success_callback_(success_callback) {}
void ResolveURICallbacks::DidResolveURL(const String& name,
const KURL& root_url,
FileSystemType type,
const String& file_path,
bool is_directory) {
DOMFileSystem* filesystem =
DOMFileSystem::Create(execution_context_.Get(), name, type, root_url);
DirectoryEntry* root = filesystem->root();
String absolute_path;
if (!DOMFileSystemBase::PathToAbsolutePath(type, root, file_path,
absolute_path)) {
InvokeOrScheduleCallback(error_callback_.Release(),
FileError::kInvalidModificationErr);
return;
}
if (is_directory)
HandleEventOrScheduleCallback(
success_callback_.Release(),
DirectoryEntry::Create(filesystem, absolute_path));
else
HandleEventOrScheduleCallback(success_callback_.Release(),
FileEntry::Create(filesystem, absolute_path));
}
// MetadataCallbacks ----------------------------------------------------------
std::unique_ptr<AsyncFileSystemCallbacks> MetadataCallbacks::Create(
MetadataCallback* success_callback,
ErrorCallbackBase* error_callback,
ExecutionContext* context,
DOMFileSystemBase* file_system) {
return WTF::WrapUnique(new MetadataCallbacks(success_callback, error_callback,
context, file_system));
}
MetadataCallbacks::MetadataCallbacks(MetadataCallback* success_callback,
ErrorCallbackBase* error_callback,
ExecutionContext* context,
DOMFileSystemBase* file_system)
: FileSystemCallbacksBase(error_callback, file_system, context),
success_callback_(success_callback) {}
void MetadataCallbacks::DidReadMetadata(const FileMetadata& metadata) {
if (success_callback_)
HandleEventOrScheduleCallback(success_callback_.Release(),
Metadata::Create(metadata));
}
// FileWriterBaseCallbacks ----------------------------------------------------
std::unique_ptr<AsyncFileSystemCallbacks> FileWriterBaseCallbacks::Create(
FileWriterBase* file_writer,
FileWriterBaseCallback* success_callback,
ErrorCallbackBase* error_callback,
ExecutionContext* context) {
return WTF::WrapUnique(new FileWriterBaseCallbacks(
file_writer, success_callback, error_callback, context));
}
FileWriterBaseCallbacks::FileWriterBaseCallbacks(
FileWriterBase* file_writer,
FileWriterBaseCallback* success_callback,
ErrorCallbackBase* error_callback,
ExecutionContext* context)
: FileSystemCallbacksBase(error_callback, nullptr, context),
file_writer_(file_writer),
success_callback_(success_callback) {}
void FileWriterBaseCallbacks::DidCreateFileWriter(
std::unique_ptr<WebFileWriter> file_writer,
long long length) {
file_writer_->Initialize(std::move(file_writer), length);
if (success_callback_)
HandleEventOrScheduleCallback(success_callback_.Release(),
file_writer_.Release());
}
// SnapshotFileCallback -------------------------------------------------------
std::unique_ptr<AsyncFileSystemCallbacks> SnapshotFileCallback::Create(
DOMFileSystemBase* filesystem,
const String& name,
const KURL& url,
BlobCallback* success_callback,
ErrorCallbackBase* error_callback,
ExecutionContext* context) {
return WTF::WrapUnique(new SnapshotFileCallback(
filesystem, name, url, success_callback, error_callback, context));
}
SnapshotFileCallback::SnapshotFileCallback(DOMFileSystemBase* filesystem,
const String& name,
const KURL& url,
BlobCallback* success_callback,
ErrorCallbackBase* error_callback,
ExecutionContext* context)
: FileSystemCallbacksBase(error_callback, filesystem, context),
name_(name),
url_(url),
success_callback_(success_callback) {}
void SnapshotFileCallback::DidCreateSnapshotFile(
const FileMetadata& metadata,
PassRefPtr<BlobDataHandle> snapshot) {
if (!success_callback_)
return;
// We can't directly use the snapshot blob data handle because the content
// type on it hasn't been set. The |snapshot| param is here to provide a a
// chain of custody thru thread bridging that is held onto until *after* we've
// coined a File with a new handle that has the correct type set on it. This
// allows the blob storage system to track when a temp file can and can't be
// safely deleted.
HandleEventOrScheduleCallback(
success_callback_.Release(),
DOMFileSystemBase::CreateFile(metadata, url_, file_system_->GetType(),
name_));
}
// VoidCallbacks --------------------------------------------------------------
std::unique_ptr<AsyncFileSystemCallbacks> VoidCallbacks::Create(
VoidCallback* success_callback,
ErrorCallbackBase* error_callback,
ExecutionContext* context,
DOMFileSystemBase* file_system) {
return WTF::WrapUnique(new VoidCallbacks(success_callback, error_callback,
context, file_system));
}
VoidCallbacks::VoidCallbacks(VoidCallback* success_callback,
ErrorCallbackBase* error_callback,
ExecutionContext* context,
DOMFileSystemBase* file_system)
: FileSystemCallbacksBase(error_callback, file_system, context),
success_callback_(success_callback) {}
void VoidCallbacks::DidSucceed() {
if (success_callback_)
HandleEventOrScheduleCallback(success_callback_.Release());
}
} // namespace blink