| /* |
| * 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/DOMFileSystemBase.h" |
| |
| #include <memory> |
| #include "core/dom/ExecutionContext.h" |
| #include "core/fileapi/File.h" |
| #include "core/fileapi/FileError.h" |
| #include "core/html/VoidCallback.h" |
| #include "modules/filesystem/DOMFilePath.h" |
| #include "modules/filesystem/DirectoryEntry.h" |
| #include "modules/filesystem/DirectoryReaderBase.h" |
| #include "modules/filesystem/EntriesCallback.h" |
| #include "modules/filesystem/Entry.h" |
| #include "modules/filesystem/EntryBase.h" |
| #include "modules/filesystem/EntryCallback.h" |
| #include "modules/filesystem/FileSystemCallbacks.h" |
| #include "modules/filesystem/MetadataCallback.h" |
| #include "platform/weborigin/SecurityOrigin.h" |
| #include "platform/wtf/Assertions.h" |
| #include "platform/wtf/text/StringBuilder.h" |
| #include "platform/wtf/text/TextEncoding.h" |
| #include "public/platform/Platform.h" |
| #include "public/platform/WebFileSystem.h" |
| #include "public/platform/WebFileSystemCallbacks.h" |
| #include "public/platform/WebSecurityOrigin.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, |
| FileSystemType type, |
| const KURL& root_url) |
| : context_(context), |
| name_(name), |
| type_(type), |
| filesystem_root_url_(root_url), |
| clonable_(false) {} |
| |
| DOMFileSystemBase::~DOMFileSystemBase() {} |
| |
| void DOMFileSystemBase::Trace(blink::Visitor* visitor) { |
| visitor->Trace(context_); |
| ScriptWrappable::Trace(visitor); |
| } |
| |
| WebFileSystem* DOMFileSystemBase::FileSystem() const { |
| Platform* platform = Platform::Current(); |
| if (!platform) |
| return nullptr; |
| return platform->FileSystem(); |
| } |
| |
| SecurityOrigin* DOMFileSystemBase::GetSecurityOrigin() const { |
| return context_->GetSecurityOrigin(); |
| } |
| |
| bool DOMFileSystemBase::IsValidType(FileSystemType type) { |
| return type == kFileSystemTypeTemporary || |
| type == kFileSystemTypePersistent || type == kFileSystemTypeIsolated || |
| type == kFileSystemTypeExternal; |
| } |
| |
| KURL DOMFileSystemBase::CreateFileSystemRootURL(const String& origin, |
| FileSystemType type) { |
| String type_string; |
| if (type == kFileSystemTypeTemporary) |
| type_string = kTemporaryPathPrefix; |
| else if (type == kFileSystemTypePersistent) |
| type_string = kPersistentPathPrefix; |
| else if (type == kFileSystemTypeExternal) |
| type_string = kExternalPathPrefix; |
| else |
| return KURL(); |
| |
| String result = "filesystem:" + origin + "/" + type_string + "/"; |
| return KURL(result); |
| } |
| |
| bool DOMFileSystemBase::SupportsToURL() const { |
| DCHECK(IsValidType(type_)); |
| return type_ != kFileSystemTypeIsolated; |
| } |
| |
| 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() == kFileSystemTypeExternal) { |
| // 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(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 != kFileSystemTypeTemporary && |
| type != kFileSystemTypePersistent) || |
| DOMFilePath::IsValidPath(absolute_path); |
| } |
| |
| bool DOMFileSystemBase::PathPrefixToFileSystemType(const String& path_prefix, |
| FileSystemType& type) { |
| if (path_prefix == kTemporaryPathPrefix) { |
| type = kFileSystemTypeTemporary; |
| return true; |
| } |
| |
| if (path_prefix == kPersistentPathPrefix) { |
| type = kFileSystemTypePersistent; |
| return true; |
| } |
| |
| if (path_prefix == kExternalPathPrefix) { |
| type = kFileSystemTypeExternal; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| File* DOMFileSystemBase::CreateFile(const FileMetadata& metadata, |
| const KURL& file_system_url, |
| 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 == kFileSystemTypeTemporary || type == kFileSystemTypePersistent) |
| return File::CreateForFileSystemFile(metadata.platform_path, name); |
| |
| const File::UserVisibility user_visibility = (type == kFileSystemTypeExternal) |
| ? 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, |
| MetadataCallback* success_callback, |
| ErrorCallbackBase* error_callback, |
| SynchronousType synchronous_type) { |
| if (!FileSystem()) { |
| ReportError(error_callback, FileError::kAbortErr); |
| return; |
| } |
| |
| std::unique_ptr<AsyncFileSystemCallbacks> callbacks(MetadataCallbacks::Create( |
| success_callback, error_callback, context_, this)); |
| callbacks->SetShouldBlockUntilCompletion(synchronous_type == kSynchronous); |
| FileSystem()->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, |
| EntryCallback* success_callback, |
| ErrorCallbackBase* error_callback, |
| SynchronousType synchronous_type) { |
| if (!FileSystem()) { |
| ReportError(error_callback, FileError::kAbortErr); |
| return; |
| } |
| |
| String destination_path; |
| if (!VerifyAndGetDestinationPathForCopyOrMove(source, parent, new_name, |
| destination_path)) { |
| ReportError(error_callback, FileError::kInvalidModificationErr); |
| return; |
| } |
| |
| std::unique_ptr<AsyncFileSystemCallbacks> callbacks(EntryCallbacks::Create( |
| success_callback, error_callback, context_, parent->filesystem(), |
| destination_path, source->isDirectory())); |
| callbacks->SetShouldBlockUntilCompletion(synchronous_type == kSynchronous); |
| |
| FileSystem()->Move( |
| CreateFileSystemURL(source), |
| parent->filesystem()->CreateFileSystemURL(destination_path), |
| std::move(callbacks)); |
| } |
| |
| void DOMFileSystemBase::Copy(const EntryBase* source, |
| EntryBase* parent, |
| const String& new_name, |
| EntryCallback* success_callback, |
| ErrorCallbackBase* error_callback, |
| SynchronousType synchronous_type) { |
| if (!FileSystem()) { |
| ReportError(error_callback, FileError::kAbortErr); |
| return; |
| } |
| |
| String destination_path; |
| if (!VerifyAndGetDestinationPathForCopyOrMove(source, parent, new_name, |
| destination_path)) { |
| ReportError(error_callback, FileError::kInvalidModificationErr); |
| return; |
| } |
| |
| std::unique_ptr<AsyncFileSystemCallbacks> callbacks(EntryCallbacks::Create( |
| success_callback, error_callback, context_, parent->filesystem(), |
| destination_path, source->isDirectory())); |
| callbacks->SetShouldBlockUntilCompletion(synchronous_type == kSynchronous); |
| |
| FileSystem()->Copy( |
| CreateFileSystemURL(source), |
| parent->filesystem()->CreateFileSystemURL(destination_path), |
| std::move(callbacks)); |
| } |
| |
| void DOMFileSystemBase::Remove(const EntryBase* entry, |
| VoidCallback* success_callback, |
| ErrorCallbackBase* error_callback, |
| SynchronousType synchronous_type) { |
| if (!FileSystem()) { |
| ReportError(error_callback, FileError::kAbortErr); |
| return; |
| } |
| |
| DCHECK(entry); |
| // We don't allow calling remove() on the root directory. |
| if (entry->fullPath() == String(DOMFilePath::kRoot)) { |
| ReportError(error_callback, FileError::kInvalidModificationErr); |
| return; |
| } |
| |
| std::unique_ptr<AsyncFileSystemCallbacks> callbacks( |
| VoidCallbacks::Create(success_callback, error_callback, context_, this)); |
| callbacks->SetShouldBlockUntilCompletion(synchronous_type == kSynchronous); |
| |
| FileSystem()->Remove(CreateFileSystemURL(entry), std::move(callbacks)); |
| } |
| |
| void DOMFileSystemBase::RemoveRecursively(const EntryBase* entry, |
| VoidCallback* success_callback, |
| ErrorCallbackBase* error_callback, |
| SynchronousType synchronous_type) { |
| if (!FileSystem()) { |
| ReportError(error_callback, FileError::kAbortErr); |
| return; |
| } |
| |
| DCHECK(entry); |
| DCHECK(entry->isDirectory()); |
| // We don't allow calling remove() on the root directory. |
| if (entry->fullPath() == String(DOMFilePath::kRoot)) { |
| ReportError(error_callback, FileError::kInvalidModificationErr); |
| return; |
| } |
| |
| std::unique_ptr<AsyncFileSystemCallbacks> callbacks( |
| VoidCallbacks::Create(success_callback, error_callback, context_, this)); |
| callbacks->SetShouldBlockUntilCompletion(synchronous_type == kSynchronous); |
| |
| FileSystem()->RemoveRecursively(CreateFileSystemURL(entry), |
| std::move(callbacks)); |
| } |
| |
| void DOMFileSystemBase::GetParent(const EntryBase* entry, |
| EntryCallback* success_callback, |
| ErrorCallbackBase* error_callback) { |
| if (!FileSystem()) { |
| ReportError(error_callback, FileError::kAbortErr); |
| return; |
| } |
| |
| DCHECK(entry); |
| String path = DOMFilePath::GetDirectory(entry->fullPath()); |
| |
| FileSystem()->DirectoryExists( |
| CreateFileSystemURL(path), |
| EntryCallbacks::Create(success_callback, error_callback, context_, this, |
| path, true)); |
| } |
| |
| void DOMFileSystemBase::GetFile(const EntryBase* entry, |
| const String& path, |
| const FileSystemFlags& flags, |
| EntryCallback* success_callback, |
| ErrorCallbackBase* error_callback, |
| SynchronousType synchronous_type) { |
| if (!FileSystem()) { |
| ReportError(error_callback, FileError::kAbortErr); |
| return; |
| } |
| |
| String absolute_path; |
| if (!PathToAbsolutePath(type_, entry, path, absolute_path)) { |
| ReportError(error_callback, FileError::kInvalidModificationErr); |
| return; |
| } |
| |
| std::unique_ptr<AsyncFileSystemCallbacks> callbacks(EntryCallbacks::Create( |
| success_callback, error_callback, context_, this, absolute_path, false)); |
| callbacks->SetShouldBlockUntilCompletion(synchronous_type == kSynchronous); |
| |
| if (flags.createFlag()) |
| FileSystem()->CreateFile(CreateFileSystemURL(absolute_path), |
| flags.exclusive(), std::move(callbacks)); |
| else |
| FileSystem()->FileExists(CreateFileSystemURL(absolute_path), |
| std::move(callbacks)); |
| } |
| |
| void DOMFileSystemBase::GetDirectory(const EntryBase* entry, |
| const String& path, |
| const FileSystemFlags& flags, |
| EntryCallback* success_callback, |
| ErrorCallbackBase* error_callback, |
| SynchronousType synchronous_type) { |
| if (!FileSystem()) { |
| ReportError(error_callback, FileError::kAbortErr); |
| return; |
| } |
| |
| String absolute_path; |
| if (!PathToAbsolutePath(type_, entry, path, absolute_path)) { |
| ReportError(error_callback, FileError::kInvalidModificationErr); |
| return; |
| } |
| |
| std::unique_ptr<AsyncFileSystemCallbacks> callbacks(EntryCallbacks::Create( |
| success_callback, error_callback, context_, this, absolute_path, true)); |
| callbacks->SetShouldBlockUntilCompletion(synchronous_type == kSynchronous); |
| |
| if (flags.createFlag()) |
| FileSystem()->CreateDirectory(CreateFileSystemURL(absolute_path), |
| flags.exclusive(), std::move(callbacks)); |
| else |
| FileSystem()->DirectoryExists(CreateFileSystemURL(absolute_path), |
| std::move(callbacks)); |
| } |
| |
| int DOMFileSystemBase::ReadDirectory(DirectoryReaderBase* reader, |
| const String& path, |
| EntriesCallback* success_callback, |
| ErrorCallbackBase* error_callback, |
| SynchronousType synchronous_type) { |
| if (!FileSystem()) { |
| ReportError(error_callback, FileError::kAbortErr); |
| return 0; |
| } |
| |
| DCHECK(DOMFilePath::IsAbsolute(path)); |
| |
| std::unique_ptr<AsyncFileSystemCallbacks> callbacks(EntriesCallbacks::Create( |
| success_callback, error_callback, context_, reader, path)); |
| callbacks->SetShouldBlockUntilCompletion(synchronous_type == kSynchronous); |
| |
| return FileSystem()->ReadDirectory(CreateFileSystemURL(path), |
| std::move(callbacks)); |
| } |
| |
| bool DOMFileSystemBase::WaitForAdditionalResult(int callbacks_id) { |
| if (!FileSystem()) |
| return false; |
| return FileSystem()->WaitForAdditionalResult(callbacks_id); |
| } |
| |
| STATIC_ASSERT_ENUM(WebFileSystem::kTypeTemporary, kFileSystemTypeTemporary); |
| STATIC_ASSERT_ENUM(WebFileSystem::kTypePersistent, kFileSystemTypePersistent); |
| STATIC_ASSERT_ENUM(WebFileSystem::kTypeExternal, kFileSystemTypeExternal); |
| STATIC_ASSERT_ENUM(WebFileSystem::kTypeIsolated, kFileSystemTypeIsolated); |
| |
| } // namespace blink |