Automatically zip document bundles used via File API
authorap@apple.com <ap@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 9 May 2014 04:37:25 +0000 (04:37 +0000)
committerap@apple.com <ap@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 9 May 2014 04:37:25 +0000 (04:37 +0000)
https://bugs.webkit.org/show_bug.cgi?id=132713
<rdar://problem/13397892>

Reviewed by Anders Carlsson.

Source/WebCore:
Tests: fast/files/filereader-zip-bundle.html
       http/tests/local/fileapi/upload-zip-bundle-as-blob.html

* FileMac.mm: Added.
(WebCore::File::shouldReplaceFile):
(WebCore::File::computeNameAndContentTypeForReplacedFile):
Added code that decides what to do with a bundle. We need this to happen in
WebProcess, because HTML5 dropzone depends on this, it needs to know file type
even before a File object can be created.

* WebCore.exp.in: Export new functions.

* WebCore.xcodeproj/project.pbxproj: Added new files.

* dom/DataTransfer.cpp: (WebCore::DataTransfer::hasFileOfType): Updated for File
interface change.

* fileapi/File.h:
* fileapi/File.cpp:
(WebCore::File::File):
(WebCore::File::computeNameAndContentType):
(WebCore::File::contentTypeForFile):
(WebCore::File::contentTypeFromFilePathOrName): Deleted.
Make it possible for a subclass to affect file name and content type calculation.

* platform/network/BlobDataFileReference.cpp:
(WebCore::BlobDataFileReference::BlobDataFileReference):
(WebCore::BlobDataFileReference::~BlobDataFileReference):
(WebCore::BlobDataFileReference::path):
(WebCore::BlobDataFileReference::size):
(WebCore::BlobDataFileReference::expectedModificationTime):
(WebCore::BlobDataFileReference::startTrackingModifications):
* platform/network/BlobDataFileReference.h:
(WebCore::BlobDataFileReference::path): Deleted.
(WebCore::BlobDataFileReference::BlobDataFileReference): Deleted.
Use original or replaced file, as appropriate.

* platform/network/mac/BlobDataFileReferenceMac.mm: Added.
(WebCore::BlobDataFileReference::generateReplacementFile): Implements generateReplacementFile().

* platform/network/FormData.h: Added a FIXME.

* xml/XMLHttpRequest.cpp: (WebCore::XMLHttpRequest::send): Always use Blob code path
for blobs, don't unwrap them into a file path.

Source/WebKit2:
* NetworkProcess/mac/com.apple.WebKit.NetworkProcess.sb.in: NetworkProcess
now uses FileCoordination (WebProcess already had this allowed).

* Shared/BlobDataFileReferenceWithSandboxExtension.h: Added final to the class.

Source/WTF:
* wtf/FeatureDefines.h: Added ENABLE_FILE_REPLACEMENT for Mac.

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

17 files changed:
Source/WTF/ChangeLog
Source/WTF/wtf/FeatureDefines.h
Source/WebCore/ChangeLog
Source/WebCore/WebCore.exp.in
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/dom/DataTransfer.cpp
Source/WebCore/fileapi/File.cpp
Source/WebCore/fileapi/File.h
Source/WebCore/fileapi/FileMac.mm [new file with mode: 0644]
Source/WebCore/platform/network/BlobDataFileReference.cpp
Source/WebCore/platform/network/BlobDataFileReference.h
Source/WebCore/platform/network/FormData.h
Source/WebCore/platform/network/mac/BlobDataFileReferenceMac.mm [new file with mode: 0644]
Source/WebCore/xml/XMLHttpRequest.cpp
Source/WebKit2/ChangeLog
Source/WebKit2/NetworkProcess/mac/com.apple.WebKit.NetworkProcess.sb.in
Source/WebKit2/Shared/BlobDataFileReferenceWithSandboxExtension.h

index 942c6d0..b85b748 100644 (file)
@@ -1,3 +1,13 @@
+2014-05-08  Alexey Proskuryakov  <ap@apple.com>
+
+        Automatically zip document bundles used via File API
+        https://bugs.webkit.org/show_bug.cgi?id=132713
+        <rdar://problem/13397892>
+
+        Reviewed by Anders Carlsson.
+
+        * wtf/FeatureDefines.h: Added ENABLE_FILE_REPLACEMENT for Mac.
+
 2014-05-07  Filip Pizlo  <fpizlo@apple.com>
 
         UNREACHABLE_FOR_PLATFORM() is meant to be a release crash.
index 01b7d7b..d0b38a5 100644 (file)
 #define ENABLE_MEDIA_SOURCE 1
 #endif
 
+#if !defined(ENABLE_FILE_REPLACEMENT)
+#define ENABLE_FILE_REPLACEMENT 1
+#endif
+
 #endif /* PLATFORM(MAC) */
 
 /* --------- Apple Windows port --------- */
index f5edf2e..7e24b6f 100644 (file)
@@ -1,3 +1,56 @@
+2014-05-08  Alexey Proskuryakov  <ap@apple.com>
+
+        Automatically zip document bundles used via File API
+        https://bugs.webkit.org/show_bug.cgi?id=132713
+        <rdar://problem/13397892>
+
+        Reviewed by Anders Carlsson.
+
+        Tests: fast/files/filereader-zip-bundle.html
+               http/tests/local/fileapi/upload-zip-bundle-as-blob.html
+
+        * FileMac.mm: Added.
+        (WebCore::File::shouldReplaceFile):
+        (WebCore::File::computeNameAndContentTypeForReplacedFile):
+        Added code that decides what to do with a bundle. We need this to happen in
+        WebProcess, because HTML5 dropzone depends on this, it needs to know file type
+        even before a File object can be created.
+
+        * WebCore.exp.in: Export new functions.
+
+        * WebCore.xcodeproj/project.pbxproj: Added new files.
+
+        * dom/DataTransfer.cpp: (WebCore::DataTransfer::hasFileOfType): Updated for File
+        interface change.
+
+        * fileapi/File.h:
+        * fileapi/File.cpp:
+        (WebCore::File::File):
+        (WebCore::File::computeNameAndContentType):
+        (WebCore::File::contentTypeForFile):
+        (WebCore::File::contentTypeFromFilePathOrName): Deleted.
+        Make it possible for a subclass to affect file name and content type calculation.
+
+        * platform/network/BlobDataFileReference.cpp:
+        (WebCore::BlobDataFileReference::BlobDataFileReference):
+        (WebCore::BlobDataFileReference::~BlobDataFileReference):
+        (WebCore::BlobDataFileReference::path):
+        (WebCore::BlobDataFileReference::size):
+        (WebCore::BlobDataFileReference::expectedModificationTime):
+        (WebCore::BlobDataFileReference::startTrackingModifications):
+        * platform/network/BlobDataFileReference.h:
+        (WebCore::BlobDataFileReference::path): Deleted.
+        (WebCore::BlobDataFileReference::BlobDataFileReference): Deleted.
+        Use original or replaced file, as appropriate. 
+
+        * platform/network/mac/BlobDataFileReferenceMac.mm: Added.
+        (WebCore::BlobDataFileReference::generateReplacementFile): Implements generateReplacementFile().
+
+        * platform/network/FormData.h: Added a FIXME.
+
+        * xml/XMLHttpRequest.cpp: (WebCore::XMLHttpRequest::send): Always use Blob code path
+        for blobs, don't unwrap them into a file path.
+
 2014-05-08  Simon Fraser  <simon.fraser@apple.com>
 
         Occasional crash under AsyncScrollingCoordinator::frameViewRootLayerDidChange() on history navigation
index c067c53..6b350cc 100644 (file)
@@ -867,6 +867,8 @@ __ZN7WebCore21AudioHardwareListener6createERNS0_6ClientE
 __ZN7WebCore21BackForwardController11itemAtIndexEi
 __ZN7WebCore21BackForwardController6goBackEv
 __ZN7WebCore21BackForwardController9goForwardEv
+__ZN7WebCore21BlobDataFileReference4pathEv
+__ZN7WebCore21BlobDataFileReferenceC2ERKN3WTF6StringE
 __ZN7WebCore21BlobDataFileReferenceD2Ev
 __ZN7WebCore21CrossThreadCopierBaseILb0ELb0EN3WTF6StringEE4copyERKS2_
 __ZN7WebCore21CrossThreadCopierBaseILb0ELb0ENS_19IDBDatabaseMetadataEE4copyERKS1_
index d1bfdb0..fd46f33 100644 (file)
                E15A36D71104572000B7B639 /* XMLNSNames.h in Headers */ = {isa = PBXBuildFile; fileRef = E15A36D61104572000B7B639 /* XMLNSNames.h */; };
                E15A36D91104572700B7B639 /* XMLNSNames.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E15A36D81104572700B7B639 /* XMLNSNames.cpp */; };
                E15FF7D518C9553800FE4C87 /* KeypressCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = E15FF7D418C9553800FE4C87 /* KeypressCommand.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               E164A2E9191AC5BB0010737D /* FileMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = E164A2E7191AC5BB0010737D /* FileMac.mm */; };
+               E164A2ED191AE6350010737D /* BlobDataFileReferenceMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = E164A2EB191AE6350010737D /* BlobDataFileReferenceMac.mm */; };
                E164FAA318315BF400DB4E61 /* CryptoKeyRSA.h in Headers */ = {isa = PBXBuildFile; fileRef = E164FAA218315BF400DB4E61 /* CryptoKeyRSA.h */; };
                E164FAA518315E1A00DB4E61 /* CryptoKeyRSAMac.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E164FAA418315E1A00DB4E61 /* CryptoKeyRSAMac.cpp */; };
                E169803D1133542D00894115 /* CRuntimeObject.h in Headers */ = {isa = PBXBuildFile; fileRef = E169803C1133542D00894115 /* CRuntimeObject.h */; };
                E15A36D61104572000B7B639 /* XMLNSNames.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XMLNSNames.h; sourceTree = "<group>"; };
                E15A36D81104572700B7B639 /* XMLNSNames.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XMLNSNames.cpp; sourceTree = "<group>"; };
                E15FF7D418C9553800FE4C87 /* KeypressCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KeypressCommand.h; sourceTree = "<group>"; };
+               E164A2E7191AC5BB0010737D /* FileMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = FileMac.mm; path = fileapi/FileMac.mm; sourceTree = "<group>"; };
+               E164A2EB191AE6350010737D /* BlobDataFileReferenceMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BlobDataFileReferenceMac.mm; sourceTree = "<group>"; };
                E164FAA218315BF400DB4E61 /* CryptoKeyRSA.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CryptoKeyRSA.h; path = keys/CryptoKeyRSA.h; sourceTree = "<group>"; };
                E164FAA418315E1A00DB4E61 /* CryptoKeyRSAMac.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CryptoKeyRSAMac.cpp; path = mac/CryptoKeyRSAMac.cpp; sourceTree = "<group>"; };
                E169803C1133542D00894115 /* CRuntimeObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CRuntimeObject.h; sourceTree = "<group>"; };
                        children = (
                                514C76420CE9234E007EF3CD /* AuthenticationMac.h */,
                                514C76430CE9234E007EF3CD /* AuthenticationMac.mm */,
+                               E164A2EB191AE6350010737D /* BlobDataFileReferenceMac.mm */,
                                5F2DBBE8178E336900141486 /* CertificateInfo.h */,
                                5F2DBBE7178E332D00141486 /* CertificateInfoMac.mm */,
                                E1424C8F164B460B00F32D40 /* CookieJarMac.mm */,
                                976D6C5F122B8A3D001FD1F7 /* BlobURL.cpp */,
                                976D6C60122B8A3D001FD1F7 /* BlobURL.h */,
                                976D6C61122B8A3D001FD1F7 /* File.cpp */,
+                               E164A2E7191AC5BB0010737D /* FileMac.mm */,
                                976D6C62122B8A3D001FD1F7 /* File.h */,
                                E1AB1EB714E9E35800449E13 /* File.idl */,
                                976D6C64122B8A3D001FD1F7 /* FileError.h */,
                                49E912AA0EFAC906009D0CAF /* Animation.cpp in Sources */,
                                316FE1110E6E1DA700BF6088 /* AnimationBase.cpp in Sources */,
                                316FE1130E6E1DA700BF6088 /* AnimationController.cpp in Sources */,
+                               E164A2ED191AE6350010737D /* BlobDataFileReferenceMac.mm in Sources */,
                                49E912AC0EFAC906009D0CAF /* AnimationList.cpp in Sources */,
                                93309DD6099E64920056E581 /* AppendNodeCommand.cpp in Sources */,
                                1A8F6BBC0DB55CDC001DB794 /* ApplicationCache.cpp in Sources */,
                                CDCFABBE18C0AF84006F8450 /* SelectionSubtreeRoot.cpp in Sources */,
                                CD27F6E51457685A0078207D /* JSMediaController.cpp in Sources */,
                                CDAB6D2D17C814EE00C60B34 /* JSMediaControlsHost.cpp in Sources */,
+                               E164A2E9191AC5BB0010737D /* FileMac.mm in Sources */,
                                FD23A12513F5FA5900F67001 /* JSMediaElementAudioSourceNode.cpp in Sources */,
                                E44614180CD6826900FADA75 /* JSMediaError.cpp in Sources */,
                                CDA98DA31601464100FEA3B1 /* JSMediaKeyError.cpp in Sources */,
index 5267630..425a5d8 100644 (file)
@@ -188,8 +188,8 @@ bool DataTransfer::hasFileOfType(const String& type)
 {
     ASSERT_WITH_SECURITY_IMPLICATION(canReadTypes());
 
-    for (const String& filename : m_pasteboard->readFilenames()) {
-        if (equalIgnoringCase(File::contentTypeFromFilePathOrName(filename), type))
+    for (const String& path : m_pasteboard->readFilenames()) {
+        if (equalIgnoringCase(File::contentTypeForFile(path), type))
             return true;
     }
 
index a33ed14..9804ffb 100644 (file)
@@ -40,22 +40,20 @@ namespace WebCore {
 File::File(const String& path)
     : Blob(uninitializedContructor)
     , m_path(path)
-    , m_name(pathGetFileName(path))
 {
     m_internalURL = BlobURL::createInternalURL();
-    m_type = contentTypeFromFilePathOrName(path);
     m_size = -1;
+    computeNameAndContentType(m_path, String(), m_name, m_type);
     ThreadableBlobRegistry::registerFileBlobURL(m_internalURL, path, m_type);
 }
 
-File::File(const String& path, const String& name)
+File::File(const String& path, const String& nameOverride)
     : Blob(uninitializedContructor)
     , m_path(path)
-    , m_name(name)
 {
     m_internalURL = BlobURL::createInternalURL();
-    m_type = contentTypeFromFilePathOrName(name);
     m_size = -1;
+    computeNameAndContentType(m_path, nameOverride, m_name, m_type);
     ThreadableBlobRegistry::registerFileBlobURL(m_internalURL, path, m_type);
 }
 
@@ -63,9 +61,9 @@ File::File(DeserializationContructor, const String& path, const URL& url, const
     : Blob(deserializationContructor, url, type, -1)
     , m_path(path)
 {
-    m_name = pathGetFileName(path);
-    // FIXME: File object serialization/deserialization does not include m_name.
-    // See SerializedScriptValue.cpp
+    // FIXME: File object serialization/deserialization does not include m_name, see SerializedScriptValue.cpp.
+    // Once it does, we should take deserialized name verbatim, not compute it again.
+    computeNameAndContentType(m_path, String(), m_name, m_type);
 }
 
 double File::lastModifiedDate() const
@@ -77,13 +75,26 @@ double File::lastModifiedDate() const
     return currentTime() * msPerSecond;
 }
 
-String File::contentTypeFromFilePathOrName(const String& name)
+void File::computeNameAndContentType(const String& path, const String& nameOverride, String& effectiveName, String& effectiveContentType)
 {
-    String type;
-    int index = name.reverseFind('.');
-    if (index != -1) {
-        type = MIMETypeRegistry::getMIMETypeForExtension(name.substring(index + 1));
+#if ENABLE(FILE_REPLACEMENT)
+    if (shouldReplaceFile(path)) {
+        computeNameAndContentTypeForReplacedFile(path, nameOverride, effectiveName, effectiveContentType);
+        return;
     }
+#endif
+    effectiveName = nameOverride.isNull() ? pathGetFileName(path) : nameOverride;
+    size_t index = effectiveName.reverseFind('.');
+    if (index != notFound)
+        effectiveContentType = MIMETypeRegistry::getMIMETypeForExtension(effectiveName.substring(index + 1));
+}
+
+String File::contentTypeForFile(const String& path)
+{
+    String name;
+    String type;
+    computeNameAndContentType(path, String(), name, type);
+
     return type;
 }
 
index 84e356e..e50e4ef 100644 (file)
@@ -47,11 +47,11 @@ public:
     }
 
     // Create a file with a name exposed to the author (via File.name and associated DOM properties) that differs from the one provided in the path.
-    static PassRefPtr<File> createWithName(const String& path, const String& name)
+    static PassRefPtr<File> createWithName(const String& path, const String& nameOverride)
     {
-        if (name.isEmpty())
+        if (nameOverride.isEmpty())
             return adoptRef(new File(path));
-        return adoptRef(new File(path, name));
+        return adoptRef(new File(path, nameOverride));
     }
 
     virtual bool isFile() const override { return true; }
@@ -62,14 +62,23 @@ public:
     // This returns the current date and time if the file's last modification date is not known (per spec: http://www.w3.org/TR/FileAPI/#dfn-lastModifiedDate).
     double lastModifiedDate() const;
 
-    static String contentTypeFromFilePathOrName(const String&);
+    static String contentTypeForFile(const String& path);
+
+#if ENABLE(FILE_REPLACEMENT)
+    static bool shouldReplaceFile(const String& path);
+#endif
 
 private:
     explicit File(const String& path);
-    File(const String& path, const String& name);
+    File(const String& path, const String& nameOverride);
 
     File(DeserializationContructor, const String& path, const URL& srcURL, const String& type);
 
+    static void computeNameAndContentType(const String& path, const String& nameOverride, String& effectiveName, String& effectiveContentType);
+#if ENABLE(FILE_REPLACEMENT)
+    static void computeNameAndContentTypeForReplacedFile(const String& path, const String& nameOverride, String& effectiveName, String& effectiveContentType);
+#endif
+
     String m_path;
     String m_name;
 };
diff --git a/Source/WebCore/fileapi/FileMac.mm b/Source/WebCore/fileapi/FileMac.mm
new file mode 100644 (file)
index 0000000..41f9543
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include "config.h"
+#include "File.h"
+
+#if ENABLE(FILE_REPLACEMENT)
+
+#include "FileSystem.h"
+
+namespace WebCore {
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+bool File::shouldReplaceFile(const String& path)
+{
+    if (path.isEmpty())
+        return false;
+
+    FSRef pathRef;
+    Boolean targetIsFolder;
+    Boolean wasAliased;
+    NSString *aliasedPath = path;
+
+    // Determine if the file is an alias, and if so, get the target path.
+    if (FSPathMakeRef((UInt8 *)[path fileSystemRepresentation], &pathRef, NULL) == noErr) {
+        if (FSResolveAliasFileWithMountFlags(&pathRef, TRUE, &targetIsFolder, &wasAliased, kResolveAliasFileNoUI) == noErr && wasAliased) {
+            char pathFromPathRef[PATH_MAX + 1]; // +1 is for \0 
+            if (FSRefMakePath(&pathRef, (unsigned char *)pathFromPathRef, PATH_MAX) == noErr)
+                aliasedPath = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:pathFromPathRef length:strlen(pathFromPathRef)];
+        }
+    }
+    
+    if (!aliasedPath)
+        return false;
+
+    return [[NSWorkspace sharedWorkspace] isFilePackageAtPath:aliasedPath];
+}
+#pragma clang diagnostic pop
+
+void File::computeNameAndContentTypeForReplacedFile(const String& path, const String& nameOverride, String& effectiveName, String& effectiveContentType)
+{
+    ASSERT(!pathGetFileName(path).endsWith('/')); // Expecting to get a path without trailing slash, even for directories.
+    effectiveContentType = ASCIILiteral("application/zip");
+    effectiveName = (nameOverride.isNull() ? pathGetFileName(path) : nameOverride) + ASCIILiteral(".zip");
+}
+
+}
+
+#endif
index 605ac8e..04e4aa2 100644 (file)
 #include "config.h"
 #include "BlobDataFileReference.h"
 
+#include "File.h"
 #include "FileMetadata.h"
 
 namespace WebCore {
 
+BlobDataFileReference::BlobDataFileReference(const String& path)
+    : m_path(path)
+#if ENABLE(FILE_REPLACEMENT)
+    , m_replacementShouldBeGenerated(false)
+#endif
+    , m_size(0)
+    , m_expectedModificationTime(invalidFileTime())
+{
+}
+
 BlobDataFileReference::~BlobDataFileReference()
 {
+#if ENABLE(FILE_REPLACEMENT)
+    if (!m_replacementPath.isNull())
+        deleteFile(m_replacementPath);
+#endif
 }
 
-unsigned long long BlobDataFileReference::size() const
+const String& BlobDataFileReference::path()
 {
+#if ENABLE(FILE_REPLACEMENT)
+    if (m_replacementShouldBeGenerated)
+        generateReplacementFile();
+
+    if (!m_replacementPath.isNull())
+        return m_replacementPath;
+#endif
+
+    return m_path;
+}
+
+unsigned long long BlobDataFileReference::size()
+{
+#if ENABLE(FILE_REPLACEMENT)
+    if (m_replacementShouldBeGenerated)
+        generateReplacementFile();
+#endif
+
     return m_size;
 }
 
-double BlobDataFileReference::expectedModificationTime() const
+double BlobDataFileReference::expectedModificationTime()
 {
+#if ENABLE(FILE_REPLACEMENT)
+    // We do not currently track modifications for generated files, because we have a snapshot.
+    // Unfortunately, this is inconsistent with regular file handling - File objects should be invalidated when underlying files change.
+    if (m_replacementShouldBeGenerated || !m_replacementPath.isNull())
+        return invalidFileTime();
+#endif
+
     return m_expectedModificationTime;
 }
 
@@ -49,12 +89,26 @@ void BlobDataFileReference::startTrackingModifications()
     // This is not done automatically by the constructor, because BlobDataFileReference is
     // also used to pass paths around before registration. Only registered blobs need to pay
     // the cost of tracking file modifications.
+
+    ASSERT(!isValidFileTime(m_expectedModificationTime));
+
+#if ENABLE(FILE_REPLACEMENT)
+    m_replacementShouldBeGenerated = File::shouldReplaceFile(m_path);
+#endif
+
+    // FIXME: Some platforms provide better ways to listen for file system object changes, consider using these.
     FileMetadata metadata;
     if (!getFileMetadata(m_path, metadata))
         return;
 
-    m_size = metadata.length;
     m_expectedModificationTime = metadata.modificationTime;
+
+#if ENABLE(FILE_REPLACEMENT)
+    if (m_replacementShouldBeGenerated)
+        return;
+#endif
+
+    m_size = metadata.length;
 }
 
 void BlobDataFileReference::prepareForFileAccess()
index 403055e..532bf6f 100644 (file)
@@ -43,23 +43,26 @@ public:
 
     void startTrackingModifications();
 
-    const String& path() const { return m_path; }
-    unsigned long long size() const;
-    double expectedModificationTime() const;
+    const String& path();
+    unsigned long long size();
+    double expectedModificationTime();
 
     virtual void prepareForFileAccess();
     virtual void revokeFileAccess();
 
 protected:
-    BlobDataFileReference(const String& path)
-        : m_path(path)
-        , m_size(0)
-        , m_expectedModificationTime(invalidFileTime())
-    {
-    }
+    BlobDataFileReference(const String& path);
 
 private:
+#if ENABLE(FILE_REPLACEMENT)
+    void generateReplacementFile();
+#endif
+
     String m_path;
+#if ENABLE(FILE_REPLACEMENT)
+    String m_replacementPath;
+    bool m_replacementShouldBeGenerated;
+#endif
     unsigned long long m_size;
     double m_expectedModificationTime;
 };
index 2b1fbe8..c06d218 100644 (file)
@@ -91,6 +91,10 @@ public:
     long long m_fileLength;
     double m_expectedFileModificationTime;
 #endif
+    // 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 for two reasons:
+    // 1. Not all platforms enable BLOB support.
+    // 2. EncodedFile form data elements do not have a valid m_expectedFileModificationTime, meaning that we always upload the latest content from disk.
     String m_generatedFilename;
     bool m_shouldGenerateFile;
     bool m_ownsGeneratedFile;
diff --git a/Source/WebCore/platform/network/mac/BlobDataFileReferenceMac.mm b/Source/WebCore/platform/network/mac/BlobDataFileReferenceMac.mm
new file mode 100644 (file)
index 0000000..3cc26d8
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include "config.h"
+#include "BlobDataFileReference.h"
+
+#if ENABLE(FILE_REPLACEMENT)
+
+#include "FileMetadata.h"
+#include "SoftLinking.h"
+#include <wtf/text/CString.h>
+
+#if defined(__has_include)
+#if __has_include(<Bom/BOMCopier.h>)
+#include <Bom/BOMCopier.h>
+#endif
+#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()
+{
+    ASSERT(m_replacementPath.isNull());
+    ASSERT(m_replacementShouldBeGenerated);
+
+    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 (!mktemp(archivePath.mutableData()))
+            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);
+    }];
+
+    m_replacementShouldBeGenerated = false;
+    if (!m_replacementPath.isNull()) {
+        FileMetadata metadata;
+        if (getFileMetadata(m_replacementPath, metadata))
+            m_size = metadata.length;
+    }
+
+    revokeFileAccess();
+}
+
+}
+
+#endif
index 65d0b81..84e492d 100644 (file)
@@ -659,13 +659,12 @@ void XMLHttpRequest::send(Blob* body, ExceptionCode& ec)
             }
         }
 
-        // FIXME: add support for uploading bundles.
         m_requestEntityBody = FormData::create();
+#if ENABLE(BLOB)
+        m_requestEntityBody->appendBlob(body->url());
+#else
         if (body->isFile())
             m_requestEntityBody->appendFile(toFile(body)->path());
-#if ENABLE(BLOB)
-        else
-            m_requestEntityBody->appendBlob(body->url());
 #endif
     }
 
@@ -680,8 +679,6 @@ void XMLHttpRequest::send(DOMFormData* body, ExceptionCode& ec)
     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFamily()) {
         m_requestEntityBody = FormData::createMultiPart(*(static_cast<FormDataList*>(body)), body->encoding(), document());
 
-        // We need to ask the client to provide the generated file names if needed. When FormData fills the element
-        // for the file, it could set a flag to use the generated file name, i.e. a package file on Mac.
         m_requestEntityBody->generateFiles(document());
 
         String contentType = getRequestHeader("Content-Type");
index 76c4df4..2381358 100644 (file)
@@ -1,3 +1,16 @@
+2014-05-08  Alexey Proskuryakov  <ap@apple.com>
+
+        Automatically zip document bundles used via File API
+        https://bugs.webkit.org/show_bug.cgi?id=132713
+        <rdar://problem/13397892>
+
+        Reviewed by Anders Carlsson.
+
+        * NetworkProcess/mac/com.apple.WebKit.NetworkProcess.sb.in: NetworkProcess
+        now uses FileCoordination (WebProcess already had this allowed).
+
+        * Shared/BlobDataFileReferenceWithSandboxExtension.h: Added final to the class.
+
 2014-05-08  Simon Fraser  <simon.fraser@apple.com>
 
         [iOS WK2] Bottom-relative position:fixed elements are misplaced on page load
index 1fad4fd..549bb72 100644 (file)
@@ -92,6 +92,7 @@
 
 ;; Various services required by CFNetwork and other frameworks
 (allow mach-lookup
+    (global-name "com.apple.FileCoordination")
     (global-name "com.apple.PowerManagement.control")
     (global-name "com.apple.SystemConfiguration.configd")
     (global-name "com.apple.cookied")
index a02e764..ef6b3ee 100644 (file)
@@ -32,7 +32,7 @@ namespace WebKit {
 
 class SandboxExtension;
 
-class BlobDataFileReferenceWithSandboxExtension : public WebCore::BlobDataFileReference {
+class BlobDataFileReferenceWithSandboxExtension final : public WebCore::BlobDataFileReference {
 public:
     static PassRefPtr<BlobDataFileReference> create(const String& path, PassRefPtr<SandboxExtension> sandboxExtension)
     {