Eliminate BlobStorageData
[WebKit.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 #if ENABLE(BLOB)
36
37 #include "BlobData.h"
38 #include "BlobPart.h"
39 #include "BlobResourceHandle.h"
40 #include "FileMetadata.h"
41 #include "FileSystem.h"
42 #include "ResourceError.h"
43 #include "ResourceHandle.h"
44 #include "ResourceRequest.h"
45 #include "ResourceResponse.h"
46 #include <wtf/MainThread.h>
47 #include <wtf/StdLibExtras.h>
48
49 #if PLATFORM(IOS)
50 #include "WebCoreThread.h"
51 #endif
52
53 namespace WebCore {
54
55 BlobRegistryImpl::~BlobRegistryImpl()
56 {
57 }
58
59 static PassRefPtr<ResourceHandle> createResourceHandle(const ResourceRequest& request, ResourceHandleClient* client)
60 {
61     return static_cast<BlobRegistryImpl&>(blobRegistry()).createResourceHandle(request, client);
62 }
63
64 static void loadResourceSynchronously(NetworkingContext*, const ResourceRequest& request, StoredCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
65 {
66     BlobData* blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(request.url());
67     BlobResourceHandle::loadResourceSynchronously(blobData, request, error, response, data);
68 }
69
70 static void registerBlobResourceHandleConstructor()
71 {
72     static bool didRegister = false;
73     if (!didRegister) {
74         ResourceHandle::registerBuiltinConstructor("blob", createResourceHandle);
75         ResourceHandle::registerBuiltinSynchronousLoader("blob", loadResourceSynchronously);
76         didRegister = true;
77     }
78 }
79
80 PassRefPtr<ResourceHandle> BlobRegistryImpl::createResourceHandle(const ResourceRequest& request, ResourceHandleClient* client)
81 {
82     RefPtr<BlobResourceHandle> handle = BlobResourceHandle::createAsync(getBlobDataFromURL(request.url()), request, client);
83     if (!handle)
84         return 0;
85
86     handle->start();
87     return handle.release();
88 }
89
90 void BlobRegistryImpl::appendStorageItems(BlobData* blobData, const BlobDataItemList& items, long long offset, long long length)
91 {
92     ASSERT(length != BlobDataItem::toEndOfFile);
93
94     BlobDataItemList::const_iterator iter = items.begin();
95     if (offset) {
96         for (; iter != items.end(); ++iter) {
97             if (offset >= iter->length)
98                 offset -= iter->length;
99             else
100                 break;
101         }
102     }
103
104     for (; iter != items.end() && length > 0; ++iter) {
105         ASSERT(iter->length != BlobDataItem::toEndOfFile);
106         long long currentLength = iter->length - offset;
107         long long newLength = currentLength > length ? length : currentLength;
108         if (iter->type == BlobDataItem::Data)
109             blobData->appendData(iter->data, iter->offset + offset, newLength);
110         else {
111             ASSERT(iter->type == BlobDataItem::File);
112             blobData->appendFile(iter->path, iter->offset + offset, newLength, iter->expectedModificationTime);
113         }
114         length -= newLength;
115         offset = 0;
116     }
117     ASSERT(!length);
118 }
119
120 void BlobRegistryImpl::registerFileBlobURL(const URL& url, const String& path, const String& contentType)
121 {
122     ASSERT(isMainThread());
123     registerBlobResourceHandleConstructor();
124
125     RefPtr<BlobData> blobData = BlobData::create();
126     blobData->setContentType(contentType);
127
128     // FIXME: Factor out size and modification tracking for a cleaner implementation.
129     FileMetadata metadata;
130     if (!getFileMetadata(path, metadata))
131         return;
132
133     blobData->appendFile(path, 0, metadata.length, metadata.modificationTime);
134     m_blobs.set(url.string(), blobData.release());
135 }
136
137 unsigned long long BlobRegistryImpl::registerBlobURL(const URL& url, Vector<BlobPart> blobParts, const String& contentType)
138 {
139     ASSERT(isMainThread());
140     registerBlobResourceHandleConstructor();
141
142     RefPtr<BlobData> blobData = BlobData::create();
143     blobData->setContentType(contentType);
144
145     // The blob data is stored in the "canonical" way. That is, it only contains a list of Data and File items.
146     // 1) The Data item is denoted by the raw data and the range.
147     // 2) The File item is denoted by the file path, the range and the expected modification time.
148     // 3) The URL item is denoted by the URL, the range and the expected modification time.
149     // All the Blob items in the passing blob data are resolved and expanded into a set of Data and File items.
150
151     unsigned long long size = 0;
152     for (BlobPart& part : blobParts) {
153         switch (part.type()) {
154         case BlobPart::Data: {
155             unsigned long long partSize = part.data().size();
156             RefPtr<RawData> rawData = RawData::create(part.moveData());
157             size += partSize;
158             blobData->appendData(rawData.release(), 0, partSize);
159             break;
160         }
161         case BlobPart::Blob: {
162             if (!m_blobs.contains(part.url().string()))
163                 return 0;
164             unsigned long long partSize = blobSize(part.url()); // As a side effect, this calculates sizes of all files in the blob.
165             size += partSize;
166             appendStorageItems(blobData.get(), m_blobs.get(part.url().string())->items(), 0, partSize);
167             break;
168         }
169         }
170     }
171
172     m_blobs.set(url.string(), blobData.release());
173     return size;
174 }
175
176 void BlobRegistryImpl::registerBlobURL(const URL& url, const URL& srcURL)
177 {
178     ASSERT(isMainThread());
179
180     BlobData* src = getBlobDataFromURL(srcURL);
181     if (!src)
182         return;
183
184     m_blobs.set(url.string(), src);
185 }
186
187 unsigned long long BlobRegistryImpl::registerBlobURLForSlice(const URL& url, const URL& srcURL, long long start, long long end)
188 {
189     ASSERT(isMainThread());
190     BlobData* originalData = getBlobDataFromURL(srcURL);
191     if (!originalData)
192         return 0;
193
194     unsigned long long originalSize = blobSize(srcURL);
195
196     // Convert the negative value that is used to select from the end.
197     if (start < 0)
198         start = start + originalSize;
199     if (end < 0)
200         end = end + originalSize;
201
202     // Clamp the range if it exceeds the size limit.
203     if (start < 0)
204         start = 0;
205     if (end < 0)
206         end = 0;
207     if (static_cast<unsigned long long>(start) >= originalSize) {
208         start = 0;
209         end = 0;
210     } else if (end < start)
211         end = start;
212     else if (static_cast<unsigned long long>(end) > originalSize)
213         end = originalSize;
214
215     unsigned long long newLength = end - start;
216     RefPtr<BlobData> newData = BlobData::create();
217     newData->setContentType(originalData->contentType());
218
219     appendStorageItems(newData.get(), originalData->items(), start, newLength);
220
221     m_blobs.set(url.string(), newData.release());
222     return newLength;
223 }
224
225 void BlobRegistryImpl::unregisterBlobURL(const URL& url)
226 {
227     ASSERT(isMainThread());
228     m_blobs.remove(url.string());
229 }
230
231 BlobData* BlobRegistryImpl::getBlobDataFromURL(const URL& url) const
232 {
233     ASSERT(isMainThread());
234     return m_blobs.get(url.string());
235 }
236
237 unsigned long long BlobRegistryImpl::blobSize(const URL& url)
238 {
239     ASSERT(isMainThread());
240     BlobData* data = getBlobDataFromURL(url);
241     if (!data)
242         return 0;
243
244     unsigned long long result = 0;
245     for (const BlobDataItem& item : data->items()) {
246         if (item.length == BlobDataItem::toEndOfFile) {
247             FileMetadata metadata;
248             if (!getFileMetadata(item.path, metadata))
249                 return 0;
250
251             // FIXME: Factor out size and modification tracking for a cleaner implementation.
252             const_cast<BlobDataItem&>(item).length = metadata.length;
253             const_cast<BlobDataItem&>(item).expectedModificationTime = metadata.modificationTime;
254         }
255         result += item.length;
256     }
257     return result;
258 }
259
260 } // namespace WebCore
261
262 #endif