Unreviewed, rolling out r240630.
[WebKit-https.git] / Source / WebKit / UIProcess / API / Cocoa / APIAttachmentCocoa.mm
1 /*
2  * Copyright (C) 2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "APIAttachment.h"
28
29 #import "PageClient.h"
30 #import <WebCore/MIMETypeRegistry.h>
31 #import <WebCore/SharedBuffer.h>
32 #if PLATFORM(IOS_FAMILY)
33 #import <MobileCoreServices/MobileCoreServices.h>
34 #else
35 #import <CoreServices/CoreServices.h>
36 #endif
37 #import <pal/spi/cocoa/NSKeyedArchiverSPI.h>
38
39 #define CAN_SECURELY_ARCHIVE_FILE_WRAPPER (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 120000)
40
41 namespace API {
42
43 static WTF::String mimeTypeInferredFromFileExtension(const API::Attachment& attachment)
44 {
45     if (NSString *fileExtension = [(NSString *)attachment.fileName() pathExtension])
46         return WebCore::MIMETypeRegistry::getMIMETypeForExtension(fileExtension);
47
48     return { };
49 }
50
51 static BOOL isDeclaredOrDynamicTypeIdentifier(NSString *type)
52 {
53     return UTTypeIsDeclared((__bridge CFStringRef)type) || UTTypeIsDynamic((__bridge CFStringRef)type);
54 }
55
56 NSFileWrapper *Attachment::fileWrapper() const
57 {
58     if (m_fileWrapperGenerator && !m_fileWrapper)
59         m_fileWrapper = m_fileWrapperGenerator();
60     return m_fileWrapper.get();
61 }
62
63 void Attachment::invalidateGeneratedFileWrapper()
64 {
65     ASSERT(m_fileWrapperGenerator);
66     m_fileWrapper = nil;
67     m_webPage->didInvalidateDataForAttachment(*this);
68 }
69
70 WTF::String Attachment::mimeType() const
71 {
72     NSString *contentType = m_contentType.isEmpty() ? mimeTypeInferredFromFileExtension(*this) : m_contentType;
73     if (!isDeclaredOrDynamicTypeIdentifier(contentType))
74         return contentType;
75
76     return adoptCF(UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)contentType, kUTTagClassMIMEType)).get();
77 }
78
79 WTF::String Attachment::utiType() const
80 {
81     NSString *contentType = m_contentType.isEmpty() ? mimeTypeInferredFromFileExtension(*this) : m_contentType;
82     if (isDeclaredOrDynamicTypeIdentifier(contentType))
83         return contentType;
84
85     return adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef)contentType, nullptr)).get();
86 }
87
88 WTF::String Attachment::fileName() const
89 {
90     auto fileWrapper = this->fileWrapper();
91
92     if ([fileWrapper filename].length)
93         return [fileWrapper filename];
94
95     return [fileWrapper preferredFilename];
96 }
97
98 void Attachment::setFileWrapperAndUpdateContentType(NSFileWrapper *fileWrapper, NSString *contentType)
99 {
100     if (!contentType.length) {
101         if (fileWrapper.directory)
102             contentType = (NSString *)kUTTypeDirectory;
103         else if (fileWrapper.regularFile) {
104             if (NSString *pathExtension = (fileWrapper.filename.length ? fileWrapper.filename : fileWrapper.preferredFilename).pathExtension)
105                 contentType = WebCore::MIMETypeRegistry::getMIMETypeForExtension(pathExtension);
106             if (!contentType.length)
107                 contentType = (NSString *)kUTTypeData;
108         }
109     }
110
111     setContentType(contentType);
112     setFileWrapper(fileWrapper);
113 }
114
115 Optional<uint64_t> Attachment::fileSizeForDisplay() const
116 {
117     auto fileWrapper = this->fileWrapper();
118
119     if (![fileWrapper isRegularFile]) {
120         // FIXME: We should display a size estimate for directory-type file wrappers.
121         return WTF::nullopt;
122     }
123
124     if (auto fileSize = [[fileWrapper fileAttributes][NSFileSize] unsignedLongLongValue])
125         return fileSize;
126
127     return [fileWrapper regularFileContents].length;
128 }
129
130 RefPtr<WebCore::SharedBuffer> Attachment::enclosingImageData() const
131 {
132     if (!m_hasEnclosingImage)
133         return nullptr;
134
135     auto fileWrapper = this->fileWrapper();
136
137     if (![fileWrapper isRegularFile])
138         return nullptr;
139
140     NSData *data = [fileWrapper regularFileContents];
141     if (!data)
142         return nullptr;
143
144     return WebCore::SharedBuffer::create(data);
145 }
146
147 bool Attachment::isEmpty() const
148 {
149     return !m_fileWrapper && !m_fileWrapperGenerator;
150 }
151
152 RefPtr<WebCore::SharedBuffer> Attachment::createSerializedRepresentation() const
153 {
154     auto fileWrapper = this->fileWrapper();
155
156     if (!fileWrapper || !m_webPage)
157         return nullptr;
158
159 #if CAN_SECURELY_ARCHIVE_FILE_WRAPPER
160     NSData *serializedData = securelyArchivedDataWithRootObject(fileWrapper);
161 #else
162     NSData *serializedData = insecurelyArchivedDataWithRootObject(fileWrapper);
163 #endif
164
165     if (!serializedData)
166         return nullptr;
167
168     return WebCore::SharedBuffer::create(serializedData);
169 }
170
171 void Attachment::updateFromSerializedRepresentation(Ref<WebCore::SharedBuffer>&& serializedRepresentation, const WTF::String& contentType)
172 {
173     if (!m_webPage)
174         return;
175
176     auto serializedData = serializedRepresentation->createNSData();
177     if (!serializedData)
178         return;
179
180 #if CAN_SECURELY_ARCHIVE_FILE_WRAPPER
181     NSFileWrapper *fileWrapper = unarchivedObjectOfClassesFromData(m_webPage->pageClient().serializableFileWrapperClasses(), serializedData.get());
182 #else
183     NSFileWrapper *fileWrapper = insecurelyUnarchiveObjectFromData(serializedData.get());
184 #endif
185
186     if (![fileWrapper isKindOfClass:NSFileWrapper.class])
187         return;
188
189     setFileWrapperAndUpdateContentType(fileWrapper, contentType);
190     m_webPage->updateAttachmentAttributes(*this, [] (auto) { });
191 }
192
193 void Attachment::setFileWrapperGenerator(Function<RetainPtr<NSFileWrapper>(void)>&& fileWrapperGenerator)
194 {
195     m_fileWrapperGenerator = WTFMove(fileWrapperGenerator);
196     m_fileWrapper = nil;
197     m_webPage->didInvalidateDataForAttachment(*this);
198 }
199
200 } // namespace API