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 "core/fileapi/FileReaderLoader.h" 34 35 #include "core/FetchInitiatorTypeNames.h" 36 #include "core/dom/ExecutionContext.h" 37 #include "core/fileapi/Blob.h" 38 #include "core/fileapi/FileReaderLoaderClient.h" 39 #include "core/html/parser/TextResourceDecoder.h" 40 #include "core/loader/ThreadableLoader.h" 41 #include "core/streams/Stream.h" 42 #include "platform/blob/BlobRegistry.h" 43 #include "platform/blob/BlobURL.h" 44 #include "platform/network/ResourceRequest.h" 45 #include "platform/network/ResourceResponse.h" 46 #include "public/platform/WebURLRequest.h" 47 #include "wtf/PassOwnPtr.h" 48 #include "wtf/PassRefPtr.h" 49 #include "wtf/RefPtr.h" 50 #include "wtf/Vector.h" 51 #include "wtf/text/Base64.h" 52 #include "wtf/text/StringBuilder.h" 53 54 namespace blink { 55 56 FileReaderLoader::FileReaderLoader(ReadType readType, FileReaderLoaderClient* client) 57 : m_readType(readType) 58 , m_client(client) 59 , m_urlForReadingIsStream(false) 60 , m_isRawDataConverted(false) 61 , m_stringResult("") 62 , m_finishedLoading(false) 63 , m_bytesLoaded(0) 64 , m_totalBytes(-1) 65 , m_hasRange(false) 66 , m_rangeStart(0) 67 , m_rangeEnd(0) 68 , m_errorCode(FileError::OK) 69 { 70 } 71 72 FileReaderLoader::~FileReaderLoader() 73 { 74 terminate(); 75 if (!m_urlForReading.isEmpty()) { 76 if (m_urlForReadingIsStream) 77 BlobRegistry::unregisterStreamURL(m_urlForReading); 78 else 79 BlobRegistry::revokePublicBlobURL(m_urlForReading); 80 } 81 } 82 83 void FileReaderLoader::startInternal(ExecutionContext& executionContext, const Stream* stream, PassRefPtr<BlobDataHandle> blobData) 84 { 85 // The blob is read by routing through the request handling layer given a temporary public url. 86 m_urlForReading = BlobURL::createPublicURL(executionContext.securityOrigin()); 87 if (m_urlForReading.isEmpty()) { 88 failed(FileError::SECURITY_ERR); 89 return; 90 } 91 92 if (blobData) { 93 ASSERT(!stream); 94 BlobRegistry::registerPublicBlobURL(executionContext.securityOrigin(), m_urlForReading, blobData); 95 } else { 96 ASSERT(stream); 97 BlobRegistry::registerStreamURL(executionContext.securityOrigin(), m_urlForReading, stream->url()); 98 } 99 100 // Construct and load the request. 101 ResourceRequest request(m_urlForReading); 102 103 // FIXME: Should this really be 'internal'? Do we know anything about the actual request that generated this fetch? 104 request.setRequestContext(WebURLRequest::RequestContextInternal); 105 106 request.setHTTPMethod("GET"); 107 if (m_hasRange) 108 request.setHTTPHeaderField("Range", AtomicString(String::format("bytes=%d-%d", m_rangeStart, m_rangeEnd))); 109 110 ThreadableLoaderOptions options; 111 options.preflightPolicy = ConsiderPreflight; 112 options.crossOriginRequestPolicy = DenyCrossOriginRequests; 113 // FIXME: Is there a directive to which this load should be subject? 114 options.contentSecurityPolicyEnforcement = DoNotEnforceContentSecurityPolicy; 115 // Use special initiator to hide the request from the inspector. 116 options.initiator = FetchInitiatorTypeNames::internal; 117 118 ResourceLoaderOptions resourceLoaderOptions; 119 resourceLoaderOptions.allowCredentials = AllowStoredCredentials; 120 121 if (m_client) 122 m_loader = ThreadableLoader::create(executionContext, this, request, options, resourceLoaderOptions); 123 else 124 ThreadableLoader::loadResourceSynchronously(executionContext, request, *this, options, resourceLoaderOptions); 125 } 126 127 void FileReaderLoader::start(ExecutionContext* executionContext, PassRefPtr<BlobDataHandle> blobData) 128 { 129 ASSERT(executionContext); 130 m_urlForReadingIsStream = false; 131 startInternal(*executionContext, 0, blobData); 132 } 133 134 void FileReaderLoader::start(ExecutionContext* executionContext, const Stream& stream, unsigned readSize) 135 { 136 ASSERT(executionContext); 137 if (readSize > 0) { 138 m_hasRange = true; 139 m_rangeStart = 0; 140 m_rangeEnd = readSize - 1; // End is inclusive so (0,0) is a 1-byte read. 141 } 142 143 m_urlForReadingIsStream = true; 144 startInternal(*executionContext, &stream, nullptr); 145 } 146 147 void FileReaderLoader::cancel() 148 { 149 m_errorCode = FileError::ABORT_ERR; 150 terminate(); 151 } 152 153 void FileReaderLoader::terminate() 154 { 155 if (m_loader) { 156 m_loader->cancel(); 157 cleanup(); 158 } 159 } 160 161 void FileReaderLoader::cleanup() 162 { 163 m_loader = nullptr; 164 165 // If we get any error, we do not need to keep a buffer around. 166 if (m_errorCode) { 167 m_rawData.clear(); 168 m_stringResult = ""; 169 m_isRawDataConverted = true; 170 m_decoder.clear(); 171 } 172 } 173 174 void FileReaderLoader::didReceiveResponse(unsigned long, const ResourceResponse& response) 175 { 176 if (response.httpStatusCode() != 200) { 177 failed(httpStatusCodeToErrorCode(response.httpStatusCode())); 178 return; 179 } 180 181 // A negative value means that the content length wasn't specified. 182 m_totalBytes = response.expectedContentLength(); 183 184 long long initialBufferLength = -1; 185 186 if (m_totalBytes >= 0) { 187 initialBufferLength = m_totalBytes; 188 } else if (m_hasRange) { 189 // Set m_totalBytes and allocate a buffer based on the specified range. 190 m_totalBytes = 1LL + m_rangeEnd - m_rangeStart; 191 initialBufferLength = m_totalBytes; 192 } else { 193 // Nothing is known about the size of the resource. Normalize 194 // m_totalBytes to -1 and initialize the buffer for receiving with the 195 // default size. 196 m_totalBytes = -1; 197 } 198 199 ASSERT(!m_rawData); 200 201 if (m_readType != ReadByClient) { 202 // Check that we can cast to unsigned since we have to do 203 // so to call ArrayBuffer's create function. 204 // FIXME: Support reading more than the current size limit of ArrayBuffer. 205 if (initialBufferLength > std::numeric_limits<unsigned>::max()) { 206 failed(FileError::NOT_READABLE_ERR); 207 return; 208 } 209 210 if (initialBufferLength < 0) 211 m_rawData = adoptPtr(new ArrayBufferBuilder()); 212 else 213 m_rawData = adoptPtr(new ArrayBufferBuilder(static_cast<unsigned>(initialBufferLength))); 214 215 if (!m_rawData || !m_rawData->isValid()) { 216 failed(FileError::NOT_READABLE_ERR); 217 return; 218 } 219 220 if (initialBufferLength >= 0) { 221 // Total size is known. Set m_rawData to ignore overflowed data. 222 m_rawData->setVariableCapacity(false); 223 } 224 } 225 226 if (m_client) 227 m_client->didStartLoading(); 228 } 229 230 void FileReaderLoader::didReceiveData(const char* data, int dataLength) 231 { 232 ASSERT(data); 233 ASSERT(dataLength > 0); 234 235 // Bail out if we already encountered an error. 236 if (m_errorCode) 237 return; 238 239 if (m_readType == ReadByClient) { 240 m_bytesLoaded += dataLength; 241 242 if (m_client) 243 m_client->didReceiveDataForClient(data, dataLength); 244 return; 245 } 246 247 unsigned bytesAppended = m_rawData->append(data, static_cast<unsigned>(dataLength)); 248 if (!bytesAppended) { 249 m_rawData.clear(); 250 m_bytesLoaded = 0; 251 failed(FileError::NOT_READABLE_ERR); 252 return; 253 } 254 m_bytesLoaded += bytesAppended; 255 m_isRawDataConverted = false; 256 257 if (m_client) 258 m_client->didReceiveData(); 259 } 260 261 void FileReaderLoader::didFinishLoading(unsigned long, double) 262 { 263 if (m_readType != ReadByClient && m_rawData) { 264 m_rawData->shrinkToFit(); 265 m_isRawDataConverted = false; 266 } 267 268 if (m_totalBytes == -1) { 269 // Update m_totalBytes only when in dynamic buffer grow mode. 270 m_totalBytes = m_bytesLoaded; 271 } 272 273 m_finishedLoading = true; 274 275 cleanup(); 276 if (m_client) 277 m_client->didFinishLoading(); 278 } 279 280 void FileReaderLoader::didFail(const ResourceError&) 281 { 282 // If we're aborting, do not proceed with normal error handling since it is covered in aborting code. 283 if (m_errorCode == FileError::ABORT_ERR) 284 return; 285 286 failed(FileError::NOT_READABLE_ERR); 287 } 288 289 void FileReaderLoader::failed(FileError::ErrorCode errorCode) 290 { 291 m_errorCode = errorCode; 292 cleanup(); 293 if (m_client) 294 m_client->didFail(m_errorCode); 295 } 296 297 FileError::ErrorCode FileReaderLoader::httpStatusCodeToErrorCode(int httpStatusCode) 298 { 299 switch (httpStatusCode) { 300 case 403: 301 return FileError::SECURITY_ERR; 302 case 404: 303 return FileError::NOT_FOUND_ERR; 304 default: 305 return FileError::NOT_READABLE_ERR; 306 } 307 } 308 309 PassRefPtr<ArrayBuffer> FileReaderLoader::arrayBufferResult() const 310 { 311 ASSERT(m_readType == ReadAsArrayBuffer); 312 313 // If the loading is not started or an error occurs, return an empty result. 314 if (!m_rawData || m_errorCode) 315 return nullptr; 316 317 return m_rawData->toArrayBuffer(); 318 } 319 320 String FileReaderLoader::stringResult() 321 { 322 ASSERT(m_readType != ReadAsArrayBuffer && m_readType != ReadAsBlob && m_readType != ReadByClient); 323 324 // If the loading is not started or an error occurs, return an empty result. 325 if (!m_rawData || m_errorCode) 326 return m_stringResult; 327 328 // If already converted from the raw data, return the result now. 329 if (m_isRawDataConverted) 330 return m_stringResult; 331 332 switch (m_readType) { 333 case ReadAsArrayBuffer: 334 // No conversion is needed. 335 break; 336 case ReadAsBinaryString: 337 m_stringResult = m_rawData->toString(); 338 m_isRawDataConverted = true; 339 break; 340 case ReadAsText: 341 convertToText(); 342 break; 343 case ReadAsDataURL: 344 // Partial data is not supported when reading as data URL. 345 if (m_finishedLoading) 346 convertToDataURL(); 347 break; 348 default: 349 ASSERT_NOT_REACHED(); 350 } 351 352 return m_stringResult; 353 } 354 355 void FileReaderLoader::convertToText() 356 { 357 m_isRawDataConverted = true; 358 359 if (!m_bytesLoaded) { 360 m_stringResult = ""; 361 return; 362 } 363 364 // Decode the data. 365 // The File API spec says that we should use the supplied encoding if it is valid. However, we choose to ignore this 366 // requirement in order to be consistent with how WebKit decodes the web content: always has the BOM override the 367 // provided encoding. 368 // FIXME: consider supporting incremental decoding to improve the perf. 369 StringBuilder builder; 370 if (!m_decoder) 371 m_decoder = TextResourceDecoder::create("text/plain", m_encoding.isValid() ? m_encoding : UTF8Encoding()); 372 builder.append(m_decoder->decode(static_cast<const char*>(m_rawData->data()), m_rawData->byteLength())); 373 374 if (m_finishedLoading) 375 builder.append(m_decoder->flush()); 376 377 m_stringResult = builder.toString(); 378 } 379 380 void FileReaderLoader::convertToDataURL() 381 { 382 m_isRawDataConverted = true; 383 384 StringBuilder builder; 385 builder.appendLiteral("data:"); 386 387 if (!m_bytesLoaded) { 388 m_stringResult = builder.toString(); 389 return; 390 } 391 392 builder.append(m_dataType); 393 builder.appendLiteral(";base64,"); 394 395 Vector<char> out; 396 base64Encode(static_cast<const char*>(m_rawData->data()), m_rawData->byteLength(), out); 397 out.append('\0'); 398 builder.append(out.data()); 399 400 m_stringResult = builder.toString(); 401 } 402 403 void FileReaderLoader::setEncoding(const String& encoding) 404 { 405 if (!encoding.isEmpty()) 406 m_encoding = WTF::TextEncoding(encoding); 407 } 408 409 } // namespace blink 410