content/base/src/nsDOMBlobBuilder.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsDOMBlobBuilder.h"
     7 #include "jsfriendapi.h"
     8 #include "mozilla/dom/BlobBinding.h"
     9 #include "mozilla/dom/FileBinding.h"
    10 #include "nsAutoPtr.h"
    11 #include "nsDOMClassInfoID.h"
    12 #include "nsIMultiplexInputStream.h"
    13 #include "nsStringStream.h"
    14 #include "nsTArray.h"
    15 #include "nsJSUtils.h"
    16 #include "nsContentUtils.h"
    17 #include "nsIScriptError.h"
    18 #include "nsIXPConnect.h"
    19 #include <algorithm>
    21 using namespace mozilla;
    22 using namespace mozilla::dom;
    24 NS_IMPL_ISUPPORTS_INHERITED(nsDOMMultipartFile, nsDOMFile,
    25                             nsIJSNativeInitializer)
    27 NS_IMETHODIMP
    28 nsDOMMultipartFile::GetSize(uint64_t* aLength)
    29 {
    30   if (mLength == UINT64_MAX) {
    31     CheckedUint64 length = 0;
    33     uint32_t i;
    34     uint32_t len = mBlobs.Length();
    35     for (i = 0; i < len; i++) {
    36       nsIDOMBlob* blob = mBlobs.ElementAt(i).get();
    37       uint64_t l = 0;
    39       nsresult rv = blob->GetSize(&l);
    40       NS_ENSURE_SUCCESS(rv, rv);
    42       length += l;
    43     }
    45     NS_ENSURE_TRUE(length.isValid(), NS_ERROR_FAILURE);
    47     mLength = length.value();
    48   }
    50   *aLength = mLength;
    51   return NS_OK;
    52 }
    54 NS_IMETHODIMP
    55 nsDOMMultipartFile::GetInternalStream(nsIInputStream** aStream)
    56 {
    57   nsresult rv;
    58   *aStream = nullptr;
    60   nsCOMPtr<nsIMultiplexInputStream> stream =
    61     do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
    62   NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
    64   uint32_t i;
    65   for (i = 0; i < mBlobs.Length(); i++) {
    66     nsCOMPtr<nsIInputStream> scratchStream;
    67     nsIDOMBlob* blob = mBlobs.ElementAt(i).get();
    69     rv = blob->GetInternalStream(getter_AddRefs(scratchStream));
    70     NS_ENSURE_SUCCESS(rv, rv);
    72     rv = stream->AppendStream(scratchStream);
    73     NS_ENSURE_SUCCESS(rv, rv);
    74   }
    76   return CallQueryInterface(stream, aStream);
    77 }
    79 already_AddRefed<nsIDOMBlob>
    80 nsDOMMultipartFile::CreateSlice(uint64_t aStart, uint64_t aLength,
    81                                 const nsAString& aContentType)
    82 {
    83   // If we clamped to nothing we create an empty blob
    84   nsTArray<nsCOMPtr<nsIDOMBlob> > blobs;
    86   uint64_t length = aLength;
    87   uint64_t skipStart = aStart;
    89   // Prune the list of blobs if we can
    90   uint32_t i;
    91   for (i = 0; length && skipStart && i < mBlobs.Length(); i++) {
    92     nsIDOMBlob* blob = mBlobs[i].get();
    94     uint64_t l;
    95     nsresult rv = blob->GetSize(&l);
    96     NS_ENSURE_SUCCESS(rv, nullptr);
    98     if (skipStart < l) {
    99       uint64_t upperBound = std::min<uint64_t>(l - skipStart, length);
   101       nsCOMPtr<nsIDOMBlob> firstBlob;
   102       rv = blob->Slice(skipStart, skipStart + upperBound,
   103                        aContentType, 3,
   104                        getter_AddRefs(firstBlob));
   105       NS_ENSURE_SUCCESS(rv, nullptr);
   107       // Avoid wrapping a single blob inside an nsDOMMultipartFile
   108       if (length == upperBound) {
   109         return firstBlob.forget();
   110       }
   112       blobs.AppendElement(firstBlob);
   113       length -= upperBound;
   114       i++;
   115       break;
   116     }
   117     skipStart -= l;
   118   }
   120   // Now append enough blobs until we're done
   121   for (; length && i < mBlobs.Length(); i++) {
   122     nsIDOMBlob* blob = mBlobs[i].get();
   124     uint64_t l;
   125     nsresult rv = blob->GetSize(&l);
   126     NS_ENSURE_SUCCESS(rv, nullptr);
   128     if (length < l) {
   129       nsCOMPtr<nsIDOMBlob> lastBlob;
   130       rv = blob->Slice(0, length, aContentType, 3,
   131                        getter_AddRefs(lastBlob));
   132       NS_ENSURE_SUCCESS(rv, nullptr);
   134       blobs.AppendElement(lastBlob);
   135     } else {
   136       blobs.AppendElement(blob);
   137     }
   138     length -= std::min<uint64_t>(l, length);
   139   }
   141   // we can create our blob now
   142   nsCOMPtr<nsIDOMBlob> blob = new nsDOMMultipartFile(blobs, aContentType);
   143   return blob.forget();
   144 }
   146 /* static */ nsresult
   147 nsDOMMultipartFile::NewFile(const nsAString& aName, nsISupports* *aNewObject)
   148 {
   149   nsCOMPtr<nsISupports> file =
   150     do_QueryObject(new nsDOMMultipartFile(aName));
   151   file.forget(aNewObject);
   152   return NS_OK;
   153 }
   155 /* static */ nsresult
   156 nsDOMMultipartFile::NewBlob(nsISupports* *aNewObject)
   157 {
   158   nsCOMPtr<nsISupports> file = do_QueryObject(new nsDOMMultipartFile());
   159   file.forget(aNewObject);
   160   return NS_OK;
   161 }
   163 static nsIDOMBlob*
   164 GetXPConnectNative(JSContext* aCx, JSObject* aObj) {
   165   nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(
   166     nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, aObj));
   167   return blob;
   168 }
   170 NS_IMETHODIMP
   171 nsDOMMultipartFile::Initialize(nsISupports* aOwner,
   172                                JSContext* aCx,
   173                                JSObject* aObj,
   174                                const JS::CallArgs& aArgs)
   175 {
   176   if (!mIsFile) {
   177     return InitBlob(aCx, aArgs.length(), aArgs.array(), GetXPConnectNative);
   178   }
   180   if (!nsContentUtils::IsCallerChrome()) {
   181     return InitFile(aCx, aArgs.length(), aArgs.array());
   182   }
   184   if (aArgs.length() > 0) {
   185     JS::Value* argv = aArgs.array();
   186     if (argv[0].isObject()) {
   187       JS::Rooted<JSObject*> obj(aCx, &argv[0].toObject());
   188       if (JS_IsArrayObject(aCx, obj)) {
   189         return InitFile(aCx, aArgs.length(), aArgs.array());
   190       }
   191     }
   192   }
   194   return InitChromeFile(aCx, aArgs.length(), aArgs.array());
   195 }
   197 nsresult
   198 nsDOMMultipartFile::InitBlob(JSContext* aCx,
   199                              uint32_t aArgc,
   200                              JS::Value* aArgv,
   201                              UnwrapFuncPtr aUnwrapFunc)
   202 {
   203   bool nativeEOL = false;
   204   if (aArgc > 1) {
   205     BlobPropertyBag d;
   206     if (!d.Init(aCx, JS::Handle<JS::Value>::fromMarkedLocation(&aArgv[1]))) {
   207       return NS_ERROR_TYPE_ERR;
   208     }
   209     mContentType = d.mType;
   210     nativeEOL = d.mEndings == EndingTypes::Native;
   211   }
   213   if (aArgc > 0) {
   214     return ParseBlobArrayArgument(aCx, aArgv[0], nativeEOL, aUnwrapFunc);
   215   }
   217   return NS_OK;
   218 }
   220 nsresult
   221 nsDOMMultipartFile::ParseBlobArrayArgument(JSContext* aCx, JS::Value& aValue,
   222                                            bool aNativeEOL,
   223                                            UnwrapFuncPtr aUnwrapFunc)
   224 {
   225   if (!aValue.isObject()) {
   226     return NS_ERROR_TYPE_ERR; // We're not interested
   227   }
   229   JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
   230   if (!JS_IsArrayObject(aCx, obj)) {
   231     return NS_ERROR_TYPE_ERR; // We're not interested
   232   }
   234   BlobSet blobSet;
   236   uint32_t length;
   237   MOZ_ALWAYS_TRUE(JS_GetArrayLength(aCx, obj, &length));
   238   for (uint32_t i = 0; i < length; ++i) {
   239     JS::Rooted<JS::Value> element(aCx);
   240     if (!JS_GetElement(aCx, obj, i, &element))
   241       return NS_ERROR_TYPE_ERR;
   243     if (element.isObject()) {
   244       JS::Rooted<JSObject*> obj(aCx, &element.toObject());
   245       nsCOMPtr<nsIDOMBlob> blob = aUnwrapFunc(aCx, obj);
   246       if (blob) {
   247         // Flatten so that multipart blobs will never nest
   248         nsDOMFileBase* file = static_cast<nsDOMFileBase*>(
   249             static_cast<nsIDOMBlob*>(blob));
   250         const nsTArray<nsCOMPtr<nsIDOMBlob> >*
   251             subBlobs = file->GetSubBlobs();
   252         if (subBlobs) {
   253           blobSet.AppendBlobs(*subBlobs);
   254         } else {
   255           blobSet.AppendBlob(blob);
   256         }
   257         continue;
   258       }
   259       if (JS_IsArrayBufferViewObject(obj)) {
   260         nsresult rv = blobSet.AppendVoidPtr(
   261                                           JS_GetArrayBufferViewData(obj),
   262                                           JS_GetArrayBufferViewByteLength(obj));
   263         NS_ENSURE_SUCCESS(rv, rv);
   264         continue;
   265       }
   266       if (JS_IsArrayBufferObject(obj)) {
   267         nsresult rv = blobSet.AppendArrayBuffer(obj);
   268         NS_ENSURE_SUCCESS(rv, rv);
   269         continue;
   270       }
   271     }
   273     // coerce it to a string
   274     JSString* str = JS::ToString(aCx, element);
   275     NS_ENSURE_TRUE(str, NS_ERROR_TYPE_ERR);
   277     nsresult rv = blobSet.AppendString(str, aNativeEOL, aCx);
   278     NS_ENSURE_SUCCESS(rv, rv);
   279   }
   281   mBlobs = blobSet.GetBlobs();
   282   return NS_OK;
   283 }
   285 NS_IMETHODIMP
   286 nsDOMMultipartFile::GetMozFullPathInternal(nsAString &aFilename)
   287 {
   288   if (!mIsFromNsiFile || mBlobs.Length() == 0) {
   289     return nsDOMFile::GetMozFullPathInternal(aFilename);
   290   }
   292   nsIDOMBlob* blob = mBlobs.ElementAt(0).get();
   293   nsDOMFileFile* file = static_cast<nsDOMFileFile*>(blob);
   294   if (!file) {
   295     return nsDOMFile::GetMozFullPathInternal(aFilename);
   296   }
   298   return file->GetMozFullPathInternal(aFilename);
   299 }
   301 nsresult
   302 nsDOMMultipartFile::InitChromeFile(JSContext* aCx,
   303                                    uint32_t aArgc,
   304                                    JS::Value* aArgv)
   305 {
   306   nsresult rv;
   308   NS_ASSERTION(!mImmutable, "Something went wrong ...");
   309   NS_ENSURE_TRUE(!mImmutable, NS_ERROR_UNEXPECTED);
   310   MOZ_ASSERT(nsContentUtils::IsCallerChrome());
   311   NS_ENSURE_TRUE(aArgc > 0, NS_ERROR_UNEXPECTED);
   313   if (aArgc > 1) {
   314     FilePropertyBag d;
   315     if (!d.Init(aCx, JS::Handle<JS::Value>::fromMarkedLocation(&aArgv[1]))) {
   316       return NS_ERROR_TYPE_ERR;
   317     }
   318     mName = d.mName;
   319     mContentType = d.mType;
   320   }
   323   // We expect to get a path to represent as a File object or
   324   // Blob object, an nsIFile, or an nsIDOMFile.
   325   nsCOMPtr<nsIFile> file;
   326   nsCOMPtr<nsIDOMBlob> blob;
   327   if (!aArgv[0].isString()) {
   328     // Lets see if it's an nsIFile
   329     if (!aArgv[0].isObject()) {
   330       return NS_ERROR_UNEXPECTED; // We're not interested
   331     }
   333     JSObject* obj = &aArgv[0].toObject();
   335     nsISupports* supports =
   336       nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj);
   337     if (!supports) {
   338       return NS_ERROR_UNEXPECTED;
   339     }
   341     blob = do_QueryInterface(supports);
   342     file = do_QueryInterface(supports);
   343     if (!blob && !file) {
   344       return NS_ERROR_UNEXPECTED;
   345     }
   347     mIsFromNsiFile = true;
   348   } else {
   349     // It's a string
   350     JSString* str = JS::ToString(aCx, JS::Handle<JS::Value>::fromMarkedLocation(&aArgv[0]));
   351     NS_ENSURE_TRUE(str, NS_ERROR_XPC_BAD_CONVERT_JS);
   353     nsDependentJSString xpcomStr;
   354     if (!xpcomStr.init(aCx, str)) {
   355       return NS_ERROR_XPC_BAD_CONVERT_JS;
   356     }
   358     rv = NS_NewLocalFile(xpcomStr, false, getter_AddRefs(file));
   359     NS_ENSURE_SUCCESS(rv, rv);
   360   }
   362   if (file) {
   363     bool exists;
   364     rv = file->Exists(&exists);
   365     NS_ENSURE_SUCCESS(rv, rv);
   366     NS_ENSURE_TRUE(exists, NS_ERROR_FILE_NOT_FOUND);
   368     bool isDir;
   369     rv = file->IsDirectory(&isDir);
   370     NS_ENSURE_SUCCESS(rv, rv);
   371     NS_ENSURE_FALSE(isDir, NS_ERROR_FILE_IS_DIRECTORY);
   373     if (mName.IsEmpty()) {
   374       file->GetLeafName(mName);
   375     }
   377     blob = new nsDOMFileFile(file);
   378   }
   380   // XXXkhuey this is terrible
   381   if (mContentType.IsEmpty()) {
   382     blob->GetType(mContentType);
   383   }
   385   BlobSet blobSet;
   386   blobSet.AppendBlob(blob);
   387   mBlobs = blobSet.GetBlobs();
   389   return NS_OK;
   390 }
   392 nsresult
   393 nsDOMMultipartFile::InitFile(JSContext* aCx,
   394                              uint32_t aArgc,
   395                              JS::Value* aArgv)
   396 {
   397   NS_ASSERTION(!mImmutable, "Something went wrong ...");
   398   NS_ENSURE_TRUE(!mImmutable, NS_ERROR_UNEXPECTED);
   400   if (aArgc < 2) {
   401     return NS_ERROR_TYPE_ERR;
   402   }
   404   // File name
   405   JSString* str = JS::ToString(aCx, JS::Handle<JS::Value>::fromMarkedLocation(&aArgv[1]));
   406   NS_ENSURE_TRUE(str, NS_ERROR_XPC_BAD_CONVERT_JS);
   408   nsDependentJSString xpcomStr;
   409   if (!xpcomStr.init(aCx, str)) {
   410     return NS_ERROR_XPC_BAD_CONVERT_JS;
   411   }
   413   mName = xpcomStr;
   415   // Optional params
   416   bool nativeEOL = false;
   417   if (aArgc > 2) {
   418     BlobPropertyBag d;
   419     if (!d.Init(aCx, JS::Handle<JS::Value>::fromMarkedLocation(&aArgv[2]))) {
   420       return NS_ERROR_TYPE_ERR;
   421     }
   422     mContentType = d.mType;
   423     nativeEOL = d.mEndings == EndingTypes::Native;
   424   }
   426   return ParseBlobArrayArgument(aCx, aArgv[0], nativeEOL, GetXPConnectNative);
   427 }
   429 nsresult
   430 BlobSet::AppendVoidPtr(const void* aData, uint32_t aLength)
   431 {
   432   NS_ENSURE_ARG_POINTER(aData);
   434   uint64_t offset = mDataLen;
   436   if (!ExpandBufferSize(aLength))
   437     return NS_ERROR_OUT_OF_MEMORY;
   439   memcpy((char*)mData + offset, aData, aLength);
   440   return NS_OK;
   441 }
   443 nsresult
   444 BlobSet::AppendString(JSString* aString, bool nativeEOL, JSContext* aCx)
   445 {
   446   nsDependentJSString xpcomStr;
   447   if (!xpcomStr.init(aCx, aString)) {
   448     return NS_ERROR_XPC_BAD_CONVERT_JS;
   449   }
   451   nsCString utf8Str = NS_ConvertUTF16toUTF8(xpcomStr);
   453   if (nativeEOL) {
   454     if (utf8Str.FindChar('\r') != kNotFound) {
   455       utf8Str.ReplaceSubstring("\r\n", "\n");
   456       utf8Str.ReplaceSubstring("\r", "\n");
   457     }
   458 #ifdef XP_WIN
   459     utf8Str.ReplaceSubstring("\n", "\r\n");
   460 #endif
   461   }
   463   return AppendVoidPtr((void*)utf8Str.Data(),
   464                        utf8Str.Length());
   465 }
   467 nsresult
   468 BlobSet::AppendBlob(nsIDOMBlob* aBlob)
   469 {
   470   NS_ENSURE_ARG_POINTER(aBlob);
   472   Flush();
   473   mBlobs.AppendElement(aBlob);
   475   return NS_OK;
   476 }
   478 nsresult
   479 BlobSet::AppendBlobs(const nsTArray<nsCOMPtr<nsIDOMBlob> >& aBlob)
   480 {
   481   Flush();
   482   mBlobs.AppendElements(aBlob);
   484   return NS_OK;
   485 }
   487 nsresult
   488 BlobSet::AppendArrayBuffer(JSObject* aBuffer)
   489 {
   490   return AppendVoidPtr(JS_GetArrayBufferData(aBuffer),
   491                        JS_GetArrayBufferByteLength(aBuffer));
   492 }

mercurial