blob: 351e387d486cc7b1736b0ddd468c27269fa58aff [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 "third_party/blink/renderer/modules/filesystem/dom_file_system_base.h"
#include <memory>
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_security_origin.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/fileapi/file.h"
#include "third_party/blink/renderer/core/fileapi/file_error.h"
#include "third_party/blink/renderer/modules/filesystem/directory_entry.h"
#include "third_party/blink/renderer/modules/filesystem/directory_reader_base.h"
#include "third_party/blink/renderer/modules/filesystem/dom_file_path.h"
#include "third_party/blink/renderer/modules/filesystem/entry.h"
#include "third_party/blink/renderer/modules/filesystem/entry_base.h"
#include "third_party/blink/renderer/modules/filesystem/file_system_callbacks.h"
#include "third_party/blink/renderer/modules/filesystem/file_system_dispatcher.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
namespace blink {
const char DOMFileSystemBase::kPersistentPathPrefix[] = "persistent";
const char DOMFileSystemBase::kTemporaryPathPrefix[] = "temporary";
const char DOMFileSystemBase::kIsolatedPathPrefix[] = "isolated";
const char DOMFileSystemBase::kExternalPathPrefix[] = "external";
DOMFileSystemBase::DOMFileSystemBase(ExecutionContext* context,
const String& name,
mojom::blink::FileSystemType type,
const KURL& root_url)
: context_(context),
name_(name),
type_(type),
filesystem_root_url_(root_url),
clonable_(false) {}
DOMFileSystemBase::~DOMFileSystemBase() = default;
void DOMFileSystemBase::Trace(blink::Visitor* visitor) {
visitor->Trace(context_);
ScriptWrappable::Trace(visitor);
}
const SecurityOrigin* DOMFileSystemBase::GetSecurityOrigin() const {
return context_->GetSecurityOrigin();
}
bool DOMFileSystemBase::IsValidType(mojom::blink::FileSystemType type) {
return type == mojom::blink::FileSystemType::kTemporary ||
type == mojom::blink::FileSystemType::kPersistent ||
type == mojom::blink::FileSystemType::kIsolated ||
type == mojom::blink::FileSystemType::kExternal;
}
KURL DOMFileSystemBase::CreateFileSystemRootURL(
const String& origin,
mojom::blink::FileSystemType type) {
String type_string;
if (type == mojom::blink::FileSystemType::kTemporary)
type_string = kTemporaryPathPrefix;
else if (type == mojom::blink::FileSystemType::kPersistent)
type_string = kPersistentPathPrefix;
else if (type == mojom::blink::FileSystemType::kExternal)
type_string = kExternalPathPrefix;
else
return KURL();
String result = "filesystem:" + origin + "/" + type_string + "/";
return KURL(result);
}
bool DOMFileSystemBase::SupportsToURL() const {
DCHECK(IsValidType(type_));
return type_ != mojom::blink::FileSystemType::kIsolated;
}
KURL DOMFileSystemBase::CreateFileSystemURL(const EntryBase* entry) const {
return CreateFileSystemURL(entry->fullPath());
}
KURL DOMFileSystemBase::CreateFileSystemURL(const String& full_path) const {
DCHECK(DOMFilePath::IsAbsolute(full_path));
if (GetType() == mojom::blink::FileSystemType::kExternal) {
// For external filesystem originString could be different from what we have
// in m_filesystemRootURL.
StringBuilder result;
result.Append("filesystem:");
result.Append(GetSecurityOrigin()->ToString());
result.Append('/');
result.Append(kExternalPathPrefix);
result.Append(filesystem_root_url_.GetPath());
// Remove the extra leading slash.
result.Append(EncodeWithURLEscapeSequences(full_path.Substring(1)));
return KURL(result.ToString());
}
// For regular types we can just append the entry's fullPath to the
// m_filesystemRootURL that should look like
// 'filesystem:<origin>/<typePrefix>'.
DCHECK(!filesystem_root_url_.IsEmpty());
KURL url = filesystem_root_url_;
// Remove the extra leading slash.
url.SetPath(url.GetPath() +
EncodeWithURLEscapeSequences(full_path.Substring(1)));
return url;
}
bool DOMFileSystemBase::PathToAbsolutePath(mojom::blink::FileSystemType type,
const EntryBase* base,
String path,
String& absolute_path) {
DCHECK(base);
if (!DOMFilePath::IsAbsolute(path))
path = DOMFilePath::Append(base->fullPath(), path);
absolute_path = DOMFilePath::RemoveExtraParentReferences(path);
return (type != mojom::blink::FileSystemType::kTemporary &&
type != mojom::blink::FileSystemType::kPersistent) ||
DOMFilePath::IsValidPath(absolute_path);
}
bool DOMFileSystemBase::PathPrefixToFileSystemType(
const String& path_prefix,
mojom::blink::FileSystemType& type) {
if (path_prefix == kTemporaryPathPrefix) {
type = mojom::blink::FileSystemType::kTemporary;
return true;
}
if (path_prefix == kPersistentPathPrefix) {
type = mojom::blink::FileSystemType::kPersistent;
return true;
}
if (path_prefix == kExternalPathPrefix) {
type = mojom::blink::FileSystemType::kExternal;
return true;
}
return false;
}
File* DOMFileSystemBase::CreateFile(const FileMetadata& metadata,
const KURL& file_system_url,
mojom::blink::FileSystemType type,
const String name) {
// For regular filesystem types (temporary or persistent), we should not cache
// file metadata as it could change File semantics. For other filesystem
// types (which could be platform-specific ones), there's a chance that the
// files are on remote filesystem. If the port has returned metadata just
// pass it to File constructor (so we may cache the metadata).
// FIXME: We should use the snapshot metadata for all files.
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=17746
if (type == mojom::blink::FileSystemType::kTemporary ||
type == mojom::blink::FileSystemType::kPersistent)
return File::CreateForFileSystemFile(metadata.platform_path, name);
const File::UserVisibility user_visibility =
(type == mojom::blink::FileSystemType::kExternal)
? File::kIsUserVisible
: File::kIsNotUserVisible;
if (!metadata.platform_path.IsEmpty()) {
// If the platformPath in the returned metadata is given, we create a File
// object for the snapshot path.
return File::CreateForFileSystemFile(name, metadata, user_visibility);
} else {
// Otherwise we create a File object for the fileSystemURL.
return File::CreateForFileSystemFile(file_system_url, metadata,
user_visibility);
}
}
void DOMFileSystemBase::GetMetadata(
const EntryBase* entry,
MetadataCallbacks::OnDidReadMetadataCallback* success_callback,
ErrorCallbackBase* error_callback,
SynchronousType synchronous_type) {
std::unique_ptr<AsyncFileSystemCallbacks> callbacks(MetadataCallbacks::Create(
success_callback, error_callback, context_, this));
FileSystemDispatcher& dispatcher = FileSystemDispatcher::From(context_);
if (synchronous_type == kSynchronous) {
dispatcher.ReadMetadataSync(CreateFileSystemURL(entry),
std::move(callbacks));
} else {
dispatcher.ReadMetadata(CreateFileSystemURL(entry), std::move(callbacks));
}
}
static bool VerifyAndGetDestinationPathForCopyOrMove(const EntryBase* source,
EntryBase* parent,
const String& new_name,
String& destination_path) {
DCHECK(source);
if (!parent || !parent->isDirectory())
return false;
if (!new_name.IsEmpty() && !DOMFilePath::IsValidName(new_name))
return false;
const bool is_same_file_system =
(*source->filesystem() == *parent->filesystem());
// It is an error to try to copy or move an entry inside itself at any depth
// if it is a directory.
if (source->isDirectory() && is_same_file_system &&
DOMFilePath::IsParentOf(source->fullPath(), parent->fullPath()))
return false;
// It is an error to copy or move an entry into its parent if a name different
// from its current one isn't provided.
if (is_same_file_system &&
(new_name.IsEmpty() || source->name() == new_name) &&
DOMFilePath::GetDirectory(source->fullPath()) == parent->fullPath())
return false;
destination_path = parent->fullPath();
if (!new_name.IsEmpty())
destination_path = DOMFilePath::Append(destination_path, new_name);
else
destination_path = DOMFilePath::Append(destination_path, source->name());
return true;
}
void DOMFileSystemBase::Move(
const EntryBase* source,
EntryBase* parent,
const String& new_name,
EntryCallbacks::OnDidGetEntryCallback* success_callback,
ErrorCallbackBase* error_callback,
SynchronousType synchronous_type) {
String destination_path;
if (!VerifyAndGetDestinationPathForCopyOrMove(source, parent, new_name,
destination_path)) {
ReportError(error_callback, base::File::FILE_ERROR_INVALID_OPERATION);
return;
}
std::unique_ptr<AsyncFileSystemCallbacks> callbacks(EntryCallbacks::Create(
success_callback, error_callback, context_, parent->filesystem(),
destination_path, source->isDirectory()));
FileSystemDispatcher& dispatcher = FileSystemDispatcher::From(context_);
const KURL& src = CreateFileSystemURL(source);
const KURL& dest =
parent->filesystem()->CreateFileSystemURL(destination_path);
if (synchronous_type == kSynchronous)
dispatcher.MoveSync(src, dest, std::move(callbacks));
else
dispatcher.Move(src, dest, std::move(callbacks));
}
void DOMFileSystemBase::Copy(
const EntryBase* source,
EntryBase* parent,
const String& new_name,
EntryCallbacks::OnDidGetEntryCallback* success_callback,
ErrorCallbackBase* error_callback,
SynchronousType synchronous_type) {
String destination_path;
if (!VerifyAndGetDestinationPathForCopyOrMove(source, parent, new_name,
destination_path)) {
ReportError(error_callback, base::File::FILE_ERROR_INVALID_OPERATION);
return;
}
std::unique_ptr<AsyncFileSystemCallbacks> callbacks(EntryCallbacks::Create(
success_callback, error_callback, context_, parent->filesystem(),
destination_path, source->isDirectory()));
const KURL& src = CreateFileSystemURL(source);
const KURL& dest =
parent->filesystem()->CreateFileSystemURL(destination_path);
FileSystemDispatcher& dispatcher = FileSystemDispatcher::From(context_);
if (synchronous_type == kSynchronous)
dispatcher.CopySync(src, dest, std::move(callbacks));
else
dispatcher.Copy(src, dest, std::move(callbacks));
}
void DOMFileSystemBase::Remove(
const EntryBase* entry,
VoidCallbacks::OnDidSucceedCallback* success_callback,
ErrorCallbackBase* error_callback,
SynchronousType synchronous_type) {
DCHECK(entry);
// We don't allow calling remove() on the root directory.
if (entry->fullPath() == String(DOMFilePath::kRoot)) {
ReportError(error_callback, base::File::FILE_ERROR_INVALID_OPERATION);
return;
}
std::unique_ptr<AsyncFileSystemCallbacks> callbacks(
VoidCallbacks::Create(success_callback, error_callback, context_, this));
const KURL& url = CreateFileSystemURL(entry);
FileSystemDispatcher& dispatcher = FileSystemDispatcher::From(context_);
if (synchronous_type == kSynchronous)
dispatcher.RemoveSync(url, /*recursive=*/false, std::move(callbacks));
else
dispatcher.Remove(url, /*recursive=*/false, std::move(callbacks));
}
void DOMFileSystemBase::RemoveRecursively(
const EntryBase* entry,
VoidCallbacks::OnDidSucceedCallback* success_callback,
ErrorCallbackBase* error_callback,
SynchronousType synchronous_type) {
DCHECK(entry);
DCHECK(entry->isDirectory());
// We don't allow calling remove() on the root directory.
if (entry->fullPath() == String(DOMFilePath::kRoot)) {
ReportError(error_callback, base::File::FILE_ERROR_INVALID_OPERATION);
return;
}
std::unique_ptr<AsyncFileSystemCallbacks> callbacks(
VoidCallbacks::Create(success_callback, error_callback, context_, this));
const KURL& url = CreateFileSystemURL(entry);
FileSystemDispatcher& dispatcher = FileSystemDispatcher::From(context_);
if (synchronous_type == kSynchronous)
dispatcher.RemoveSync(url, /*recursive=*/true, std::move(callbacks));
else
dispatcher.Remove(url, /*recursive=*/true, std::move(callbacks));
}
void DOMFileSystemBase::GetParent(
const EntryBase* entry,
EntryCallbacks::OnDidGetEntryCallback* success_callback,
ErrorCallbackBase* error_callback) {
DCHECK(entry);
String path = DOMFilePath::GetDirectory(entry->fullPath());
FileSystemDispatcher::From(context_).Exists(
CreateFileSystemURL(path), /*is_directory=*/true,
EntryCallbacks::Create(success_callback, error_callback, context_, this,
path, true));
}
void DOMFileSystemBase::GetFile(
const EntryBase* entry,
const String& path,
const FileSystemFlags* flags,
EntryCallbacks::OnDidGetEntryCallback* success_callback,
ErrorCallbackBase* error_callback,
SynchronousType synchronous_type) {
String absolute_path;
if (!PathToAbsolutePath(type_, entry, path, absolute_path)) {
ReportError(error_callback, base::File::FILE_ERROR_INVALID_OPERATION);
return;
}
std::unique_ptr<AsyncFileSystemCallbacks> callbacks(EntryCallbacks::Create(
success_callback, error_callback, context_, this, absolute_path, false));
const KURL& url = CreateFileSystemURL(absolute_path);
FileSystemDispatcher& dispatcher = FileSystemDispatcher::From(context_);
if (flags->createFlag()) {
if (synchronous_type == kSynchronous)
dispatcher.CreateFileSync(url, flags->exclusive(), std::move(callbacks));
else
dispatcher.CreateFile(url, flags->exclusive(), std::move(callbacks));
} else {
if (synchronous_type == kSynchronous) {
dispatcher.ExistsSync(url, /*is_directory=*/false, std::move(callbacks));
} else {
dispatcher.Exists(url, /*is_directory=*/false, std::move(callbacks));
}
}
}
void DOMFileSystemBase::GetDirectory(
const EntryBase* entry,
const String& path,
const FileSystemFlags* flags,
EntryCallbacks::OnDidGetEntryCallback* success_callback,
ErrorCallbackBase* error_callback,
SynchronousType synchronous_type) {
String absolute_path;
if (!PathToAbsolutePath(type_, entry, path, absolute_path)) {
ReportError(error_callback, base::File::FILE_ERROR_INVALID_OPERATION);
return;
}
std::unique_ptr<AsyncFileSystemCallbacks> callbacks(EntryCallbacks::Create(
success_callback, error_callback, context_, this, absolute_path, true));
const KURL& url = CreateFileSystemURL(absolute_path);
FileSystemDispatcher& dispatcher = FileSystemDispatcher::From(context_);
if (flags->createFlag()) {
if (synchronous_type == kSynchronous) {
dispatcher.CreateDirectorySync(url, flags->exclusive(),
/*recursive=*/false, std::move(callbacks));
} else {
dispatcher.CreateDirectory(url, flags->exclusive(), /*recursive=*/false,
std::move(callbacks));
}
} else {
if (synchronous_type == kSynchronous) {
dispatcher.ExistsSync(url, /*is_directory=*/true, std::move(callbacks));
} else {
dispatcher.Exists(url, /*is_directory=*/true, std::move(callbacks));
}
}
}
void DOMFileSystemBase::ReadDirectory(
DirectoryReaderBase* reader,
const String& path,
EntriesCallbacks::OnDidGetEntriesCallback* success_callback,
ErrorCallbackBase* error_callback,
SynchronousType synchronous_type) {
DCHECK(DOMFilePath::IsAbsolute(path));
std::unique_ptr<AsyncFileSystemCallbacks> callbacks(EntriesCallbacks::Create(
success_callback, error_callback, context_, reader, path));
FileSystemDispatcher& dispatcher = FileSystemDispatcher::From(context_);
const KURL& url = CreateFileSystemURL(path);
if (synchronous_type == kSynchronous) {
dispatcher.ReadDirectorySync(url, std::move(callbacks));
} else {
dispatcher.ReadDirectory(url, std::move(callbacks));
}
}
} // namespace blink