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