38c354e55eb59a8f9d753a42472b18b1efd680fb
[WebKit-https.git] / Source / WebCore / Modules / entriesapi / DOMFileSystem.cpp
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "DOMFileSystem.h"
28
29 #include "File.h"
30 #include "FileMetadata.h"
31 #include "FileSystem.h"
32 #include "FileSystemDirectoryEntry.h"
33 #include "FileSystemFileEntry.h"
34 #include <wtf/CrossThreadCopier.h>
35 #include <wtf/UUID.h>
36
37 namespace WebCore {
38
39 struct ListedChild {
40     String filename;
41     FileMetadata::Type type;
42
43     ListedChild isolatedCopy() const { return { filename.isolatedCopy(), type }; }
44 };
45
46 static ExceptionOr<Vector<ListedChild>> listDirectoryWithMetadata(const String& fullPath)
47 {
48     ASSERT(!isMainThread());
49     if (!fileIsDirectory(fullPath, ShouldFollowSymbolicLinks::No))
50         return Exception { NotFoundError, "Path no longer exists or is no longer a directory" };
51
52     auto childPaths = listDirectory(fullPath, "*");
53     Vector<ListedChild> listedChildren;
54     listedChildren.reserveInitialCapacity(childPaths.size());
55     for (auto& childPath : childPaths) {
56         FileMetadata metadata;
57         if (!getFileMetadata(childPath, metadata, ShouldFollowSymbolicLinks::No))
58             continue;
59         listedChildren.uncheckedAppend(ListedChild { pathGetFileName(childPath), metadata.type });
60     }
61     return WTFMove(listedChildren);
62 }
63
64 static ExceptionOr<Vector<Ref<FileSystemEntry>>> toFileSystemEntries(DOMFileSystem& fileSystem, ExceptionOr<Vector<ListedChild>>&& listedChildren, const String& parentVirtualPath)
65 {
66     ASSERT(isMainThread());
67     if (listedChildren.hasException())
68         return listedChildren.releaseException();
69
70     Vector<Ref<FileSystemEntry>> entries;
71     entries.reserveInitialCapacity(listedChildren.returnValue().size());
72     for (auto& child : listedChildren.returnValue()) {
73         String virtualPath = parentVirtualPath + "/" + child.filename;
74         switch (child.type) {
75         case FileMetadata::TypeFile:
76             entries.uncheckedAppend(FileSystemFileEntry::create(fileSystem, virtualPath));
77             break;
78         case FileMetadata::TypeDirectory:
79             entries.uncheckedAppend(FileSystemDirectoryEntry::create(fileSystem, virtualPath));
80             break;
81         default:
82             break;
83         }
84     }
85     return WTFMove(entries);
86 }
87
88 DOMFileSystem::DOMFileSystem(Ref<File>&& file)
89     : m_name(createCanonicalUUIDString())
90     , m_file(WTFMove(file))
91     , m_rootPath(directoryName(m_file->path()))
92     , m_workQueue(WorkQueue::create("DOMFileSystem work queue"))
93 {
94     ASSERT(!m_rootPath.endsWith('/'));
95 }
96
97 DOMFileSystem::~DOMFileSystem()
98 {
99 }
100
101 Ref<FileSystemDirectoryEntry> DOMFileSystem::root()
102 {
103     return FileSystemDirectoryEntry::create(*this, ASCIILiteral("/"));
104 }
105
106 Ref<FileSystemEntry> DOMFileSystem::fileAsEntry()
107 {
108     if (m_file->isDirectory())
109         return FileSystemDirectoryEntry::create(*this, "/" + m_file->name());
110     return FileSystemFileEntry::create(*this, "/" + m_file->name());
111 }
112
113 // https://wicg.github.io/entries-api/#evaluate-a-path
114 String DOMFileSystem::evaluatePath(const String& virtualPath)
115 {
116     ASSERT(virtualPath[0] == '/');
117     auto components = virtualPath.split('/');
118
119     Vector<String> resolvedComponents;
120     resolvedComponents.reserveInitialCapacity(components.size());
121     for (auto& component : components) {
122         if (component == ".")
123             continue;
124         if (component == "..") {
125             if (!resolvedComponents.isEmpty())
126                 resolvedComponents.removeLast();
127             continue;
128         }
129         resolvedComponents.uncheckedAppend(component);
130     }
131
132     return pathByAppendingComponents(m_rootPath, resolvedComponents);
133 }
134
135 void DOMFileSystem::listDirectory(FileSystemDirectoryEntry& directory, DirectoryListingCallback&& completionHandler)
136 {
137     ASSERT(&directory.filesystem() == this);
138
139     String directoryVirtualPath = directory.virtualPath();
140     auto fullPath = evaluatePath(directoryVirtualPath);
141     if (fullPath == m_rootPath) {
142         Vector<Ref<FileSystemEntry>> children;
143         children.append(fileAsEntry());
144         completionHandler(WTFMove(children));
145         return;
146     }
147
148     m_workQueue->dispatch([this, completionHandler = WTFMove(completionHandler), fullPath = fullPath.isolatedCopy(), directoryVirtualPath = directoryVirtualPath.isolatedCopy()]() mutable {
149         auto listedChildren = listDirectoryWithMetadata(fullPath);
150         callOnMainThread([this, completionHandler = WTFMove(completionHandler), listedChildren = crossThreadCopy(listedChildren), directoryVirtualPath = directoryVirtualPath.isolatedCopy()]() mutable {
151             completionHandler(toFileSystemEntries(*this, WTFMove(listedChildren), directoryVirtualPath));
152         });
153     });
154 }
155
156 } // namespace WebCore