content/base/src/nsDOMFileReader.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

     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 "nsDOMFileReader.h"
     8 #include "nsContentCID.h"
     9 #include "nsContentUtils.h"
    10 #include "nsDOMClassInfoID.h"
    11 #include "nsDOMFile.h"
    12 #include "nsError.h"
    13 #include "nsIConverterInputStream.h"
    14 #include "nsIDocument.h"
    15 #include "nsIFile.h"
    16 #include "nsIFileStreams.h"
    17 #include "nsIInputStream.h"
    18 #include "nsIMIMEService.h"
    19 #include "nsIUnicodeDecoder.h"
    20 #include "nsNetCID.h"
    21 #include "nsNetUtil.h"
    23 #include "nsLayoutCID.h"
    24 #include "nsXPIDLString.h"
    25 #include "nsReadableUtils.h"
    26 #include "nsIURI.h"
    27 #include "nsStreamUtils.h"
    28 #include "nsXPCOM.h"
    29 #include "nsIDOMEventListener.h"
    30 #include "nsJSEnvironment.h"
    31 #include "nsIScriptGlobalObject.h"
    32 #include "nsCExternalHandlerService.h"
    33 #include "nsIStreamConverterService.h"
    34 #include "nsCycleCollectionParticipant.h"
    35 #include "nsIScriptObjectPrincipal.h"
    36 #include "nsHostObjectProtocolHandler.h"
    37 #include "mozilla/Base64.h"
    38 #include "mozilla/DOMEventTargetHelper.h"
    39 #include "mozilla/Preferences.h"
    40 #include "mozilla/dom/EncodingUtils.h"
    41 #include "mozilla/dom/FileReaderBinding.h"
    42 #include "xpcpublic.h"
    43 #include "nsIScriptSecurityManager.h"
    44 #include "nsDOMJSUtils.h"
    46 #include "jsfriendapi.h"
    48 using namespace mozilla;
    49 using namespace mozilla::dom;
    51 #define LOAD_STR "load"
    52 #define LOADSTART_STR "loadstart"
    53 #define LOADEND_STR "loadend"
    55 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMFileReader)
    57 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMFileReader,
    58                                                   FileIOObject)
    59   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFile)
    60   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
    61 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    63 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMFileReader,
    64                                                 FileIOObject)
    65   tmp->mResultArrayBuffer = nullptr;
    66   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFile)
    67   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal)
    68 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    71 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsDOMFileReader,
    72                                                DOMEventTargetHelper)
    73   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer)
    74 NS_IMPL_CYCLE_COLLECTION_TRACE_END
    76 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMFileReader)
    77   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    78   NS_INTERFACE_MAP_ENTRY(nsIDOMFileReader)
    79   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
    80   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    81 NS_INTERFACE_MAP_END_INHERITING(FileIOObject)
    83 NS_IMPL_ADDREF_INHERITED(nsDOMFileReader, FileIOObject)
    84 NS_IMPL_RELEASE_INHERITED(nsDOMFileReader, FileIOObject)
    86 NS_IMPL_EVENT_HANDLER(nsDOMFileReader, load)
    87 NS_IMPL_EVENT_HANDLER(nsDOMFileReader, loadend)
    88 NS_IMPL_EVENT_HANDLER(nsDOMFileReader, loadstart)
    89 NS_IMPL_FORWARD_EVENT_HANDLER(nsDOMFileReader, abort, FileIOObject)
    90 NS_IMPL_FORWARD_EVENT_HANDLER(nsDOMFileReader, progress, FileIOObject)
    91 NS_IMPL_FORWARD_EVENT_HANDLER(nsDOMFileReader, error, FileIOObject)
    93 void
    94 nsDOMFileReader::RootResultArrayBuffer()
    95 {
    96   mozilla::HoldJSObjects(this);
    97 }
    99 //nsDOMFileReader constructors/initializers
   101 nsDOMFileReader::nsDOMFileReader()
   102   : mFileData(nullptr),
   103     mDataLen(0), mDataFormat(FILE_AS_BINARY),
   104     mResultArrayBuffer(nullptr)
   105 {
   106   SetDOMStringToNull(mResult);
   107   SetIsDOMBinding();
   108 }
   110 nsDOMFileReader::~nsDOMFileReader()
   111 {
   112   FreeFileData();
   113   mResultArrayBuffer = nullptr;
   114   mozilla::DropJSObjects(this);
   115 }
   118 /**
   119  * This Init method is called from the factory constructor.
   120  */
   121 nsresult
   122 nsDOMFileReader::Init()
   123 {
   124   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
   125   nsCOMPtr<nsIPrincipal> principal;
   126   if (secMan) {
   127     secMan->GetSystemPrincipal(getter_AddRefs(principal));
   128   }
   129   NS_ENSURE_STATE(principal);
   130   mPrincipal.swap(principal);
   132   // Instead of grabbing some random global from the context stack,
   133   // let's use the default one (junk scope) for now.
   134   // We should move away from this Init...
   135   nsCOMPtr<nsIGlobalObject> global = xpc::GetJunkScopeGlobal();
   136   NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
   137   BindToOwner(global);
   138   return NS_OK;
   139 }
   141 /* static */ already_AddRefed<nsDOMFileReader>
   142 nsDOMFileReader::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
   143 {
   144   nsRefPtr<nsDOMFileReader> fileReader = new nsDOMFileReader();
   146   nsCOMPtr<nsPIDOMWindow> owner = do_QueryInterface(aGlobal.GetAsSupports());
   147   if (!owner) {
   148     NS_WARNING("Unexpected nsIJSNativeInitializer owner");
   149     aRv.Throw(NS_ERROR_FAILURE);
   150     return nullptr;
   151   }
   153   fileReader->BindToOwner(owner);
   155   // This object is bound to a |window|,
   156   // so reset the principal.
   157   nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal = do_QueryInterface(owner);
   158   if (!scriptPrincipal) {
   159     aRv.Throw(NS_ERROR_FAILURE);
   160     return nullptr;
   161   }
   162   fileReader->mPrincipal = scriptPrincipal->GetPrincipal();
   163   return fileReader.forget();
   164 }
   166 // nsIInterfaceRequestor
   168 NS_IMETHODIMP
   169 nsDOMFileReader::GetInterface(const nsIID & aIID, void **aResult)
   170 {
   171   return QueryInterface(aIID, aResult);
   172 }
   174 // nsIDOMFileReader
   176 NS_IMETHODIMP
   177 nsDOMFileReader::GetReadyState(uint16_t *aReadyState)
   178 {
   179   *aReadyState = ReadyState();
   180   return NS_OK;
   181 }
   183 void
   184 nsDOMFileReader::GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
   185                            ErrorResult& aRv)
   186 {
   187   aRv = GetResult(aCx, aResult);
   188 }
   190 NS_IMETHODIMP
   191 nsDOMFileReader::GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult)
   192 {
   193   JS::Rooted<JS::Value> result(aCx);
   194   if (mDataFormat == FILE_AS_ARRAYBUFFER) {
   195     if (mReadyState == nsIDOMFileReader::DONE && mResultArrayBuffer) {
   196       result.setObject(*mResultArrayBuffer);
   197     } else {
   198       result.setNull();
   199     }
   200     if (!JS_WrapValue(aCx, &result)) {
   201       return NS_ERROR_FAILURE;
   202     }
   203     aResult.set(result);
   204     return NS_OK;
   205   }
   207   nsString tmpResult = mResult;
   208   if (!xpc::StringToJsval(aCx, tmpResult, aResult)) {
   209     return NS_ERROR_FAILURE;
   210   }
   211   return NS_OK;
   212 }
   214 NS_IMETHODIMP
   215 nsDOMFileReader::GetError(nsISupports** aError)
   216 {
   217   NS_IF_ADDREF(*aError = GetError());
   218   return NS_OK;
   219 }
   221 NS_IMETHODIMP
   222 nsDOMFileReader::ReadAsArrayBuffer(nsIDOMBlob* aFile, JSContext* aCx)
   223 {
   224   NS_ENSURE_TRUE(aFile, NS_ERROR_NULL_POINTER);
   225   ErrorResult rv;
   226   ReadAsArrayBuffer(aCx, aFile, rv);
   227   return rv.ErrorCode();
   228 }
   230 NS_IMETHODIMP
   231 nsDOMFileReader::ReadAsBinaryString(nsIDOMBlob* aFile)
   232 {
   233   NS_ENSURE_TRUE(aFile, NS_ERROR_NULL_POINTER);
   234   ErrorResult rv;
   235   ReadAsBinaryString(aFile, rv);
   236   return rv.ErrorCode();
   237 }
   239 NS_IMETHODIMP
   240 nsDOMFileReader::ReadAsText(nsIDOMBlob* aFile,
   241                             const nsAString &aCharset)
   242 {
   243   NS_ENSURE_TRUE(aFile, NS_ERROR_NULL_POINTER);
   244   ErrorResult rv;
   245   ReadAsText(aFile, aCharset, rv);
   246   return rv.ErrorCode();
   247 }
   249 NS_IMETHODIMP
   250 nsDOMFileReader::ReadAsDataURL(nsIDOMBlob* aFile)
   251 {
   252   NS_ENSURE_TRUE(aFile, NS_ERROR_NULL_POINTER);
   253   ErrorResult rv;
   254   ReadAsDataURL(aFile, rv);
   255   return rv.ErrorCode();
   256 }
   258 NS_IMETHODIMP
   259 nsDOMFileReader::Abort()
   260 {
   261   ErrorResult rv;
   262   FileIOObject::Abort(rv);
   263   return rv.ErrorCode();
   264 }
   266 /* virtual */ void
   267 nsDOMFileReader::DoAbort(nsAString& aEvent)
   268 {
   269   // Revert status and result attributes
   270   SetDOMStringToNull(mResult);
   271   mResultArrayBuffer = nullptr;
   273   // Non-null channel indicates a read is currently active
   274   if (mChannel) {
   275     // Cancel request requires an error status
   276     mChannel->Cancel(NS_ERROR_FAILURE);
   277     mChannel = nullptr;
   278   }
   279   mFile = nullptr;
   281   //Clean up memory buffer
   282   FreeFileData();
   284   // Tell the base class which event to dispatch
   285   aEvent = NS_LITERAL_STRING(LOADEND_STR);
   286 }
   288 static
   289 NS_METHOD
   290 ReadFuncBinaryString(nsIInputStream* in,
   291                      void* closure,
   292                      const char* fromRawSegment,
   293                      uint32_t toOffset,
   294                      uint32_t count,
   295                      uint32_t *writeCount)
   296 {
   297   char16_t* dest = static_cast<char16_t*>(closure) + toOffset;
   298   char16_t* end = dest + count;
   299   const unsigned char* source = (const unsigned char*)fromRawSegment;
   300   while (dest != end) {
   301     *dest = *source;
   302     ++dest;
   303     ++source;
   304   }
   305   *writeCount = count;
   307   return NS_OK;
   308 }
   310 nsresult
   311 nsDOMFileReader::DoOnDataAvailable(nsIRequest *aRequest,
   312                                    nsISupports *aContext,
   313                                    nsIInputStream *aInputStream,
   314                                    uint64_t aOffset,
   315                                    uint32_t aCount)
   316 {
   317   if (mDataFormat == FILE_AS_BINARY) {
   318     //Continuously update our binary string as data comes in
   319     NS_ASSERTION(mResult.Length() == aOffset,
   320                  "unexpected mResult length");
   321     uint32_t oldLen = mResult.Length();
   322     if (uint64_t(oldLen) + aCount > UINT32_MAX)
   323       return NS_ERROR_OUT_OF_MEMORY;
   325     char16_t *buf = nullptr;
   326     mResult.GetMutableData(&buf, oldLen + aCount, fallible_t());
   327     NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
   329     uint32_t bytesRead = 0;
   330     aInputStream->ReadSegments(ReadFuncBinaryString, buf + oldLen, aCount,
   331                                &bytesRead);
   332     NS_ASSERTION(bytesRead == aCount, "failed to read data");
   333   }
   334   else if (mDataFormat == FILE_AS_ARRAYBUFFER) {
   335     uint32_t bytesRead = 0;
   336     aInputStream->Read((char*)JS_GetArrayBufferData(mResultArrayBuffer) + aOffset,
   337                        aCount, &bytesRead);
   338     NS_ASSERTION(bytesRead == aCount, "failed to read data");
   339   }
   340   else {
   341     //Update memory buffer to reflect the contents of the file
   342     if (aOffset + aCount > UINT32_MAX) {
   343       // PR_Realloc doesn't support over 4GB memory size even if 64-bit OS
   344       return NS_ERROR_OUT_OF_MEMORY;
   345     }
   346     mFileData = (char *)moz_realloc(mFileData, aOffset + aCount);
   347     NS_ENSURE_TRUE(mFileData, NS_ERROR_OUT_OF_MEMORY);
   349     uint32_t bytesRead = 0;
   350     aInputStream->Read(mFileData + aOffset, aCount, &bytesRead);
   351     NS_ASSERTION(bytesRead == aCount, "failed to read data");
   353     mDataLen += aCount;
   354   }
   356   return NS_OK;
   357 }
   359 nsresult
   360 nsDOMFileReader::DoOnStopRequest(nsIRequest *aRequest,
   361                                  nsISupports *aContext,
   362                                  nsresult aStatus,
   363                                  nsAString& aSuccessEvent,
   364                                  nsAString& aTerminationEvent)
   365 {
   366   // Make sure we drop all the objects that could hold files open now.
   367   nsCOMPtr<nsIChannel> channel;
   368   mChannel.swap(channel);
   370   nsCOMPtr<nsIDOMBlob> file;
   371   mFile.swap(file);
   373   aSuccessEvent = NS_LITERAL_STRING(LOAD_STR);
   374   aTerminationEvent = NS_LITERAL_STRING(LOADEND_STR);
   376   // Clear out the data if necessary
   377   if (NS_FAILED(aStatus)) {
   378     FreeFileData();
   379     return NS_OK;
   380   }
   382   nsresult rv = NS_OK;
   383   switch (mDataFormat) {
   384     case FILE_AS_ARRAYBUFFER:
   385       break; //Already accumulated mResultArrayBuffer
   386     case FILE_AS_BINARY:
   387       break; //Already accumulated mResult
   388     case FILE_AS_TEXT:
   389       if (!mFileData) {
   390         if (mDataLen) {
   391           rv = NS_ERROR_OUT_OF_MEMORY;
   392           break;
   393         }
   394         rv = GetAsText(file, mCharset, "", mDataLen, mResult);
   395         break;
   396       }
   397       rv = GetAsText(file, mCharset, mFileData, mDataLen, mResult);
   398       break;
   399     case FILE_AS_DATAURL:
   400       rv = GetAsDataURL(file, mFileData, mDataLen, mResult);
   401       break;
   402   }
   404   mResult.SetIsVoid(false);
   406   FreeFileData();
   408   return rv;
   409 }
   411 // Helper methods
   413 void
   414 nsDOMFileReader::ReadFileContent(JSContext* aCx,
   415                                  nsIDOMBlob* aFile,
   416                                  const nsAString &aCharset,
   417                                  eDataFormat aDataFormat,
   418                                  ErrorResult& aRv)
   419 {
   420   MOZ_ASSERT(aFile);
   422   //Implicit abort to clear any other activity going on
   423   Abort();
   424   mError = nullptr;
   425   SetDOMStringToNull(mResult);
   426   mTransferred = 0;
   427   mTotal = 0;
   428   mReadyState = nsIDOMFileReader::EMPTY;
   429   FreeFileData();
   431   mFile = aFile;
   432   mDataFormat = aDataFormat;
   433   CopyUTF16toUTF8(aCharset, mCharset);
   435   //Establish a channel with our file
   436   {
   437     // Hold the internal URL alive only as long as necessary
   438     // After the channel is created it will own whatever is backing
   439     // the DOMFile.
   440     nsDOMFileInternalUrlHolder urlHolder(mFile, mPrincipal);
   442     nsCOMPtr<nsIURI> uri;
   443     aRv = NS_NewURI(getter_AddRefs(uri), urlHolder.mUrl);
   444     NS_ENSURE_SUCCESS_VOID(aRv.ErrorCode());
   446     nsCOMPtr<nsILoadGroup> loadGroup;
   447     if (HasOrHasHadOwner()) {
   448       if (!GetOwner()) {
   449         aRv.Throw(NS_ERROR_FAILURE);
   450         return;
   451       }
   452       nsIDocument* doc = GetOwner()->GetExtantDoc();
   453       if (doc) {
   454         loadGroup = doc->GetDocumentLoadGroup();
   455       }
   456     }
   458     aRv = NS_NewChannel(getter_AddRefs(mChannel), uri, nullptr, loadGroup,
   459                         nullptr, nsIRequest::LOAD_BACKGROUND);
   460     NS_ENSURE_SUCCESS_VOID(aRv.ErrorCode());
   461   }
   463   //Obtain the total size of the file before reading
   464   mTotal = mozilla::dom::kUnknownSize;
   465   mFile->GetSize(&mTotal);
   467   aRv = mChannel->AsyncOpen(this, nullptr);
   468   NS_ENSURE_SUCCESS_VOID(aRv.ErrorCode());
   470   //FileReader should be in loading state here
   471   mReadyState = nsIDOMFileReader::LOADING;
   472   DispatchProgressEvent(NS_LITERAL_STRING(LOADSTART_STR));
   474   if (mDataFormat == FILE_AS_ARRAYBUFFER) {
   475     RootResultArrayBuffer();
   476     mResultArrayBuffer = JS_NewArrayBuffer(aCx, mTotal);
   477     if (!mResultArrayBuffer) {
   478       NS_WARNING("Failed to create JS array buffer");
   479       aRv.Throw(NS_ERROR_FAILURE);
   480     }
   481   }
   482 }
   484 nsresult
   485 nsDOMFileReader::GetAsText(nsIDOMBlob *aFile,
   486                            const nsACString &aCharset,
   487                            const char *aFileData,
   488                            uint32_t aDataLen,
   489                            nsAString& aResult)
   490 {
   491   // The BOM sniffing is baked into the "decode" part of the Encoding
   492   // Standard, which the File API references.
   493   nsAutoCString encoding;
   494   if (!nsContentUtils::CheckForBOM(
   495         reinterpret_cast<const unsigned char *>(aFileData),
   496         aDataLen,
   497         encoding)) {
   498     // BOM sniffing failed. Try the API argument.
   499     if (!EncodingUtils::FindEncodingForLabel(aCharset,
   500                                              encoding)) {
   501       // API argument failed. Try the type property of the blob.
   502       nsAutoString type16;
   503       aFile->GetType(type16);
   504       NS_ConvertUTF16toUTF8 type(type16);
   505       nsAutoCString specifiedCharset;
   506       bool haveCharset;
   507       int32_t charsetStart, charsetEnd;
   508       NS_ExtractCharsetFromContentType(type,
   509                                        specifiedCharset,
   510                                        &haveCharset,
   511                                        &charsetStart,
   512                                        &charsetEnd);
   513       if (!EncodingUtils::FindEncodingForLabel(specifiedCharset, encoding)) {
   514         // Type property failed. Use UTF-8.
   515         encoding.AssignLiteral("UTF-8");
   516       }
   517     }
   518   }
   520   nsDependentCSubstring data(aFileData, aDataLen);
   521   return nsContentUtils::ConvertStringFromEncoding(encoding, data, aResult);
   522 }
   524 nsresult
   525 nsDOMFileReader::GetAsDataURL(nsIDOMBlob *aFile,
   526                               const char *aFileData,
   527                               uint32_t aDataLen,
   528                               nsAString& aResult)
   529 {
   530   aResult.AssignLiteral("data:");
   532   nsresult rv;
   533   nsString contentType;
   534   rv = aFile->GetType(contentType);
   535   if (NS_SUCCEEDED(rv) && !contentType.IsEmpty()) {
   536     aResult.Append(contentType);
   537   } else {
   538     aResult.AppendLiteral("application/octet-stream");
   539   }
   540   aResult.AppendLiteral(";base64,");
   542   nsCString encodedData;
   543   rv = Base64Encode(Substring(aFileData, aDataLen), encodedData);
   544   NS_ENSURE_SUCCESS(rv, rv);
   546   if (!AppendASCIItoUTF16(encodedData, aResult, fallible_t())) {
   547     return NS_ERROR_OUT_OF_MEMORY;
   548   }
   550   return NS_OK;
   551 }
   553 /* virtual */ JSObject*
   554 nsDOMFileReader::WrapObject(JSContext* aCx)
   555 {
   556   return FileReaderBinding::Wrap(aCx, this);
   557 }

mercurial