Changeset 221510 in webkit


Ignore:
Timestamp:
Sep 1, 2017 5:19:55 PM (7 years ago)
Author:
Chris Dumez
Message:

Implement FileSystemDirectoryEntry.getFile()
https://bugs.webkit.org/show_bug.cgi?id=176167
<rdar://problem/34187775>

Reviewed by Andreas Kling.

Source/WebCore:

Implement FileSystemDirectoryEntry.getFile():

Test: editing/pasteboard/datatransfer-items-drop-getFile.html

  • Modules/entriesapi/DOMFileSystem.cpp:

(WebCore::isValidPathNameCharacter):
(WebCore::isValidPathSegment):
(WebCore::isValidRelativeVirtualPath):
(WebCore::isValidAbsoluteVirtualPath):
(WebCore::isValidVirtualPath):
Implement various path validation functions as per:

(WebCore::resolveRelativeVirtualPath):

  • Use StringView for second parameter for efficiency. Had to keep a String for the first parameter because I need the split to return a Vector.
  • If the input path is absolute, call recursively with '/' as base path so that the virtual path gets sanitized (i.e. '..' and '.' in the absolute URL get resolved).

(WebCore::DOMFileSystem::listDirectory):
Use String instead of auto. It is not much longer and is clearer.

(WebCore::validatePathIsDirectory):
(WebCore::DOMFileSystem::getParent):
Move logic to validate that the path is a directory from getParent() to a
separate function. This only makes sure the ScriptExecutionContext is only
ref'd / deref'd on the main thread.

(WebCore::validatePathIsFile):
Logic for validating that a path is a file, used by getFile().

(WebCore::DOMFileSystem::getFile):
Implement getFile() as per:

  • Modules/entriesapi/DOMFileSystem.h:
  • Modules/entriesapi/FileSystemDirectoryEntry.cpp:

(WebCore::FileSystemDirectoryEntry::getFile):
Add implementation of FileSystemDirectoryEntry::getFile() which merely calls
DOMFileSystem::getFile().

LayoutTests:

Add layout test coverage.

  • editing/pasteboard/datatransfer-items-drop-getFile-expected.txt: Added.
  • editing/pasteboard/datatransfer-items-drop-getFile.html: Added.
Location:
trunk
Files:
2 added
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r221507 r221510  
     12017-09-01  Chris Dumez  <cdumez@apple.com>
     2
     3        Implement FileSystemDirectoryEntry.getFile()
     4        https://bugs.webkit.org/show_bug.cgi?id=176167
     5        <rdar://problem/34187775>
     6
     7        Reviewed by Andreas Kling.
     8
     9        Add layout test coverage.
     10
     11        * editing/pasteboard/datatransfer-items-drop-getFile-expected.txt: Added.
     12        * editing/pasteboard/datatransfer-items-drop-getFile.html: Added.
     13
    1142017-09-01  Matt Lewis  <jlewis3@apple.com>
    215
  • trunk/LayoutTests/platform/wk2/TestExpectations

    r221481 r221510  
    568568editing/pasteboard/datatransfer-items-drop-directoryReader-root.html
    569569editing/pasteboard/datatransfer-items-drop-getAsEntry.html
     570editing/pasteboard/datatransfer-items-drop-getFile.html
    570571editing/pasteboard/datatransfer-items-drop-getParent-root.html
    571572editing/pasteboard/datatransfer-items-drop-getParent.html
  • trunk/Source/WebCore/ChangeLog

    r221509 r221510  
     12017-09-01  Chris Dumez  <cdumez@apple.com>
     2
     3        Implement FileSystemDirectoryEntry.getFile()
     4        https://bugs.webkit.org/show_bug.cgi?id=176167
     5        <rdar://problem/34187775>
     6
     7        Reviewed by Andreas Kling.
     8
     9        Implement FileSystemDirectoryEntry.getFile():
     10        - https://wicg.github.io/entries-api/#dom-filesystemdirectoryentry-getfile
     11
     12        Test: editing/pasteboard/datatransfer-items-drop-getFile.html
     13
     14        * Modules/entriesapi/DOMFileSystem.cpp:
     15        (WebCore::isValidPathNameCharacter):
     16        (WebCore::isValidPathSegment):
     17        (WebCore::isValidRelativeVirtualPath):
     18        (WebCore::isValidAbsoluteVirtualPath):
     19        (WebCore::isValidVirtualPath):
     20        Implement various path validation functions as per:
     21        - https://wicg.github.io/entries-api/#names-paths
     22
     23        (WebCore::resolveRelativeVirtualPath):
     24        - Use StringView for second parameter for efficiency. Had to keep
     25          a String for the first parameter because I need the split to
     26          return a Vector.
     27        - If the input path is absolute, call recursively with '/' as
     28          base path so that the virtual path gets sanitized (i.e. '..'
     29          and '.' in the absolute URL get resolved).
     30
     31        (WebCore::DOMFileSystem::listDirectory):
     32        Use String instead of auto. It is not much longer and is clearer.
     33
     34        (WebCore::validatePathIsDirectory):
     35        (WebCore::DOMFileSystem::getParent):
     36        Move logic to validate that the path is a directory from getParent() to a
     37        separate function. This only makes sure the ScriptExecutionContext is only
     38        ref'd / deref'd on the main thread.
     39
     40        (WebCore::validatePathIsFile):
     41        Logic for validating that a path is a file, used by getFile().
     42
     43        (WebCore::DOMFileSystem::getFile):
     44        Implement getFile() as per:
     45        - https://wicg.github.io/entries-api/#dom-filesystemdirectoryentry-getfile
     46
     47        * Modules/entriesapi/DOMFileSystem.h:
     48        * Modules/entriesapi/FileSystemDirectoryEntry.cpp:
     49        (WebCore::FileSystemDirectoryEntry::getFile):
     50        Add implementation of FileSystemDirectoryEntry::getFile() which merely calls
     51        DOMFileSystem::getFile().
     52
    1532017-09-01  Brady Eidson  <beidson@apple.com>
    254
  • trunk/Source/WebCore/Modules/entriesapi/DOMFileSystem.cpp

    r221481 r221510  
    8888}
    8989
    90 static bool isAbsoluteVirtualPath(const String& virtualPath)
    91 {
    92     return !virtualPath.isEmpty() && virtualPath[0] == '/';
     90// https://wicg.github.io/entries-api/#name
     91static bool isValidPathNameCharacter(UChar c)
     92{
     93    return c != '\0' && c != '/' && c != '\\';
     94}
     95
     96// https://wicg.github.io/entries-api/#path-segment
     97static bool isValidPathSegment(StringView segment)
     98{
     99    ASSERT(!segment.isEmpty());
     100    if (segment == "." || segment == "..")
     101        return true;
     102
     103    for (unsigned i = 0; i < segment.length(); ++i) {
     104        if (!isValidPathNameCharacter(segment[i]))
     105            return false;
     106    }
     107    return true;
     108}
     109
     110// https://wicg.github.io/entries-api/#relative-path
     111static bool isValidRelativeVirtualPath(StringView virtualPath)
     112{
     113    if (virtualPath.isEmpty())
     114        return false;
     115
     116    if (virtualPath[0] == '/')
     117        return false;
     118
     119    auto segments = virtualPath.split('/');
     120    for (auto segment : segments) {
     121        if (!isValidPathSegment(segment))
     122            return false;
     123    }
     124    return true;
     125}
     126
     127// https://wicg.github.io/entries-api/#valid-path
     128static bool isValidVirtualPath(StringView virtualPath)
     129{
     130    if (virtualPath.isEmpty())
     131        return false;
     132    if (virtualPath[0] == '/')
     133        return isValidRelativeVirtualPath(virtualPath.substring(1));
     134    return isValidRelativeVirtualPath(virtualPath);
    93135}
    94136
     
    119161
    120162// https://wicg.github.io/entries-api/#resolve-a-relative-path
    121 static String resolveRelativePath(const String& virtualPath, const String& relativePath)
    122 {
    123     ASSERT(virtualPath[0] == '/');
    124     if (isAbsoluteVirtualPath(relativePath))
    125         return relativePath;
    126 
    127     auto virtualPathSegments = virtualPath.split('/');
    128     auto relativePathSegments = relativePath.split('/');
    129     for (auto& segment : relativePathSegments) {
     163static String resolveRelativeVirtualPath(const String& baseVirtualPath, StringView relativeVirtualPath)
     164{
     165    ASSERT(baseVirtualPath[0] == '/');
     166    if (relativeVirtualPath[0] == '/')
     167        return resolveRelativeVirtualPath(ASCIILiteral("/"), relativeVirtualPath.substring(1));
     168
     169    auto virtualPathSegments = baseVirtualPath.split('/');
     170    auto relativePathSegments = relativeVirtualPath.split('/');
     171    for (auto segment : relativePathSegments) {
    130172        ASSERT(!segment.isEmpty());
    131173        if (segment == ".")
     
    136178            continue;
    137179        }
    138         virtualPathSegments.append(segment);
     180        virtualPathSegments.append(segment.toString());
    139181    }
    140182
     
    177219
    178220    String directoryVirtualPath = directory.virtualPath();
    179     auto fullPath = evaluatePath(directoryVirtualPath);
     221    String fullPath = evaluatePath(directoryVirtualPath);
    180222    if (fullPath == m_rootPath) {
    181223        Vector<Ref<FileSystemEntry>> children;
     
    193235}
    194236
     237static ExceptionOr<String> validatePathIsDirectory(const String& fullPath, String&& virtualPath)
     238{
     239    ASSERT(!isMainThread());
     240
     241    if (!fileIsDirectory(fullPath, ShouldFollowSymbolicLinks::No))
     242        return Exception { NotFoundError, "Path no longer exists or is no longer a directory" };
     243    return WTFMove(virtualPath);
     244}
     245
    195246void DOMFileSystem::getParent(ScriptExecutionContext& context, FileSystemEntry& entry, GetParentCallback&& completionCallback)
    196247{
    197     String virtualPath = resolveRelativePath(entry.virtualPath(), ASCIILiteral(".."));
     248    ASSERT(&entry.filesystem() == this);
     249
     250    String virtualPath = resolveRelativeVirtualPath(entry.virtualPath(), "..");
    198251    ASSERT(virtualPath[0] == '/');
    199252    String fullPath = evaluatePath(virtualPath);
    200253    m_workQueue->dispatch([this, context = makeRef(context), fullPath = crossThreadCopy(fullPath), virtualPath = crossThreadCopy(virtualPath), completionCallback = WTFMove(completionCallback)]() mutable {
    201         if (!fileIsDirectory(fullPath, ShouldFollowSymbolicLinks::No)) {
    202             callOnMainThread([completionCallback = WTFMove(completionCallback)] {
    203                 completionCallback(Exception { NotFoundError, "Path no longer exists or is no longer a directory" });
    204             });
    205             return;
    206         }
    207         callOnMainThread([this, context = WTFMove(context), virtualPath = crossThreadCopy(virtualPath), completionCallback = WTFMove(completionCallback)] {
    208             completionCallback(FileSystemDirectoryEntry::create(context, *this, virtualPath));
     254        auto validatedVirtualPath = validatePathIsDirectory(fullPath, WTFMove(virtualPath));
     255        callOnMainThread([this, context = WTFMove(context), validatedVirtualPath = crossThreadCopy(validatedVirtualPath), completionCallback = WTFMove(completionCallback)]() mutable {
     256            if (validatedVirtualPath.hasException())
     257                completionCallback(validatedVirtualPath.releaseException());
     258            else
     259                completionCallback(FileSystemDirectoryEntry::create(context, *this, validatedVirtualPath.releaseReturnValue()));
    209260        });
    210261    });
    211262}
    212263
     264static ExceptionOr<String> validatePathIsFile(const String& fullPath, String&& virtualPath)
     265{
     266    ASSERT(!isMainThread());
     267
     268    FileMetadata metadata;
     269    if (!getFileMetadata(fullPath, metadata, ShouldFollowSymbolicLinks::No))
     270        return Exception { NotFoundError, ASCIILiteral("File does not exist") };
     271
     272    if (metadata.type != FileMetadata::TypeFile)
     273        return Exception { TypeMismatchError, ASCIILiteral("Entry at path is not a file") };
     274
     275    return WTFMove(virtualPath);
     276}
     277
     278// https://wicg.github.io/entries-api/#dom-filesystemdirectoryentry-getfile
     279void DOMFileSystem::getFile(ScriptExecutionContext& context, FileSystemDirectoryEntry& directory, const String& virtualPath, const FileSystemDirectoryEntry::Flags& flags, GetFileCallback&& completionCallback)
     280{
     281    ASSERT(&directory.filesystem() == this);
     282
     283    if (!isValidVirtualPath(virtualPath)) {
     284        callOnMainThread([completionCallback = WTFMove(completionCallback)] {
     285            completionCallback(Exception { TypeMismatchError, ASCIILiteral("Path is invalid") });
     286        });
     287        return;
     288    }
     289
     290    if (flags.create) {
     291        callOnMainThread([completionCallback = WTFMove(completionCallback)] {
     292            completionCallback(Exception { SecurityError, ASCIILiteral("create flag cannot be true") });
     293        });
     294        return;
     295    }
     296
     297    String resolvedVirtualPath = resolveRelativeVirtualPath(directory.virtualPath(), virtualPath);
     298    ASSERT(resolvedVirtualPath[0] == '/');
     299    String fullPath = evaluatePath(resolvedVirtualPath);
     300    m_workQueue->dispatch([this, context = makeRef(context), fullPath = crossThreadCopy(fullPath), resolvedVirtualPath = crossThreadCopy(resolvedVirtualPath), completionCallback = WTFMove(completionCallback)]() mutable {
     301        auto validatedVirtualPath = validatePathIsFile(fullPath, WTFMove(resolvedVirtualPath));
     302        callOnMainThread([this, context = WTFMove(context), validatedVirtualPath = crossThreadCopy(validatedVirtualPath), completionCallback = WTFMove(completionCallback)]() mutable {
     303            if (validatedVirtualPath.hasException())
     304                completionCallback(validatedVirtualPath.releaseException());
     305            else
     306                completionCallback(FileSystemFileEntry::create(context, *this, validatedVirtualPath.releaseReturnValue()));
     307        });
     308    });
     309}
     310
    213311} // namespace WebCore
  • trunk/Source/WebCore/Modules/entriesapi/DOMFileSystem.h

    r221481 r221510  
    2727
    2828#include "ExceptionOr.h"
     29#include "FileSystemDirectoryEntry.h"
    2930#include "ScriptWrappable.h"
    3031#include <wtf/RefCounted.h>
     
    3536
    3637class File;
    37 class FileSystemDirectoryEntry;
     38class FileSystemFileEntry;
    3839class FileSystemEntry;
    3940class ScriptExecutionContext;
     
    5859    void getParent(ScriptExecutionContext&, FileSystemEntry&, GetParentCallback&&);
    5960
     61    using GetFileCallback = WTF::Function<void(ExceptionOr<Ref<FileSystemFileEntry>>&&)>;
     62    void getFile(ScriptExecutionContext&, FileSystemDirectoryEntry&, const String& virtualPath, const FileSystemDirectoryEntry::Flags&, GetFileCallback&&);
     63
    6064private:
    6165    explicit DOMFileSystem(Ref<File>&&);
  • trunk/Source/WebCore/Modules/entriesapi/FileSystemDirectoryEntry.cpp

    r221481 r221510  
    2828
    2929#include "DOMException.h"
     30#include "DOMFileSystem.h"
    3031#include "ErrorCallback.h"
    3132#include "FileSystemDirectoryReader.h"
     33#include "FileSystemEntryCallback.h"
     34#include "FileSystemFileEntry.h"
     35#include "ScriptExecutionContext.h"
    3236
    3337namespace WebCore {
     
    4347}
    4448
    45 void FileSystemDirectoryEntry::getFile(ScriptExecutionContext& context, const String&, const Flags&, RefPtr<FileSystemEntryCallback>&&, RefPtr<ErrorCallback>&& errorCallback)
     49void FileSystemDirectoryEntry::getFile(ScriptExecutionContext& context, const String& path, const Flags& flags, RefPtr<FileSystemEntryCallback>&& successCallback, RefPtr<ErrorCallback>&& errorCallback)
    4650{
    47     if (errorCallback)
    48         errorCallback->scheduleCallback(context, DOMException::create(NotSupportedError));
     51    if (!successCallback && !errorCallback)
     52        return;
     53
     54    filesystem().getFile(context, *this, path, flags, [this, pendingActivity = makePendingActivity(*this), successCallback = WTFMove(successCallback), errorCallback = WTFMove(errorCallback)](auto&& result) {
     55        if (result.hasException()) {
     56            if (errorCallback)
     57                errorCallback->handleEvent(DOMException::create(result.releaseException()));
     58            return;
     59        }
     60        if (successCallback)
     61            successCallback->handleEvent(result.releaseReturnValue());
     62    });
    4963}
    5064
Note: See TracChangeset for help on using the changeset viewer.