Implement FileSystemDirectoryReader.readEntries()
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 31 Aug 2017 03:48:21 +0000 (03:48 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 31 Aug 2017 03:48:21 +0000 (03:48 +0000)
https://bugs.webkit.org/show_bug.cgi?id=176091
<rdar://problem/34168015>

Reviewed by Andreas Kling.

Source/WebCore:

Tests: editing/pasteboard/datatransfer-items-drop-directoryReader-error.html
       editing/pasteboard/datatransfer-items-drop-directoryReader-root.html
       editing/pasteboard/datatransfer-items-drop-directoryReader.html

* Modules/entriesapi/DOMFileSystem.cpp:
(WebCore::ListedChild::isolatedCopy const):
(WebCore::listDirectoryWithMetadata):
(WebCore::toFileSystemEntries):
(WebCore::DOMFileSystem::DOMFileSystem):
(WebCore::DOMFileSystem::root):
(WebCore::DOMFileSystem::fileAsEntry):
(WebCore::DOMFileSystem::evaluatePath):
(WebCore::DOMFileSystem::listDirectory):
* Modules/entriesapi/DOMFileSystem.h:
(WebCore::DOMFileSystem::createEntryForFile):
* Modules/entriesapi/DOMFileSystem.idl:
- Implement directory listing operation for supporting
  FileSystemDirectoryReader::readEntries().
- Implement evaluatePath() operation as per:
  - https://wicg.github.io/entries-api/#evaluate-a-path
- DOMFileSystem should no longer hold a strong reference to
  the root entry and FileSystemEntry now holds a strong
  reference to the DOMFileSystem and this would create a
  cycle.

* Modules/entriesapi/FileSystemDirectoryEntry.cpp:
(WebCore::FileSystemDirectoryEntry::createReader):
* Modules/entriesapi/FileSystemDirectoryEntry.h:
* Modules/entriesapi/FileSystemDirectoryEntry.idl:
Have createReader() take a ScriptExecutionContext, which is needed
to construct a FileSystemDirectoryReader, now that FileSystemDirectoryReader
is an ActiveDOMObject.

* Modules/entriesapi/FileSystemDirectoryReader.cpp:
(WebCore::FileSystemDirectoryReader::FileSystemDirectoryReader):
(WebCore::FileSystemDirectoryReader::activeDOMObjectName const):
(WebCore::FileSystemDirectoryReader::canSuspendForDocumentSuspension const):
(WebCore::FileSystemDirectoryReader::readEntries):
* Modules/entriesapi/FileSystemDirectoryReader.h:
* Modules/entriesapi/FileSystemDirectoryReader.idl:
Provide implementation for FileSystemDirectoryReader.readEntries() as per:
- https://wicg.github.io/entries-api/#dom-filesystemdirectoryreader-readentries
For the actual directory listing operation, we ask the DOMFileSystem, which is
where all filesystem operations should live.
Also made the FileSystemDirectoryReader an ActiveDOMObject to keep it and its
wrapper alive while a file system operation is pending.

* Modules/entriesapi/FileSystemEntry.cpp:
(WebCore::FileSystemEntry::~FileSystemEntry):
(WebCore::FileSystemEntry::filesystem const):
* Modules/entriesapi/FileSystemEntry.h:
Make FileSystemEntry keep a strong reference to its DOMFileSystem object.
Previously, the DOMFileSystem was kept alive by the DataTransferItem but
this was unsafe because FileSystemEntry may outlive the DataTransferItem.

* dom/ActiveDOMObject.h:
(WebCore::ActiveDOMObject::PendingActivity::PendingActivity):
(WebCore::ActiveDOMObject::PendingActivity::~PendingActivity):
(WebCore::ActiveDOMObject::makePendingActivity):
Add PendingActivity / makePendingActivity() as a less error-prone
replacement for setPendingActivity() / unsetPendingActivity().

* dom/DOMException.cpp:
(WebCore::DOMException::create):
* dom/DOMException.h:
Add factory to construct a DOMException from an Exception.

* dom/DataTransferItem.cpp:
(WebCore::DataTransferItem::getAsEntry const):
* dom/DataTransferItem.h:

* dom/Exception.h:
(WebCore::Exception::isolatedCopy const):
* dom/ExceptionOr.h:
Make ExceptionOr<> / Exception work with CrossThreadCopier for convenience.

* html/FileListCreator.cpp:
(WebCore::FileListCreator::FileListCreator):
Use crossThreadCopy() instead of longer form.

* platform/FileSystem.h:
* platform/glib/FileSystemGlib.cpp:
(WebCore::pathByAppendingComponents):
* platform/posix/FileSystemPOSIX.cpp:
(WebCore::pathByAppendingComponents):
* platform/win/FileSystemWin.cpp:
(WebCore::pathByAppendingComponents):
Add pathByAppendingComponents() utility function, which is similar to
pathByAppendingComponent() but supports appending multiple components
in an efficient fashion.

Source/WTF:

* wtf/CrossThreadCopier.h:
(WTF::crossThreadCopy):
* wtf/CrossThreadTask.h:
Move crossThreadCopy() from CrossThreadTask.h to CrossThreadCopier.h and
add "using WTF::crossThreadCopy" statement to make it more easily usable
from WebCore.

LayoutTests:

Add layout test coverage.

* editing/editing.js:
(moveMouseToCenterOfElement):
(dragFilesOntoElement):
* editing/pasteboard/datatransfer-items-drop-directoryReader-error-expected.txt: Added.
* editing/pasteboard/datatransfer-items-drop-directoryReader-error.html: Added.
* editing/pasteboard/datatransfer-items-drop-directoryReader-expected.txt: Added.
* editing/pasteboard/datatransfer-items-drop-directoryReader-root-expected.txt: Added.
* editing/pasteboard/datatransfer-items-drop-directoryReader-root.html: Added.
* editing/pasteboard/datatransfer-items-drop-directoryReader.html: Added.
* editing/pasteboard/datatransfer-items-drop-getAsEntry.html:
* platform/wk2/TestExpectations:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@221414 268f45cc-cd09-0410-ab3c-d52691b4dbfc

37 files changed:
LayoutTests/ChangeLog
LayoutTests/editing/editing.js
LayoutTests/editing/pasteboard/datatransfer-items-drop-directoryReader-error-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/datatransfer-items-drop-directoryReader-error.html [new file with mode: 0644]
LayoutTests/editing/pasteboard/datatransfer-items-drop-directoryReader-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/datatransfer-items-drop-directoryReader-root-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/datatransfer-items-drop-directoryReader-root.html [new file with mode: 0644]
LayoutTests/editing/pasteboard/datatransfer-items-drop-directoryReader.html [new file with mode: 0644]
LayoutTests/editing/pasteboard/datatransfer-items-drop-getAsEntry.html
LayoutTests/platform/wk2/TestExpectations
Source/WTF/ChangeLog
Source/WTF/wtf/CrossThreadCopier.h
Source/WTF/wtf/CrossThreadTask.h
Source/WebCore/ChangeLog
Source/WebCore/Modules/entriesapi/DOMFileSystem.cpp
Source/WebCore/Modules/entriesapi/DOMFileSystem.h
Source/WebCore/Modules/entriesapi/DOMFileSystem.idl
Source/WebCore/Modules/entriesapi/FileSystemDirectoryEntry.cpp
Source/WebCore/Modules/entriesapi/FileSystemDirectoryEntry.h
Source/WebCore/Modules/entriesapi/FileSystemDirectoryEntry.idl
Source/WebCore/Modules/entriesapi/FileSystemDirectoryReader.cpp
Source/WebCore/Modules/entriesapi/FileSystemDirectoryReader.h
Source/WebCore/Modules/entriesapi/FileSystemDirectoryReader.idl
Source/WebCore/Modules/entriesapi/FileSystemEntry.cpp
Source/WebCore/Modules/entriesapi/FileSystemEntry.h
Source/WebCore/dom/ActiveDOMObject.h
Source/WebCore/dom/DOMException.cpp
Source/WebCore/dom/DOMException.h
Source/WebCore/dom/DataTransferItem.cpp
Source/WebCore/dom/DataTransferItem.h
Source/WebCore/dom/Exception.h
Source/WebCore/dom/ExceptionOr.h
Source/WebCore/html/FileListCreator.cpp
Source/WebCore/platform/FileSystem.h
Source/WebCore/platform/glib/FileSystemGlib.cpp
Source/WebCore/platform/posix/FileSystemPOSIX.cpp
Source/WebCore/platform/win/FileSystemWin.cpp

index 0bc2462..bc9f75d 100644 (file)
@@ -1,3 +1,25 @@
+2017-08-30  Chris Dumez  <cdumez@apple.com>
+
+        Implement FileSystemDirectoryReader.readEntries()
+        https://bugs.webkit.org/show_bug.cgi?id=176091
+        <rdar://problem/34168015>
+
+        Reviewed by Andreas Kling.
+
+        Add layout test coverage.
+
+        * editing/editing.js:
+        (moveMouseToCenterOfElement):
+        (dragFilesOntoElement):
+        * editing/pasteboard/datatransfer-items-drop-directoryReader-error-expected.txt: Added.
+        * editing/pasteboard/datatransfer-items-drop-directoryReader-error.html: Added.
+        * editing/pasteboard/datatransfer-items-drop-directoryReader-expected.txt: Added.
+        * editing/pasteboard/datatransfer-items-drop-directoryReader-root-expected.txt: Added.
+        * editing/pasteboard/datatransfer-items-drop-directoryReader-root.html: Added.
+        * editing/pasteboard/datatransfer-items-drop-directoryReader.html: Added.
+        * editing/pasteboard/datatransfer-items-drop-getAsEntry.html:
+        * platform/wk2/TestExpectations:
+
 2017-08-30  Myles C. Maxfield  <mmaxfield@apple.com>
 
         Previous elements with lang= can affect fonts selected for subsequent elements
index ec3221d..26d56a9 100644 (file)
@@ -399,6 +399,26 @@ function moveSelectionBackwardByLineBoundaryCommand() {
 
 //-------------------------------------------------------------------------------------------------------
 
+function moveMouseToCenterOfElement(element) {
+    if (!window.eventSender)
+        return;
+
+    const centerX = element.offsetLeft + element.offsetWidth / 2;
+    const centerY = element.offsetTop + element.offsetHeight / 2;
+    eventSender.mouseMoveTo(centerX, centerY);
+}
+
+function dragFilesOntoElement(element, files) {
+    if (!window.eventSender)
+        return;
+
+    eventSender.beginDragWithFiles(files);
+    moveMouseToCenterOfElement(element);
+    eventSender.mouseUp();
+}
+
+//-------------------------------------------------------------------------------------------------------
+
 function doubleClick(x, y) {
     eventSender.mouseMoveTo(x, y);
     eventSender.mouseDown();
diff --git a/LayoutTests/editing/pasteboard/datatransfer-items-drop-directoryReader-error-expected.txt b/LayoutTests/editing/pasteboard/datatransfer-items-drop-directoryReader-error-expected.txt
new file mode 100644 (file)
index 0000000..e37d8c5
--- /dev/null
@@ -0,0 +1,11 @@
+Error case coverage for fileSystemDirectoryReader.readEntries()
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS dataTransfer.items.length is 1
+PASS ex.name is "InvalidStateError"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/editing/pasteboard/datatransfer-items-drop-directoryReader-error.html b/LayoutTests/editing/pasteboard/datatransfer-items-drop-directoryReader-error.html
new file mode 100644 (file)
index 0000000..cbdc5b6
--- /dev/null
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../resources/js-test.js"></script>
+<script src="../editing.js"></script>
+</head>
+<body>
+<div id="dropzone" style="width: 200px; height: 200px; background-color: grey;"></div>
+<script>
+description("Error case coverage for fileSystemDirectoryReader.readEntries()");
+jsTestIsAsync = true;
+
+var dropzone = document.getElementById('dropzone');
+dropzone.ondrop = function(e) {
+    e.preventDefault();
+    dataTransfer = e.dataTransfer;
+
+    shouldBe("dataTransfer.items.length", "1");
+
+    dirEntry = dataTransfer.items[0].webkitGetAsEntry();
+    let reader = dirEntry.createReader();
+    reader.readEntries(function() {});
+    reader.readEntries(function(entries) {
+        testFailed("Calling readEntries on a reader with the reading flag set did not throw an exception");
+        finishJSTest();
+    }, function(e) {
+        ex = e;
+        shouldBeEqualToString("ex.name", "InvalidStateError");
+        finishJSTest()
+    });
+};
+
+dropzone.ondragover = function(ev) {
+    ev.preventDefault();
+}
+
+onload = function() {
+    dragFilesOntoElement(dropzone, ['../../fast/forms/file/resources/testFiles']);
+}
+</script>
+</body>
+</html>
diff --git a/LayoutTests/editing/pasteboard/datatransfer-items-drop-directoryReader-expected.txt b/LayoutTests/editing/pasteboard/datatransfer-items-drop-directoryReader-expected.txt
new file mode 100644 (file)
index 0000000..39ea6aa
--- /dev/null
@@ -0,0 +1,41 @@
+Basic test coverage for fileSystemDirectoryReader.readEntries()
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS dataTransfer.items.length is 1
+PASS entry.isDirectory is true
+PASS entry.name is "testFiles"
+PASS entry.fullPath is "/testFiles"
+PASS childEntries.length is 4
+PASS childEntries[0].name is "file1.txt"
+PASS childEntries[0].fullPath is "/testFiles/file1.txt"
+PASS childEntries[0].isFile is true
+PASS childEntries[1].name is "file2.txt"
+PASS childEntries[1].fullPath is "/testFiles/file2.txt"
+PASS childEntries[1].isFile is true
+PASS childEntries[2].name is "subfolder1"
+PASS childEntries[2].fullPath is "/testFiles/subfolder1"
+PASS childEntries[2].isDirectory is true
+PASS childEntries[3].name is "subfolder2"
+PASS childEntries[3].fullPath is "/testFiles/subfolder2"
+PASS childEntries[3].isDirectory is true
+PASS grandChildEntries.length is 1
+PASS grandChildEntries[0].name is "file3.txt"
+PASS grandChildEntries[0].fullPath is "/testFiles/subfolder1/file3.txt"
+PASS grandChildEntries[0].isFile is true
+PASS grandChildEntries.length is 2
+PASS grandChildEntries[0].name is "file4.txt"
+PASS grandChildEntries[0].fullPath is "/testFiles/subfolder2/file4.txt"
+PASS grandChildEntries[0].isFile is true
+PASS grandChildEntries[1].name is "subfolder2a"
+PASS grandChildEntries[1].fullPath is "/testFiles/subfolder2/subfolder2a"
+PASS grandChildEntries[1].isDirectory is true
+PASS greatGrandChildEntries.length is 1
+PASS greatGrandChildEntries[0].name is "file5.txt"
+PASS greatGrandChildEntries[0].fullPath is "/testFiles/subfolder2/subfolder2a/file5.txt"
+PASS greatGrandChildEntries[0].isFile is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/editing/pasteboard/datatransfer-items-drop-directoryReader-root-expected.txt b/LayoutTests/editing/pasteboard/datatransfer-items-drop-directoryReader-root-expected.txt
new file mode 100644 (file)
index 0000000..d13005f
--- /dev/null
@@ -0,0 +1,22 @@
+Basic test coverage for fileSystemDirectoryReader.readEntries()
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS dataTransfer.items.length is 2
+PASS entry1.isFile is true
+PASS entry1.name is "test.txt"
+PASS entry1.fullPath is "/test.txt"
+PASS entryFound.isFile is true
+PASS entryFound.name is "test.txt"
+PASS entryFound.fullPath is "/test.txt"
+PASS entry2.isDirectory is true
+PASS entry2.name is "testFiles"
+PASS entry2.fullPath is "/testFiles"
+PASS entryFound.isDirectory is true
+PASS entryFound.name is "testFiles"
+PASS entryFound.fullPath is "/testFiles"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/editing/pasteboard/datatransfer-items-drop-directoryReader-root.html b/LayoutTests/editing/pasteboard/datatransfer-items-drop-directoryReader-root.html
new file mode 100644 (file)
index 0000000..d36ad83
--- /dev/null
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../resources/js-test.js"></script>
+<script src="../editing.js"></script>
+</head>
+<body>
+<div id="dropzone" style="width: 200px; height: 200px; background-color: grey;"></div>
+<script>
+description("Basic test coverage for fileSystemDirectoryReader.readEntries()");
+jsTestIsAsync = true;
+
+function getEntriesAsPromise(dirEntry) {
+    return new Promise((resolve, reject) => {
+        let result = [];
+        let reader = dirEntry.createReader();
+        let doBatch = () => {
+            reader.readEntries(entries => {
+            if (entries.length > 0) {
+                entries.forEach(e => result.push(e));
+                doBatch();
+            } else {
+                resolve(result);
+            }
+        }, reject);
+    };
+    doBatch();
+  });
+}
+
+function findEntryByName(entries, name)
+{
+    for (let childEntry of entries) {
+        if (childEntry.name == name)
+            return childEntry
+    }
+    return null;
+}
+
+var dropzone = document.getElementById('dropzone');
+dropzone.ondrop = function(e) {
+    e.preventDefault();
+    dataTransfer = e.dataTransfer;
+
+    shouldBe("dataTransfer.items.length", "2");
+
+    entry1 = dataTransfer.items[0].webkitGetAsEntry();
+    entry2 = dataTransfer.items[1].webkitGetAsEntry();
+
+    shouldBeTrue("entry1.isFile");
+    shouldBeEqualToString("entry1.name", "test.txt");
+    shouldBeEqualToString("entry1.fullPath", "/test.txt");
+    getEntriesAsPromise(entry1.filesystem.root).then(function(entries) {
+        entryFound = findEntryByName(entries, "test.txt");
+        shouldBeTrue("entryFound.isFile");
+        shouldBeEqualToString("entryFound.name", "test.txt");
+        shouldBeEqualToString("entryFound.fullPath", "/test.txt");
+
+        shouldBeTrue("entry2.isDirectory");
+        shouldBeEqualToString("entry2.name", "testFiles");
+        shouldBeEqualToString("entry2.fullPath", "/testFiles");
+        getEntriesAsPromise(entry2.filesystem.root).then(function(entries) {
+            entryFound = findEntryByName(entries, "testFiles");
+            shouldBeTrue("entryFound.isDirectory");
+            shouldBeEqualToString("entryFound.name", "testFiles");
+            shouldBeEqualToString("entryFound.fullPath", "/testFiles");
+
+            finishJSTest();
+        }, function(e) {
+            testFailed("readEntries failed: " + e);
+            finishJSTest();
+        });
+    }, function(e) {
+        testFailed("readEntries failed: " + e);
+        finishJSTest();
+    });
+};
+
+dropzone.ondragover = function(ev) {
+    ev.preventDefault();
+}
+
+onload = function() {
+    dragFilesOntoElement(dropzone, ['../../fast/forms/resources/test.txt', '../../fast/forms/file/resources/testFiles']);
+}
+</script>
+</body>
+</html>
diff --git a/LayoutTests/editing/pasteboard/datatransfer-items-drop-directoryReader.html b/LayoutTests/editing/pasteboard/datatransfer-items-drop-directoryReader.html
new file mode 100644 (file)
index 0000000..26e8bc9
--- /dev/null
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../resources/js-test.js"></script>
+<script src="../editing.js"></script>
+</head>
+<body>
+<div id="dropzone" style="width: 200px; height: 200px; background-color: grey;"></div>
+<script>
+description("Basic test coverage for fileSystemDirectoryReader.readEntries()");
+jsTestIsAsync = true;
+
+function getEntriesAsPromise(dirEntry) {
+    return new Promise((resolve, reject) => {
+        let result = [];
+        let reader = dirEntry.createReader();
+        let doBatch = () => {
+            reader.readEntries(entries => {
+            if (entries.length > 0) {
+                entries.forEach(e => result.push(e));
+                doBatch();
+            } else {
+                resolve(result);
+            }
+        }, reject);
+    };
+    doBatch();
+  });
+}
+
+var dropzone = document.getElementById('dropzone');
+dropzone.ondrop = function(e) {
+    e.preventDefault();
+    dataTransfer = e.dataTransfer;
+
+    shouldBe("dataTransfer.items.length", "1");
+
+    entry = dataTransfer.items[0].webkitGetAsEntry();
+    shouldBeTrue("entry.isDirectory");
+    shouldBeEqualToString("entry.name", "testFiles");
+    shouldBeEqualToString("entry.fullPath", "/testFiles");
+
+    getEntriesAsPromise(entry).then(function(entries) {
+        childEntries = entries;
+        shouldBe("childEntries.length", "4");
+        shouldBeEqualToString("childEntries[0].name", "file1.txt");
+        shouldBeEqualToString("childEntries[0].fullPath", "/testFiles/file1.txt");
+        shouldBeTrue("childEntries[0].isFile");
+
+        shouldBeEqualToString("childEntries[1].name", "file2.txt");
+        shouldBeEqualToString("childEntries[1].fullPath", "/testFiles/file2.txt");
+        shouldBeTrue("childEntries[1].isFile");
+
+        shouldBeEqualToString("childEntries[2].name", "subfolder1");
+        shouldBeEqualToString("childEntries[2].fullPath", "/testFiles/subfolder1");
+        shouldBeTrue("childEntries[2].isDirectory");
+
+        shouldBeEqualToString("childEntries[3].name", "subfolder2");
+        shouldBeEqualToString("childEntries[3].fullPath", "/testFiles/subfolder2");
+        shouldBeTrue("childEntries[3].isDirectory");
+
+        getEntriesAsPromise(childEntries[2]).then(function(entries) {
+            grandChildEntries = entries;
+            shouldBe("grandChildEntries.length", "1");
+            shouldBeEqualToString("grandChildEntries[0].name", "file3.txt");
+            shouldBeEqualToString("grandChildEntries[0].fullPath", "/testFiles/subfolder1/file3.txt");
+            shouldBeTrue("grandChildEntries[0].isFile");
+
+            getEntriesAsPromise(childEntries[3]).then(function(entries) {
+                grandChildEntries = entries;
+                shouldBe("grandChildEntries.length", "2");
+                shouldBeEqualToString("grandChildEntries[0].name", "file4.txt");
+                shouldBeEqualToString("grandChildEntries[0].fullPath", "/testFiles/subfolder2/file4.txt");
+                shouldBeTrue("grandChildEntries[0].isFile");
+
+                shouldBeEqualToString("grandChildEntries[1].name", "subfolder2a");
+                shouldBeEqualToString("grandChildEntries[1].fullPath", "/testFiles/subfolder2/subfolder2a");
+                shouldBeTrue("grandChildEntries[1].isDirectory");
+
+                getEntriesAsPromise(grandChildEntries[1]).then(function(entries) {
+                    greatGrandChildEntries = entries;
+                    shouldBe("greatGrandChildEntries.length", "1");
+                    shouldBeEqualToString("greatGrandChildEntries[0].name", "file5.txt");
+                    shouldBeEqualToString("greatGrandChildEntries[0].fullPath", "/testFiles/subfolder2/subfolder2a/file5.txt");
+                    shouldBeTrue("greatGrandChildEntries[0].isFile");
+
+                    finishJSTest();
+                }, function(e) {
+                    testFailed("readEntries failed: " + e);
+                    finishJSTest();
+                });
+            }, function(e) {
+                testFailed("readEntries failed: " + e);
+                finishJSTest();
+            });
+        }, function(e) {
+            testFailed("readEntries failed: " + e);
+            finishJSTest();
+        });
+    }, function(e) {
+        testFailed("readEntries failed: " + e);
+        finishJSTest();
+    });
+};
+
+dropzone.ondragover = function(ev) {
+    ev.preventDefault();
+}
+
+onload = function() {
+    dragFilesOntoElement(dropzone, ['../../fast/forms/file/resources/testFiles']);
+}
+</script>
+</body>
+</html>
index c0b8356..4024892 100644 (file)
@@ -2,6 +2,7 @@
 <html>
 <head>
 <script src="../../resources/js-test.js"></script>
+<script src="../editing.js"></script>
 </head>
 <body>
 <div id="dropzone" style="width: 200px; height: 200px; background-color: grey;"></div>
@@ -9,21 +10,6 @@
 description("Basic test coverage for dataTransferItem.webkitGetAsEntry()");
 jsTestIsAsync = true;
 
-function moveMouseToCenterOfElement(element) {
-    var centerX = element.offsetLeft + element.offsetWidth / 2;
-    var centerY = element.offsetTop + element.offsetHeight / 2;
-    eventSender.mouseMoveTo(centerX, centerY);
-}
-
-function dragFilesOntoElement(element, files) {
-    if (!window.eventSender)
-        return;
-
-    eventSender.beginDragWithFiles(files);
-    moveMouseToCenterOfElement(element);
-    eventSender.mouseUp();
-}
-
 var dropzone = document.getElementById('dropzone');
 dropzone.ondrop = function(e) {
     e.preventDefault();
index f84fa81..8a2a2a1 100644 (file)
@@ -563,6 +563,9 @@ platform/mac/fast/events/objc-event-api.html
 
 # WebKitTestRunner needs an implementation of eventSender.beginDragWithFiles
 # https://bugs.webkit.org/show_bug.cgi?id=64285
+editing/pasteboard/datatransfer-items-drop-directoryReader.html
+editing/pasteboard/datatransfer-items-drop-directoryReader-error.html
+editing/pasteboard/datatransfer-items-drop-directoryReader-root.html
 editing/pasteboard/datatransfer-items-drop-getAsEntry.html
 editing/pasteboard/datatransfer-items-drop-plaintext-file.html
 editing/pasteboard/file-drag-to-editable.html [ Skip ]
index 49504d2..89f1652 100644 (file)
@@ -1,3 +1,18 @@
+2017-08-30  Chris Dumez  <cdumez@apple.com>
+
+        Implement FileSystemDirectoryReader.readEntries()
+        https://bugs.webkit.org/show_bug.cgi?id=176091
+        <rdar://problem/34168015>
+
+        Reviewed by Andreas Kling.
+
+        * wtf/CrossThreadCopier.h:
+        (WTF::crossThreadCopy):
+        * wtf/CrossThreadTask.h:
+        Move crossThreadCopy() from CrossThreadTask.h to CrossThreadCopier.h and
+        add "using WTF::crossThreadCopy" statement to make it more easily usable
+        from WebCore.
+
 2017-08-30  Matt Lewis  <jlewis3@apple.com>
 
         Unreviewed, rolling out r221384.
index 83f7b4b..aaeb554 100644 (file)
@@ -139,9 +139,15 @@ template<typename T> struct CrossThreadCopierBase<false, false, HashSet<T> > {
         return destination;
     }
 };
+
+template<typename T> T crossThreadCopy(const T& source)
+{
+    return CrossThreadCopier<T>::copy(source);
+}
     
 } // namespace WTF
 
 using WTF::CrossThreadCopierBaseHelper;
 using WTF::CrossThreadCopierBase;
 using WTF::CrossThreadCopier;
+using WTF::crossThreadCopy;
index 0a66ec1..57b7179 100644 (file)
@@ -50,12 +50,6 @@ protected:
     Function<void ()> m_taskFunction;
 };
 
-template <typename T>
-T crossThreadCopy(const T& t)
-{
-    return CrossThreadCopier<T>::copy(t);
-}
-
 template <typename F, typename ArgsTuple, size_t... ArgsIndex>
 void callFunctionForCrossThreadTaskImpl(F function, ArgsTuple&& args, std::index_sequence<ArgsIndex...>)
 {
index 44f3ac3..63542d7 100644 (file)
@@ -1,3 +1,102 @@
+2017-08-30  Chris Dumez  <cdumez@apple.com>
+
+        Implement FileSystemDirectoryReader.readEntries()
+        https://bugs.webkit.org/show_bug.cgi?id=176091
+        <rdar://problem/34168015>
+
+        Reviewed by Andreas Kling.
+
+        Tests: editing/pasteboard/datatransfer-items-drop-directoryReader-error.html
+               editing/pasteboard/datatransfer-items-drop-directoryReader-root.html
+               editing/pasteboard/datatransfer-items-drop-directoryReader.html
+
+        * Modules/entriesapi/DOMFileSystem.cpp:
+        (WebCore::ListedChild::isolatedCopy const):
+        (WebCore::listDirectoryWithMetadata):
+        (WebCore::toFileSystemEntries):
+        (WebCore::DOMFileSystem::DOMFileSystem):
+        (WebCore::DOMFileSystem::root):
+        (WebCore::DOMFileSystem::fileAsEntry):
+        (WebCore::DOMFileSystem::evaluatePath):
+        (WebCore::DOMFileSystem::listDirectory):
+        * Modules/entriesapi/DOMFileSystem.h:
+        (WebCore::DOMFileSystem::createEntryForFile):
+        * Modules/entriesapi/DOMFileSystem.idl:
+        - Implement directory listing operation for supporting
+          FileSystemDirectoryReader::readEntries().
+        - Implement evaluatePath() operation as per:
+          - https://wicg.github.io/entries-api/#evaluate-a-path
+        - DOMFileSystem should no longer hold a strong reference to
+          the root entry and FileSystemEntry now holds a strong
+          reference to the DOMFileSystem and this would create a
+          cycle.
+
+        * Modules/entriesapi/FileSystemDirectoryEntry.cpp:
+        (WebCore::FileSystemDirectoryEntry::createReader):
+        * Modules/entriesapi/FileSystemDirectoryEntry.h:
+        * Modules/entriesapi/FileSystemDirectoryEntry.idl:
+        Have createReader() take a ScriptExecutionContext, which is needed
+        to construct a FileSystemDirectoryReader, now that FileSystemDirectoryReader
+        is an ActiveDOMObject.
+
+        * Modules/entriesapi/FileSystemDirectoryReader.cpp:
+        (WebCore::FileSystemDirectoryReader::FileSystemDirectoryReader):
+        (WebCore::FileSystemDirectoryReader::activeDOMObjectName const):
+        (WebCore::FileSystemDirectoryReader::canSuspendForDocumentSuspension const):
+        (WebCore::FileSystemDirectoryReader::readEntries):
+        * Modules/entriesapi/FileSystemDirectoryReader.h:
+        * Modules/entriesapi/FileSystemDirectoryReader.idl:
+        Provide implementation for FileSystemDirectoryReader.readEntries() as per:
+        - https://wicg.github.io/entries-api/#dom-filesystemdirectoryreader-readentries
+        For the actual directory listing operation, we ask the DOMFileSystem, which is
+        where all filesystem operations should live.
+        Also made the FileSystemDirectoryReader an ActiveDOMObject to keep it and its
+        wrapper alive while a file system operation is pending.
+
+        * Modules/entriesapi/FileSystemEntry.cpp:
+        (WebCore::FileSystemEntry::~FileSystemEntry):
+        (WebCore::FileSystemEntry::filesystem const):
+        * Modules/entriesapi/FileSystemEntry.h:
+        Make FileSystemEntry keep a strong reference to its DOMFileSystem object.
+        Previously, the DOMFileSystem was kept alive by the DataTransferItem but
+        this was unsafe because FileSystemEntry may outlive the DataTransferItem.
+
+        * dom/ActiveDOMObject.h:
+        (WebCore::ActiveDOMObject::PendingActivity::PendingActivity):
+        (WebCore::ActiveDOMObject::PendingActivity::~PendingActivity):
+        (WebCore::ActiveDOMObject::makePendingActivity):
+        Add PendingActivity / makePendingActivity() as a less error-prone
+        replacement for setPendingActivity() / unsetPendingActivity().
+
+        * dom/DOMException.cpp:
+        (WebCore::DOMException::create):
+        * dom/DOMException.h:
+        Add factory to construct a DOMException from an Exception.
+
+        * dom/DataTransferItem.cpp:
+        (WebCore::DataTransferItem::getAsEntry const):
+        * dom/DataTransferItem.h:
+
+        * dom/Exception.h:
+        (WebCore::Exception::isolatedCopy const):
+        * dom/ExceptionOr.h:
+        Make ExceptionOr<> / Exception work with CrossThreadCopier for convenience.
+
+        * html/FileListCreator.cpp:
+        (WebCore::FileListCreator::FileListCreator):
+        Use crossThreadCopy() instead of longer form.
+
+        * platform/FileSystem.h:
+        * platform/glib/FileSystemGlib.cpp:
+        (WebCore::pathByAppendingComponents):
+        * platform/posix/FileSystemPOSIX.cpp:
+        (WebCore::pathByAppendingComponents):
+        * platform/win/FileSystemWin.cpp:
+        (WebCore::pathByAppendingComponents):
+        Add pathByAppendingComponents() utility function, which is similar to
+        pathByAppendingComponent() but supports appending multiple components
+        in an efficient fashion.
+
 2017-08-30  Antti Koivisto  <antti@apple.com>
 
         Factor RenderMultiColumnFlowThread construction and destruction into RenderTreeUpdater helper
index a839a52..9e9ec5d 100644 (file)
 #include "config.h"
 #include "DOMFileSystem.h"
 
+#include "File.h"
+#include "FileMetadata.h"
+#include "FileSystem.h"
 #include "FileSystemDirectoryEntry.h"
+#include "FileSystemFileEntry.h"
+#include <wtf/CrossThreadCopier.h>
+#include <wtf/UUID.h>
 
 namespace WebCore {
 
-DOMFileSystem::DOMFileSystem(const String& name)
-    : m_name(name)
-    , m_root(FileSystemDirectoryEntry::create(*this, ASCIILiteral("/")))
+struct ListedChild {
+    String filename;
+    FileMetadata::Type type;
+
+    ListedChild isolatedCopy() const { return { filename.isolatedCopy(), type }; }
+};
+
+static ExceptionOr<Vector<ListedChild>> listDirectoryWithMetadata(const String& fullPath)
 {
+    ASSERT(!isMainThread());
+    if (!fileIsDirectory(fullPath))
+        return Exception { NotFoundError, "Path no longer exists or is no longer a directory" };
+
+    auto childPaths = listDirectory(fullPath, "*");
+    Vector<ListedChild> listedChildren;
+    listedChildren.reserveInitialCapacity(childPaths.size());
+    for (auto& childPath : childPaths) {
+        FileMetadata metadata;
+        if (!getFileMetadata(childPath, metadata))
+            continue;
+        listedChildren.uncheckedAppend(ListedChild { pathGetFileName(childPath), metadata.type });
+    }
+    return WTFMove(listedChildren);
+}
+
+static ExceptionOr<Vector<Ref<FileSystemEntry>>> toFileSystemEntries(DOMFileSystem& fileSystem, ExceptionOr<Vector<ListedChild>>&& listedChildren, const String& parentVirtualPath)
+{
+    ASSERT(isMainThread());
+    if (listedChildren.hasException())
+        return listedChildren.releaseException();
+
+    Vector<Ref<FileSystemEntry>> entries;
+    entries.reserveInitialCapacity(listedChildren.returnValue().size());
+    for (auto& child : listedChildren.returnValue()) {
+        String virtualPath = parentVirtualPath + "/" + child.filename;
+        switch (child.type) {
+        case FileMetadata::TypeFile:
+            entries.uncheckedAppend(FileSystemFileEntry::create(fileSystem, virtualPath));
+            break;
+        case FileMetadata::TypeDirectory:
+            entries.uncheckedAppend(FileSystemDirectoryEntry::create(fileSystem, virtualPath));
+            break;
+        default:
+            break;
+        }
+    }
+    return WTFMove(entries);
+}
+
+DOMFileSystem::DOMFileSystem(Ref<File>&& file)
+    : m_name(createCanonicalUUIDString())
+    , m_file(WTFMove(file))
+    , m_rootPath(directoryName(m_file->path()))
+    , m_workQueue(WorkQueue::create("DOMFileSystem work queue"))
+{
+    ASSERT(!m_rootPath.endsWith('/'));
 }
 
 DOMFileSystem::~DOMFileSystem()
 {
 }
 
+Ref<FileSystemDirectoryEntry> DOMFileSystem::root()
+{
+    return FileSystemDirectoryEntry::create(*this, ASCIILiteral("/"));
+}
+
+Ref<FileSystemEntry> DOMFileSystem::fileAsEntry()
+{
+    if (m_file->isDirectory())
+        return FileSystemDirectoryEntry::create(*this, "/" + m_file->name());
+    return FileSystemFileEntry::create(*this, "/" + m_file->name());
+}
+
+// https://wicg.github.io/entries-api/#evaluate-a-path
+String DOMFileSystem::evaluatePath(const String& virtualPath)
+{
+    ASSERT(virtualPath[0] == '/');
+    auto components = virtualPath.split('/');
+
+    Vector<String> resolvedComponents;
+    resolvedComponents.reserveInitialCapacity(components.size());
+    for (auto& component : components) {
+        if (component == ".")
+            continue;
+        if (component == "..") {
+            if (!resolvedComponents.isEmpty())
+                resolvedComponents.removeLast();
+            continue;
+        }
+        resolvedComponents.uncheckedAppend(component);
+    }
+
+    return pathByAppendingComponents(m_rootPath, resolvedComponents);
+}
+
+void DOMFileSystem::listDirectory(FileSystemDirectoryEntry& directory, DirectoryListingCallback&& completionHandler)
+{
+    ASSERT(&directory.filesystem() == this);
+
+    String directoryVirtualPath = directory.virtualPath();
+    auto fullPath = evaluatePath(directoryVirtualPath);
+    if (fullPath == m_rootPath) {
+        Vector<Ref<FileSystemEntry>> children;
+        children.append(fileAsEntry());
+        completionHandler(WTFMove(children));
+        return;
+    }
+
+    m_workQueue->dispatch([this, completionHandler = WTFMove(completionHandler), fullPath = fullPath.isolatedCopy(), directoryVirtualPath = directoryVirtualPath.isolatedCopy()]() mutable {
+        auto listedChildren = listDirectoryWithMetadata(fullPath);
+        callOnMainThread([this, completionHandler = WTFMove(completionHandler), listedChildren = crossThreadCopy(listedChildren), directoryVirtualPath = directoryVirtualPath.isolatedCopy()]() mutable {
+            completionHandler(toFileSystemEntries(*this, WTFMove(listedChildren), directoryVirtualPath));
+        });
+    });
+}
+
 } // namespace WebCore
index f368c0d..6d3c5d3 100644 (file)
 
 #pragma once
 
+#include "ExceptionOr.h"
 #include "ScriptWrappable.h"
 #include <wtf/RefCounted.h>
+#include <wtf/WorkQueue.h>
 #include <wtf/text/WTFString.h>
 
 namespace WebCore {
 
+class File;
 class FileSystemDirectoryEntry;
 class FileSystemEntry;
 
 class DOMFileSystem : public ScriptWrappable, public RefCounted<DOMFileSystem> {
 public:
-    static Ref<DOMFileSystem> create(const String& name)
+    static Ref<FileSystemEntry> createEntryForFile(Ref<File>&& file)
     {
-        return adoptRef(*new DOMFileSystem(name));
+        auto fileSystem = adoptRef(*new DOMFileSystem(WTFMove(file)));
+        return fileSystem->fileAsEntry();
     }
 
     ~DOMFileSystem();
 
     const String& name() const { return m_name; }
-    FileSystemDirectoryEntry& root() const { return m_root; }
+    Ref<FileSystemDirectoryEntry> root();
+
+    using DirectoryListingCallback = WTF::Function<void(ExceptionOr<Vector<Ref<FileSystemEntry>>>&&)>;
+    void listDirectory(FileSystemDirectoryEntry&, DirectoryListingCallback&&);
 
 private:
-    explicit DOMFileSystem(const String& name);
+    explicit DOMFileSystem(Ref<File>&&);
+
+    String evaluatePath(const String& virtualPath);
+    Ref<FileSystemEntry> fileAsEntry();
 
     String m_name;
-    Ref<FileSystemDirectoryEntry> m_root;
+    Ref<File> m_file;
+    String m_rootPath;
+    Ref<WorkQueue> m_workQueue;
+
 };
 
 } // namespace WebCore
index 8cba302..b25ec65 100644 (file)
@@ -29,5 +29,5 @@
     InterfaceName=FileSystem,
 ] interface DOMFileSystem {
     readonly attribute USVString name;
-    readonly attribute FileSystemDirectoryEntry root;
+    [CachedAttribute] readonly attribute FileSystemDirectoryEntry root;
 };
index d70d2d0..c915b00 100644 (file)
@@ -37,9 +37,9 @@ FileSystemDirectoryEntry::FileSystemDirectoryEntry(DOMFileSystem& filesystem, co
 {
 }
 
-Ref<FileSystemDirectoryReader> FileSystemDirectoryEntry::createReader()
+Ref<FileSystemDirectoryReader> FileSystemDirectoryEntry::createReader(ScriptExecutionContext& context)
 {
-    return FileSystemDirectoryReader::create(*this);
+    return FileSystemDirectoryReader::create(context, *this);
 }
 
 void FileSystemDirectoryEntry::getFile(ScriptExecutionContext& context, const String&, const Flags&, RefPtr<FileSystemEntryCallback>&&, RefPtr<ErrorCallback>&& errorCallback)
index f474c69..8db6f93 100644 (file)
@@ -41,7 +41,7 @@ public:
         return adoptRef(*new FileSystemDirectoryEntry(filesystem, virtualPath));
     }
 
-    Ref<FileSystemDirectoryReader> createReader();
+    Ref<FileSystemDirectoryReader> createReader(ScriptExecutionContext&);
 
     struct Flags {
         bool create { false };
index 28bdfc7..40f5c5d 100644 (file)
@@ -26,7 +26,7 @@
 [
     EnabledAtRuntime=DirectoryUpload,
 ] interface FileSystemDirectoryEntry : FileSystemEntry {
-    FileSystemDirectoryReader createReader();
+    [CallWith=ScriptExecutionContext] FileSystemDirectoryReader createReader();
 
     [CallWith=ScriptExecutionContext] void getFile(optional USVString? path,
         optional FileSystemFlags options,
index ed9ee63..07d8168 100644 (file)
 #include "FileSystemDirectoryReader.h"
 
 #include "DOMException.h"
+#include "DOMFileSystem.h"
 #include "ErrorCallback.h"
 #include "FileSystemDirectoryEntry.h"
 #include "FileSystemEntriesCallback.h"
+#include <wtf/MainThread.h>
 
 namespace WebCore {
 
-FileSystemDirectoryReader::FileSystemDirectoryReader(FileSystemDirectoryEntry& directory)
-    : m_directory(directory)
+FileSystemDirectoryReader::FileSystemDirectoryReader(ScriptExecutionContext& context, FileSystemDirectoryEntry& directory)
+    : ActiveDOMObject(&context)
+    , m_directory(directory)
 {
+    suspendIfNeeded();
 }
 
 FileSystemDirectoryReader::~FileSystemDirectoryReader()
 {
 }
 
-void FileSystemDirectoryReader::readEntries(ScriptExecutionContext& context, Ref<FileSystemEntriesCallback>&&, RefPtr<ErrorCallback>&& errorCallback)
+const char* FileSystemDirectoryReader::activeDOMObjectName() const
 {
-    if (errorCallback)
-        errorCallback->scheduleCallback(context, DOMException::create(NotSupportedError));
+    return "FileSystemDirectoryReader";
+}
+
+bool FileSystemDirectoryReader::canSuspendForDocumentSuspension() const
+{
+    return !hasPendingActivity();
+}
+
+// https://wicg.github.io/entries-api/#dom-filesystemdirectoryentry-readentries
+void FileSystemDirectoryReader::readEntries(ScriptExecutionContext& context, Ref<FileSystemEntriesCallback>&& successCallback, RefPtr<ErrorCallback>&& errorCallback)
+{
+    if (m_isReading) {
+        if (errorCallback)
+            errorCallback->scheduleCallback(context, DOMException::create(Exception { InvalidStateError, ASCIILiteral("Directory reader is already reading") }));
+        return;
+    }
+
+    if (m_error) {
+        if (errorCallback)
+            errorCallback->scheduleCallback(context, DOMException::create(*m_error));
+        return;
+    }
+
+    if (m_isDone) {
+        successCallback->scheduleCallback(context, { });
+        return;
+    }
+
+    m_isReading = true;
+    auto pendingActivity = makePendingActivity(*this);
+    callOnMainThread([this, successCallback = WTFMove(successCallback), errorCallback = WTFMove(errorCallback), pendingActivity = WTFMove(pendingActivity)]() mutable {
+        m_isReading = false;
+        m_directory->filesystem().listDirectory(m_directory, [this, successCallback = WTFMove(successCallback), errorCallback = WTFMove(errorCallback), pendingActivity = WTFMove(pendingActivity)](ExceptionOr<Vector<Ref<FileSystemEntry>>>&& result) {
+            if (result.hasException()) {
+                m_error = result.releaseException();
+                if (errorCallback)
+                    errorCallback->handleEvent(DOMException::create(*m_error));
+                return;
+            }
+            m_isDone = true;
+            successCallback->handleEvent(result.releaseReturnValue());
+        });
+    });
 }
 
 } // namespace WebCore
index 8a09944..4fb6479 100644 (file)
@@ -25,6 +25,8 @@
 
 #pragma once
 
+#include "ActiveDOMObject.h"
+#include "Exception.h"
 #include "ScriptWrappable.h"
 #include <wtf/RefCounted.h>
 #include <wtf/text/WTFString.h>
@@ -36,11 +38,11 @@ class FileSystemDirectoryEntry;
 class FileSystemEntriesCallback;
 class ScriptExecutionContext;
 
-class FileSystemDirectoryReader : public ScriptWrappable, public RefCounted<FileSystemDirectoryReader> {
+class FileSystemDirectoryReader final : public ScriptWrappable, public ActiveDOMObject, public RefCounted<FileSystemDirectoryReader> {
 public:
-    static Ref<FileSystemDirectoryReader> create(FileSystemDirectoryEntry& directory)
+    static Ref<FileSystemDirectoryReader> create(ScriptExecutionContext& context, FileSystemDirectoryEntry& directory)
     {
-        return adoptRef(*new FileSystemDirectoryReader(directory));
+        return adoptRef(*new FileSystemDirectoryReader(context, directory));
     }
 
     ~FileSystemDirectoryReader();
@@ -48,9 +50,15 @@ public:
     void readEntries(ScriptExecutionContext&, Ref<FileSystemEntriesCallback>&&, RefPtr<ErrorCallback>&&);
 
 private:
-    explicit FileSystemDirectoryReader(FileSystemDirectoryEntry&);
+    FileSystemDirectoryReader(ScriptExecutionContext&, FileSystemDirectoryEntry&);
+
+    const char* activeDOMObjectName() const final;
+    bool canSuspendForDocumentSuspension() const final;
 
     Ref<FileSystemDirectoryEntry> m_directory;
+    std::optional<Exception> m_error;
+    bool m_isReading { false };
+    bool m_isDone { false };
 };
 
 }
index dd0cb04..65d2c61 100644 (file)
@@ -25,7 +25,6 @@
 
 [
     EnabledAtRuntime=DirectoryUpload,
-    ImplementationLacksVTable,
 ] interface FileSystemDirectoryReader {
     [CallWith=ScriptExecutionContext] void readEntries(FileSystemEntriesCallback successCallback, optional ErrorCallback? errorCallback);
 };
index b04963c..6aae82c 100644 (file)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "FileSystemEntry.h"
 
+#include "DOMFileSystem.h"
 #include "FileSystem.h"
 
 namespace WebCore {
@@ -37,4 +38,14 @@ FileSystemEntry::FileSystemEntry(DOMFileSystem& filesystem, const String& virtua
 {
 }
 
+FileSystemEntry::~FileSystemEntry()
+{
+}
+
+DOMFileSystem& FileSystemEntry::filesystem() const
+{
+    return m_filesystem.get();
+}
+
+
 } // namespace WebCore
index 031dc27..a74d87e 100644 (file)
@@ -35,20 +35,20 @@ class DOMFileSystem;
 
 class FileSystemEntry : public ScriptWrappable, public RefCounted<FileSystemEntry> {
 public:
-    virtual ~FileSystemEntry() { }
+    virtual ~FileSystemEntry();
 
     virtual bool isFile() const { return false; }
     virtual bool isDirectory() const { return false; }
 
     const String& name() const { return m_name; }
     const String& virtualPath() const { return m_virtualPath; }
-    DOMFileSystem& filesystem() const { return m_filesystem; }
+    DOMFileSystem& filesystem() const;
 
 protected:
     FileSystemEntry(DOMFileSystem&, const String& virtualPath);
 
 private:
-    DOMFileSystem& m_filesystem;
+    Ref<DOMFileSystem> m_filesystem;
     String m_name;
     String m_virtualPath;
 };
index c47b1f4..e27053f 100644 (file)
@@ -29,6 +29,7 @@
 #include "ContextDestructionObserver.h"
 #include <wtf/Assertions.h>
 #include <wtf/Forward.h>
+#include <wtf/RefCounted.h>
 
 namespace WebCore {
 
@@ -83,6 +84,31 @@ public:
         thisObject->deref();
     }
 
+    template<class T>
+    class PendingActivity : public RefCounted<PendingActivity<T>> {
+    public:
+        explicit PendingActivity(T& thisObject)
+            : m_thisObject(thisObject)
+        {
+            ++(m_thisObject->m_pendingActivityCount);
+        }
+
+        ~PendingActivity()
+        {
+            ASSERT(m_thisObject->m_pendingActivityCount > 0);
+            --(m_thisObject->m_pendingActivityCount);
+        }
+
+    private:
+        Ref<T> m_thisObject;
+    };
+
+    template<class T> Ref<PendingActivity<T>> makePendingActivity(T& thisObject)
+    {
+        ASSERT(&thisObject == this);
+        return adoptRef(*new PendingActivity<T>(thisObject));
+    }
+
 protected:
     explicit ActiveDOMObject(ScriptExecutionContext*);
     virtual ~ActiveDOMObject();
index e28b0d1..c98ffc1 100644 (file)
@@ -29,6 +29,8 @@
 #include "config.h"
 #include "DOMException.h"
 
+#include "Exception.h"
+
 namespace WebCore {
 
 // This array needs to be kept in sync with the ExceptionCode enumeration.
@@ -99,6 +101,12 @@ Ref<DOMException> DOMException::create(const String& message, const String& name
     return adoptRef(*new DOMException(legacyCodeFromName(name), name, message));
 }
 
+Ref<DOMException> DOMException::create(const Exception& exception)
+{
+    auto& description = DOMException::description(exception.code());
+    return adoptRef(*new DOMException(description.legacyCode, description.name, exception.message().isEmpty() ? description.message : exception.message()));
+}
+
 DOMException::DOMException(LegacyCode legacyCode, const String& name, const String& message)
     : m_legacyCode(legacyCode)
     , m_name(name)
index c5332b9..dabe930 100644 (file)
 
 namespace WebCore {
 
+class Exception;
+
 class DOMException : public RefCounted<DOMException> {
 public:
     static Ref<DOMException> create(ExceptionCode, const String* message = nullptr);
+    static Ref<DOMException> create(const Exception&);
 
     // For DOM bindings.
     static Ref<DOMException> create(const String& message, const String& name);
index 6b5d7a5..e32282b 100644 (file)
@@ -40,7 +40,6 @@
 #include "FileSystemFileEntry.h"
 #include "ScriptExecutionContext.h"
 #include "StringCallback.h"
-#include <wtf/UUID.h>
 
 namespace WebCore {
 
@@ -112,13 +111,7 @@ RefPtr<FileSystemEntry> DataTransferItem::getAsEntry() const
     if (!file)
         return nullptr;
 
-    if (!m_fileSystem)
-        m_fileSystem = DOMFileSystem::create(createCanonicalUUIDString());
-
-    String virtualPath = "/" + file->name();
-    if (file->isDirectory())
-        return FileSystemDirectoryEntry::create(*m_fileSystem, virtualPath);
-    return FileSystemFileEntry::create(*m_fileSystem, virtualPath);
+    return DOMFileSystem::createEntryForFile(*file);
 }
 
 } // namespace WebCore
index bea1cf1..7133043 100644 (file)
@@ -72,7 +72,6 @@ private:
     WeakPtr<DataTransferItemList> m_list;
     const String m_type;
     RefPtr<File> m_file;
-    mutable RefPtr<DOMFileSystem> m_fileSystem;
 };
 
 }
index 5f8a8d2..097b917 100644 (file)
@@ -39,6 +39,11 @@ public:
     const String& message() const { return m_message; }
     String&& releaseMessage() { return WTFMove(m_message); }
 
+    Exception isolatedCopy() const
+    {
+        return Exception { m_code, m_message.isolatedCopy() };
+    }
+
 private:
     ExceptionCode m_code;
     String m_message;
index 979d4a7..ff95522 100644 (file)
@@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #pragma once
 
 #include "Exception.h"
+#include <wtf/CrossThreadCopier.h>
 #include <wtf/Expected.h>
 
 namespace WebCore {
@@ -180,3 +181,15 @@ inline ExceptionOr<void> isolatedCopy(ExceptionOr<void>&& value)
 }
 
 }
+
+namespace WTF {
+template<typename T> struct CrossThreadCopierBase<false, false, WebCore::ExceptionOr<T> > {
+    typedef WebCore::ExceptionOr<T> Type;
+    static Type copy(const Type& source)
+    {
+        if (source.hasException())
+            return crossThreadCopy(source.exception());
+        return crossThreadCopy(source.returnValue());
+    }
+};
+}
index b1645aa..19bd44a 100644 (file)
@@ -65,7 +65,7 @@ FileListCreator::FileListCreator(const Vector<FileChooserFileInfo>& paths, Shoul
         // Resolve directories on a background thread to avoid blocking the main thread.
         m_completionHander = WTFMove(completionHandler);
         m_workQueue = WorkQueue::create("FileListCreator Work Queue");
-        m_workQueue->dispatch([this, protectedThis = makeRef(*this), paths = CrossThreadCopier<Vector<FileChooserFileInfo>>::copy(paths)]() mutable {
+        m_workQueue->dispatch([this, protectedThis = makeRef(*this), paths = crossThreadCopy(paths)]() mutable {
             auto fileList = createFileList<ShouldResolveDirectories::Yes>(paths);
             callOnMainThread([this, protectedThis = WTFMove(protectedThis), fileList = WTFMove(fileList)]() mutable {
                 if (auto completionHander = WTFMove(m_completionHander))
index fae5ae3..59e133e 100644 (file)
@@ -101,6 +101,7 @@ WEBCORE_EXPORT bool getFileCreationTime(const String&, time_t& result); // Not a
 bool getFileMetadata(const String&, FileMetadata&);
 bool fileIsDirectory(const String&);
 WEBCORE_EXPORT String pathByAppendingComponent(const String& path, const String& component);
+String pathByAppendingComponents(const String& path, const Vector<String>& components);
 String lastComponentOfPathIgnoringTrailingSlash(const String& path);
 WEBCORE_EXPORT bool makeAllDirectories(const String& path);
 String homeDirectoryPath();
index 5be36e9..e3c5d63 100644 (file)
@@ -35,6 +35,7 @@
 #include <wtf/glib/GRefPtr.h>
 #include <wtf/glib/GUniquePtr.h>
 #include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
 #include <wtf/text/WTFString.h>
 
 namespace WebCore {
@@ -180,6 +181,17 @@ String pathByAppendingComponent(const String& path, const String& component)
     return path + G_DIR_SEPARATOR_S + component;
 }
 
+String pathByAppendingComponents(const String& path, const Vector<String>& components)
+{
+    StringBuilder builder;
+    builder.append(path);
+    for (auto& component : components) {
+        builder.append(G_DIR_SEPARATOR_S);
+        builder.append(component);
+    }
+    return builder.toString();
+}
+
 bool makeAllDirectories(const String& path)
 {
     GUniquePtr<gchar> filename = unescapedFilename(path);
index 0531930..ea176dd 100644 (file)
@@ -40,6 +40,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
 #include <wtf/text/WTFString.h>
 
 namespace WebCore {
@@ -270,6 +271,17 @@ String pathByAppendingComponent(const String& path, const String& component)
     return path + "/" + component;
 }
 
+String pathByAppendingComponents(const String& path, const Vector<String>& components)
+{
+    StringBuilder builder;
+    builder.append(path);
+    for (auto& component : components) {
+        builder.append('/');
+        builder.append(component);
+    }
+    return builder.toString();
+}
+
 bool makeAllDirectories(const String& path)
 {
     CString fullPath = fileSystemRepresentation(path);
index 2d12774..af0072f 100644 (file)
@@ -41,6 +41,7 @@
 #include <wtf/CryptographicallyRandomNumber.h>
 #include <wtf/HashMap.h>
 #include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
 
 namespace WebCore {
 
@@ -201,6 +202,14 @@ String pathByAppendingComponent(const String& path, const String& component)
     return String::adopt(WTFMove(buffer));
 }
 
+String pathByAppendingComponents(const String& path, const Vector<String>& components)
+{
+    String result = path;
+    for (auto& component : components)
+        result = pathByAppendingComponent(result, component);
+    return result;
+}
+
 #if !USE(CF)
 
 CString fileSystemRepresentation(const String& path)