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