Move FormData zip file generation to NetworkProcess and enable it for all WebKit...
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 1 Aug 2019 22:29:52 +0000 (22:29 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 1 Aug 2019 22:29:52 +0000 (22:29 +0000)
https://bugs.webkit.org/show_bug.cgi?id=200102
<rdar://problem/53275114>

Patch by Alex Christensen <achristensen@webkit.org> on 2019-08-01
Reviewed by Darin Adler.

Source/WebCore:

To enable directory uploading in WebKit2, we extended WebKit1's model of asking the application to generate a file for uploading.
This means the WebProcess needed access to everything necessary to zip a whole directory, and clients that have not implemented
the strange WKBundlePageUIClient callbacks won't be able to upload directories.  Safari's implementation had already been copied
to BlobDataFileReference::generateReplacementFile, so I reused that code to do the zipping.  Instead of a complicated model of
keeping track of a filename, possibly a generated filename, and whether we think we own the file or not and having nobody clean up,
we now do the generation, use, and cleaning up in the network process starting with a new function generateFilesForUpload.
This removes unimplemented SPI in WebUIDelegatePrivate in WebKitLegacy and stops calling the WKBundlePageUIClient related to upload
file generation and replaces them with automatic behavior equivalent to Safari's implementation of the WKBundlePageUIClient calls.
Since we no longer need to do these file operations in the WebProcess, I am also reverting r245322 and r246077 which tightens the sandbox.

Covered by an API test.

* Modules/fetch/FetchBody.cpp:
(WebCore::FetchBody::extract):
(WebCore::FetchBody::bodyAsFormData const):
* loader/FormSubmission.cpp:
(WebCore::FormSubmission::create):
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::submitForm):
(WebCore::FrameLoader::loadDifferentDocumentItem):
* loader/ResourceLoader.cpp:
(WebCore::ResourceLoader::didReceiveResponse):
(WebCore::ResourceLoader::cleanupForError):
* page/Chrome.cpp:
(WebCore::ChromeClient::shouldReplaceWithGeneratedFileForUpload): Deleted.
(WebCore::ChromeClient::generateReplacementFile): Deleted.
* page/ChromeClient.h:
* platform/network/FormData.cpp:
(WebCore::FormData::FormData):
(WebCore::FormData::~FormData):
(WebCore::FormData::createMultiPart):
(WebCore::FormDataElement::lengthInBytes const):
(WebCore::FormData::appendFile):
(WebCore::FormData::appendFileRange):
(WebCore::FormData::appendMultiPartFileValue):
(WebCore::FormData::appendMultiPartKeyValuePairItems):
(WebCore::FormData::resolveBlobReferences):
(WebCore::generateFileForUpload):
(WebCore::FormData::generateFilesForUpload):
(WebCore::FormData::generateFiles): Deleted.
(WebCore::FormData::hasGeneratedFiles const): Deleted.
(WebCore::FormData::hasOwnedGeneratedFiles const): Deleted.
(WebCore::FormData::removeGeneratedFilesIfNeeded): Deleted.
* platform/network/FormData.h:
(WebCore::FormDataElement::FormDataElement):
(WebCore::FormDataElement::EncodedFileData::isolatedCopy const):
(WebCore::FormDataElement::EncodedFileData::operator== const):
(WebCore::FormDataElement::EncodedFileData::encode const):
(WebCore::FormDataElement::EncodedFileData::decode):
* platform/network/cf/FormDataStreamCFNet.cpp:
(WebCore::advanceCurrentStream):
(WebCore::formCreate):
(WebCore::formFinalize):
(WebCore::createHTTPBodyCFReadStream):
* platform/network/mac/BlobDataFileReferenceMac.mm:
(WebCore::generateFileForUpload):
(WebCore::BlobDataFileReference::generateReplacementFile):
* xml/XMLHttpRequest.cpp:
(WebCore::XMLHttpRequest::send):

Source/WebKit:

* NetworkProcess/NetworkResourceLoadParameters.cpp:
(WebKit::NetworkResourceLoadParameters::encode const):
* Platform/IPC/FormDataReference.h:
(IPC::FormDataReference::encode const):
* Resources/SandboxProfiles/ios/com.apple.WebKit.Networking.sb:
* WebProcess/InjectedBundle/API/APIInjectedBundlePageUIClient.h:
(API::InjectedBundle::PageUIClient::shouldGenerateFileForUpload): Deleted.
(API::InjectedBundle::PageUIClient::generateFileForUpload): Deleted.
* WebProcess/InjectedBundle/InjectedBundlePageUIClient.cpp:
(WebKit::InjectedBundlePageUIClient::shouldGenerateFileForUpload): Deleted.
(WebKit::InjectedBundlePageUIClient::generateFileForUpload): Deleted.
* WebProcess/InjectedBundle/InjectedBundlePageUIClient.h:
* WebProcess/WebCoreSupport/WebChromeClient.cpp:
(WebKit::WebChromeClient::shouldReplaceWithGeneratedFileForUpload): Deleted.
(WebKit::WebChromeClient::generateReplacementFile): Deleted.
* WebProcess/WebCoreSupport/WebChromeClient.h:
* WebProcess/com.apple.WebProcess.sb.in:

Source/WebKitLegacy/mac:

* DefaultDelegates/WebDefaultUIDelegate.mm:
(-[WebDefaultUIDelegate webView:shouldReplaceUploadFile:usingGeneratedFilename:]): Deleted.
(-[WebDefaultUIDelegate webView:generateReplacementFile:]): Deleted.
* WebCoreSupport/WebChromeClient.h:
* WebCoreSupport/WebChromeClient.mm:
(WebChromeClient::shouldReplaceWithGeneratedFileForUpload): Deleted.
(WebChromeClient::generateReplacementFile): Deleted.
* WebView/WebUIDelegatePrivate.h:

Source/WTF:

Move code from BlobDataFileReference::generateReplacementFile to FileSystem::createZipArchive.

* wtf/FileSystem.cpp:
(WTF::FileSystemImpl::createZipArchive):
* wtf/FileSystem.h:
* wtf/cocoa/FileSystemCocoa.mm:
(WTF::FileSystemImpl::createZipArchive):

Tools:

Add an API test that is Mac-only right now because runOpenPanelWithParameters is only supported on Mac for some reason
and because clicking on a TestWKWebView only works on Mac.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/UploadDirectory.mm: Added.
(-[UploadDelegate initWithDirectory:]):
(-[UploadDelegate webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:]):
(-[UploadDelegate sentDirectory]):
(TEST):
* TestWebKitAPI/cocoa/TestWKWebView.h:
* TestWebKitAPI/cocoa/TestWKWebView.mm:
(-[TestWKWebView sendClickAtPoint:]):

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

36 files changed:
Source/WTF/ChangeLog
Source/WTF/wtf/FileSystem.cpp
Source/WTF/wtf/FileSystem.h
Source/WTF/wtf/cocoa/FileSystemCocoa.mm
Source/WebCore/ChangeLog
Source/WebCore/Modules/fetch/FetchBody.cpp
Source/WebCore/loader/FormSubmission.cpp
Source/WebCore/loader/FrameLoader.cpp
Source/WebCore/loader/ResourceLoader.cpp
Source/WebCore/page/Chrome.cpp
Source/WebCore/page/ChromeClient.h
Source/WebCore/platform/network/FormData.cpp
Source/WebCore/platform/network/FormData.h
Source/WebCore/platform/network/cf/FormDataStreamCFNet.cpp
Source/WebCore/platform/network/mac/BlobDataFileReferenceMac.mm
Source/WebCore/xml/XMLHttpRequest.cpp
Source/WebKit/ChangeLog
Source/WebKit/NetworkProcess/NetworkResourceLoadParameters.cpp
Source/WebKit/Platform/IPC/FormDataReference.h
Source/WebKit/Resources/SandboxProfiles/ios/com.apple.WebKit.Networking.sb
Source/WebKit/WebProcess/InjectedBundle/API/APIInjectedBundlePageUIClient.h
Source/WebKit/WebProcess/InjectedBundle/InjectedBundlePageUIClient.cpp
Source/WebKit/WebProcess/InjectedBundle/InjectedBundlePageUIClient.h
Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp
Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.h
Source/WebKit/WebProcess/com.apple.WebProcess.sb.in
Source/WebKitLegacy/mac/ChangeLog
Source/WebKitLegacy/mac/DefaultDelegates/WebDefaultUIDelegate.mm
Source/WebKitLegacy/mac/WebCoreSupport/WebChromeClient.h
Source/WebKitLegacy/mac/WebCoreSupport/WebChromeClient.mm
Source/WebKitLegacy/mac/WebView/WebUIDelegatePrivate.h
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKitCocoa/UploadDirectory.mm [new file with mode: 0644]
Tools/TestWebKitAPI/cocoa/TestWKWebView.h
Tools/TestWebKitAPI/cocoa/TestWKWebView.mm

index c4a7b61..2ecde16 100644 (file)
@@ -1,3 +1,19 @@
+2019-08-01  Alex Christensen  <achristensen@webkit.org>
+
+        Move FormData zip file generation to NetworkProcess and enable it for all WebKit clients for uploading directories
+        https://bugs.webkit.org/show_bug.cgi?id=200102
+        <rdar://problem/53275114>
+
+        Reviewed by Darin Adler.
+
+        Move code from BlobDataFileReference::generateReplacementFile to FileSystem::createZipArchive.
+
+        * wtf/FileSystem.cpp:
+        (WTF::FileSystemImpl::createZipArchive):
+        * wtf/FileSystem.h:
+        * wtf/cocoa/FileSystemCocoa.mm:
+        (WTF::FileSystemImpl::createZipArchive):
+
 2019-08-01  Per Arne Vollan  <pvollan@apple.com>
 
         Initialize memory pressure flag in MemoryPressureHandler
index 4331245..fa854e0 100644 (file)
@@ -385,5 +385,12 @@ void makeSafeToUseMemoryMapForPath(const String&)
 }
 #endif
 
+#if !PLATFORM(COCOA)
+String createTemporaryZipArchive(const String& directory)
+{
+    return { };
+}
+#endif
+
 } // namespace FileSystemImpl
 } // namespace WTF
index 6ae3862..5fc0bfe 100644 (file)
@@ -124,6 +124,7 @@ WTF_EXPORT_PRIVATE String directoryName(const String&);
 WTF_EXPORT_PRIVATE bool getVolumeFreeSpace(const String&, uint64_t&);
 WTF_EXPORT_PRIVATE Optional<int32_t> getFileDeviceId(const CString&);
 WTF_EXPORT_PRIVATE bool createSymbolicLink(const String& targetPath, const String& symbolicLinkPath);
+WTF_EXPORT_PRIVATE String createTemporaryZipArchive(const String& directory);
 
 WTF_EXPORT_PRIVATE void setMetadataURL(const String& path, const String& urlString, const String& referrer = { });
 
index 257bca5..163bf71 100644 (file)
 #import "config.h"
 #import <wtf/FileSystem.h>
 
+#include <wtf/SoftLinking.h>
+
+#if USE(APPLE_INTERNAL_SDK)
+#include <Bom/BOMCopier.h>
+#endif
+
+typedef struct _BOMCopier* BOMCopier;
+
+SOFT_LINK_PRIVATE_FRAMEWORK(Bom)
+SOFT_LINK(Bom, BOMCopierNew, BOMCopier, (), ())
+SOFT_LINK(Bom, BOMCopierFree, void, (BOMCopier copier), (copier))
+SOFT_LINK(Bom, BOMCopierCopyWithOptions, int, (BOMCopier copier, const char* fromObj, const char* toObj, CFDictionaryRef options), (copier, fromObj, toObj, options))
+
+#define kBOMCopierOptionCreatePKZipKey CFSTR("createPKZip")
+#define kBOMCopierOptionSequesterResourcesKey CFSTR("sequesterResources")
+#define kBOMCopierOptionKeepParentKey CFSTR("keepParent")
+#define kBOMCopierOptionCopyResourcesKey CFSTR("copyResources")
+
 @interface WTFWebFileManagerDelegate : NSObject <NSFileManagerDelegate>
 @end
 
@@ -48,6 +66,32 @@ namespace WTF {
 
 namespace FileSystemImpl {
 
+String createTemporaryZipArchive(const String& path)
+{
+    String temporaryFile;
+    
+    RetainPtr<NSFileCoordinator> coordinator = adoptNS([[NSFileCoordinator alloc] initWithFilePresenter:nil]);
+    [coordinator coordinateReadingItemAtURL:[NSURL fileURLWithPath:path] options:NSFileCoordinatorReadingWithoutChanges error:nullptr byAccessor:[&](NSURL *newURL) mutable {
+        CString archivePath([NSTemporaryDirectory() stringByAppendingPathComponent:@"WebKitGeneratedFileXXXXXX"].fileSystemRepresentation);
+        if (mkstemp(archivePath.mutableData()) == -1)
+            return;
+        
+        NSDictionary *options = @{
+            (__bridge id)kBOMCopierOptionCreatePKZipKey : @YES,
+            (__bridge id)kBOMCopierOptionSequesterResourcesKey : @YES,
+            (__bridge id)kBOMCopierOptionKeepParentKey : @YES,
+            (__bridge id)kBOMCopierOptionCopyResourcesKey : @YES,
+        };
+        
+        BOMCopier copier = BOMCopierNew();
+        if (!BOMCopierCopyWithOptions(copier, newURL.path.fileSystemRepresentation, archivePath.data(), (__bridge CFDictionaryRef)options))
+            temporaryFile = String::fromUTF8(archivePath);
+        BOMCopierFree(copier);
+    }];
+    
+    return temporaryFile;
+}
+
 String homeDirectoryPath()
 {
     return NSHomeDirectory();
index cf2e3d1..08f84e2 100644 (file)
@@ -1,3 +1,71 @@
+2019-08-01  Alex Christensen  <achristensen@webkit.org>
+
+        Move FormData zip file generation to NetworkProcess and enable it for all WebKit clients for uploading directories
+        https://bugs.webkit.org/show_bug.cgi?id=200102
+        <rdar://problem/53275114>
+
+        Reviewed by Darin Adler.
+
+        To enable directory uploading in WebKit2, we extended WebKit1's model of asking the application to generate a file for uploading.
+        This means the WebProcess needed access to everything necessary to zip a whole directory, and clients that have not implemented
+        the strange WKBundlePageUIClient callbacks won't be able to upload directories.  Safari's implementation had already been copied
+        to BlobDataFileReference::generateReplacementFile, so I reused that code to do the zipping.  Instead of a complicated model of
+        keeping track of a filename, possibly a generated filename, and whether we think we own the file or not and having nobody clean up,
+        we now do the generation, use, and cleaning up in the network process starting with a new function generateFilesForUpload.
+        This removes unimplemented SPI in WebUIDelegatePrivate in WebKitLegacy and stops calling the WKBundlePageUIClient related to upload
+        file generation and replaces them with automatic behavior equivalent to Safari's implementation of the WKBundlePageUIClient calls.
+        Since we no longer need to do these file operations in the WebProcess, I am also reverting r245322 and r246077 which tightens the sandbox.
+
+        Covered by an API test.
+
+        * Modules/fetch/FetchBody.cpp:
+        (WebCore::FetchBody::extract):
+        (WebCore::FetchBody::bodyAsFormData const):
+        * loader/FormSubmission.cpp:
+        (WebCore::FormSubmission::create):
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::submitForm):
+        (WebCore::FrameLoader::loadDifferentDocumentItem):
+        * loader/ResourceLoader.cpp:
+        (WebCore::ResourceLoader::didReceiveResponse):
+        (WebCore::ResourceLoader::cleanupForError):
+        * page/Chrome.cpp:
+        (WebCore::ChromeClient::shouldReplaceWithGeneratedFileForUpload): Deleted.
+        (WebCore::ChromeClient::generateReplacementFile): Deleted.
+        * page/ChromeClient.h:
+        * platform/network/FormData.cpp:
+        (WebCore::FormData::FormData):
+        (WebCore::FormData::~FormData):
+        (WebCore::FormData::createMultiPart):
+        (WebCore::FormDataElement::lengthInBytes const):
+        (WebCore::FormData::appendFile):
+        (WebCore::FormData::appendFileRange):
+        (WebCore::FormData::appendMultiPartFileValue):
+        (WebCore::FormData::appendMultiPartKeyValuePairItems):
+        (WebCore::FormData::resolveBlobReferences):
+        (WebCore::generateFileForUpload):
+        (WebCore::FormData::generateFilesForUpload):
+        (WebCore::FormData::generateFiles): Deleted.
+        (WebCore::FormData::hasGeneratedFiles const): Deleted.
+        (WebCore::FormData::hasOwnedGeneratedFiles const): Deleted.
+        (WebCore::FormData::removeGeneratedFilesIfNeeded): Deleted.
+        * platform/network/FormData.h:
+        (WebCore::FormDataElement::FormDataElement):
+        (WebCore::FormDataElement::EncodedFileData::isolatedCopy const):
+        (WebCore::FormDataElement::EncodedFileData::operator== const):
+        (WebCore::FormDataElement::EncodedFileData::encode const):
+        (WebCore::FormDataElement::EncodedFileData::decode):
+        * platform/network/cf/FormDataStreamCFNet.cpp:
+        (WebCore::advanceCurrentStream):
+        (WebCore::formCreate):
+        (WebCore::formFinalize):
+        (WebCore::createHTTPBodyCFReadStream):
+        * platform/network/mac/BlobDataFileReferenceMac.mm:
+        (WebCore::generateFileForUpload):
+        (WebCore::BlobDataFileReference::generateReplacementFile):
+        * xml/XMLHttpRequest.cpp:
+        (WebCore::XMLHttpRequest::send):
+
 2019-08-01  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         [Text autosizing] [iPadOS] Add targeted hacks to address some remaining text autosizing issues
index 987e455..33de4f3 100644 (file)
@@ -40,7 +40,7 @@
 
 namespace WebCore {
 
-FetchBody FetchBody::extract(ScriptExecutionContext& context, Init&& value, String& contentType)
+FetchBody FetchBody::extract(ScriptExecutionContext&, Init&& value, String& contentType)
 {
     return WTF::switchOn(value, [&](RefPtr<Blob>& value) mutable {
         Ref<const Blob> blob = value.releaseNonNull();
@@ -49,7 +49,7 @@ FetchBody FetchBody::extract(ScriptExecutionContext& context, Init&& value, Stri
         return FetchBody(WTFMove(blob));
     }, [&](RefPtr<DOMFormData>& value) mutable {
         Ref<DOMFormData> domFormData = value.releaseNonNull();
-        auto formData = FormData::createMultiPart(domFormData.get(), &downcast<Document>(context));
+        auto formData = FormData::createMultiPart(domFormData.get());
         contentType = makeString("multipart/form-data; boundary=", formData->boundary().data());
         return FetchBody(WTFMove(formData));
     }, [&](RefPtr<URLSearchParams>& value) mutable {
@@ -250,9 +250,8 @@ RefPtr<FormData> FetchBody::bodyAsFormData(ScriptExecutionContext& context) cons
     if (isArrayBufferView())
         return FormData::create(arrayBufferViewBody().baseAddress(), arrayBufferViewBody().byteLength());
     if (isFormData()) {
-        ASSERT(!context.isWorkerGlobalScope());
+        ASSERT_UNUSED(context, !context.isWorkerGlobalScope());
         auto body = makeRef(const_cast<FormData&>(formDataBody()));
-        body->generateFiles(&downcast<Document>(context));
         return body;
     }
     if (auto* data = m_consumer.data())
index be03e4a..0f9246f 100644 (file)
@@ -198,7 +198,7 @@ Ref<FormSubmission> FormSubmission::create(HTMLFormElement& form, const Attribut
     String boundary;
 
     if (isMultiPartForm) {
-        formData = FormData::createMultiPart(domFormData, &document);
+        formData = FormData::createMultiPart(domFormData);
         boundary = formData->boundary().data();
     } else {
         formData = FormData::create(domFormData, attributes.method() == Method::Get ? FormData::FormURLEncoded : FormData::parseEncodingType(encodingType));
index fb4c5b8..47f766a 100644 (file)
@@ -473,7 +473,6 @@ void FrameLoader::submitForm(Ref<FormSubmission>&& submission)
         m_submittedFormURL = submission->requestURL();
     }
 
-    submission->data().generateFiles(m_frame.document());
     submission->setReferrer(outgoingReferrer());
     submission->setOrigin(outgoingOrigin());
 
@@ -3773,8 +3772,6 @@ void FrameLoader::loadDifferentDocumentItem(HistoryItem& item, HistoryItem* from
     // If this was a repost that failed the page cache, we might try to repost the form.
     NavigationAction action;
     if (formData) {
-        formData->generateFiles(m_frame.document());
-
         request.setHTTPMethod("POST");
         request.setHTTPBody(WTFMove(formData));
         request.setHTTPContentType(item.formContentType());
index 1f6f353..a76656c 100644 (file)
@@ -492,9 +492,6 @@ void ResourceLoader::didReceiveResponse(const ResourceResponse& r, CompletionHan
 
     m_response = r;
 
-    if (FormData* data = m_request.httpBody())
-        data->removeGeneratedFilesIfNeeded();
-
     if (m_options.sendLoadCallbacks == SendCallbackPolicy::SendCallbacks)
         frameLoader()->notifier().didReceiveResponse(this, m_response);
 }
@@ -579,9 +576,6 @@ void ResourceLoader::didFail(const ResourceError& error)
 
 void ResourceLoader::cleanupForError(const ResourceError& error)
 {
-    if (FormData* data = m_request.httpBody())
-        data->removeGeneratedFilesIfNeeded();
-
     if (m_notifiedLoadComplete)
         return;
     m_notifiedLoadComplete = true;
index 220d8c2..9758329 100644 (file)
@@ -525,17 +525,6 @@ void Chrome::windowScreenDidChange(PlatformDisplayID displayID)
 #endif
 }
 
-bool ChromeClient::shouldReplaceWithGeneratedFileForUpload(const String&, String&)
-{
-    return false;
-}
-
-String ChromeClient::generateReplacementFile(const String&)
-{
-    ASSERT_NOT_REACHED();
-    return String();
-}
-
 bool Chrome::selectItemWritingDirectionIsNatural()
 {
     return m_client.selectItemWritingDirectionIsNatural();
index f993e20..c25a024 100644 (file)
@@ -235,9 +235,6 @@ public:
     // the new cache.
     virtual void reachedApplicationCacheOriginQuota(SecurityOrigin&, int64_t totalSpaceNeeded) = 0;
 
-    virtual bool shouldReplaceWithGeneratedFileForUpload(const String& path, String& generatedFilename);
-    virtual String generateReplacementFile(const String& path);
-
 #if ENABLE(IOS_TOUCH_EVENTS)
     virtual void didPreventDefaultForEvent() = 0;
 #endif
index 7e94c9a..d6f2916 100644 (file)
@@ -27,7 +27,6 @@
 #include "Chrome.h"
 #include "ChromeClient.h"
 #include "DOMFormData.h"
-#include "Document.h"
 #include "File.h"
 #include "FormDataBuilder.h"
 #include "Page.h"
@@ -50,22 +49,10 @@ inline FormData::FormData(const FormData& data)
     , m_alwaysStream(false)
     , m_containsPasswordData(data.m_containsPasswordData)
 {
-    // We shouldn't be copying FormData that hasn't already removed its generated files
-    // but just in case, make sure the new FormData is ready to generate its own files.
-    for (auto& element : m_elements) {
-        if (auto* fileData = WTF::get_if<FormDataElement::EncodedFileData>(element.data)) {
-            fileData->generatedFilename = { };
-            fileData->ownsGeneratedFile = false;
-        }
-    }
 }
 
 FormData::~FormData()
 {
-    // This cleanup should've happened when the form submission finished.
-    // Just in case, let's assert, and do the cleanup anyway in release builds.
-    ASSERT(!hasOwnedGeneratedFiles());
-    removeGeneratedFilesIfNeeded();
 }
 
 Ref<FormData> FormData::create()
@@ -109,10 +96,10 @@ Ref<FormData> FormData::create(const DOMFormData& formData, EncodingType encodin
     return result;
 }
 
-Ref<FormData> FormData::createMultiPart(const DOMFormData& formData, Document* document)
+Ref<FormData> FormData::createMultiPart(const DOMFormData& formData)
 {
     auto result = create();
-    result->appendMultiPartKeyValuePairItems(formData, document);
+    result->appendMultiPartKeyValuePairItems(formData);
     return result;
 }
 
@@ -146,7 +133,7 @@ uint64_t FormDataElement::lengthInBytes() const
             if (fileData.fileLength != BlobDataItem::toEndOfFile)
                 return static_cast<uint64_t>(fileData.fileLength);
             long long fileSize;
-            if (FileSystem::getFileSize(fileData.shouldGenerateFile ? fileData.generatedFilename : fileData.filename, fileSize))
+            if (FileSystem::getFileSize(fileData.filename, fileSize))
                 return static_cast<uint64_t>(fileSize);
             return static_cast<uint64_t>(0);
         }, [] (const FormDataElement::EncodedBlobData& blobData) {
@@ -184,15 +171,15 @@ void FormData::appendData(const void* data, size_t size)
     m_elements.append(WTFMove(vector));
 }
 
-void FormData::appendFile(const String& filename, bool shouldGenerateFile)
+void FormData::appendFile(const String& filename)
 {
-    m_elements.append(FormDataElement(filename, 0, BlobDataItem::toEndOfFile, WTF::nullopt, shouldGenerateFile));
+    m_elements.append(FormDataElement(filename, 0, BlobDataItem::toEndOfFile, WTF::nullopt));
     m_lengthInBytes = WTF::nullopt;
 }
 
-void FormData::appendFileRange(const String& filename, long long start, long long length, Optional<WallTime> expectedModificationTime, bool shouldGenerateFile)
+void FormData::appendFileRange(const String& filename, long long start, long long length, Optional<WallTime> expectedModificationTime)
 {
-    m_elements.append(FormDataElement(filename, start, length, expectedModificationTime, shouldGenerateFile));
+    m_elements.append(FormDataElement(filename, start, length, expectedModificationTime));
     m_lengthInBytes = WTF::nullopt;
 }
 
@@ -207,22 +194,10 @@ static Vector<uint8_t> normalizeStringData(TextEncoding& encoding, const String&
     return normalizeLineEndingsToCRLF(encoding.encode(value, UnencodableHandling::Entities));
 }
 
-void FormData::appendMultiPartFileValue(const File& file, Vector<char>& header, TextEncoding& encoding, Document* document)
+void FormData::appendMultiPartFileValue(const File& file, Vector<char>& header, TextEncoding& encoding)
 {
     auto name = file.name();
 
-    // Let the application specify a filename if it's going to generate a replacement file for the upload.
-    bool shouldGenerateFile = false;
-    auto& path = file.path();
-    if (!path.isEmpty()) {
-        if (Page* page = document->page()) {
-            String generatedFileName;
-            shouldGenerateFile = page->chrome().client().shouldReplaceWithGeneratedFileForUpload(path, generatedFileName);
-            if (shouldGenerateFile)
-                name = generatedFileName;
-        }
-    }
-
     // We have to include the filename=".." part in the header, even if the filename is empty
     FormDataBuilder::addFilenameToMultiPartHeader(header, encoding, name);
 
@@ -238,7 +213,7 @@ void FormData::appendMultiPartFileValue(const File& file, Vector<char>& header,
     appendData(header.data(), header.size());
 
     if (!file.path().isEmpty())
-        appendFile(file.path(), shouldGenerateFile);
+        appendFile(file.path());
     else if (file.size())
         appendBlob(file.url());
 }
@@ -252,7 +227,7 @@ void FormData::appendMultiPartStringValue(const String& string, Vector<char>& he
     appendData(normalizedStringData.data(), normalizedStringData.size());
 }
 
-void FormData::appendMultiPartKeyValuePairItems(const DOMFormData& formData, Document* document)
+void FormData::appendMultiPartKeyValuePairItems(const DOMFormData& formData)
 {
     m_boundary = FormDataBuilder::generateUniqueBoundaryString();
 
@@ -266,7 +241,7 @@ void FormData::appendMultiPartKeyValuePairItems(const DOMFormData& formData, Doc
         FormDataBuilder::beginMultiPartHeader(header, m_boundary.data(), normalizedName);
 
         if (WTF::holds_alternative<RefPtr<File>>(item.data))
-            appendMultiPartFileValue(*WTF::get<RefPtr<File>>(item.data), header, encoding, document);
+            appendMultiPartFileValue(*WTF::get<RefPtr<File>>(item.data), header, encoding);
         else
             appendMultiPartStringValue(WTF::get<String>(item.data), header, encoding);
 
@@ -359,7 +334,7 @@ Ref<FormData> FormData::resolveBlobReferences(BlobRegistry& blobRegistry)
             [&] (const Vector<char>& bytes) {
                 newFormData->appendData(bytes.data(), bytes.size());
             }, [&] (const FormDataElement::EncodedFileData& fileData) {
-                newFormData->appendFileRange(fileData.filename, fileData.fileStart, fileData.fileLength, fileData.expectedFileModificationTime, fileData.shouldGenerateFile);
+                newFormData->appendFileRange(fileData.filename, fileData.fileStart, fileData.fileLength, fileData.expectedFileModificationTime);
             }, [&] (const FormDataElement::EncodedBlobData& blobData) {
                 appendBlobResolved(blobRegistry, newFormData.get(), blobData.url);
             }
@@ -368,66 +343,41 @@ Ref<FormData> FormData::resolveBlobReferences(BlobRegistry& blobRegistry)
     return newFormData;
 }
 
-void FormData::generateFiles(Document* document)
-{
-    Page* page = document->page();
-    if (!page)
-        return;
-
-    for (auto& element : m_elements) {
-        if (auto* fileData = WTF::get_if<FormDataElement::EncodedFileData>(element.data)) {
-            if (fileData->shouldGenerateFile) {
-                ASSERT(!fileData->ownsGeneratedFile);
-                ASSERT(fileData->generatedFilename.isEmpty());
-                if (!fileData->generatedFilename.isEmpty())
-                    continue;
-                fileData->generatedFilename = page->chrome().client().generateReplacementFile(fileData->filename);
-                if (!fileData->generatedFilename.isEmpty())
-                    fileData->ownsGeneratedFile = true;
-            }
-        }
-    }
-}
-
-bool FormData::hasGeneratedFiles() const
+FormDataForUpload FormData::prepareForUpload()
 {
+    Vector<String> generatedFiles;
     for (auto& element : m_elements) {
-        if (auto* fileData = WTF::get_if<FormDataElement::EncodedFileData>(element.data)) {
-            if (!fileData->generatedFilename.isEmpty())
-                return true;
-        }
+        auto* fileData = WTF::get_if<FormDataElement::EncodedFileData>(element.data);
+        if (!fileData)
+            continue;
+        if (!FileSystem::fileIsDirectory(fileData->filename, FileSystem::ShouldFollowSymbolicLinks::Yes))
+            continue;
+        if (fileData->fileStart || fileData->fileLength != BlobDataItem::toEndOfFile)
+            continue;
+        if (!fileData->fileModificationTimeMatchesExpectation())
+            continue;
+
+        auto generatedFilename = FileSystem::createTemporaryZipArchive(fileData->filename);
+        if (!generatedFilename)
+            continue;
+        fileData->filename = generatedFilename;
+        generatedFiles.append(WTFMove(generatedFilename));
     }
-    return false;
+    
+    return { *this, WTFMove(generatedFiles) };
 }
 
-bool FormData::hasOwnedGeneratedFiles() const
+FormDataForUpload::FormDataForUpload(FormData& data, Vector<String>&& temporaryZipFiles)
+    : m_data(data)
+    , m_temporaryZipFiles(WTFMove(temporaryZipFiles))
 {
-    for (auto& element : m_elements) {
-        if (auto* fileData = WTF::get_if<FormDataElement::EncodedFileData>(element.data)) {
-            if (fileData->ownsGeneratedFile) {
-                ASSERT(!fileData->generatedFilename.isEmpty());
-                return true;
-            }
-        }
-    }
-    return false;
 }
 
-void FormData::removeGeneratedFilesIfNeeded()
+FormDataForUpload::~FormDataForUpload()
 {
-    for (auto& element : m_elements) {
-        if (auto* fileData = WTF::get_if<FormDataElement::EncodedFileData>(element.data)) {
-            if (fileData->ownsGeneratedFile) {
-                ASSERT(!fileData->generatedFilename.isEmpty());
-                ASSERT(fileData->shouldGenerateFile);
-                String directory = FileSystem::directoryName(fileData->generatedFilename);
-                FileSystem::deleteFile(fileData->generatedFilename);
-                FileSystem::deleteEmptyDirectory(directory);
-                fileData->generatedFilename = String();
-                fileData->ownsGeneratedFile = false;
-            }
-        }
-    }
+    ASSERT(isMainThread());
+    for (auto& file : m_temporaryZipFiles)
+        FileSystem::deleteFile(file);
 }
 
 uint64_t FormData::lengthInBytes() const
@@ -460,4 +410,19 @@ URL FormData::asBlobURL() const
     return { };
 }
 
+bool FormDataElement::EncodedFileData::fileModificationTimeMatchesExpectation() const
+{
+    if (!expectedFileModificationTime)
+        return true;
+
+    auto fileModificationTime = FileSystem::getFileModificationTime(filename);
+    if (!fileModificationTime)
+        return false;
+
+    if (fileModificationTime->secondsSinceEpoch().secondsAs<time_t>() != expectedFileModificationTime->secondsSinceEpoch().secondsAs<time_t>())
+        return false;
+
+    return true;
+}
+
 } // namespace WebCore
index 29d557a..20bf62e 100644 (file)
@@ -31,7 +31,6 @@ namespace WebCore {
 
 class BlobRegistry;
 class DOMFormData;
-class Document;
 class File;
 class SharedBuffer;
 class TextEncoding;
@@ -46,8 +45,8 @@ struct FormDataElement {
         : data(WTFMove(data)) { }
     explicit FormDataElement(Vector<char>&& array)
         : data(WTFMove(array)) { }
-    FormDataElement(const String& filename, int64_t fileStart, int64_t fileLength, Optional<WallTime> expectedFileModificationTime, bool shouldGenerateFile)
-        : data(EncodedFileData { filename, fileStart, fileLength, expectedFileModificationTime, { }, shouldGenerateFile, false }) { }
+    FormDataElement(const String& filename, int64_t fileStart, int64_t fileLength, Optional<WallTime> expectedFileModificationTime)
+        : data(EncodedFileData { filename, fileStart, fileLength, expectedFileModificationTime }) { }
     explicit FormDataElement(const URL& blobURL)
         : data(EncodedBlobData { blobURL }) { }
 
@@ -73,17 +72,12 @@ struct FormDataElement {
         int64_t fileStart { 0 };
         int64_t fileLength { 0 };
         Optional<WallTime> expectedFileModificationTime;
-        String generatedFilename;
-        bool shouldGenerateFile { false };
-        bool ownsGeneratedFile { false };
 
-        // FIXME: Generated file support in FormData is almost identical to Blob, they should be merged.
-        // We can't just switch to using Blobs for all files because EncodedFile form data elements do not
-        // have a valid expectedFileModificationTime, meaning we always upload the latest content from disk.
+        bool fileModificationTimeMatchesExpectation() const;
 
         EncodedFileData isolatedCopy() const
         {
-            return { filename.isolatedCopy(), fileStart, fileLength, expectedFileModificationTime, generatedFilename.isolatedCopy(), shouldGenerateFile, ownsGeneratedFile };
+            return { filename.isolatedCopy(), fileStart, fileLength, expectedFileModificationTime };
         }
         
         bool operator==(const EncodedFileData& other) const
@@ -91,14 +85,11 @@ struct FormDataElement {
             return filename == other.filename
                 && fileStart == other.fileStart
                 && fileLength == other.fileLength
-                && expectedFileModificationTime == other.expectedFileModificationTime
-                && generatedFilename == other.generatedFilename
-                && shouldGenerateFile == other.shouldGenerateFile
-                && ownsGeneratedFile == other.ownsGeneratedFile;
+                && expectedFileModificationTime == other.expectedFileModificationTime;
         }
         template<typename Encoder> void encode(Encoder& encoder) const
         {
-            encoder << filename << fileStart << fileLength << expectedFileModificationTime << generatedFilename << shouldGenerateFile;
+            encoder << filename << fileStart << fileLength << expectedFileModificationTime;
         }
         template<typename Decoder> static Optional<EncodedFileData> decode(Decoder& decoder)
         {
@@ -121,27 +112,12 @@ struct FormDataElement {
             decoder >> expectedFileModificationTime;
             if (!expectedFileModificationTime)
                 return WTF::nullopt;
-            
-            Optional<String> generatedFilename;
-            decoder >> generatedFilename;
-            if (!generatedFilename)
-                return WTF::nullopt;
-
-            Optional<bool> shouldGenerateFile;
-            decoder >> shouldGenerateFile;
-            if (!shouldGenerateFile)
-                return WTF::nullopt;
 
-            bool ownsGeneratedFile = false;
-            
             return {{
                 WTFMove(*filename),
                 WTFMove(*fileStart),
                 WTFMove(*fileLength),
-                WTFMove(*expectedFileModificationTime),
-                WTFMove(*generatedFilename),
-                WTFMove(*shouldGenerateFile),
-                WTFMove(ownsGeneratedFile)
+                WTFMove(*expectedFileModificationTime)
             }};
         }
 
@@ -189,6 +165,22 @@ struct FormDataElement {
     Data data;
 };
 
+class FormData;
+
+struct FormDataForUpload {
+public:
+    FormDataForUpload(FormDataForUpload&&) = default;
+    ~FormDataForUpload();
+
+    FormData& data() { return m_data.get(); }
+private:
+    friend class FormData;
+    FormDataForUpload(FormData&, Vector<String>&&);
+    
+    Ref<FormData> m_data;
+    Vector<String> m_temporaryZipFiles;
+};
+
 class FormData : public RefCounted<FormData> {
 public:
     enum EncodingType {
@@ -204,7 +196,7 @@ public:
     static Ref<FormData> create(const Vector<char>&);
     static Ref<FormData> create(const Vector<uint8_t>&);
     static Ref<FormData> create(const DOMFormData&, EncodingType = FormURLEncoded);
-    static Ref<FormData> createMultiPart(const DOMFormData&, Document*);
+    static Ref<FormData> createMultiPart(const DOMFormData&);
     WEBCORE_EXPORT ~FormData();
 
     // FIXME: Both these functions perform a deep copy of m_elements, but differ in handling of other data members.
@@ -218,8 +210,8 @@ public:
     static RefPtr<FormData> decode(Decoder&);
 
     WEBCORE_EXPORT void appendData(const void* data, size_t);
-    void appendFile(const String& filePath, bool shouldGenerateFile = false);
-    WEBCORE_EXPORT void appendFileRange(const String& filename, long long start, long long length, Optional<WallTime> expectedModificationTime, bool shouldGenerateFile = false);
+    void appendFile(const String& filePath);
+    WEBCORE_EXPORT void appendFileRange(const String& filename, long long start, long long length, Optional<WallTime> expectedModificationTime);
     WEBCORE_EXPORT void appendBlob(const URL& blobURL);
 
     WEBCORE_EXPORT Vector<char> flatten() const; // omits files
@@ -229,15 +221,14 @@ public:
     // If the FormData has no blob references to resolve, this is returned.
     WEBCORE_EXPORT Ref<FormData> resolveBlobReferences(BlobRegistry&);
 
+    WEBCORE_EXPORT FormDataForUpload prepareForUpload();
+
     bool isEmpty() const { return m_elements.isEmpty(); }
     const Vector<FormDataElement>& elements() const { return m_elements; }
     const Vector<char>& boundary() const { return m_boundary; }
 
     RefPtr<SharedBuffer> asSharedBuffer() const;
 
-    void generateFiles(Document*);
-    void removeGeneratedFilesIfNeeded();
-
     bool alwaysStream() const { return m_alwaysStream; }
     void setAlwaysStream(bool alwaysStream) { m_alwaysStream = alwaysStream; }
 
@@ -266,14 +257,11 @@ private:
     FormData();
     FormData(const FormData&);
 
-    void appendMultiPartFileValue(const File&, Vector<char>& header, TextEncoding&, Document*);
+    void appendMultiPartFileValue(const File&, Vector<char>& header, TextEncoding&);
     void appendMultiPartStringValue(const String&, Vector<char>& header, TextEncoding&);
-    void appendMultiPartKeyValuePairItems(const DOMFormData&, Document*);
+    void appendMultiPartKeyValuePairItems(const DOMFormData&);
     void appendNonMultiPartKeyValuePairItems(const DOMFormData&, EncodingType);
 
-    bool hasGeneratedFiles() const;
-    bool hasOwnedGeneratedFiles() const;
-
     Vector<FormDataElement> m_elements;
 
     int64_t m_identifier { 0 };
index dfdde71..47f1541 100644 (file)
@@ -92,20 +92,23 @@ CFStringRef formDataStreamLengthPropertyName()
 }
 
 struct FormCreationContext {
-    RefPtr<FormData> formData;
+    FormDataForUpload data;
     unsigned long long streamLength;
 };
 
 struct FormStreamFields {
-    RefPtr<FormData> formData;
+    FormStreamFields(FormDataForUpload&& data)
+        : data(WTFMove(data)) { }
+
+    FormDataForUpload data;
     SchedulePairHashSet scheduledRunLoopPairs;
     Vector<FormDataElement> remainingElements; // in reverse order
-    CFReadStreamRef currentStream;
-    long long currentStreamRangeLength;
+    CFReadStreamRef currentStream { nullptr };
+    long long currentStreamRangeLength { BlobDataItem::toEndOfFile };
     MallocPtr<char> currentData;
-    CFReadStreamRef formStream;
-    unsigned long long streamLength;
-    unsigned long long bytesSent;
+    CFReadStreamRef formStream { nullptr };
+    unsigned long long streamLength { 0 };
+    unsigned long long bytesSent { 0 };
     Lock streamIsBeingOpenedOrClosedLock;
 };
 
@@ -145,15 +148,11 @@ static bool advanceCurrentStream(FormStreamFields* form)
             form->currentData = WTFMove(data);
             return true;
         }, [form] (const FormDataElement::EncodedFileData& fileData) {
-            // Check if the file has been changed or not if required.
-            if (fileData.expectedFileModificationTime) {
-                auto fileModificationTime = FileSystem::getFileModificationTime(fileData.filename);
-                if (!fileModificationTime)
-                    return false;
-                if (fileModificationTime->secondsSinceEpoch().secondsAs<time_t>() != fileData.expectedFileModificationTime->secondsSinceEpoch().secondsAs<time_t>())
-                    return false;
-            }
-            const String& path = fileData.shouldGenerateFile ? fileData.generatedFilename : fileData.filename;
+            // Check if the file has been changed.
+            if (!fileData.fileModificationTimeMatchesExpectation())
+                return false;
+
+            const String& path = fileData.filename;
             form->currentStream = CFReadStreamCreateWithFile(0, FileSystem::pathAsURL(path).get());
             if (!form->currentStream) {
                 // The file must have been removed or become unreadable.
@@ -208,24 +207,20 @@ static bool openNextStream(FormStreamFields* form)
 static void* formCreate(CFReadStreamRef stream, void* context)
 {
     FormCreationContext* formContext = static_cast<FormCreationContext*>(context);
-
-    FormStreamFields* newInfo = new FormStreamFields;
-    newInfo->formData = WTFMove(formContext->formData);
-    newInfo->currentStream = 0;
-    newInfo->currentStreamRangeLength = BlobDataItem::toEndOfFile;
+    
+    FormStreamFields* newInfo = new FormStreamFields(WTFMove(formContext->data));
     newInfo->formStream = stream; // Don't retain. That would create a reference cycle.
     newInfo->streamLength = formContext->streamLength;
-    newInfo->bytesSent = 0;
     
     callOnMainThread([formContext] {
         delete formContext;
     });
 
     // Append in reverse order since we remove elements from the end.
-    size_t size = newInfo->formData->elements().size();
+    size_t size = newInfo->data.data().elements().size();
     newInfo->remainingElements.reserveInitialCapacity(size);
     for (size_t i = 0; i < size; ++i)
-        newInfo->remainingElements.uncheckedAppend(newInfo->formData->elements()[size - i - 1]);
+        newInfo->remainingElements.uncheckedAppend(newInfo->data.data().elements()[size - i - 1]);
 
     return newInfo;
 }
@@ -317,7 +312,7 @@ static CFTypeRef formCopyProperty(CFReadStreamRef, CFStringRef propertyName, voi
     FormStreamFields* form = static_cast<FormStreamFields*>(context);
 
     if (kCFCompareEqualTo == CFStringCompare(propertyName, formDataPointerPropertyName, 0)) {
-        long long formDataAsNumber = static_cast<long long>(reinterpret_cast<intptr_t>(form->formData.get()));
+        long long formDataAsNumber = static_cast<long long>(reinterpret_cast<intptr_t>(&form->data.data()));
         return CFNumberCreate(0, kCFNumberLongLongType, &formDataAsNumber);
     }
 
@@ -378,13 +373,14 @@ static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, vo
 RetainPtr<CFReadStreamRef> createHTTPBodyCFReadStream(FormData& formData)
 {
     auto resolvedFormData = formData.resolveBlobReferences(blobRegistry());
+    auto dataForUpload = resolvedFormData->prepareForUpload();
 
     // Precompute the content length so CFNetwork doesn't use chunked mode.
     unsigned long long length = 0;
-    for (auto& element : resolvedFormData->elements())
+    for (auto& element : dataForUpload.data().elements())
         length += element.lengthInBytes();
 
-    FormCreationContext* formContext = new FormCreationContext { WTFMove(resolvedFormData), length };
+    FormCreationContext* formContext = new FormCreationContext { WTFMove(dataForUpload), length };
     CFReadStreamCallBacksV1 callBacks = { 1, formCreate, formFinalize, nullptr, formOpen, nullptr, formRead, nullptr, formCanRead, formClose, formCopyProperty, nullptr, nullptr, formSchedule, formUnschedule };
     return adoptCF(CFReadStreamCreate(nullptr, static_cast<const void*>(&callBacks), formContext));
 }
index b692920..114bbd0 100644 (file)
 
 #include <wtf/FileMetadata.h>
 #include <wtf/FileSystem.h>
-#include <wtf/SoftLinking.h>
 #include <wtf/text/CString.h>
 
-#if USE(APPLE_INTERNAL_SDK)
-#include <Bom/BOMCopier.h>
-#endif
-
-typedef struct _BOMCopier* BOMCopier;
-
-SOFT_LINK_PRIVATE_FRAMEWORK(Bom)
-SOFT_LINK(Bom, BOMCopierNew, BOMCopier, (), ())
-SOFT_LINK(Bom, BOMCopierFree, void, (BOMCopier copier), (copier))
-SOFT_LINK(Bom, BOMCopierCopyWithOptions, int, (BOMCopier copier, const char* fromObj, const char* toObj, CFDictionaryRef options), (copier, fromObj, toObj, options))
-
-#define kBOMCopierOptionCreatePKZipKey CFSTR("createPKZip")
-#define kBOMCopierOptionSequesterResourcesKey CFSTR("sequesterResources")
-#define kBOMCopierOptionKeepParentKey CFSTR("keepParent")
-#define kBOMCopierOptionCopyResourcesKey CFSTR("copyResources")
-
 namespace WebCore {
 
 void BlobDataFileReference::generateReplacementFile()
@@ -58,25 +41,9 @@ void BlobDataFileReference::generateReplacementFile()
 
     prepareForFileAccess();
 
-    RetainPtr<NSFileCoordinator> coordinator = adoptNS([[NSFileCoordinator alloc] initWithFilePresenter:nil]);
-    [coordinator coordinateReadingItemAtURL:[NSURL fileURLWithPath:m_path] options:NSFileCoordinatorReadingWithoutChanges error:nullptr byAccessor:^(NSURL *newURL) {
-        // The archive is put into a subdirectory of temporary directory for historic reasons. Changing this will require WebCore to change at the same time.
-        CString archivePath([NSTemporaryDirectory() stringByAppendingPathComponent:@"WebKitGeneratedFileXXXXXX"].fileSystemRepresentation);
-        if (mkstemp(archivePath.mutableData()) == -1)
-            return;
-
-        NSDictionary *options = @{
-            (__bridge id)kBOMCopierOptionCreatePKZipKey : @YES,
-            (__bridge id)kBOMCopierOptionSequesterResourcesKey : @YES,
-            (__bridge id)kBOMCopierOptionKeepParentKey : @YES,
-            (__bridge id)kBOMCopierOptionCopyResourcesKey : @YES,
-        };
-
-        BOMCopier copier = BOMCopierNew();
-        if (!BOMCopierCopyWithOptions(copier, newURL.path.fileSystemRepresentation, archivePath.data(), (__bridge CFDictionaryRef)options))
-            m_replacementPath = String::fromUTF8(archivePath);
-        BOMCopierFree(copier);
-    }];
+    auto generatedFile = FileSystem::createTemporaryZipArchive(m_path);
+    if (!generatedFile.isNull())
+        m_replacementPath = WTFMove(generatedFile);
 
     m_replacementShouldBeGenerated = false;
     if (!m_replacementPath.isNull()) {
index 5c6474f..0428033 100644 (file)
@@ -533,8 +533,7 @@ ExceptionOr<void> XMLHttpRequest::send(DOMFormData& body)
         return WTFMove(result.value());
 
     if (m_method != "GET" && m_method != "HEAD") {
-        m_requestEntityBody = FormData::createMultiPart(body, document());
-        m_requestEntityBody->generateFiles(document());
+        m_requestEntityBody = FormData::createMultiPart(body);
         if (!m_requestHeaders.contains(HTTPHeaderName::ContentType))
             m_requestHeaders.set(HTTPHeaderName::ContentType, makeString("multipart/form-data; boundary=", m_requestEntityBody->boundary().data()));
     }
index de0a639..3466137 100644 (file)
@@ -1,3 +1,29 @@
+2019-08-01  Alex Christensen  <achristensen@webkit.org>
+
+        Move FormData zip file generation to NetworkProcess and enable it for all WebKit clients for uploading directories
+        https://bugs.webkit.org/show_bug.cgi?id=200102
+        <rdar://problem/53275114>
+
+        Reviewed by Darin Adler.
+
+        * NetworkProcess/NetworkResourceLoadParameters.cpp:
+        (WebKit::NetworkResourceLoadParameters::encode const):
+        * Platform/IPC/FormDataReference.h:
+        (IPC::FormDataReference::encode const):
+        * Resources/SandboxProfiles/ios/com.apple.WebKit.Networking.sb:
+        * WebProcess/InjectedBundle/API/APIInjectedBundlePageUIClient.h:
+        (API::InjectedBundle::PageUIClient::shouldGenerateFileForUpload): Deleted.
+        (API::InjectedBundle::PageUIClient::generateFileForUpload): Deleted.
+        * WebProcess/InjectedBundle/InjectedBundlePageUIClient.cpp:
+        (WebKit::InjectedBundlePageUIClient::shouldGenerateFileForUpload): Deleted.
+        (WebKit::InjectedBundlePageUIClient::generateFileForUpload): Deleted.
+        * WebProcess/InjectedBundle/InjectedBundlePageUIClient.h:
+        * WebProcess/WebCoreSupport/WebChromeClient.cpp:
+        (WebKit::WebChromeClient::shouldReplaceWithGeneratedFileForUpload): Deleted.
+        (WebKit::WebChromeClient::generateReplacementFile): Deleted.
+        * WebProcess/WebCoreSupport/WebChromeClient.h:
+        * WebProcess/com.apple.WebProcess.sb.in:
+
 2019-08-01  Tim Horton  <timothy_horton@apple.com>
 
         REGRESSION: HSBC Personal Banking download/print dialog is usually positioned off screen on iPad
index 0e44594..bf8a0a6 100644 (file)
@@ -57,7 +57,7 @@ void NetworkResourceLoadParameters::encode(IPC::Encoder& encoder) const
         for (size_t i = 0, count = elements.size(); i < count; ++i) {
             const FormDataElement& element = elements[i];
             if (auto* fileData = WTF::get_if<FormDataElement::EncodedFileData>(element.data)) {
-                const String& path = fileData->shouldGenerateFile ? fileData->generatedFilename : fileData->filename;
+                const String& path = fileData->filename;
                 SandboxExtension::createHandle(path, SandboxExtension::Type::ReadOnly, requestBodySandboxExtensions[extensionIndex++]);
             }
         }
index 8a395a6..bc497c6 100644 (file)
@@ -60,7 +60,7 @@ public:
         size_t extensionIndex = 0;
         for (auto& element : elements) {
             if (auto* fileData = WTF::get_if<WebCore::FormDataElement::EncodedFileData>(element.data)) {
-                const String& path = fileData->shouldGenerateFile ? fileData->generatedFilename : fileData->filename;
+                const String& path = fileData->filename;
                 WebKit::SandboxExtension::createHandle(path, WebKit::SandboxExtension::Type::ReadOnly, sandboxExtensionHandles[extensionIndex++]);
             }
         }
index 80716ec..b6d1b18 100644 (file)
@@ -83,6 +83,7 @@
     (global-name "com.apple.passd.library"))
 
 (allow mach-lookup
+    (global-name "com.apple.FileCoordination")
     (global-name "com.apple.dmd.policy")
     (global-name "com.apple.siri.context.service")
     (global-name "com.apple.ctcategories.service"))
index eaca3e7..95f4382 100644 (file)
@@ -58,9 +58,6 @@ public:
     virtual void mouseDidMoveOverElement(WebKit::WebPage*, const WebCore::HitTestResult&, OptionSet<WebKit::WebEvent::Modifier>, RefPtr<API::Object>& userData) { UNUSED_PARAM(userData); }
     virtual void pageDidScroll(WebKit::WebPage*) { }
 
-    virtual WTF::String shouldGenerateFileForUpload(WebKit::WebPage*, const WTF::String& originalFilePath) { UNUSED_PARAM(originalFilePath); return WTF::String(); }
-    virtual WTF::String generateFileForUpload(WebKit::WebPage*, const WTF::String& originalFilePath) { UNUSED_PARAM(originalFilePath); return emptyString(); }
-
     enum class UIElementVisibility {
         Unknown,
         Visible,
index 58633d9..3ffb803 100644 (file)
@@ -93,22 +93,6 @@ void InjectedBundlePageUIClient::pageDidScroll(WebPage* page)
     m_client.pageDidScroll(toAPI(page), m_client.base.clientInfo);
 }
 
-String InjectedBundlePageUIClient::shouldGenerateFileForUpload(WebPage* page, const String& originalFilePath)
-{
-    if (!m_client.shouldGenerateFileForUpload)
-        return String();
-    RefPtr<API::String> generatedFilePath = adoptRef(toImpl(m_client.shouldGenerateFileForUpload(toAPI(page), toAPI(originalFilePath.impl()), m_client.base.clientInfo)));
-    return generatedFilePath ? generatedFilePath->string() : String();
-}
-
-String InjectedBundlePageUIClient::generateFileForUpload(WebPage* page, const String& originalFilePath)
-{
-    if (!m_client.generateFileForUpload)
-        return String();
-    RefPtr<API::String> generatedFilePath = adoptRef(toImpl(m_client.generateFileForUpload(toAPI(page), toAPI(originalFilePath.impl()), m_client.base.clientInfo)));
-    return generatedFilePath ? generatedFilePath->string() : String();
-}
-
 static API::InjectedBundle::PageUIClient::UIElementVisibility toUIElementVisibility(WKBundlePageUIElementVisibility visibility)
 {
     switch (visibility) {
index 59f1b1c..627844b 100644 (file)
@@ -54,9 +54,6 @@ public:
     void mouseDidMoveOverElement(WebPage*, const WebCore::HitTestResult&, OptionSet<WebEvent::Modifier>, RefPtr<API::Object>& userData) override;
     void pageDidScroll(WebPage*) override;
 
-    String shouldGenerateFileForUpload(WebPage*, const String& originalFilePath) override;
-    String generateFileForUpload(WebPage*, const String& originalFilePath) override;
-    
     UIElementVisibility statusBarIsVisible(WebPage*) override;
     UIElementVisibility menuBarIsVisible(WebPage*) override;
     UIElementVisibility toolbarsAreVisible(WebPage*) override;
index 1a024f7..3c872f5 100644 (file)
@@ -789,17 +789,6 @@ void WebChromeClient::reachedApplicationCacheOriginQuota(SecurityOrigin& origin,
     cacheStorage.storeUpdatedQuotaForOrigin(&origin, newQuota);
 }
 
-bool WebChromeClient::shouldReplaceWithGeneratedFileForUpload(const String& path, String& generatedFilename)
-{
-    generatedFilename = m_page.injectedBundleUIClient().shouldGenerateFileForUpload(&m_page, path);
-    return !generatedFilename.isNull();
-}
-
-String WebChromeClient::generateReplacementFile(const String& path)
-{
-    return m_page.injectedBundleUIClient().generateFileForUpload(&m_page, path);
-}
-
 #if ENABLE(INPUT_TYPE_COLOR)
 
 std::unique_ptr<ColorChooser> WebChromeClient::createColorChooser(ColorChooserClient& client, const Color& initialColor)
index afb5f7e..ced36a6 100644 (file)
@@ -142,9 +142,6 @@ private:
 
     void reachedMaxAppCacheSize(int64_t spaceNeeded) final;
     void reachedApplicationCacheOriginQuota(WebCore::SecurityOrigin&, int64_t spaceNeeded) final;
-
-    bool shouldReplaceWithGeneratedFileForUpload(const String& path, String& generatedFilename) final;
-    String generateReplacementFile(const String& path) final;
     
 #if ENABLE(INPUT_TYPE_COLOR)
     std::unique_ptr<WebCore::ColorChooser> createColorChooser(WebCore::ColorChooserClient&, const WebCore::Color&) final;
index dfd4bd9..394d5a2 100644 (file)
 (allow mach-lookup
        (global-name "com.apple.webinspector"))
 
-(allow mach-lookup
-    (global-name "com.apple.FileCoordination"))
-
 ;; Various services required by AppKit and other frameworks
 (allow mach-lookup
 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 101400
         (syscall-number SYS_stat64_extended) ;; <rdar://problem/50473330>
         (syscall-number SYS_lstat_extended)
         (syscall-number SYS_lstat64_extended)
-        (syscall-number SYS_iopolicysys)
-        (syscall-number SYS_workq_open)
-        (syscall-number SYS_getgroups)
         (syscall-number SYS_fgetattrlist) ;; <rdar://problem/50931110>
         (syscall-number SYS_kqueue) ;; <rdar://problem/49609201>
         (syscall-number SYS_kqueue_workloop_ctl) ;; <rdar://problem/50999499>
-        (syscall-number SYS_open_dprotected_np)
-        (syscall-number SYS_flistxattr)
-        (syscall-number SYS_fsetattrlist)
-        (syscall-number SYS_fchown)
-        (syscall-number SYS_fchflags)
-        (syscall-number SYS_fstat64_extended)
     )
 )
 
index 0b892cf..f67ca0a 100644 (file)
@@ -1,3 +1,20 @@
+2019-08-01  Alex Christensen  <achristensen@webkit.org>
+
+        Move FormData zip file generation to NetworkProcess and enable it for all WebKit clients for uploading directories
+        https://bugs.webkit.org/show_bug.cgi?id=200102
+        <rdar://problem/53275114>
+
+        Reviewed by Darin Adler.
+
+        * DefaultDelegates/WebDefaultUIDelegate.mm:
+        (-[WebDefaultUIDelegate webView:shouldReplaceUploadFile:usingGeneratedFilename:]): Deleted.
+        (-[WebDefaultUIDelegate webView:generateReplacementFile:]): Deleted.
+        * WebCoreSupport/WebChromeClient.h:
+        * WebCoreSupport/WebChromeClient.mm:
+        (WebChromeClient::shouldReplaceWithGeneratedFileForUpload): Deleted.
+        (WebChromeClient::generateReplacementFile): Deleted.
+        * WebView/WebUIDelegatePrivate.h:
+
 2019-07-25  Dean Jackson  <dino@apple.com>
 
         Add helper for ignoring deprecated implementation warnings
index 358aa69..55201a6 100644 (file)
@@ -256,16 +256,6 @@ static WebDefaultUIDelegate *sharedDelegate = nil;
 {
 }
 
-- (BOOL)webView:(WebView *)sender shouldReplaceUploadFile:(NSString *)path usingGeneratedFilename:(NSString **)filename
-{
-    return NO;
-}
-
-- (NSString *)webView:(WebView *)sender generateReplacementFile:(NSString *)path
-{
-    return nil;
-}
-
 #if PLATFORM(IOS_FAMILY)
 - (void)webViewSupportedOrientationsUpdated:(WebView *)sender
 {
index b341249..fdc4cc7 100644 (file)
@@ -158,9 +158,6 @@ private:
     void enableSuddenTermination() final;
     void disableSuddenTermination() final;
 
-    bool shouldReplaceWithGeneratedFileForUpload(const String& path, String &generatedFilename) final;
-    String generateReplacementFile(const String& path) final;
-
 #if !PLATFORM(IOS_FAMILY)
     void elementDidFocus(WebCore::Element&) override;
     void elementDidBlur(WebCore::Element&) override;
index 3f96b4a..e1a8989 100644 (file)
@@ -855,20 +855,6 @@ void WebChromeClient::disableSuddenTermination()
 #endif
 }
 
-bool WebChromeClient::shouldReplaceWithGeneratedFileForUpload(const String& path, String& generatedFilename)
-{
-    NSString* filename;
-    if (![[m_webView _UIDelegateForwarder] webView:m_webView shouldReplaceUploadFile:path usingGeneratedFilename:&filename])
-        return false;
-    generatedFilename = filename;
-    return true;
-}
-
-String WebChromeClient::generateReplacementFile(const String& path)
-{
-    return [[m_webView _UIDelegateForwarder] webView:m_webView generateReplacementFile:path];
-}
-
 #if !PLATFORM(IOS_FAMILY)
 void WebChromeClient::elementDidFocus(WebCore::Element& element)
 {
index 1ae2bc2..a0e7161 100644 (file)
@@ -248,9 +248,6 @@ extern NSString *WebConsoleMessageErrorMessageLevel;
 
 - (WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features;
 
-- (BOOL)webView:(WebView *)sender shouldReplaceUploadFile:(NSString *)path usingGeneratedFilename:(NSString **)filename;
-- (NSString *)webView:(WebView *)sender generateReplacementFile:(NSString *)path;
-
 /*!
     @method webView:decidePolicyForGeolocationRequestFromOrigin:frame:listener:
     @param webView The WebView sending the delegate method.
index cfe9e78..9a509d4 100644 (file)
@@ -1,3 +1,24 @@
+2019-08-01  Alex Christensen  <achristensen@webkit.org>
+
+        Move FormData zip file generation to NetworkProcess and enable it for all WebKit clients for uploading directories
+        https://bugs.webkit.org/show_bug.cgi?id=200102
+        <rdar://problem/53275114>
+
+        Reviewed by Darin Adler.
+
+        Add an API test that is Mac-only right now because runOpenPanelWithParameters is only supported on Mac for some reason
+        and because clicking on a TestWKWebView only works on Mac.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKitCocoa/UploadDirectory.mm: Added.
+        (-[UploadDelegate initWithDirectory:]):
+        (-[UploadDelegate webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:]):
+        (-[UploadDelegate sentDirectory]):
+        (TEST):
+        * TestWebKitAPI/cocoa/TestWKWebView.h:
+        * TestWebKitAPI/cocoa/TestWKWebView.mm:
+        (-[TestWKWebView sendClickAtPoint:]):
+
 2019-08-01  Zhifei Fang  <zhifei_fang@apple.com>
 
         [results.webkit.org] Timeline.CanvasXAxisComponent height should be defined by option
index 003e830..9be2103 100644 (file)
                5C23DF0B2246015800F454B6 /* Challenge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C23DF0A2245C9D700F454B6 /* Challenge.mm */; };
                5C2936931D5BF70D00DEAB1E /* CookieAcceptPolicy.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C2936911D5BF63E00DEAB1E /* CookieAcceptPolicy.mm */; };
                5C2936961D5C00ED00DEAB1E /* CookieMessage.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 5C2936941D5BFD1900DEAB1E /* CookieMessage.html */; };
+               5C3A77AA22F20BEA003827FF /* UploadDirectory.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C3A77A922F20B8A003827FF /* UploadDirectory.mm */; };
                5C3B1D2622A74F6700BCF4D0 /* ContextMenus.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C3B1D2522A74EA400BCF4D0 /* ContextMenus.mm */; };
                5C4259462266A68A0039AA7A /* BasicProposedCredentialPlugIn.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C42594422669E9B0039AA7A /* BasicProposedCredentialPlugIn.mm */; };
                5C4A84951F7EEFFC00ACFC54 /* Configuration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C4A84941F7EEFD400ACFC54 /* Configuration.mm */; };
                5C23DF0A2245C9D700F454B6 /* Challenge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Challenge.mm; sourceTree = "<group>"; };
                5C2936911D5BF63E00DEAB1E /* CookieAcceptPolicy.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CookieAcceptPolicy.mm; sourceTree = "<group>"; };
                5C2936941D5BFD1900DEAB1E /* CookieMessage.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = CookieMessage.html; sourceTree = "<group>"; };
+               5C3A77A922F20B8A003827FF /* UploadDirectory.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = UploadDirectory.mm; sourceTree = "<group>"; };
                5C3B1D2522A74EA400BCF4D0 /* ContextMenus.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ContextMenus.mm; sourceTree = "<group>"; };
                5C42594422669E9B0039AA7A /* BasicProposedCredentialPlugIn.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BasicProposedCredentialPlugIn.mm; sourceTree = "<group>"; };
                5C4A84941F7EEFD400ACFC54 /* Configuration.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Configuration.mm; sourceTree = "<group>"; };
                                F4CD74C820FDB49600DE3794 /* TestURLSchemeHandler.mm */,
                                C22FA32A228F8708009D7988 /* TextWidth.mm */,
                                5CB40B4D1F4B98BE007DC7B9 /* UIDelegate.mm */,
+                               5C3A77A922F20B8A003827FF /* UploadDirectory.mm */,
                                7CC3E1FA197E234100BE6252 /* UserContentController.mm */,
                                7C882E031C80C624006BF731 /* UserContentWorld.mm */,
                                7C882E041C80C624006BF731 /* UserContentWorldPlugIn.mm */,
                                5C9D923A22D7E2B0008E9266 /* UnifiedSource4-mm.mm in Sources */,
                                5C9D923C22D7E2B0008E9266 /* UnifiedSource5-mm.mm in Sources */,
                                5C9D923D22D7E2B0008E9266 /* UnifiedSource5.cpp in Sources */,
+                               5C3A77AA22F20BEA003827FF /* UploadDirectory.mm in Sources */,
                                5C6E27A7224EEBEA00128736 /* URLCanonicalization.mm in Sources */,
                                E3A1E77F21B25B39008C6007 /* URLParserTextEncoding.cpp in Sources */,
                                7CCE7F271A411AF600447C4C /* UserContentController.mm in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/UploadDirectory.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/UploadDirectory.mm
new file mode 100644 (file)
index 0000000..849278b
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2019 Apple 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:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+#import "config.h"
+
+#if PLATFORM(MAC)
+
+#import "DragAndDropSimulator.h"
+#import "TCPServer.h"
+#import "TestNavigationDelegate.h"
+#import "TestWKWebView.h"
+#import "Utilities.h"
+#import <WebKit/WebKit.h>
+#import <wtf/RetainPtr.h>
+#import <wtf/text/WTFString.h>
+
+@interface UploadDelegate : NSObject <WKUIDelegate>
+- (instancetype)initWithDirectory:(NSURL *)directory;
+- (BOOL)sentDirectory;
+@end
+
+@implementation UploadDelegate {
+    RetainPtr<NSURL> _directory;
+    BOOL _sentDirectory;
+}
+
+- (instancetype)initWithDirectory:(NSURL *)directory
+{
+    if (!(self = [super init]))
+        return nil;
+    _directory = directory;
+    return self;
+}
+
+- (void)webView:(WKWebView *)webView runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSArray<NSURL *> * _Nullable URLs))completionHandler
+{
+    completionHandler(@[_directory.get()]);
+    _sentDirectory = YES;
+}
+
+- (BOOL)sentDirectory
+{
+    return _sentDirectory;
+}
+
+@end
+
+TEST(WebKit, UploadDirectory)
+{
+    NSFileManager *fileManager = [NSFileManager defaultManager];
+    NSError *error = nil;
+    NSURL *directory = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"UploadDirectory"] isDirectory:YES];
+    EXPECT_FALSE([fileManager fileExistsAtPath:directory.path]);
+    EXPECT_TRUE([fileManager createDirectoryAtURL:directory withIntermediateDirectories:YES attributes:nil error:&error]);
+    EXPECT_FALSE(error);
+    NSData *testData = [@"testdata" dataUsingEncoding:NSUTF8StringEncoding];
+    EXPECT_TRUE([fileManager createFileAtPath:[directory.path stringByAppendingPathComponent:@"testfile"] contents:testData attributes:nil]);
+
+    {
+        using namespace TestWebKitAPI;
+        TCPServer server([] (int socket) {
+            TCPServer::read(socket);
+            const char* response =
+            "HTTP/1.1 200 OK\r\n"
+            "Content-Type: text/html\r\n"
+            "Content-Length: 123\r\n\r\n"
+            "<form id='form' action='/upload.php' method='post' enctype='multipart/form-data'><input type='file' name='testname'></form>";
+            TCPServer::write(socket, response, strlen(response));
+
+            auto header = TCPServer::read(socket);
+            EXPECT_TRUE(String(header.data(), header.size()).contains("Content-Length: 543"));
+            size_t bodyBytesRead = 0;
+            while (bodyBytesRead < 543)
+                bodyBytesRead += TCPServer::read(socket).size();
+            EXPECT_EQ(bodyBytesRead, 543ull);
+            const char* secondResponse =
+            "HTTP/1.1 200 OK\r\n"
+            "Content-Length: 0\r\n\r\n";
+            TCPServer::write(socket, secondResponse, strlen(secondResponse));
+        });
+
+        auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
+        auto delegate = adoptNS([[UploadDelegate alloc] initWithDirectory:directory]);
+        [webView setUIDelegate:delegate.get()];
+
+        [webView synchronouslyLoadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/", server.port()]]]];
+
+        auto chooseFileButtonLocation = NSMakePoint(10, 590);
+        [webView sendClickAtPoint:chooseFileButtonLocation];
+        while (![delegate sentDirectory])
+            TestWebKitAPI::Util::spinRunLoop();
+        [webView evaluateJavaScript:@"document.getElementById('form').submit()" completionHandler:nil];
+        [webView _test_waitForDidFinishNavigation];
+    }
+
+    EXPECT_TRUE([fileManager removeItemAtPath:directory.path error:&error]);
+    EXPECT_FALSE(error);
+}
+
+#endif
index ede0680..f0fed5d 100644 (file)
 - (void)mouseUpAtPoint:(NSPoint)pointInWindow;
 - (void)mouseMoveToPoint:(NSPoint)pointInWindow withFlags:(NSEventModifierFlags)flags;
 - (void)sendClicksAtPoint:(NSPoint)pointInWindow numberOfClicks:(NSUInteger)numberOfClicks;
+- (void)sendClickAtPoint:(NSPoint)pointInWindow;
 - (NSWindow *)hostWindow;
 - (void)typeCharacter:(char)character;
 @end
index 3da8172..546ca6e 100644 (file)
@@ -593,6 +593,11 @@ static WKContentView *recursiveFindWKContentView(UIView *view)
     }
 }
 
+- (void)sendClickAtPoint:(NSPoint)pointInWindow
+{
+    [self sendClicksAtPoint:pointInWindow numberOfClicks:1];
+}
+
 - (void)mouseEnterAtPoint:(NSPoint)pointInWindow
 {
     [self mouseEntered:[self _mouseEventWithType:NSEventTypeMouseEntered atLocation:pointInWindow]];