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 | |
33 | #include "BlobResourceHandle.h" |
34 | |
35 | #include "AsyncFileStream.h" |
36 | #include "BlobData.h" |
37 | #include "FileStream.h" |
38 | #include "HTTPHeaderNames.h" |
39 | #include "HTTPParsers.h" |
40 | #include "ParsedContentRange.h" |
41 | #include "ResourceError.h" |
42 | #include "ResourceHandleClient.h" |
43 | #include "ResourceRequest.h" |
44 | #include "ResourceResponse.h" |
45 | #include "SharedBuffer.h" |
46 | #include <wtf/CompletionHandler.h> |
47 | #include <wtf/FileSystem.h> |
48 | #include <wtf/MainThread.h> |
49 | #include <wtf/Ref.h> |
50 | #include <wtf/URL.h> |
51 | |
52 | namespace WebCore { |
53 | |
54 | static const unsigned bufferSize = 512 * 1024; |
55 | |
56 | static const int httpOK = 200; |
57 | static const int httpPartialContent = 206; |
58 | static const int httpNotAllowed = 403; |
59 | static const int httpRequestedRangeNotSatisfiable = 416; |
60 | static const int httpInternalError = 500; |
61 | static const char* httpOKText = "OK" ; |
62 | static const char* httpPartialContentText = "Partial Content" ; |
63 | static const char* httpNotAllowedText = "Not Allowed" ; |
64 | static const char* httpRequestedRangeNotSatisfiableText = "Requested Range Not Satisfiable" ; |
65 | static const char* httpInternalErrorText = "Internal Server Error" ; |
66 | |
67 | static const char* const webKitBlobResourceDomain = "WebKitBlobResource" ; |
68 | |
69 | /////////////////////////////////////////////////////////////////////////////// |
70 | // BlobResourceSynchronousLoader |
71 | |
72 | namespace { |
73 | |
74 | class BlobResourceSynchronousLoader : public ResourceHandleClient { |
75 | public: |
76 | BlobResourceSynchronousLoader(ResourceError&, ResourceResponse&, Vector<char>&); |
77 | |
78 | void didReceiveResponseAsync(ResourceHandle*, ResourceResponse&&, CompletionHandler<void()>&&) final; |
79 | void didFail(ResourceHandle*, const ResourceError&) final; |
80 | void willSendRequestAsync(ResourceHandle*, ResourceRequest&&, ResourceResponse&&, CompletionHandler<void(ResourceRequest&&)>&&) final; |
81 | #if USE(PROTECTION_SPACE_AUTH_CALLBACK) |
82 | void canAuthenticateAgainstProtectionSpaceAsync(ResourceHandle*, const ProtectionSpace&, CompletionHandler<void(bool)>&&) final; |
83 | #endif |
84 | |
85 | private: |
86 | ResourceError& m_error; |
87 | ResourceResponse& m_response; |
88 | Vector<char>& m_data; |
89 | }; |
90 | |
91 | BlobResourceSynchronousLoader::BlobResourceSynchronousLoader(ResourceError& error, ResourceResponse& response, Vector<char>& data) |
92 | : m_error(error) |
93 | , m_response(response) |
94 | , m_data(data) |
95 | { |
96 | } |
97 | |
98 | void BlobResourceSynchronousLoader::willSendRequestAsync(ResourceHandle*, ResourceRequest&& request, ResourceResponse&&, CompletionHandler<void(ResourceRequest&&)>&& completionHandler) |
99 | { |
100 | ASSERT_NOT_REACHED(); |
101 | completionHandler(WTFMove(request)); |
102 | } |
103 | |
104 | #if USE(PROTECTION_SPACE_AUTH_CALLBACK) |
105 | void BlobResourceSynchronousLoader::canAuthenticateAgainstProtectionSpaceAsync(ResourceHandle*, const ProtectionSpace&, CompletionHandler<void(bool)>&& completionHandler) |
106 | { |
107 | ASSERT_NOT_REACHED(); |
108 | completionHandler(false); |
109 | } |
110 | #endif |
111 | |
112 | void BlobResourceSynchronousLoader::didReceiveResponseAsync(ResourceHandle* handle, ResourceResponse&& response, CompletionHandler<void()>&& completionHandler) |
113 | { |
114 | // We cannot handle the size that is more than maximum integer. |
115 | if (response.expectedContentLength() > INT_MAX) { |
116 | m_error = ResourceError(webKitBlobResourceDomain, static_cast<int>(BlobResourceHandle::Error::NotReadableError), response.url(), "File is too large" ); |
117 | completionHandler(); |
118 | return; |
119 | } |
120 | |
121 | m_response = response; |
122 | |
123 | // Read all the data. |
124 | m_data.resize(static_cast<size_t>(response.expectedContentLength())); |
125 | static_cast<BlobResourceHandle*>(handle)->readSync(m_data.data(), static_cast<int>(m_data.size())); |
126 | completionHandler(); |
127 | } |
128 | |
129 | void BlobResourceSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error) |
130 | { |
131 | m_error = error; |
132 | } |
133 | |
134 | } |
135 | |
136 | /////////////////////////////////////////////////////////////////////////////// |
137 | // BlobResourceHandle |
138 | |
139 | Ref<BlobResourceHandle> BlobResourceHandle::createAsync(BlobData* blobData, const ResourceRequest& request, ResourceHandleClient* client) |
140 | { |
141 | return adoptRef(*new BlobResourceHandle(blobData, request, client, true)); |
142 | } |
143 | |
144 | void BlobResourceHandle::loadResourceSynchronously(BlobData* blobData, const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) |
145 | { |
146 | if (!equalLettersIgnoringASCIICase(request.httpMethod(), "get" )) { |
147 | error = ResourceError(webKitBlobResourceDomain, static_cast<int>(Error::MethodNotAllowed), response.url(), "Request method must be GET" ); |
148 | return; |
149 | } |
150 | |
151 | BlobResourceSynchronousLoader loader(error, response, data); |
152 | RefPtr<BlobResourceHandle> handle = adoptRef(new BlobResourceHandle(blobData, request, &loader, false)); |
153 | handle->start(); |
154 | } |
155 | |
156 | BlobResourceHandle::BlobResourceHandle(BlobData* blobData, const ResourceRequest& request, ResourceHandleClient* client, bool async) |
157 | : ResourceHandle { nullptr, request, client, false /* defersLoading */, false /* shouldContentSniff */, true /* shouldContentEncodingSniff */ } |
158 | , m_blobData { blobData } |
159 | , m_async { async } |
160 | { |
161 | if (m_async) |
162 | m_asyncStream = std::make_unique<AsyncFileStream>(*this); |
163 | else |
164 | m_stream = std::make_unique<FileStream>(); |
165 | } |
166 | |
167 | BlobResourceHandle::~BlobResourceHandle() = default; |
168 | |
169 | void BlobResourceHandle::cancel() |
170 | { |
171 | m_asyncStream = nullptr; |
172 | m_fileOpened = false; |
173 | |
174 | m_aborted = true; |
175 | |
176 | ResourceHandle::cancel(); |
177 | } |
178 | |
179 | void BlobResourceHandle::start() |
180 | { |
181 | if (!m_async) { |
182 | doStart(); |
183 | return; |
184 | } |
185 | |
186 | // Finish this async call quickly and return. |
187 | callOnMainThread([protectedThis = makeRef(*this)]() mutable { |
188 | protectedThis->doStart(); |
189 | }); |
190 | } |
191 | |
192 | void BlobResourceHandle::doStart() |
193 | { |
194 | ASSERT(isMainThread()); |
195 | |
196 | // Do not continue if the request is aborted or an error occurs. |
197 | if (erroredOrAborted()) |
198 | return; |
199 | |
200 | if (!equalLettersIgnoringASCIICase(firstRequest().httpMethod(), "get" )) { |
201 | notifyFail(Error::MethodNotAllowed); |
202 | return; |
203 | } |
204 | |
205 | // If the blob data is not found, fail now. |
206 | if (!m_blobData) { |
207 | notifyFail(Error::NotFoundError); |
208 | return; |
209 | } |
210 | |
211 | // Parse the "Range" header we care about. |
212 | String range = firstRequest().httpHeaderField(HTTPHeaderName::Range); |
213 | if (!range.isEmpty() && !parseRange(range, m_rangeOffset, m_rangeEnd, m_rangeSuffixLength)) { |
214 | m_errorCode = Error::RangeError; |
215 | notifyResponse(); |
216 | return; |
217 | } |
218 | |
219 | if (m_async) |
220 | getSizeForNext(); |
221 | else { |
222 | Ref<BlobResourceHandle> protectedThis(*this); // getSizeForNext calls the client |
223 | for (size_t i = 0; i < m_blobData->items().size() && !erroredOrAborted(); ++i) |
224 | getSizeForNext(); |
225 | notifyResponse(); |
226 | } |
227 | } |
228 | |
229 | void BlobResourceHandle::getSizeForNext() |
230 | { |
231 | ASSERT(isMainThread()); |
232 | |
233 | // Do we finish validating and counting size for all items? |
234 | if (m_sizeItemCount >= m_blobData->items().size()) { |
235 | seek(); |
236 | |
237 | // Start reading if in asynchronous mode. |
238 | if (m_async) { |
239 | Ref<BlobResourceHandle> protectedThis(*this); |
240 | notifyResponse(); |
241 | } |
242 | return; |
243 | } |
244 | |
245 | const BlobDataItem& item = m_blobData->items().at(m_sizeItemCount); |
246 | switch (item.type()) { |
247 | case BlobDataItem::Type::Data: |
248 | didGetSize(item.length()); |
249 | break; |
250 | case BlobDataItem::Type::File: |
251 | // Files know their sizes, but asking the stream to verify that the file wasn't modified. |
252 | if (m_async) |
253 | m_asyncStream->getSize(item.file()->path(), item.file()->expectedModificationTime()); |
254 | else |
255 | didGetSize(m_stream->getSize(item.file()->path(), item.file()->expectedModificationTime())); |
256 | break; |
257 | default: |
258 | ASSERT_NOT_REACHED(); |
259 | } |
260 | } |
261 | |
262 | void BlobResourceHandle::didGetSize(long long size) |
263 | { |
264 | ASSERT(isMainThread()); |
265 | |
266 | // Do not continue if the request is aborted or an error occurs. |
267 | if (erroredOrAborted()) |
268 | return; |
269 | |
270 | // If the size is -1, it means the file has been moved or changed. Fail now. |
271 | if (size == -1) { |
272 | notifyFail(Error::NotFoundError); |
273 | return; |
274 | } |
275 | |
276 | // The size passed back is the size of the whole file. If the underlying item is a sliced file, we need to use the slice length. |
277 | const BlobDataItem& item = m_blobData->items().at(m_sizeItemCount); |
278 | size = item.length(); |
279 | |
280 | // Cache the size. |
281 | m_itemLengthList.append(size); |
282 | |
283 | // Count the size. |
284 | m_totalSize += size; |
285 | m_totalRemainingSize += size; |
286 | m_sizeItemCount++; |
287 | |
288 | // Continue with the next item. |
289 | getSizeForNext(); |
290 | } |
291 | |
292 | void BlobResourceHandle::seek() |
293 | { |
294 | ASSERT(isMainThread()); |
295 | |
296 | // Convert from the suffix length to the range. |
297 | if (m_rangeSuffixLength != kPositionNotSpecified) { |
298 | m_rangeOffset = m_totalRemainingSize - m_rangeSuffixLength; |
299 | m_rangeEnd = m_rangeOffset + m_rangeSuffixLength - 1; |
300 | } |
301 | |
302 | // Bail out if the range is not provided. |
303 | if (m_rangeOffset == kPositionNotSpecified) |
304 | return; |
305 | |
306 | // Skip the initial items that are not in the range. |
307 | long long offset = m_rangeOffset; |
308 | for (m_readItemCount = 0; m_readItemCount < m_blobData->items().size() && offset >= m_itemLengthList[m_readItemCount]; ++m_readItemCount) |
309 | offset -= m_itemLengthList[m_readItemCount]; |
310 | |
311 | // Set the offset that need to jump to for the first item in the range. |
312 | m_currentItemReadSize = offset; |
313 | |
314 | // Adjust the total remaining size in order not to go beyond the range. |
315 | if (m_rangeEnd != kPositionNotSpecified) { |
316 | long long rangeSize = m_rangeEnd - m_rangeOffset + 1; |
317 | if (m_totalRemainingSize > rangeSize) |
318 | m_totalRemainingSize = rangeSize; |
319 | } else |
320 | m_totalRemainingSize -= m_rangeOffset; |
321 | } |
322 | |
323 | int BlobResourceHandle::readSync(char* buf, int length) |
324 | { |
325 | ASSERT(isMainThread()); |
326 | |
327 | ASSERT(!m_async); |
328 | Ref<BlobResourceHandle> protectedThis(*this); |
329 | |
330 | int offset = 0; |
331 | int remaining = length; |
332 | while (remaining) { |
333 | // Do not continue if the request is aborted or an error occurs. |
334 | if (erroredOrAborted()) |
335 | break; |
336 | |
337 | // If there is no more remaining data to read, we are done. |
338 | if (!m_totalRemainingSize || m_readItemCount >= m_blobData->items().size()) |
339 | break; |
340 | |
341 | const BlobDataItem& item = m_blobData->items().at(m_readItemCount); |
342 | int bytesRead = 0; |
343 | if (item.type() == BlobDataItem::Type::Data) |
344 | bytesRead = readDataSync(item, buf + offset, remaining); |
345 | else if (item.type() == BlobDataItem::Type::File) |
346 | bytesRead = readFileSync(item, buf + offset, remaining); |
347 | else |
348 | ASSERT_NOT_REACHED(); |
349 | |
350 | if (bytesRead > 0) { |
351 | offset += bytesRead; |
352 | remaining -= bytesRead; |
353 | } |
354 | } |
355 | |
356 | int result; |
357 | if (erroredOrAborted()) |
358 | result = -1; |
359 | else |
360 | result = length - remaining; |
361 | |
362 | if (result > 0) |
363 | notifyReceiveData(buf, result); |
364 | |
365 | if (!result) |
366 | notifyFinish(); |
367 | |
368 | return result; |
369 | } |
370 | |
371 | int BlobResourceHandle::readDataSync(const BlobDataItem& item, char* buf, int length) |
372 | { |
373 | ASSERT(isMainThread()); |
374 | |
375 | ASSERT(!m_async); |
376 | |
377 | long long remaining = item.length() - m_currentItemReadSize; |
378 | int bytesToRead = (length > remaining) ? static_cast<int>(remaining) : length; |
379 | if (bytesToRead > m_totalRemainingSize) |
380 | bytesToRead = static_cast<int>(m_totalRemainingSize); |
381 | memcpy(buf, item.data().data() + item.offset() + m_currentItemReadSize, bytesToRead); |
382 | m_totalRemainingSize -= bytesToRead; |
383 | |
384 | m_currentItemReadSize += bytesToRead; |
385 | if (m_currentItemReadSize == item.length()) { |
386 | m_readItemCount++; |
387 | m_currentItemReadSize = 0; |
388 | } |
389 | |
390 | return bytesToRead; |
391 | } |
392 | |
393 | int BlobResourceHandle::readFileSync(const BlobDataItem& item, char* buf, int length) |
394 | { |
395 | ASSERT(isMainThread()); |
396 | |
397 | ASSERT(!m_async); |
398 | |
399 | if (!m_fileOpened) { |
400 | long long bytesToRead = m_itemLengthList[m_readItemCount] - m_currentItemReadSize; |
401 | if (bytesToRead > m_totalRemainingSize) |
402 | bytesToRead = m_totalRemainingSize; |
403 | bool success = m_stream->openForRead(item.file()->path(), item.offset() + m_currentItemReadSize, bytesToRead); |
404 | m_currentItemReadSize = 0; |
405 | if (!success) { |
406 | m_errorCode = Error::NotReadableError; |
407 | return 0; |
408 | } |
409 | |
410 | m_fileOpened = true; |
411 | } |
412 | |
413 | int bytesRead = m_stream->read(buf, length); |
414 | if (bytesRead < 0) { |
415 | m_errorCode = Error::NotReadableError; |
416 | return 0; |
417 | } |
418 | if (!bytesRead) { |
419 | m_stream->close(); |
420 | m_fileOpened = false; |
421 | m_readItemCount++; |
422 | } else |
423 | m_totalRemainingSize -= bytesRead; |
424 | |
425 | return bytesRead; |
426 | } |
427 | |
428 | void BlobResourceHandle::readAsync() |
429 | { |
430 | ASSERT(isMainThread()); |
431 | |
432 | // Do not continue if the request is aborted or an error occurs. |
433 | if (erroredOrAborted()) |
434 | return; |
435 | |
436 | // If there is no more remaining data to read, we are done. |
437 | if (!m_totalRemainingSize || m_readItemCount >= m_blobData->items().size()) { |
438 | notifyFinish(); |
439 | return; |
440 | } |
441 | |
442 | const BlobDataItem& item = m_blobData->items().at(m_readItemCount); |
443 | if (item.type() == BlobDataItem::Type::Data) |
444 | readDataAsync(item); |
445 | else if (item.type() == BlobDataItem::Type::File) |
446 | readFileAsync(item); |
447 | else |
448 | ASSERT_NOT_REACHED(); |
449 | } |
450 | |
451 | void BlobResourceHandle::readDataAsync(const BlobDataItem& item) |
452 | { |
453 | ASSERT(isMainThread()); |
454 | ASSERT(item.data().data()); |
455 | |
456 | Ref<BlobResourceHandle> protectedThis(*this); |
457 | |
458 | long long bytesToRead = item.length() - m_currentItemReadSize; |
459 | if (bytesToRead > m_totalRemainingSize) |
460 | bytesToRead = m_totalRemainingSize; |
461 | consumeData(reinterpret_cast<const char*>(item.data().data()->data()) + item.offset() + m_currentItemReadSize, static_cast<int>(bytesToRead)); |
462 | m_currentItemReadSize = 0; |
463 | } |
464 | |
465 | void BlobResourceHandle::readFileAsync(const BlobDataItem& item) |
466 | { |
467 | ASSERT(isMainThread()); |
468 | |
469 | if (m_fileOpened) { |
470 | m_asyncStream->read(m_buffer.data(), m_buffer.size()); |
471 | return; |
472 | } |
473 | |
474 | long long bytesToRead = m_itemLengthList[m_readItemCount] - m_currentItemReadSize; |
475 | if (bytesToRead > m_totalRemainingSize) |
476 | bytesToRead = static_cast<int>(m_totalRemainingSize); |
477 | m_asyncStream->openForRead(item.file()->path(), item.offset() + m_currentItemReadSize, bytesToRead); |
478 | m_fileOpened = true; |
479 | m_currentItemReadSize = 0; |
480 | } |
481 | |
482 | void BlobResourceHandle::didOpen(bool success) |
483 | { |
484 | ASSERT(m_async); |
485 | |
486 | if (!success) { |
487 | failed(Error::NotReadableError); |
488 | return; |
489 | } |
490 | |
491 | // Continue the reading. |
492 | readAsync(); |
493 | } |
494 | |
495 | void BlobResourceHandle::didRead(int bytesRead) |
496 | { |
497 | if (bytesRead < 0) { |
498 | failed(Error::NotReadableError); |
499 | return; |
500 | } |
501 | |
502 | consumeData(m_buffer.data(), bytesRead); |
503 | } |
504 | |
505 | void BlobResourceHandle::consumeData(const char* data, int bytesRead) |
506 | { |
507 | ASSERT(m_async); |
508 | Ref<BlobResourceHandle> protectedThis(*this); |
509 | |
510 | m_totalRemainingSize -= bytesRead; |
511 | |
512 | // Notify the client. |
513 | if (bytesRead) |
514 | notifyReceiveData(data, bytesRead); |
515 | |
516 | if (m_fileOpened) { |
517 | // When the current item is a file item, the reading is completed only if bytesRead is 0. |
518 | if (!bytesRead) { |
519 | // Close the file. |
520 | m_fileOpened = false; |
521 | m_asyncStream->close(); |
522 | |
523 | // Move to the next item. |
524 | m_readItemCount++; |
525 | } |
526 | } else { |
527 | // Otherwise, we read the current text item as a whole and move to the next item. |
528 | m_readItemCount++; |
529 | } |
530 | |
531 | // Continue the reading. |
532 | readAsync(); |
533 | } |
534 | |
535 | void BlobResourceHandle::failed(Error errorCode) |
536 | { |
537 | ASSERT(m_async); |
538 | Ref<BlobResourceHandle> protectedThis(*this); |
539 | |
540 | // Notify the client. |
541 | notifyFail(errorCode); |
542 | |
543 | // Close the file if needed. |
544 | if (m_fileOpened) { |
545 | m_fileOpened = false; |
546 | m_asyncStream->close(); |
547 | } |
548 | } |
549 | |
550 | void BlobResourceHandle::notifyResponse() |
551 | { |
552 | if (!client()) |
553 | return; |
554 | |
555 | if (m_errorCode != Error::NoError) { |
556 | Ref<BlobResourceHandle> protectedThis(*this); |
557 | notifyResponseOnError(); |
558 | notifyFinish(); |
559 | } else |
560 | notifyResponseOnSuccess(); |
561 | } |
562 | |
563 | void BlobResourceHandle::notifyResponseOnSuccess() |
564 | { |
565 | ASSERT(isMainThread()); |
566 | |
567 | bool isRangeRequest = m_rangeOffset != kPositionNotSpecified; |
568 | ResourceResponse response(firstRequest().url(), m_blobData->contentType(), m_totalRemainingSize, String()); |
569 | response.setHTTPStatusCode(isRangeRequest ? httpPartialContent : httpOK); |
570 | response.setHTTPStatusText(isRangeRequest ? httpPartialContentText : httpOKText); |
571 | |
572 | response.setHTTPHeaderField(HTTPHeaderName::ContentType, m_blobData->contentType()); |
573 | response.setHTTPHeaderField(HTTPHeaderName::ContentLength, String::number(m_totalRemainingSize)); |
574 | |
575 | if (isRangeRequest) |
576 | response.setHTTPHeaderField(HTTPHeaderName::ContentRange, ParsedContentRange(m_rangeOffset, m_rangeEnd, m_totalSize).headerValue()); |
577 | // FIXME: If a resource identified with a blob: URL is a File object, user agents must use that file's name attribute, |
578 | // as if the response had a Content-Disposition header with the filename parameter set to the File's name attribute. |
579 | // Notably, this will affect a name suggested in "File Save As". |
580 | |
581 | client()->didReceiveResponseAsync(this, WTFMove(response), [this, protectedThis = makeRef(*this)] { |
582 | m_buffer.resize(bufferSize); |
583 | readAsync(); |
584 | }); |
585 | } |
586 | |
587 | void BlobResourceHandle::notifyResponseOnError() |
588 | { |
589 | ASSERT(m_errorCode != Error::NoError); |
590 | |
591 | ResourceResponse response(firstRequest().url(), "text/plain" , 0, String()); |
592 | switch (m_errorCode) { |
593 | case Error::RangeError: |
594 | response.setHTTPStatusCode(httpRequestedRangeNotSatisfiable); |
595 | response.setHTTPStatusText(httpRequestedRangeNotSatisfiableText); |
596 | break; |
597 | case Error::SecurityError: |
598 | response.setHTTPStatusCode(httpNotAllowed); |
599 | response.setHTTPStatusText(httpNotAllowedText); |
600 | break; |
601 | default: |
602 | response.setHTTPStatusCode(httpInternalError); |
603 | response.setHTTPStatusText(httpInternalErrorText); |
604 | break; |
605 | } |
606 | |
607 | client()->didReceiveResponseAsync(this, WTFMove(response), [this, protectedThis = makeRef(*this)] { |
608 | m_buffer.resize(bufferSize); |
609 | readAsync(); |
610 | }); |
611 | } |
612 | |
613 | void BlobResourceHandle::notifyReceiveData(const char* data, int bytesRead) |
614 | { |
615 | if (client()) |
616 | client()->didReceiveBuffer(this, SharedBuffer::create(reinterpret_cast<const uint8_t*>(data), bytesRead), bytesRead); |
617 | } |
618 | |
619 | void BlobResourceHandle::notifyFail(Error errorCode) |
620 | { |
621 | if (client()) |
622 | client()->didFail(this, ResourceError(webKitBlobResourceDomain, static_cast<int>(errorCode), firstRequest().url(), String())); |
623 | } |
624 | |
625 | static void doNotifyFinish(BlobResourceHandle& handle) |
626 | { |
627 | if (handle.aborted()) |
628 | return; |
629 | |
630 | if (!handle.client()) |
631 | return; |
632 | |
633 | handle.client()->didFinishLoading(&handle); |
634 | } |
635 | |
636 | void BlobResourceHandle::notifyFinish() |
637 | { |
638 | if (!m_async) { |
639 | doNotifyFinish(*this); |
640 | return; |
641 | } |
642 | |
643 | // Schedule to notify the client from a standalone function because the client might dispose the handle immediately from the callback function |
644 | // while we still have BlobResourceHandle calls in the stack. |
645 | callOnMainThread([protectedThis = makeRef(*this)]() mutable { |
646 | doNotifyFinish(protectedThis); |
647 | }); |
648 | |
649 | } |
650 | |
651 | } // namespace WebCore |
652 | |