1/*
2 * Copyright (C) 2010 Google 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 are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "Blob.h"
33
34#include "BlobBuilder.h"
35#include "BlobPart.h"
36#include "BlobURL.h"
37#include "File.h"
38#include "ScriptExecutionContext.h"
39#include "SharedBuffer.h"
40#include "ThreadableBlobRegistry.h"
41#include <wtf/IsoMallocInlines.h>
42#include <wtf/NeverDestroyed.h>
43#include <wtf/text/CString.h>
44
45namespace WebCore {
46
47WTF_MAKE_ISO_ALLOCATED_IMPL(Blob);
48
49class BlobURLRegistry final : public URLRegistry {
50public:
51 void registerURL(SecurityOrigin*, const URL&, URLRegistrable&) override;
52 void unregisterURL(const URL&) override;
53
54 static URLRegistry& registry();
55};
56
57
58void BlobURLRegistry::registerURL(SecurityOrigin* origin, const URL& publicURL, URLRegistrable& blob)
59{
60 ASSERT(&blob.registry() == this);
61 ThreadableBlobRegistry::registerBlobURL(origin, publicURL, static_cast<Blob&>(blob).url());
62}
63
64void BlobURLRegistry::unregisterURL(const URL& url)
65{
66 ThreadableBlobRegistry::unregisterBlobURL(url);
67}
68
69URLRegistry& BlobURLRegistry::registry()
70{
71 static NeverDestroyed<BlobURLRegistry> instance;
72 return instance;
73}
74
75Blob::Blob(UninitializedContructor)
76{
77}
78
79Blob::Blob()
80 : m_size(0)
81{
82 m_internalURL = BlobURL::createInternalURL();
83 ThreadableBlobRegistry::registerBlobURL(m_internalURL, { }, { });
84}
85
86Blob::Blob(Vector<BlobPartVariant>&& blobPartVariants, const BlobPropertyBag& propertyBag)
87 : m_internalURL(BlobURL::createInternalURL())
88 , m_type(normalizedContentType(propertyBag.type))
89 , m_size(-1)
90{
91 BlobBuilder builder(propertyBag.endings);
92 for (auto& blobPartVariant : blobPartVariants) {
93 WTF::switchOn(blobPartVariant,
94 [&] (auto& part) {
95 builder.append(WTFMove(part));
96 }
97 );
98 }
99
100 ThreadableBlobRegistry::registerBlobURL(m_internalURL, builder.finalize(), m_type);
101}
102
103Blob::Blob(const SharedBuffer& buffer, const String& contentType)
104 : m_type(contentType)
105 , m_size(buffer.size())
106{
107 Vector<uint8_t> data;
108 data.append(buffer.data(), buffer.size());
109
110 Vector<BlobPart> blobParts;
111 blobParts.append(BlobPart(WTFMove(data)));
112 m_internalURL = BlobURL::createInternalURL();
113 ThreadableBlobRegistry::registerBlobURL(m_internalURL, WTFMove(blobParts), contentType);
114}
115
116Blob::Blob(Vector<uint8_t>&& data, const String& contentType)
117 : m_type(contentType)
118 , m_size(data.size())
119{
120 Vector<BlobPart> blobParts;
121 blobParts.append(BlobPart(WTFMove(data)));
122 m_internalURL = BlobURL::createInternalURL();
123 ThreadableBlobRegistry::registerBlobURL(m_internalURL, WTFMove(blobParts), contentType);
124}
125
126Blob::Blob(ReferencingExistingBlobConstructor, const Blob& blob)
127 : m_internalURL(BlobURL::createInternalURL())
128 , m_type(blob.type())
129 , m_size(blob.size())
130{
131 ThreadableBlobRegistry::registerBlobURL(m_internalURL, { BlobPart(blob.url()) } , m_type);
132}
133
134Blob::Blob(DeserializationContructor, const URL& srcURL, const String& type, long long size, const String& fileBackedPath)
135 : m_type(normalizedContentType(type))
136 , m_size(size)
137{
138 m_internalURL = BlobURL::createInternalURL();
139 if (fileBackedPath.isEmpty())
140 ThreadableBlobRegistry::registerBlobURL(nullptr, m_internalURL, srcURL);
141 else
142 ThreadableBlobRegistry::registerBlobURLOptionallyFileBacked(m_internalURL, srcURL, fileBackedPath, m_type);
143}
144
145Blob::Blob(const URL& srcURL, long long start, long long end, const String& type)
146 : m_type(normalizedContentType(type))
147 , m_size(-1) // size is not necessarily equal to end - start.
148{
149 m_internalURL = BlobURL::createInternalURL();
150 ThreadableBlobRegistry::registerBlobURLForSlice(m_internalURL, srcURL, start, end);
151}
152
153Blob::~Blob()
154{
155 ThreadableBlobRegistry::unregisterBlobURL(m_internalURL);
156}
157
158unsigned long long Blob::size() const
159{
160 if (m_size < 0) {
161 // FIXME: JavaScript cannot represent sizes as large as unsigned long long, we need to
162 // come up with an exception to throw if file size is not representable.
163 unsigned long long actualSize = ThreadableBlobRegistry::blobSize(m_internalURL);
164 m_size = WTF::isInBounds<long long>(actualSize) ? static_cast<long long>(actualSize) : 0;
165 }
166
167 return static_cast<unsigned long long>(m_size);
168}
169
170bool Blob::isValidContentType(const String& contentType)
171{
172 // FIXME: Do we really want to treat the empty string and null string as valid content types?
173 unsigned length = contentType.length();
174 for (unsigned i = 0; i < length; ++i) {
175 if (contentType[i] < 0x20 || contentType[i] > 0x7e)
176 return false;
177 }
178 return true;
179}
180
181String Blob::normalizedContentType(const String& contentType)
182{
183 if (!isValidContentType(contentType))
184 return emptyString();
185 return contentType.convertToASCIILowercase();
186}
187
188#if !ASSERT_DISABLED
189bool Blob::isNormalizedContentType(const String& contentType)
190{
191 // FIXME: Do we really want to treat the empty string and null string as valid content types?
192 unsigned length = contentType.length();
193 for (size_t i = 0; i < length; ++i) {
194 if (contentType[i] < 0x20 || contentType[i] > 0x7e)
195 return false;
196 if (isASCIIUpper(contentType[i]))
197 return false;
198 }
199 return true;
200}
201
202bool Blob::isNormalizedContentType(const CString& contentType)
203{
204 // FIXME: Do we really want to treat the empty string and null string as valid content types?
205 size_t length = contentType.length();
206 const char* characters = contentType.data();
207 for (size_t i = 0; i < length; ++i) {
208 if (characters[i] < 0x20 || characters[i] > 0x7e)
209 return false;
210 if (isASCIIUpper(characters[i]))
211 return false;
212 }
213 return true;
214}
215#endif
216
217URLRegistry& Blob::registry() const
218{
219 return BlobURLRegistry::registry();
220}
221
222
223} // namespace WebCore
224