Use "= default" to denote default constructor or destructor
[WebKit-https.git] / Source / WebCore / platform / network / BlobRegistryImpl.cpp
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  * Copyright (C) 2013 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "BlobRegistryImpl.h"
34
35 #include "BlobData.h"
36 #include "BlobPart.h"
37 #include "BlobResourceHandle.h"
38 #include "FileMetadata.h"
39 #include "FileSystem.h"
40 #include "ResourceError.h"
41 #include "ResourceHandle.h"
42 #include "ResourceRequest.h"
43 #include "ResourceResponse.h"
44 #include "ScopeGuard.h"
45 #include <wtf/MainThread.h>
46 #include <wtf/NeverDestroyed.h>
47 #include <wtf/StdLibExtras.h>
48 #include <wtf/WorkQueue.h>
49
50 namespace WebCore {
51
52 BlobRegistryImpl::~BlobRegistryImpl() = default;
53
54 static Ref<ResourceHandle> createBlobResourceHandle(const ResourceRequest& request, ResourceHandleClient* client)
55 {
56     return static_cast<BlobRegistryImpl&>(blobRegistry()).createResourceHandle(request, client);
57 }
58
59 static void loadBlobResourceSynchronously(NetworkingContext*, const ResourceRequest& request, StoredCredentialsPolicy, ResourceError& error, ResourceResponse& response, Vector<char>& data)
60 {
61     BlobData* blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(request.url());
62     BlobResourceHandle::loadResourceSynchronously(blobData, request, error, response, data);
63 }
64
65 static void registerBlobResourceHandleConstructor()
66 {
67     static bool didRegister = false;
68     if (!didRegister) {
69         ResourceHandle::registerBuiltinConstructor("blob", createBlobResourceHandle);
70         ResourceHandle::registerBuiltinSynchronousLoader("blob", loadBlobResourceSynchronously);
71         didRegister = true;
72     }
73 }
74
75 Ref<ResourceHandle> BlobRegistryImpl::createResourceHandle(const ResourceRequest& request, ResourceHandleClient* client)
76 {
77     auto handle = BlobResourceHandle::createAsync(getBlobDataFromURL(request.url()), request, client);
78     handle->start();
79     return WTFMove(handle);
80 }
81
82 void BlobRegistryImpl::appendStorageItems(BlobData* blobData, const BlobDataItemList& items, long long offset, long long length)
83 {
84     ASSERT(length != BlobDataItem::toEndOfFile);
85
86     BlobDataItemList::const_iterator iter = items.begin();
87     if (offset) {
88         for (; iter != items.end(); ++iter) {
89             if (offset >= iter->length())
90                 offset -= iter->length();
91             else
92                 break;
93         }
94     }
95
96     for (; iter != items.end() && length > 0; ++iter) {
97         long long currentLength = iter->length() - offset;
98         long long newLength = currentLength > length ? length : currentLength;
99         if (iter->type() == BlobDataItem::Type::Data)
100             blobData->appendData(iter->data(), iter->offset() + offset, newLength);
101         else {
102             ASSERT(iter->type() == BlobDataItem::Type::File);
103             blobData->appendFile(iter->file(), iter->offset() + offset, newLength);
104         }
105         length -= newLength;
106         offset = 0;
107     }
108     ASSERT(!length);
109 }
110
111 void BlobRegistryImpl::registerFileBlobURL(const URL& url, Ref<BlobDataFileReference>&& file, const String& contentType)
112 {
113     ASSERT(isMainThread());
114     registerBlobResourceHandleConstructor();
115
116     auto blobData = BlobData::create(contentType);
117     blobData->appendFile(WTFMove(file));
118     m_blobs.set(url.string(), WTFMove(blobData));
119 }
120
121 void BlobRegistryImpl::registerBlobURL(const URL& url, Vector<BlobPart>&& blobParts, const String& contentType)
122 {
123     ASSERT(isMainThread());
124     registerBlobResourceHandleConstructor();
125
126     auto blobData = BlobData::create(contentType);
127
128     // The blob data is stored in the "canonical" way. That is, it only contains a list of Data and File items.
129     // 1) The Data item is denoted by the raw data and the range.
130     // 2) The File item is denoted by the file path, the range and the expected modification time.
131     // 3) The URL item is denoted by the URL, the range and the expected modification time.
132     // All the Blob items in the passing blob data are resolved and expanded into a set of Data and File items.
133
134     for (BlobPart& part : blobParts) {
135         switch (part.type()) {
136         case BlobPart::Data: {
137             auto movedData = part.moveData();
138             auto data = ThreadSafeDataBuffer::create(WTFMove(movedData));
139             blobData->appendData(data);
140             break;
141         }
142         case BlobPart::Blob: {
143             if (auto blob = m_blobs.get(part.url().string())) {
144                 for (const BlobDataItem& item : blob->items())
145                     blobData->m_items.append(item);
146             }
147             break;
148         }
149         }
150     }
151
152     m_blobs.set(url.string(), WTFMove(blobData));
153 }
154
155 void BlobRegistryImpl::registerBlobURL(const URL& url, const URL& srcURL)
156 {
157     registerBlobURLOptionallyFileBacked(url, srcURL, nullptr, { });
158 }
159
160 void BlobRegistryImpl::registerBlobURLOptionallyFileBacked(const URL& url, const URL& srcURL, RefPtr<BlobDataFileReference>&& file, const String& contentType)
161 {
162     ASSERT(isMainThread());
163     registerBlobResourceHandleConstructor();
164
165     BlobData* src = getBlobDataFromURL(srcURL);
166     if (src) {
167         m_blobs.set(url.string(), src);
168         return;
169     }
170
171     if (!file || file->path().isEmpty())
172         return;
173
174     auto backingFile = BlobData::create(contentType);
175     backingFile->appendFile(file.releaseNonNull());
176
177     m_blobs.set(url.string(), WTFMove(backingFile));
178 }
179
180 void BlobRegistryImpl::registerBlobURLForSlice(const URL& url, const URL& srcURL, long long start, long long end)
181 {
182     ASSERT(isMainThread());
183     BlobData* originalData = getBlobDataFromURL(srcURL);
184     if (!originalData)
185         return;
186
187     unsigned long long originalSize = blobSize(srcURL);
188
189     // Convert the negative value that is used to select from the end.
190     if (start < 0)
191         start = start + originalSize;
192     if (end < 0)
193         end = end + originalSize;
194
195     // Clamp the range if it exceeds the size limit.
196     if (start < 0)
197         start = 0;
198     if (end < 0)
199         end = 0;
200     if (static_cast<unsigned long long>(start) >= originalSize) {
201         start = 0;
202         end = 0;
203     } else if (end < start)
204         end = start;
205     else if (static_cast<unsigned long long>(end) > originalSize)
206         end = originalSize;
207
208     unsigned long long newLength = end - start;
209     auto newData = BlobData::create(originalData->contentType());
210
211     appendStorageItems(newData.ptr(), originalData->items(), start, newLength);
212
213     m_blobs.set(url.string(), WTFMove(newData));
214 }
215
216 void BlobRegistryImpl::unregisterBlobURL(const URL& url)
217 {
218     ASSERT(isMainThread());
219     m_blobs.remove(url.string());
220 }
221
222 BlobData* BlobRegistryImpl::getBlobDataFromURL(const URL& url) const
223 {
224     ASSERT(isMainThread());
225     return m_blobs.get(url.string());
226 }
227
228 unsigned long long BlobRegistryImpl::blobSize(const URL& url)
229 {
230     ASSERT(isMainThread());
231     BlobData* data = getBlobDataFromURL(url);
232     if (!data)
233         return 0;
234
235     unsigned long long result = 0;
236     for (const BlobDataItem& item : data->items())
237         result += item.length();
238
239     return result;
240 }
241
242 static WorkQueue& blobUtilityQueue()
243 {
244     static auto& queue = WorkQueue::create("org.webkit.BlobUtility", WorkQueue::Type::Serial, WorkQueue::QOS::Background).leakRef();
245     return queue;
246 }
247
248 struct BlobForFileWriting {
249     String blobURL;
250     Vector<std::pair<String, ThreadSafeDataBuffer>> filePathsOrDataBuffers;
251 };
252
253 void BlobRegistryImpl::writeBlobsToTemporaryFiles(const Vector<String>& blobURLs, Function<void (const Vector<String>& filePaths)>&& completionHandler)
254 {
255     Vector<BlobForFileWriting> blobsForWriting;
256     for (auto& url : blobURLs) {
257         blobsForWriting.append({ });
258         blobsForWriting.last().blobURL = url.isolatedCopy();
259
260         auto* blobData = getBlobDataFromURL({ ParsedURLString, url });
261         if (!blobData) {
262             Vector<String> filePaths;
263             completionHandler(filePaths);
264             return;
265         }
266
267         for (auto& item : blobData->items()) {
268             switch (item.type()) {
269             case BlobDataItem::Type::Data:
270                 blobsForWriting.last().filePathsOrDataBuffers.append({ { }, item.data() });
271                 break;
272             case BlobDataItem::Type::File:
273                 blobsForWriting.last().filePathsOrDataBuffers.append({ item.file()->path().isolatedCopy(), { } });
274                 break;
275             default:
276                 ASSERT_NOT_REACHED();
277             }
278         }
279     }
280
281     blobUtilityQueue().dispatch([blobsForWriting = WTFMove(blobsForWriting), completionHandler = WTFMove(completionHandler)]() mutable {
282         Vector<String> filePaths;
283
284         auto performWriting = [blobsForWriting = WTFMove(blobsForWriting), &filePaths]() {
285             for (auto& blob : blobsForWriting) {
286                 PlatformFileHandle file;
287                 String tempFilePath = openTemporaryFile(ASCIILiteral("Blob"), file);
288
289                 ScopeGuard fileCloser([file]() mutable {
290                     closeFile(file);
291                 });
292                 
293                 if (tempFilePath.isEmpty() || !isHandleValid(file)) {
294                     LOG_ERROR("Failed to open temporary file for writing a Blob to IndexedDB");
295                     return false;
296                 }
297
298                 for (auto& part : blob.filePathsOrDataBuffers) {
299                     if (part.second.data()) {
300                         int length = part.second.data()->size();
301                         if (writeToFile(file, reinterpret_cast<const char*>(part.second.data()->data()), length) != length) {
302                             LOG_ERROR("Failed writing a Blob to temporary file for storage in IndexedDB");
303                             return false;
304                         }
305                     } else {
306                         ASSERT(!part.first.isEmpty());
307                         if (!appendFileContentsToFileHandle(part.first, file)) {
308                             LOG_ERROR("Failed copying File contents to a Blob temporary file for storage in IndexedDB (%s to %s)", part.first.utf8().data(), tempFilePath.utf8().data());
309                             return false;
310                         }
311                     }
312                 }
313
314                 filePaths.append(tempFilePath.isolatedCopy());
315             }
316
317             return true;
318         };
319
320         if (!performWriting())
321             filePaths.clear();
322
323         callOnMainThread([completionHandler = WTFMove(completionHandler), filePaths = WTFMove(filePaths)]() {
324             completionHandler(filePaths);
325         });
326     });
327 }
328
329 } // namespace WebCore