1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/nsDOMBlobBuilder.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,492 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsDOMBlobBuilder.h" 1.10 +#include "jsfriendapi.h" 1.11 +#include "mozilla/dom/BlobBinding.h" 1.12 +#include "mozilla/dom/FileBinding.h" 1.13 +#include "nsAutoPtr.h" 1.14 +#include "nsDOMClassInfoID.h" 1.15 +#include "nsIMultiplexInputStream.h" 1.16 +#include "nsStringStream.h" 1.17 +#include "nsTArray.h" 1.18 +#include "nsJSUtils.h" 1.19 +#include "nsContentUtils.h" 1.20 +#include "nsIScriptError.h" 1.21 +#include "nsIXPConnect.h" 1.22 +#include <algorithm> 1.23 + 1.24 +using namespace mozilla; 1.25 +using namespace mozilla::dom; 1.26 + 1.27 +NS_IMPL_ISUPPORTS_INHERITED(nsDOMMultipartFile, nsDOMFile, 1.28 + nsIJSNativeInitializer) 1.29 + 1.30 +NS_IMETHODIMP 1.31 +nsDOMMultipartFile::GetSize(uint64_t* aLength) 1.32 +{ 1.33 + if (mLength == UINT64_MAX) { 1.34 + CheckedUint64 length = 0; 1.35 + 1.36 + uint32_t i; 1.37 + uint32_t len = mBlobs.Length(); 1.38 + for (i = 0; i < len; i++) { 1.39 + nsIDOMBlob* blob = mBlobs.ElementAt(i).get(); 1.40 + uint64_t l = 0; 1.41 + 1.42 + nsresult rv = blob->GetSize(&l); 1.43 + NS_ENSURE_SUCCESS(rv, rv); 1.44 + 1.45 + length += l; 1.46 + } 1.47 + 1.48 + NS_ENSURE_TRUE(length.isValid(), NS_ERROR_FAILURE); 1.49 + 1.50 + mLength = length.value(); 1.51 + } 1.52 + 1.53 + *aLength = mLength; 1.54 + return NS_OK; 1.55 +} 1.56 + 1.57 +NS_IMETHODIMP 1.58 +nsDOMMultipartFile::GetInternalStream(nsIInputStream** aStream) 1.59 +{ 1.60 + nsresult rv; 1.61 + *aStream = nullptr; 1.62 + 1.63 + nsCOMPtr<nsIMultiplexInputStream> stream = 1.64 + do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1"); 1.65 + NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE); 1.66 + 1.67 + uint32_t i; 1.68 + for (i = 0; i < mBlobs.Length(); i++) { 1.69 + nsCOMPtr<nsIInputStream> scratchStream; 1.70 + nsIDOMBlob* blob = mBlobs.ElementAt(i).get(); 1.71 + 1.72 + rv = blob->GetInternalStream(getter_AddRefs(scratchStream)); 1.73 + NS_ENSURE_SUCCESS(rv, rv); 1.74 + 1.75 + rv = stream->AppendStream(scratchStream); 1.76 + NS_ENSURE_SUCCESS(rv, rv); 1.77 + } 1.78 + 1.79 + return CallQueryInterface(stream, aStream); 1.80 +} 1.81 + 1.82 +already_AddRefed<nsIDOMBlob> 1.83 +nsDOMMultipartFile::CreateSlice(uint64_t aStart, uint64_t aLength, 1.84 + const nsAString& aContentType) 1.85 +{ 1.86 + // If we clamped to nothing we create an empty blob 1.87 + nsTArray<nsCOMPtr<nsIDOMBlob> > blobs; 1.88 + 1.89 + uint64_t length = aLength; 1.90 + uint64_t skipStart = aStart; 1.91 + 1.92 + // Prune the list of blobs if we can 1.93 + uint32_t i; 1.94 + for (i = 0; length && skipStart && i < mBlobs.Length(); i++) { 1.95 + nsIDOMBlob* blob = mBlobs[i].get(); 1.96 + 1.97 + uint64_t l; 1.98 + nsresult rv = blob->GetSize(&l); 1.99 + NS_ENSURE_SUCCESS(rv, nullptr); 1.100 + 1.101 + if (skipStart < l) { 1.102 + uint64_t upperBound = std::min<uint64_t>(l - skipStart, length); 1.103 + 1.104 + nsCOMPtr<nsIDOMBlob> firstBlob; 1.105 + rv = blob->Slice(skipStart, skipStart + upperBound, 1.106 + aContentType, 3, 1.107 + getter_AddRefs(firstBlob)); 1.108 + NS_ENSURE_SUCCESS(rv, nullptr); 1.109 + 1.110 + // Avoid wrapping a single blob inside an nsDOMMultipartFile 1.111 + if (length == upperBound) { 1.112 + return firstBlob.forget(); 1.113 + } 1.114 + 1.115 + blobs.AppendElement(firstBlob); 1.116 + length -= upperBound; 1.117 + i++; 1.118 + break; 1.119 + } 1.120 + skipStart -= l; 1.121 + } 1.122 + 1.123 + // Now append enough blobs until we're done 1.124 + for (; length && i < mBlobs.Length(); i++) { 1.125 + nsIDOMBlob* blob = mBlobs[i].get(); 1.126 + 1.127 + uint64_t l; 1.128 + nsresult rv = blob->GetSize(&l); 1.129 + NS_ENSURE_SUCCESS(rv, nullptr); 1.130 + 1.131 + if (length < l) { 1.132 + nsCOMPtr<nsIDOMBlob> lastBlob; 1.133 + rv = blob->Slice(0, length, aContentType, 3, 1.134 + getter_AddRefs(lastBlob)); 1.135 + NS_ENSURE_SUCCESS(rv, nullptr); 1.136 + 1.137 + blobs.AppendElement(lastBlob); 1.138 + } else { 1.139 + blobs.AppendElement(blob); 1.140 + } 1.141 + length -= std::min<uint64_t>(l, length); 1.142 + } 1.143 + 1.144 + // we can create our blob now 1.145 + nsCOMPtr<nsIDOMBlob> blob = new nsDOMMultipartFile(blobs, aContentType); 1.146 + return blob.forget(); 1.147 +} 1.148 + 1.149 +/* static */ nsresult 1.150 +nsDOMMultipartFile::NewFile(const nsAString& aName, nsISupports* *aNewObject) 1.151 +{ 1.152 + nsCOMPtr<nsISupports> file = 1.153 + do_QueryObject(new nsDOMMultipartFile(aName)); 1.154 + file.forget(aNewObject); 1.155 + return NS_OK; 1.156 +} 1.157 + 1.158 +/* static */ nsresult 1.159 +nsDOMMultipartFile::NewBlob(nsISupports* *aNewObject) 1.160 +{ 1.161 + nsCOMPtr<nsISupports> file = do_QueryObject(new nsDOMMultipartFile()); 1.162 + file.forget(aNewObject); 1.163 + return NS_OK; 1.164 +} 1.165 + 1.166 +static nsIDOMBlob* 1.167 +GetXPConnectNative(JSContext* aCx, JSObject* aObj) { 1.168 + nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface( 1.169 + nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, aObj)); 1.170 + return blob; 1.171 +} 1.172 + 1.173 +NS_IMETHODIMP 1.174 +nsDOMMultipartFile::Initialize(nsISupports* aOwner, 1.175 + JSContext* aCx, 1.176 + JSObject* aObj, 1.177 + const JS::CallArgs& aArgs) 1.178 +{ 1.179 + if (!mIsFile) { 1.180 + return InitBlob(aCx, aArgs.length(), aArgs.array(), GetXPConnectNative); 1.181 + } 1.182 + 1.183 + if (!nsContentUtils::IsCallerChrome()) { 1.184 + return InitFile(aCx, aArgs.length(), aArgs.array()); 1.185 + } 1.186 + 1.187 + if (aArgs.length() > 0) { 1.188 + JS::Value* argv = aArgs.array(); 1.189 + if (argv[0].isObject()) { 1.190 + JS::Rooted<JSObject*> obj(aCx, &argv[0].toObject()); 1.191 + if (JS_IsArrayObject(aCx, obj)) { 1.192 + return InitFile(aCx, aArgs.length(), aArgs.array()); 1.193 + } 1.194 + } 1.195 + } 1.196 + 1.197 + return InitChromeFile(aCx, aArgs.length(), aArgs.array()); 1.198 +} 1.199 + 1.200 +nsresult 1.201 +nsDOMMultipartFile::InitBlob(JSContext* aCx, 1.202 + uint32_t aArgc, 1.203 + JS::Value* aArgv, 1.204 + UnwrapFuncPtr aUnwrapFunc) 1.205 +{ 1.206 + bool nativeEOL = false; 1.207 + if (aArgc > 1) { 1.208 + BlobPropertyBag d; 1.209 + if (!d.Init(aCx, JS::Handle<JS::Value>::fromMarkedLocation(&aArgv[1]))) { 1.210 + return NS_ERROR_TYPE_ERR; 1.211 + } 1.212 + mContentType = d.mType; 1.213 + nativeEOL = d.mEndings == EndingTypes::Native; 1.214 + } 1.215 + 1.216 + if (aArgc > 0) { 1.217 + return ParseBlobArrayArgument(aCx, aArgv[0], nativeEOL, aUnwrapFunc); 1.218 + } 1.219 + 1.220 + return NS_OK; 1.221 +} 1.222 + 1.223 +nsresult 1.224 +nsDOMMultipartFile::ParseBlobArrayArgument(JSContext* aCx, JS::Value& aValue, 1.225 + bool aNativeEOL, 1.226 + UnwrapFuncPtr aUnwrapFunc) 1.227 +{ 1.228 + if (!aValue.isObject()) { 1.229 + return NS_ERROR_TYPE_ERR; // We're not interested 1.230 + } 1.231 + 1.232 + JS::Rooted<JSObject*> obj(aCx, &aValue.toObject()); 1.233 + if (!JS_IsArrayObject(aCx, obj)) { 1.234 + return NS_ERROR_TYPE_ERR; // We're not interested 1.235 + } 1.236 + 1.237 + BlobSet blobSet; 1.238 + 1.239 + uint32_t length; 1.240 + MOZ_ALWAYS_TRUE(JS_GetArrayLength(aCx, obj, &length)); 1.241 + for (uint32_t i = 0; i < length; ++i) { 1.242 + JS::Rooted<JS::Value> element(aCx); 1.243 + if (!JS_GetElement(aCx, obj, i, &element)) 1.244 + return NS_ERROR_TYPE_ERR; 1.245 + 1.246 + if (element.isObject()) { 1.247 + JS::Rooted<JSObject*> obj(aCx, &element.toObject()); 1.248 + nsCOMPtr<nsIDOMBlob> blob = aUnwrapFunc(aCx, obj); 1.249 + if (blob) { 1.250 + // Flatten so that multipart blobs will never nest 1.251 + nsDOMFileBase* file = static_cast<nsDOMFileBase*>( 1.252 + static_cast<nsIDOMBlob*>(blob)); 1.253 + const nsTArray<nsCOMPtr<nsIDOMBlob> >* 1.254 + subBlobs = file->GetSubBlobs(); 1.255 + if (subBlobs) { 1.256 + blobSet.AppendBlobs(*subBlobs); 1.257 + } else { 1.258 + blobSet.AppendBlob(blob); 1.259 + } 1.260 + continue; 1.261 + } 1.262 + if (JS_IsArrayBufferViewObject(obj)) { 1.263 + nsresult rv = blobSet.AppendVoidPtr( 1.264 + JS_GetArrayBufferViewData(obj), 1.265 + JS_GetArrayBufferViewByteLength(obj)); 1.266 + NS_ENSURE_SUCCESS(rv, rv); 1.267 + continue; 1.268 + } 1.269 + if (JS_IsArrayBufferObject(obj)) { 1.270 + nsresult rv = blobSet.AppendArrayBuffer(obj); 1.271 + NS_ENSURE_SUCCESS(rv, rv); 1.272 + continue; 1.273 + } 1.274 + } 1.275 + 1.276 + // coerce it to a string 1.277 + JSString* str = JS::ToString(aCx, element); 1.278 + NS_ENSURE_TRUE(str, NS_ERROR_TYPE_ERR); 1.279 + 1.280 + nsresult rv = blobSet.AppendString(str, aNativeEOL, aCx); 1.281 + NS_ENSURE_SUCCESS(rv, rv); 1.282 + } 1.283 + 1.284 + mBlobs = blobSet.GetBlobs(); 1.285 + return NS_OK; 1.286 +} 1.287 + 1.288 +NS_IMETHODIMP 1.289 +nsDOMMultipartFile::GetMozFullPathInternal(nsAString &aFilename) 1.290 +{ 1.291 + if (!mIsFromNsiFile || mBlobs.Length() == 0) { 1.292 + return nsDOMFile::GetMozFullPathInternal(aFilename); 1.293 + } 1.294 + 1.295 + nsIDOMBlob* blob = mBlobs.ElementAt(0).get(); 1.296 + nsDOMFileFile* file = static_cast<nsDOMFileFile*>(blob); 1.297 + if (!file) { 1.298 + return nsDOMFile::GetMozFullPathInternal(aFilename); 1.299 + } 1.300 + 1.301 + return file->GetMozFullPathInternal(aFilename); 1.302 +} 1.303 + 1.304 +nsresult 1.305 +nsDOMMultipartFile::InitChromeFile(JSContext* aCx, 1.306 + uint32_t aArgc, 1.307 + JS::Value* aArgv) 1.308 +{ 1.309 + nsresult rv; 1.310 + 1.311 + NS_ASSERTION(!mImmutable, "Something went wrong ..."); 1.312 + NS_ENSURE_TRUE(!mImmutable, NS_ERROR_UNEXPECTED); 1.313 + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); 1.314 + NS_ENSURE_TRUE(aArgc > 0, NS_ERROR_UNEXPECTED); 1.315 + 1.316 + if (aArgc > 1) { 1.317 + FilePropertyBag d; 1.318 + if (!d.Init(aCx, JS::Handle<JS::Value>::fromMarkedLocation(&aArgv[1]))) { 1.319 + return NS_ERROR_TYPE_ERR; 1.320 + } 1.321 + mName = d.mName; 1.322 + mContentType = d.mType; 1.323 + } 1.324 + 1.325 + 1.326 + // We expect to get a path to represent as a File object or 1.327 + // Blob object, an nsIFile, or an nsIDOMFile. 1.328 + nsCOMPtr<nsIFile> file; 1.329 + nsCOMPtr<nsIDOMBlob> blob; 1.330 + if (!aArgv[0].isString()) { 1.331 + // Lets see if it's an nsIFile 1.332 + if (!aArgv[0].isObject()) { 1.333 + return NS_ERROR_UNEXPECTED; // We're not interested 1.334 + } 1.335 + 1.336 + JSObject* obj = &aArgv[0].toObject(); 1.337 + 1.338 + nsISupports* supports = 1.339 + nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj); 1.340 + if (!supports) { 1.341 + return NS_ERROR_UNEXPECTED; 1.342 + } 1.343 + 1.344 + blob = do_QueryInterface(supports); 1.345 + file = do_QueryInterface(supports); 1.346 + if (!blob && !file) { 1.347 + return NS_ERROR_UNEXPECTED; 1.348 + } 1.349 + 1.350 + mIsFromNsiFile = true; 1.351 + } else { 1.352 + // It's a string 1.353 + JSString* str = JS::ToString(aCx, JS::Handle<JS::Value>::fromMarkedLocation(&aArgv[0])); 1.354 + NS_ENSURE_TRUE(str, NS_ERROR_XPC_BAD_CONVERT_JS); 1.355 + 1.356 + nsDependentJSString xpcomStr; 1.357 + if (!xpcomStr.init(aCx, str)) { 1.358 + return NS_ERROR_XPC_BAD_CONVERT_JS; 1.359 + } 1.360 + 1.361 + rv = NS_NewLocalFile(xpcomStr, false, getter_AddRefs(file)); 1.362 + NS_ENSURE_SUCCESS(rv, rv); 1.363 + } 1.364 + 1.365 + if (file) { 1.366 + bool exists; 1.367 + rv = file->Exists(&exists); 1.368 + NS_ENSURE_SUCCESS(rv, rv); 1.369 + NS_ENSURE_TRUE(exists, NS_ERROR_FILE_NOT_FOUND); 1.370 + 1.371 + bool isDir; 1.372 + rv = file->IsDirectory(&isDir); 1.373 + NS_ENSURE_SUCCESS(rv, rv); 1.374 + NS_ENSURE_FALSE(isDir, NS_ERROR_FILE_IS_DIRECTORY); 1.375 + 1.376 + if (mName.IsEmpty()) { 1.377 + file->GetLeafName(mName); 1.378 + } 1.379 + 1.380 + blob = new nsDOMFileFile(file); 1.381 + } 1.382 + 1.383 + // XXXkhuey this is terrible 1.384 + if (mContentType.IsEmpty()) { 1.385 + blob->GetType(mContentType); 1.386 + } 1.387 + 1.388 + BlobSet blobSet; 1.389 + blobSet.AppendBlob(blob); 1.390 + mBlobs = blobSet.GetBlobs(); 1.391 + 1.392 + return NS_OK; 1.393 +} 1.394 + 1.395 +nsresult 1.396 +nsDOMMultipartFile::InitFile(JSContext* aCx, 1.397 + uint32_t aArgc, 1.398 + JS::Value* aArgv) 1.399 +{ 1.400 + NS_ASSERTION(!mImmutable, "Something went wrong ..."); 1.401 + NS_ENSURE_TRUE(!mImmutable, NS_ERROR_UNEXPECTED); 1.402 + 1.403 + if (aArgc < 2) { 1.404 + return NS_ERROR_TYPE_ERR; 1.405 + } 1.406 + 1.407 + // File name 1.408 + JSString* str = JS::ToString(aCx, JS::Handle<JS::Value>::fromMarkedLocation(&aArgv[1])); 1.409 + NS_ENSURE_TRUE(str, NS_ERROR_XPC_BAD_CONVERT_JS); 1.410 + 1.411 + nsDependentJSString xpcomStr; 1.412 + if (!xpcomStr.init(aCx, str)) { 1.413 + return NS_ERROR_XPC_BAD_CONVERT_JS; 1.414 + } 1.415 + 1.416 + mName = xpcomStr; 1.417 + 1.418 + // Optional params 1.419 + bool nativeEOL = false; 1.420 + if (aArgc > 2) { 1.421 + BlobPropertyBag d; 1.422 + if (!d.Init(aCx, JS::Handle<JS::Value>::fromMarkedLocation(&aArgv[2]))) { 1.423 + return NS_ERROR_TYPE_ERR; 1.424 + } 1.425 + mContentType = d.mType; 1.426 + nativeEOL = d.mEndings == EndingTypes::Native; 1.427 + } 1.428 + 1.429 + return ParseBlobArrayArgument(aCx, aArgv[0], nativeEOL, GetXPConnectNative); 1.430 +} 1.431 + 1.432 +nsresult 1.433 +BlobSet::AppendVoidPtr(const void* aData, uint32_t aLength) 1.434 +{ 1.435 + NS_ENSURE_ARG_POINTER(aData); 1.436 + 1.437 + uint64_t offset = mDataLen; 1.438 + 1.439 + if (!ExpandBufferSize(aLength)) 1.440 + return NS_ERROR_OUT_OF_MEMORY; 1.441 + 1.442 + memcpy((char*)mData + offset, aData, aLength); 1.443 + return NS_OK; 1.444 +} 1.445 + 1.446 +nsresult 1.447 +BlobSet::AppendString(JSString* aString, bool nativeEOL, JSContext* aCx) 1.448 +{ 1.449 + nsDependentJSString xpcomStr; 1.450 + if (!xpcomStr.init(aCx, aString)) { 1.451 + return NS_ERROR_XPC_BAD_CONVERT_JS; 1.452 + } 1.453 + 1.454 + nsCString utf8Str = NS_ConvertUTF16toUTF8(xpcomStr); 1.455 + 1.456 + if (nativeEOL) { 1.457 + if (utf8Str.FindChar('\r') != kNotFound) { 1.458 + utf8Str.ReplaceSubstring("\r\n", "\n"); 1.459 + utf8Str.ReplaceSubstring("\r", "\n"); 1.460 + } 1.461 +#ifdef XP_WIN 1.462 + utf8Str.ReplaceSubstring("\n", "\r\n"); 1.463 +#endif 1.464 + } 1.465 + 1.466 + return AppendVoidPtr((void*)utf8Str.Data(), 1.467 + utf8Str.Length()); 1.468 +} 1.469 + 1.470 +nsresult 1.471 +BlobSet::AppendBlob(nsIDOMBlob* aBlob) 1.472 +{ 1.473 + NS_ENSURE_ARG_POINTER(aBlob); 1.474 + 1.475 + Flush(); 1.476 + mBlobs.AppendElement(aBlob); 1.477 + 1.478 + return NS_OK; 1.479 +} 1.480 + 1.481 +nsresult 1.482 +BlobSet::AppendBlobs(const nsTArray<nsCOMPtr<nsIDOMBlob> >& aBlob) 1.483 +{ 1.484 + Flush(); 1.485 + mBlobs.AppendElements(aBlob); 1.486 + 1.487 + return NS_OK; 1.488 +} 1.489 + 1.490 +nsresult 1.491 +BlobSet::AppendArrayBuffer(JSObject* aBuffer) 1.492 +{ 1.493 + return AppendVoidPtr(JS_GetArrayBufferData(aBuffer), 1.494 + JS_GetArrayBufferByteLength(aBuffer)); 1.495 +}