Improve use of NeverDestroyed
[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()
53 {
54 }
55
56 static Ref<ResourceHandle> createBlobResourceHandle(const ResourceRequest& request, ResourceHandleClient* client)
57 {
58     return static_cast<BlobRegistryImpl&>(blobRegistry()).createResourceHandle(request, client);
59 }
60
61 static void loadBlobResourceSynchronously(NetworkingContext*, const ResourceRequest& request, StoredCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
62 {
63     BlobData* blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(request.url());
64     BlobResourceHandle::loadResourceSynchronously(blobData, request, error, response, data);
65 }
66
67 static void registerBlobResourceHandleConstructor()
68 {
69     static bool didRegister = false;
70     if (!didRegister) {
71         ResourceHandle::registerBuiltinConstructor("blob", createBlobResourceHandle);
72         ResourceHandle::registerBuiltinSynchronousLoader("blob", loadBlobResourceSynchronously);
73         didRegister = true;
74     }
75 }
76
77 Ref<ResourceHandle> BlobRegistryImpl::createResourceHandle(const ResourceRequest& request, ResourceHandleClient* client)
78 {
79     auto handle = BlobResourceHandle::createAsync(getBlobDataFromURL(request.url()), request, client);
80     handle->start();
81     return WTFMove(handle);
82 }
83
84 void BlobRegistryImpl::appendStorageItems(BlobData* blobData, const BlobDataItemList& items, long long offset, long long length)
85 {
86     ASSERT(length != BlobDataItem::toEndOfFile);
87
88     BlobDataItemList::const_iterator iter = items.begin();
89     if (offset) {
90         for (; iter != items.end(); ++iter) {
91             if (offset >= iter->length())
92                 offset -= iter->length();
93             else
94                 break;
95         }
96     }
97
98     for (; iter != items.end() && length > 0; ++iter) {
99         long long currentLength = iter->length() - offset;
100         long long newLength = currentLength > length ? length : currentLength;
101         if (iter->type() == BlobDataItem::Type::Data)
102             blobData->appendData(iter->data(), iter->offset() + offset, newLength);
103         else {
104             ASSERT(iter->type() == BlobDataItem::Type::File);
105             blobData->appendFile(iter->file(), iter->offset() + offset, newLength);
106         }
107         length -= newLength;
108         offset = 0;
109     }
110     ASSERT(!length);
111 }
112
113 void BlobRegistryImpl::registerFileBlobURL(const URL& url, Ref<BlobDataFileReference>&& file, const String& contentType)
114 {
115     ASSERT(isMainThread());
116     registerBlobResourceHandleConstructor();
117
118     auto blobData = BlobData::create(contentType);
119     blobData->appendFile(WTFMove(file));
120     m_blobs.set(url.string(), WTFMove(blobData));
121 }
122
123 void BlobRegistryImpl::registerBlobURL(const URL& url, Vector<BlobPart>&& blobParts, const String& contentType)
124 {
125     ASSERT(isMainThread());
126     registerBlobResourceHandleConstructor();
127
128     auto blobData = BlobData::create(contentType);
129
130     // The blob data is stored in the "canonical" way. That is, it only contains a list of Data and File items.
131     // 1) The Data item is denoted by the raw data and the range.
132     // 2) The File item is denoted by the file path, the range and the expected modification time.
133     // 3) The URL item is denoted by the URL, the range and the expected modification time.
134     // All the Blob items in the passing blob data are resolved and expanded into a set of Data and File items.
135
136     for (BlobPart& part : blobParts) {
137         switch (part.type()) {
138         case BlobPart::Data: {
139             auto movedData = part.moveData();
140             auto data = ThreadSafeDataBuffer::create(WTFMove(movedData));
141             blobData->appendData(data);
142             break;
143         }
144         case BlobPart::Blob: {
145             if (auto blob = m_blobs.get(part.url().string())) {
146                 for (const BlobDataItem& item : blob->items())
147                     blobData->m_items.append(item);
148             }
149             break;
150         }
151         }
152     }
153
154     m_blobs.set(url.string(), WTFMove(blobData));
155 }
156
157 void BlobRegistryImpl::registerBlobURL(const URL& url, const URL& srcURL)
158 {
159     registerBlobURLOptionallyFileBacked(url, srcURL, nullptr, { });
160 }
161
162 void BlobRegistryImpl::registerBlobURLOptionallyFileBacked(const URL& url, const URL& srcURL, RefPtr<BlobDataFileReference>&& file, const String& contentType)
163 {
164     ASSERT(isMainThread());
165     registerBlobResourceHandleConstructor();
166
167     BlobData* src = getBlobDataFromURL(srcURL);
168     if (src) {
169         m_blobs.set(url.string(), src);
170         return;
171     }
172
173     if (!file || file->path().isEmpty())
174         return;
175
176     auto backingFile = BlobData::create(contentType);
177     backingFile->appendFile(file.releaseNonNull());
178
179     m_blobs.set(url.string(), WTFMove(backingFile));
180 }
181
182 void BlobRegistryImpl::registerBlobURLForSlice(const URL& url, const URL& srcURL, long long start, long long end)
183 {
184     ASSERT(isMainThread());
185     BlobData* originalData = getBlobDataFromURL(srcURL);
186     if (!originalData)
187         return;
188
189     unsigned long long originalSize = blobSize(srcURL);
190
191     // Convert the negative value that is used to select from the end.
192     if (start < 0)
193         start = start + originalSize;
194     if (end < 0)
195         end = end + originalSize;
196
197     // Clamp the range if it exceeds the size limit.
198     if (start < 0)
199         start = 0;
200     if (end < 0)
201         end = 0;
202     if (static_cast<unsigned long long>(start) >= originalSize) {
203         start = 0;
204         end = 0;
205     } else if (end < start)
206         end = start;
207     else if (static_cast<unsigned long long>(end) > originalSize)
208         end = originalSize;
209
210     unsigned long long newLength = end - start;
211     auto newData = BlobData::create(originalData->contentType());
212
213     appendStorageItems(newData.ptr(), originalData->items(), start, newLength);
214
215     m_blobs.set(url.string(), WTFMove(newData));
216 }
217
218 void BlobRegistryImpl::unregisterBlobURL(const URL& url)
219 {
220     ASSERT(isMainThread());
221     m_blobs.remove(url.string());
222 }
223
224 BlobData* BlobRegistryImpl::getBlobDataFromURL(const URL& url) const
225 {
226     ASSERT(isMainThread());
227     return m_blobs.get(url.string());
228 }
229
230 unsigned long long BlobRegistryImpl::blobSize(const URL& url)
231 {
232     ASSERT(isMainThread());
233     BlobData* data = getBlobDataFromURL(url);
234     if (!data)
235         return 0;
236
237     unsigned long long result = 0;
238     for (const BlobDataItem& item : data->items())
239         result += item.length();
240
241     return result;
242 }
243
244 static WorkQueue& blobUtilityQueue()
245 {
246     static auto& queue = WorkQueue::create("org.webkit.BlobUtility", WorkQueue::Type::Serial, WorkQueue::QOS::Background).leakRef();
247     return queue;
248 }
249
250 struct BlobForFileWriting {
251     String blobURL;
252     Vector<std::pair<String, ThreadSafeDataBuffer>> filePathsOrDataBuffers;
253 };
254
255 void BlobRegistryImpl::writeBlobsToTemporaryFiles(const Vector<String>& blobURLs, Function<void (const Vector<String>& filePaths)>&& completionHandler)
256 {
257     Vector<BlobForFileWriting> blobsForWriting;
258     for (auto& url : blobURLs) {
259         blobsForWriting.append({ });
260         blobsForWriting.last().blobURL = url.isolatedCopy();
261
262         auto* blobData = getBlobDataFromURL({ ParsedURLString, url });
263         if (!blobData) {
264             Vector<String> filePaths;
265             completionHandler(filePaths);
266             return;
267         }
268
269         for (auto& item : blobData->items()) {
270             switch (item.type()) {
271             case BlobDataItem::Type::Data:
272                 blobsForWriting.last().filePathsOrDataBuffers.append({ { }, item.data() });
273                 break;
274             case BlobDataItem::Type::File:
275                 blobsForWriting.last().filePathsOrDataBuffers.append({ item.file()->path().isolatedCopy(), { } });
276                 break;
277             default:
278                 ASSERT_NOT_REACHED();
279             }
280         }
281     }
282
283     blobUtilityQueue().dispatch([blobsForWriting = WTFMove(blobsForWriting), completionHandler = WTFMove(completionHandler)]() mutable {
284         Vector<String> filePaths;
285
286         auto performWriting = [blobsForWriting = WTFMove(blobsForWriting), &filePaths]() {
287             for (auto& blob : blobsForWriting) {
288                 PlatformFileHandle file;
289                 String tempFilePath = openTemporaryFile(ASCIILiteral("Blob"), file);
290
291                 ScopeGuard fileCloser([file]() mutable {
292                     closeFile(file);
293                 });
294                 
295                 if (tempFilePath.isEmpty() || !isHandleValid(file)) {
296                     LOG_ERROR("Failed to open temporary file for writing a Blob to IndexedDB");
297                     return false;
298                 }
299
300                 for (auto& part : blob.filePathsOrDataBuffers) {
301                     if (part.second.data()) {
302                         int length = part.second.data()->size();
303                         if (writeToFile(file, reinterpret_cast<const char*>(part.second.data()->data()), length) != length) {
304                             LOG_ERROR("Failed writing a Blob to temporary file for storage in IndexedDB");
305                             return false;
306                         }
307                     } else {
308                         ASSERT(!part.first.isEmpty());
309                         if (!appendFileContentsToFileHandle(part.first, file)) {
310                             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());
311                             return false;
312                         }
313                     }
314                 }
315
316                 filePaths.append(tempFilePath.isolatedCopy());
317             }
318
319             return true;
320         };
321
322         if (!performWriting())
323             filePaths.clear();
324
325         callOnMainThread([completionHandler = WTFMove(completionHandler), filePaths = WTFMove(filePaths)]() {
326             completionHandler(filePaths);
327         });
328     });
329 }
330
331 } // namespace WebCore