toolkit/components/osfile/NativeOSFileInternals.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 /**
     6  * Native implementation of some OS.File operations.
     7  */
     9 #include "nsString.h"
    10 #include "nsNetCID.h"
    11 #include "nsThreadUtils.h"
    12 #include "nsXPCOMCID.h"
    13 #include "nsCycleCollectionParticipant.h"
    14 #include "nsServiceManagerUtils.h"
    15 #include "nsProxyRelease.h"
    17 #include "nsINativeOSFileInternals.h"
    18 #include "NativeOSFileInternals.h"
    19 #include "mozilla/dom/NativeOSFileInternalsBinding.h"
    21 #include "nsIUnicodeDecoder.h"
    22 #include "nsIEventTarget.h"
    24 #include "mozilla/dom/EncodingUtils.h"
    25 #include "mozilla/DebugOnly.h"
    26 #include "mozilla/Scoped.h"
    27 #include "mozilla/HoldDropJSObjects.h"
    28 #include "mozilla/TimeStamp.h"
    30 #include "prio.h"
    31 #include "prerror.h"
    32 #include "private/pprio.h"
    34 #include "jsapi.h"
    35 #include "jsfriendapi.h"
    36 #include "js/Utility.h"
    37 #include "xpcpublic.h"
    39 #include <algorithm>
    40 #if defined(XP_UNIX)
    41 #include <unistd.h>
    42 #include <errno.h>
    43 #include <fcntl.h>
    44 #include <sys/stat.h>
    45 #include <sys/uio.h>
    46 #endif // defined (XP_UNIX)
    48 #if defined(XP_WIN)
    49 #include <windows.h>
    50 #endif // defined (XP_WIN)
    52 namespace mozilla {
    54 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, PR_Close)
    56 namespace {
    58 // Utilities for safely manipulating ArrayBuffer contents even in the
    59 // absence of a JSContext.
    61 /**
    62  * The C buffer underlying to an ArrayBuffer. Throughout the code, we manipulate
    63  * this instead of a void* buffer, as this lets us transfer data across threads
    64  * and into JavaScript without copy.
    65  */
    66 struct ArrayBufferContents {
    67   /**
    68    * The data of the ArrayBuffer. This is the pointer manipulated to
    69    * read/write the contents of the buffer.
    70    */
    71   uint8_t* data;
    72   /**
    73    * The number of bytes in the ArrayBuffer.
    74    */
    75   size_t nbytes;
    76 };
    78 /**
    79  * RAII for ArrayBufferContents.
    80  */
    81 struct ScopedArrayBufferContentsTraits {
    82   typedef ArrayBufferContents type;
    83   const static type empty() {
    84     type result = {0, 0};
    85     return result;
    86   }
    87   const static void release(type ptr) {
    88     js_free(ptr.data);
    89     ptr.data = nullptr;
    90     ptr.nbytes = 0;
    91   }
    92 };
    94 struct ScopedArrayBufferContents: public Scoped<ScopedArrayBufferContentsTraits> {
    95   ScopedArrayBufferContents(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM):
    96     Scoped<ScopedArrayBufferContentsTraits>(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_TO_PARENT)
    97   { }
    98   ScopedArrayBufferContents(const ArrayBufferContents& v
    99                             MOZ_GUARD_OBJECT_NOTIFIER_PARAM):
   100     Scoped<ScopedArrayBufferContentsTraits>(v MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
   101   { }
   102   ScopedArrayBufferContents& operator=(ArrayBufferContents ptr) {
   103     Scoped<ScopedArrayBufferContentsTraits>::operator=(ptr);
   104     return *this;
   105   }
   107   /**
   108    * Request memory for this ArrayBufferContent. This memory may later
   109    * be used to create an ArrayBuffer object (possibly on another
   110    * thread) without copy.
   111    *
   112    * @return true In case of success, false otherwise.
   113    */
   114   bool Allocate(uint32_t length) {
   115     dispose();
   116     ArrayBufferContents& value = rwget();
   117     void *ptr = JS_AllocateArrayBufferContents(/*no context available*/nullptr, length);
   118     if (ptr) {
   119       value.data = (uint8_t *) ptr;
   120       value.nbytes = length;
   121       return true;
   122     }
   123     return false;
   124   }
   125 private:
   126   explicit ScopedArrayBufferContents(ScopedArrayBufferContents& source) MOZ_DELETE;
   127   ScopedArrayBufferContents& operator=(ScopedArrayBufferContents& source) MOZ_DELETE;
   128 };
   130 ///////// Cross-platform issues
   132 // Platform specific constants. As OS.File always uses OS-level
   133 // errors, we need to map a few high-level errors to OS-level
   134 // constants.
   135 #if defined(XP_UNIX)
   136 #define OS_ERROR_NOMEM ENOMEM
   137 #define OS_ERROR_INVAL EINVAL
   138 #define OS_ERROR_TOO_LARGE EFBIG
   139 #define OS_ERROR_RACE EIO
   140 #elif defined(XP_WIN)
   141 #define OS_ERROR_NOMEM ERROR_NOT_ENOUGH_MEMORY
   142 #define OS_ERROR_INVAL ERROR_BAD_ARGUMENTS
   143 #define OS_ERROR_TOO_LARGE ERROR_FILE_TOO_LARGE
   144 #define OS_ERROR_RACE ERROR_SHARING_VIOLATION
   145 #else
   146 #error "We do not have platform-specific constants for this platform"
   147 #endif
   149 ///////// Results of OS.File operations
   151 /**
   152  * Base class for results passed to the callbacks.
   153  *
   154  * This base class implements caching of JS values returned to the client.
   155  * We make use of this caching in derived classes e.g. to avoid accidents
   156  * when we transfer data allocated on another thread into JS. Note that
   157  * this caching can lead to cycles (e.g. if a client adds a back-reference
   158  * in the JS value), so we implement all Cycle Collector primitives in
   159  * AbstractResult.
   160  */
   161 class AbstractResult: public nsINativeOSFileResult {
   162 public:
   163   NS_DECL_NSINATIVEOSFILERESULT
   164   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   165   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AbstractResult)
   167   /**
   168    * Construct the result object. Must be called on the main thread
   169    * as the AbstractResult is cycle-collected.
   170    *
   171    * @param aStartDate The instant at which the operation was
   172    * requested.  Used to collect Telemetry statistics.
   173    */
   174   AbstractResult(TimeStamp aStartDate)
   175     : mStartDate(aStartDate)
   176   {
   177     MOZ_ASSERT(NS_IsMainThread());
   178     mozilla::HoldJSObjects(this);
   179   }
   180   virtual ~AbstractResult() {
   181     MOZ_ASSERT(NS_IsMainThread());
   182     DropJSData();
   183     mozilla::DropJSObjects(this);
   184   }
   186   /**
   187    * Setup the AbstractResult once data is available.
   188    *
   189    * @param aDispatchDate The instant at which the IO thread received
   190    * the operation request. Used to collect Telemetry statistics.
   191    * @param aExecutionDuration The duration of the operation on the
   192    * IO thread.
   193    */
   194   void Init(TimeStamp aDispatchDate,
   195             TimeDuration aExecutionDuration) {
   196     MOZ_ASSERT(!NS_IsMainThread());
   198     mDispatchDuration = (aDispatchDate - mStartDate);
   199     mExecutionDuration = aExecutionDuration;
   200   }
   202   /**
   203    * Drop any data that could lead to a cycle.
   204    */
   205   void DropJSData() {
   206     mCachedResult = JS::UndefinedValue();
   207   }
   209 protected:
   210   virtual nsresult GetCacheableResult(JSContext *cx, JS::MutableHandleValue aResult) = 0;
   212 private:
   213   TimeStamp mStartDate;
   214   TimeDuration mDispatchDuration;
   215   TimeDuration mExecutionDuration;
   216   JS::Heap<JS::Value> mCachedResult;
   217 };
   219 NS_IMPL_CYCLE_COLLECTING_ADDREF(AbstractResult)
   220 NS_IMPL_CYCLE_COLLECTING_RELEASE(AbstractResult)
   222 NS_IMPL_CYCLE_COLLECTION_CLASS(AbstractResult)
   224 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AbstractResult)
   225   NS_INTERFACE_MAP_ENTRY(nsINativeOSFileResult)
   226   NS_INTERFACE_MAP_ENTRY(nsISupports)
   227 NS_INTERFACE_MAP_END
   229 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AbstractResult)
   230   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedResult)
   231 NS_IMPL_CYCLE_COLLECTION_TRACE_END
   233 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AbstractResult)
   234   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   235 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   237 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AbstractResult)
   238   tmp->DropJSData();
   239 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   241 NS_IMETHODIMP
   242 AbstractResult::GetDispatchDurationMS(double *aDispatchDuration)
   243 {
   244   *aDispatchDuration = mDispatchDuration.ToMilliseconds();
   245   return NS_OK;
   246 }
   248 NS_IMETHODIMP
   249 AbstractResult::GetExecutionDurationMS(double *aExecutionDuration)
   250 {
   251   *aExecutionDuration = mExecutionDuration.ToMilliseconds();
   252   return NS_OK;
   253 }
   255 NS_IMETHODIMP
   256 AbstractResult::GetResult(JSContext *cx, JS::MutableHandleValue aResult)
   257 {
   258   if (mCachedResult.isUndefined()) {
   259     nsresult rv = GetCacheableResult(cx, aResult);
   260     if (NS_FAILED(rv)) {
   261       return rv;
   262     }
   263     mCachedResult = aResult;
   264     return NS_OK;
   265   }
   266   aResult.set(mCachedResult);
   267   return NS_OK;
   268 }
   270 /**
   271  * Return a result as a string.
   272  *
   273  * In this implementation, attribute |result| is a string. Strings are
   274  * passed to JS without copy.
   275  */
   276 class StringResult MOZ_FINAL : public AbstractResult
   277 {
   278 public:
   279   StringResult(TimeStamp aStartDate)
   280     : AbstractResult(aStartDate)
   281   {
   282   }
   284   /**
   285    * Initialize the object once the contents of the result as available.
   286    *
   287    * @param aContents The string to pass to JavaScript. Ownership of the
   288    * string and its contents is passed to StringResult. The string must
   289    * be valid UTF-16.
   290    */
   291   void Init(TimeStamp aDispatchDate,
   292             TimeDuration aExecutionDuration,
   293             nsString& aContents) {
   294     AbstractResult::Init(aDispatchDate, aExecutionDuration);
   295     mContents = aContents;
   296   }
   298 protected:
   299   nsresult GetCacheableResult(JSContext* cx, JS::MutableHandleValue aResult) MOZ_OVERRIDE;
   301 private:
   302   nsString mContents;
   303 };
   305 nsresult
   306 StringResult::GetCacheableResult(JSContext* cx, JS::MutableHandleValue aResult)
   307 {
   308   MOZ_ASSERT(NS_IsMainThread());
   309   MOZ_ASSERT(mContents.get());
   311   // Convert mContents to a js string without copy. Note that this
   312   // may have the side-effect of stealing the contents of the string
   313   // from XPCOM and into JS.
   314   if (!xpc::StringToJsval(cx, mContents, aResult)) {
   315     return NS_ERROR_FAILURE;
   316   }
   317   return NS_OK;
   318 }
   321 /**
   322  * Return a result as a Uint8Array.
   323  *
   324  * In this implementation, attribute |result| is a Uint8Array. The array
   325  * is passed to JS without memory copy.
   326  */
   327 class TypedArrayResult MOZ_FINAL : public AbstractResult
   328 {
   329 public:
   330   TypedArrayResult(TimeStamp aStartDate)
   331     : AbstractResult(aStartDate)
   332   {
   333   }
   335   /**
   336    * @param aContents The contents to pass to JS. Calling this method.
   337    * transmits ownership of the ArrayBufferContents to the TypedArrayResult.
   338    * Do not reuse this value anywhere else.
   339    */
   340   void Init(TimeStamp aDispatchDate,
   341             TimeDuration aExecutionDuration,
   342             ArrayBufferContents aContents) {
   343     AbstractResult::Init(aDispatchDate, aExecutionDuration);
   344     mContents = aContents;
   345   }
   347 protected:
   348   nsresult GetCacheableResult(JSContext* cx, JS::MutableHandleValue aResult) MOZ_OVERRIDE;
   349 private:
   350   ScopedArrayBufferContents mContents;
   351 };
   353 nsresult
   354 TypedArrayResult::GetCacheableResult(JSContext* cx, JS::MutableHandle<JS::Value> aResult)
   355 {
   356   MOZ_ASSERT(NS_IsMainThread());
   357   // We cannot simply construct a typed array using contents.data as
   358   // this would allow us to have several otherwise unrelated
   359   // ArrayBuffers with the same underlying C buffer. As this would be
   360   // very unsafe, we need to cache the result once we have it.
   362   const ArrayBufferContents& contents = mContents.get();
   363   MOZ_ASSERT(contents.data);
   365   JS::Rooted<JSObject*>
   366     arrayBuffer(cx, JS_NewArrayBufferWithContents(cx, contents.nbytes, contents.data));
   367   if (!arrayBuffer) {
   368     return NS_ERROR_OUT_OF_MEMORY;
   369   }
   371   JS::Rooted<JSObject*>
   372     result(cx, JS_NewUint8ArrayWithBuffer(cx, arrayBuffer,
   373                                           0, contents.nbytes));
   374   if (!result) {
   375     return NS_ERROR_OUT_OF_MEMORY;
   376   }
   377   // The memory of contents has been allocated on a thread that
   378   // doesn't have a JSRuntime, hence without a context. Now that we
   379   // have a context, attach the memory to where it belongs.
   380   JS_updateMallocCounter(cx, contents.nbytes);
   381   mContents.forget();
   383   aResult.setObject(*result);
   384   return NS_OK;
   385 }
   387 //////// Callback events
   389 /**
   390  * An event used to notify asynchronously of an error.
   391  */
   392 class ErrorEvent MOZ_FINAL : public nsRunnable {
   393 public:
   394   /**
   395    * @param aOnSuccess The success callback.
   396    * @param aOnError The error callback.
   397    * @param aDiscardedResult The discarded result.
   398    * @param aOperation The name of the operation, used for error reporting.
   399    * @param aOSError The OS error of the operation, as returned by errno/
   400    * GetLastError().
   401    *
   402    * Note that we pass both the success callback and the error
   403    * callback, as well as the discarded result to ensure that they are
   404    * all released on the main thread, rather than on the IO thread
   405    * (which would hopefully segfault). Also, we pass the callbacks as
   406    * alread_AddRefed to ensure that we do not manipulate main-thread
   407    * only refcounters off the main thread.
   408    */
   409   ErrorEvent(already_AddRefed<nsINativeOSFileSuccessCallback>&& aOnSuccess,
   410              already_AddRefed<nsINativeOSFileErrorCallback>&& aOnError,
   411              already_AddRefed<AbstractResult>& aDiscardedResult,
   412              const nsACString& aOperation,
   413              int32_t aOSError)
   414     : mOnSuccess(aOnSuccess)
   415     , mOnError(aOnError)
   416     , mDiscardedResult(aDiscardedResult)
   417     , mOSError(aOSError)
   418     , mOperation(aOperation)
   419     {
   420       MOZ_ASSERT(!NS_IsMainThread());
   421     }
   423   NS_METHOD Run() {
   424     MOZ_ASSERT(NS_IsMainThread());
   425     (void)mOnError->Complete(mOperation, mOSError);
   427     // Ensure that the callbacks are released on the main thread.
   428     mOnSuccess = nullptr;
   429     mOnError = nullptr;
   430     mDiscardedResult = nullptr;
   432     return NS_OK;
   433   }
   434  private:
   435   // The callbacks. Maintained as nsRefPtr as they are generally
   436   // xpconnect values, which cannot be manipulated with nsCOMPtr off
   437   // the main thread. We store both the success callback and the
   438   // error callback to ensure that they are safely released on the
   439   // main thread.
   440   nsRefPtr<nsINativeOSFileSuccessCallback> mOnSuccess;
   441   nsRefPtr<nsINativeOSFileErrorCallback> mOnError;
   442   nsRefPtr<AbstractResult> mDiscardedResult;
   443   int32_t mOSError;
   444   nsCString mOperation;
   445 };
   447 /**
   448  * An event used to notify of a success.
   449  */
   450 class SuccessEvent MOZ_FINAL : public nsRunnable {
   451 public:
   452   /**
   453    * @param aOnSuccess The success callback.
   454    * @param aOnError The error callback.
   455    *
   456    * Note that we pass both the success callback and the error
   457    * callback to ensure that they are both released on the main
   458    * thread, rather than on the IO thread (which would hopefully
   459    * segfault). Also, we pass them as alread_AddRefed to ensure that
   460    * we do not manipulate xpconnect refcounters off the main thread
   461    * (which is illegal).
   462    */
   463   SuccessEvent(already_AddRefed<nsINativeOSFileSuccessCallback>&& aOnSuccess,
   464                already_AddRefed<nsINativeOSFileErrorCallback>&& aOnError,
   465                already_AddRefed<nsINativeOSFileResult>& aResult)
   466     : mOnSuccess(aOnSuccess)
   467     , mOnError(aOnError)
   468     , mResult(aResult)
   469     {
   470       MOZ_ASSERT(!NS_IsMainThread());
   471     }
   473   NS_METHOD Run() {
   474     MOZ_ASSERT(NS_IsMainThread());
   475     (void)mOnSuccess->Complete(mResult);
   477     // Ensure that the callbacks are released on the main thread.
   478     mOnSuccess = nullptr;
   479     mOnError = nullptr;
   480     mResult = nullptr;
   482     return NS_OK;
   483   }
   484  private:
   485   // The callbacks. Maintained as nsRefPtr as they are generally
   486   // xpconnect values, which cannot be manipulated with nsCOMPtr off
   487   // the main thread. We store both the success callback and the
   488   // error callback to ensure that they are safely released on the
   489   // main thread.
   490   nsRefPtr<nsINativeOSFileSuccessCallback> mOnSuccess;
   491   nsRefPtr<nsINativeOSFileErrorCallback> mOnError;
   492   nsRefPtr<nsINativeOSFileResult> mResult;
   493 };
   496 //////// Action events
   498 /**
   499  * Base class shared by actions.
   500  */
   501 class AbstractDoEvent: public nsRunnable {
   502 public:
   503   AbstractDoEvent(already_AddRefed<nsINativeOSFileSuccessCallback>& aOnSuccess,
   504                   already_AddRefed<nsINativeOSFileErrorCallback>& aOnError)
   505     : mOnSuccess(aOnSuccess)
   506     , mOnError(aOnError)
   507 #if defined(DEBUG)
   508     , mResolved(false)
   509 #endif // defined(DEBUG)
   510   {
   511     MOZ_ASSERT(NS_IsMainThread());
   512   }
   514   /**
   515    * Fail, asynchronously.
   516    */
   517   void Fail(const nsACString& aOperation,
   518             already_AddRefed<AbstractResult>&& aDiscardedResult,
   519             int32_t aOSError = 0) {
   520     Resolve();
   521     nsRefPtr<ErrorEvent> event = new ErrorEvent(mOnSuccess.forget(),
   522                                                 mOnError.forget(),
   523                                                 aDiscardedResult,
   524                                                 aOperation,
   525                                                 aOSError);
   526     nsresult rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
   527     if (NS_FAILED(rv)) {
   528       // Last ditch attempt to release on the main thread - some of
   529       // the members of event are not thread-safe, so letting the
   530       // pointer go out of scope would cause a crash.
   531       nsCOMPtr<nsIThread> main = do_GetMainThread();
   532       NS_ProxyRelease(main, event);
   533     }
   534   }
   536   /**
   537    * Succeed, asynchronously.
   538    */
   539   void Succeed(already_AddRefed<nsINativeOSFileResult>&& aResult) {
   540     Resolve();
   541     nsRefPtr<SuccessEvent> event = new SuccessEvent(mOnSuccess.forget(),
   542                                                     mOnError.forget(),
   543                                                     aResult);
   544     nsresult rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
   545     if (NS_FAILED(rv)) {
   546       // Last ditch attempt to release on the main thread - some of
   547       // the members of event are not thread-safe, so letting the
   548       // pointer go out of scope would cause a crash.
   549       nsCOMPtr<nsIThread> main = do_GetMainThread();
   550       NS_ProxyRelease(main, event);
   551     }
   553   }
   555 private:
   557   /**
   558    * Mark the event as complete, for debugging purposes.
   559    */
   560   void Resolve() {
   561 #if defined(DEBUG)
   562     MOZ_ASSERT(!mResolved);
   563     mResolved = true;
   564 #endif // defined(DEBUG)
   565   }
   567 private:
   568   nsRefPtr<nsINativeOSFileSuccessCallback> mOnSuccess;
   569   nsRefPtr<nsINativeOSFileErrorCallback> mOnError;
   570 #if defined(DEBUG)
   571   // |true| once the action is complete
   572   bool mResolved;
   573 #endif // defined(DEBUG)
   574 };
   576 /**
   577  * An abstract event implementing reading from a file.
   578  *
   579  * Concrete subclasses are responsible for handling the
   580  * data obtained from the file and possibly post-processing it.
   581  */
   582 class AbstractReadEvent: public AbstractDoEvent {
   583 public:
   584   /**
   585    * @param aPath The path of the file.
   586    */
   587   AbstractReadEvent(const nsAString& aPath,
   588                     const uint64_t aBytes,
   589                     already_AddRefed<nsINativeOSFileSuccessCallback>& aOnSuccess,
   590                     already_AddRefed<nsINativeOSFileErrorCallback>& aOnError)
   591     : AbstractDoEvent(aOnSuccess, aOnError)
   592     , mPath(aPath)
   593     , mBytes(aBytes)
   594   {
   595     MOZ_ASSERT(NS_IsMainThread());
   596   }
   598   NS_METHOD Run() MOZ_OVERRIDE {
   599     MOZ_ASSERT(!NS_IsMainThread());
   600     TimeStamp dispatchDate = TimeStamp::Now();
   602     nsresult rv = BeforeRead();
   603     if (NS_FAILED(rv)) {
   604       // Error reporting is handled by BeforeRead();
   605       return NS_OK;
   606     }
   608     ScopedArrayBufferContents buffer;
   609     rv = Read(buffer);
   610     if (NS_FAILED(rv)) {
   611       // Error reporting is handled by Read();
   612       return NS_OK;
   613     }
   615     AfterRead(dispatchDate, buffer);
   616     return NS_OK;
   617   }
   619  private:
   620   /**
   621    * Read synchronously.
   622    *
   623    * Must be called off the main thread.
   624    *
   625    * @param aBuffer The destination buffer.
   626    */
   627   nsresult Read(ScopedArrayBufferContents& aBuffer)
   628   {
   629     MOZ_ASSERT(!NS_IsMainThread());
   631     ScopedPRFileDesc file;
   632 #if defined(XP_WIN)
   633     // On Windows, we can't use PR_OpenFile because it doesn't
   634     // handle UTF-16 encoding, which is pretty bad. In addition,
   635     // PR_OpenFile opens files without sharing, which is not the
   636     // general semantics of OS.File.
   637     HANDLE handle =
   638       ::CreateFileW(mPath.get(),
   639                     GENERIC_READ,
   640                     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
   641                     /*Security attributes*/nullptr,
   642                     OPEN_EXISTING,
   643                     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
   644                     /*Template file*/ nullptr);
   646     if (handle == INVALID_HANDLE_VALUE) {
   647       Fail(NS_LITERAL_CSTRING("open"), nullptr, ::GetLastError());
   648       return NS_ERROR_FAILURE;
   649     }
   651     file = PR_ImportFile((PROsfd)handle);
   652     if (!file) {
   653       // |file| is closed by PR_ImportFile
   654       Fail(NS_LITERAL_CSTRING("ImportFile"), nullptr, PR_GetOSError());
   655       return NS_ERROR_FAILURE;
   656     }
   658 #else
   659     // On other platforms, PR_OpenFile will do.
   660     NS_ConvertUTF16toUTF8 path(mPath);
   661     file = PR_OpenFile(path.get(), PR_RDONLY, 0);
   662     if (!file) {
   663       Fail(NS_LITERAL_CSTRING("open"), nullptr, PR_GetOSError());
   664       return NS_ERROR_FAILURE;
   665     }
   667 #endif // defined(XP_XIN)
   669     PRFileInfo64 stat;
   670     if (PR_GetOpenFileInfo64(file, &stat) != PR_SUCCESS) {
   671       Fail(NS_LITERAL_CSTRING("stat"), nullptr, PR_GetOSError());
   672       return NS_ERROR_FAILURE;
   673     }
   675     uint64_t bytes = std::min((uint64_t)stat.size, mBytes);
   676     if (bytes > UINT32_MAX) {
   677       Fail(NS_LITERAL_CSTRING("Arithmetics"), nullptr, OS_ERROR_INVAL);
   678       return NS_ERROR_FAILURE;
   679     }
   681     if (!aBuffer.Allocate(bytes)) {
   682       Fail(NS_LITERAL_CSTRING("allocate"), nullptr, OS_ERROR_NOMEM);
   683       return NS_ERROR_FAILURE;
   684     }
   686     uint64_t total_read = 0;
   687     int32_t just_read = 0;
   688     char* dest_chars = reinterpret_cast<char*>(aBuffer.rwget().data);
   689     do {
   690       just_read = PR_Read(file, dest_chars + total_read,
   691                           std::min(uint64_t(PR_INT32_MAX), bytes - total_read));
   692       if (just_read == -1) {
   693         Fail(NS_LITERAL_CSTRING("read"), nullptr, PR_GetOSError());
   694         return NS_ERROR_FAILURE;
   695       }
   696       total_read += just_read;
   697     } while (just_read != 0 && total_read < bytes);
   698     if (total_read != bytes) {
   699       // We seem to have a race condition here.
   700       Fail(NS_LITERAL_CSTRING("read"), nullptr, OS_ERROR_RACE);
   701       return NS_ERROR_FAILURE;
   702     }
   704     return NS_OK;
   705   }
   707 protected:
   708   /**
   709    * Any steps that need to be taken before reading.
   710    *
   711    * In case of error, this method should call Fail() and return
   712    * a failure code.
   713    */
   714   virtual
   715   nsresult BeforeRead() {
   716     return NS_OK;
   717   }
   719   /**
   720    * Proceed after reading.
   721    */
   722   virtual
   723   void AfterRead(TimeStamp aDispatchDate, ScopedArrayBufferContents& aBuffer) = 0;
   725  protected:
   726   const nsString mPath;
   727   const uint64_t mBytes;
   728 };
   730 /**
   731  * An implementation of a Read event that provides the data
   732  * as a TypedArray.
   733  */
   734 class DoReadToTypedArrayEvent MOZ_FINAL : public AbstractReadEvent {
   735 public:
   736   DoReadToTypedArrayEvent(const nsAString& aPath,
   737                           const uint32_t aBytes,
   738                           already_AddRefed<nsINativeOSFileSuccessCallback>&& aOnSuccess,
   739                           already_AddRefed<nsINativeOSFileErrorCallback>&& aOnError)
   740     : AbstractReadEvent(aPath, aBytes,
   741                         aOnSuccess, aOnError)
   742     , mResult(new TypedArrayResult(TimeStamp::Now()))
   743   { }
   745   ~DoReadToTypedArrayEvent() {
   746     // If AbstractReadEvent::Run() has bailed out, we may need to cleanup
   747     // mResult, which is main-thread only data
   748     if (!mResult) {
   749       return;
   750     }
   751     nsCOMPtr<nsIThread> main = do_GetMainThread();
   752     (void)NS_ProxyRelease(main, mResult);
   753   }
   755 protected:
   756   void AfterRead(TimeStamp aDispatchDate,
   757                  ScopedArrayBufferContents& aBuffer) MOZ_OVERRIDE {
   758     MOZ_ASSERT(!NS_IsMainThread());
   759     mResult->Init(aDispatchDate, TimeStamp::Now() - aDispatchDate, aBuffer.forget());
   760     Succeed(mResult.forget());
   761   }
   763  private:
   764   nsRefPtr<TypedArrayResult> mResult;
   765 };
   767 /**
   768  * An implementation of a Read event that provides the data
   769  * as a JavaScript string.
   770  */
   771 class DoReadToStringEvent MOZ_FINAL : public AbstractReadEvent {
   772 public:
   773   DoReadToStringEvent(const nsAString& aPath,
   774                       const nsACString& aEncoding,
   775                       const uint32_t aBytes,
   776                       already_AddRefed<nsINativeOSFileSuccessCallback>&& aOnSuccess,
   777                       already_AddRefed<nsINativeOSFileErrorCallback>&& aOnError)
   778     : AbstractReadEvent(aPath, aBytes, aOnSuccess, aOnError)
   779     , mEncoding(aEncoding)
   780     , mResult(new StringResult(TimeStamp::Now()))
   781   { }
   783   ~DoReadToStringEvent() {
   784     // If AbstraactReadEvent::Run() has bailed out, we may need to cleanup
   785     // mResult, which is main-thread only data
   786     if (!mResult) {
   787       return;
   788     }
   789     nsCOMPtr<nsIThread> main = do_GetMainThread();
   790     (void)NS_ProxyRelease(main, mResult);
   791   }
   793 protected:
   794   nsresult BeforeRead() MOZ_OVERRIDE {
   795     // Obtain the decoder. We do this before reading to avoid doing
   796     // any unnecessary I/O in case the name of the encoding is incorrect.
   797     MOZ_ASSERT(!NS_IsMainThread());
   798     nsAutoCString encodingName;
   799     if (!dom::EncodingUtils::FindEncodingForLabel(mEncoding, encodingName)) {
   800       Fail(NS_LITERAL_CSTRING("Decode"), mResult.forget(), OS_ERROR_INVAL);
   801       return NS_ERROR_FAILURE;
   802     }
   803     mDecoder = dom::EncodingUtils::DecoderForEncoding(encodingName);
   804     if (!mDecoder) {
   805       Fail(NS_LITERAL_CSTRING("DecoderForEncoding"), mResult.forget(), OS_ERROR_INVAL);
   806       return NS_ERROR_FAILURE;
   807     }
   809     return NS_OK;
   810   }
   812   void AfterRead(TimeStamp aDispatchDate,
   813                  ScopedArrayBufferContents& aBuffer) MOZ_OVERRIDE {
   814     MOZ_ASSERT(!NS_IsMainThread());
   816     int32_t maxChars;
   817     const char* sourceChars = reinterpret_cast<const char*>(aBuffer.get().data);
   818     int32_t sourceBytes = aBuffer.get().nbytes;
   819     if (sourceBytes < 0) {
   820       Fail(NS_LITERAL_CSTRING("arithmetics"), mResult.forget(), OS_ERROR_TOO_LARGE);
   821       return;
   822     }
   824     nsresult rv = mDecoder->GetMaxLength(sourceChars, sourceBytes, &maxChars);
   825     if (NS_FAILED(rv)) {
   826       Fail(NS_LITERAL_CSTRING("GetMaxLength"), mResult.forget(), OS_ERROR_INVAL);
   827       return;
   828     }
   830     if (maxChars < 0) {
   831       Fail(NS_LITERAL_CSTRING("arithmetics"), mResult.forget(), OS_ERROR_TOO_LARGE);
   832       return;
   833     }
   835     nsString resultString;
   836     resultString.SetLength(maxChars);
   837     if (resultString.Length() != (nsString::size_type)maxChars) {
   838       Fail(NS_LITERAL_CSTRING("allocation"), mResult.forget(), OS_ERROR_TOO_LARGE);
   839       return;
   840     }
   843     rv = mDecoder->Convert(sourceChars, &sourceBytes,
   844                            resultString.BeginWriting(), &maxChars);
   845     MOZ_ASSERT(NS_SUCCEEDED(rv));
   846     resultString.SetLength(maxChars);
   848     mResult->Init(aDispatchDate, TimeStamp::Now() - aDispatchDate, resultString);
   849     Succeed(mResult.forget());
   850   }
   852  private:
   853   nsCString mEncoding;
   854   nsCOMPtr<nsIUnicodeDecoder> mDecoder;
   855   nsRefPtr<StringResult> mResult;
   856 };
   858 } // osfile
   860 // The OS.File service
   862 NS_IMPL_ISUPPORTS(NativeOSFileInternalsService, nsINativeOSFileInternalsService);
   864 NS_IMETHODIMP
   865 NativeOSFileInternalsService::Read(const nsAString& aPath,
   866                                    JS::HandleValue aOptions,
   867                                    nsINativeOSFileSuccessCallback *aOnSuccess,
   868                                    nsINativeOSFileErrorCallback *aOnError,
   869                                    JSContext* cx)
   870 {
   871   // Extract options
   872   nsCString encoding;
   873   uint64_t bytes = UINT64_MAX;
   875   if (aOptions.isObject()) {
   876     dom::NativeOSFileReadOptions dict;
   877     if (!dict.Init(cx, aOptions)) {
   878       return NS_ERROR_INVALID_ARG;
   879     }
   881     if (dict.mEncoding.WasPassed()) {
   882       CopyUTF16toUTF8(dict.mEncoding.Value(), encoding);
   883     }
   885     if (dict.mBytes.WasPassed() && !dict.mBytes.Value().IsNull()) {
   886       bytes = dict.mBytes.Value().Value();
   887     }
   888   }
   890   // Prepare the off main thread event and dispatch it
   891   nsCOMPtr<nsINativeOSFileSuccessCallback> onSuccess(aOnSuccess);
   892   nsCOMPtr<nsINativeOSFileErrorCallback> onError(aOnError);
   894   nsRefPtr<AbstractDoEvent> event;
   895   if (encoding.IsEmpty()) {
   896     event = new DoReadToTypedArrayEvent(aPath, bytes,
   897                                         onSuccess.forget(),
   898                                         onError.forget());
   899   } else {
   900     event = new DoReadToStringEvent(aPath, encoding, bytes,
   901                                     onSuccess.forget(),
   902                                     onError.forget());
   903   }
   905   nsresult rv;
   906   nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
   908   if (NS_FAILED(rv)) {
   909     return rv;
   910   }
   911   return target->Dispatch(event, NS_DISPATCH_NORMAL);
   912 }
   914 } // namespace mozilla

mercurial