diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index 9fa8a099894e..4a8ea6286ebb 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -297,7 +297,6 @@ @BINPATH@/components/storage.xpt @BINPATH@/components/telemetry.xpt @BINPATH@/components/toolkit_finalizationwitness.xpt -@BINPATH@/components/toolkit_osfile.xpt @BINPATH@/components/toolkitprofile.xpt #ifdef MOZ_ENABLE_XREMOTE @BINPATH@/components/toolkitremote.xpt diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 17762485515c..6d8ef3c6839f 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -310,7 +310,6 @@ @BINPATH@/components/spellchecker.xpt @BINPATH@/components/storage.xpt @BINPATH@/components/toolkit_finalizationwitness.xpt -@BINPATH@/components/toolkit_osfile.xpt @BINPATH@/components/toolkitprofile.xpt #ifdef MOZ_ENABLE_XREMOTE @BINPATH@/components/toolkitremote.xpt diff --git a/dom/webidl/NativeOSFileInternals.webidl b/dom/webidl/NativeOSFileInternals.webidl deleted file mode 100644 index 36cc1a3a18c9..000000000000 --- a/dom/webidl/NativeOSFileInternals.webidl +++ /dev/null @@ -1,21 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtaone at http://mozilla.org/MPL/2.0/. */ - -/** - * Options for nsINativeOSFileInternals::Read - */ -dictionary NativeOSFileReadOptions -{ - /** - * If specified, convert the raw bytes to a String - * with the specified encoding. Otherwise, return - * the raw bytes as a TypedArray. - */ - DOMString? encoding; - - /** - * If specified, limit the number of bytes to read. - */ - unsigned long long? bytes; -}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 7d359d269198..101317043bb4 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -244,7 +244,6 @@ WEBIDL_FILES = [ 'MozWakeLock.webidl', 'MutationEvent.webidl', 'MutationObserver.webidl', - 'NativeOSFileInternals.webidl', 'NetDashboard.webidl', 'NetworkOptions.webidl', 'Node.webidl', diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in index 9becbb6b0f5d..84f3cdeabed9 100644 --- a/mobile/android/installer/package-manifest.in +++ b/mobile/android/installer/package-manifest.in @@ -246,7 +246,6 @@ @BINPATH@/components/storage.xpt @BINPATH@/components/telemetry.xpt @BINPATH@/components/toolkit_finalizationwitness.xpt -@BINPATH@/components/toolkit_osfile.xpt @BINPATH@/components/toolkitprofile.xpt #ifdef MOZ_ENABLE_XREMOTE @BINPATH@/components/toolkitremote.xpt diff --git a/toolkit/components/build/nsToolkitCompsModule.cpp b/toolkit/components/build/nsToolkitCompsModule.cpp index 3765e37468f1..b0cbf16aa94a 100644 --- a/toolkit/components/build/nsToolkitCompsModule.cpp +++ b/toolkit/components/build/nsToolkitCompsModule.cpp @@ -35,7 +35,6 @@ #include "nsBrowserStatusFilter.h" #include "mozilla/FinalizationWitnessService.h" -#include "mozilla/NativeOSFileInternals.h" using namespace mozilla; @@ -90,7 +89,6 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsBrowserStatusFilter) NS_GENERIC_FACTORY_CONSTRUCTOR(nsUpdateProcessor) #endif NS_GENERIC_FACTORY_CONSTRUCTOR(FinalizationWitnessService) -NS_GENERIC_FACTORY_CONSTRUCTOR(NativeOSFileInternalsService) NS_DEFINE_NAMED_CID(NS_TOOLKIT_APPSTARTUP_CID); NS_DEFINE_NAMED_CID(NS_USERINFO_CID); @@ -116,7 +114,6 @@ NS_DEFINE_NAMED_CID(NS_CHARSETMENU_CID); NS_DEFINE_NAMED_CID(NS_UPDATEPROCESSOR_CID); #endif NS_DEFINE_NAMED_CID(FINALIZATIONWITNESSSERVICE_CID); -NS_DEFINE_NAMED_CID(NATIVE_OSFILE_INTERNALS_SERVICE_CID); static const Module::CIDEntry kToolkitCIDs[] = { { &kNS_TOOLKIT_APPSTARTUP_CID, false, nullptr, nsAppStartupConstructor }, @@ -143,7 +140,6 @@ static const Module::CIDEntry kToolkitCIDs[] = { { &kNS_UPDATEPROCESSOR_CID, false, nullptr, nsUpdateProcessorConstructor }, #endif { &kFINALIZATIONWITNESSSERVICE_CID, false, nullptr, FinalizationWitnessServiceConstructor }, - { &kNATIVE_OSFILE_INTERNALS_SERVICE_CID, false, nullptr, NativeOSFileInternalsServiceConstructor }, { nullptr } }; @@ -173,7 +169,6 @@ static const Module::ContractIDEntry kToolkitContracts[] = { { NS_UPDATEPROCESSOR_CONTRACTID, &kNS_UPDATEPROCESSOR_CID }, #endif { FINALIZATIONWITNESSSERVICE_CONTRACTID, &kFINALIZATIONWITNESSSERVICE_CID }, - { NATIVE_OSFILE_INTERNALS_SERVICE_CONTRACTID, &kNATIVE_OSFILE_INTERNALS_SERVICE_CID }, { nullptr } }; diff --git a/toolkit/components/crashes/CrashManager.jsm b/toolkit/components/crashes/CrashManager.jsm index 5c3f98dc7495..14da9a625afe 100644 --- a/toolkit/components/crashes/CrashManager.jsm +++ b/toolkit/components/crashes/CrashManager.jsm @@ -595,7 +595,7 @@ CrashStore.prototype = Object.freeze({ try { let decoder = new TextDecoder(); - let data = yield OS.File.read(this._storePath, {compression: "lz4"}); + let data = yield OS.File.read(this._storePath, null, {compression: "lz4"}); data = JSON.parse(decoder.decode(data)); if (data.corruptDate) { diff --git a/toolkit/components/osfile/NativeOSFileInternals.cpp b/toolkit/components/osfile/NativeOSFileInternals.cpp deleted file mode 100644 index 16b1900fa18f..000000000000 --- a/toolkit/components/osfile/NativeOSFileInternals.cpp +++ /dev/null @@ -1,921 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Native implementation of some OS.File operations. - */ - -#include "nsString.h" -#include "nsNetCID.h" -#include "nsThreadUtils.h" -#include "nsXPCOMCID.h" -#include "nsCycleCollectionParticipant.h" -#include "nsServiceManagerUtils.h" -#include "nsProxyRelease.h" - -#include "nsINativeOSFileInternals.h" -#include "NativeOSFileInternals.h" -#include "mozilla/dom/NativeOSFileInternalsBinding.h" - -#include "nsIUnicodeDecoder.h" -#include "nsIEventTarget.h" - -#include "mozilla/dom/EncodingUtils.h" -#include "mozilla/DebugOnly.h" -#include "mozilla/Scoped.h" -#include "mozilla/HoldDropJSObjects.h" -#include "mozilla/TimeStamp.h" - -#include "prio.h" -#include "prerror.h" -#include "private/pprio.h" - -#include "jsapi.h" -#include "jsfriendapi.h" -#include "js/Utility.h" -#include "xpcpublic.h" - -#include -#if defined(XP_UNIX) -#include -#include -#include -#include -#include -#endif // defined (XP_UNIX) - -#if defined(XP_WIN) -#include -#endif // defined (XP_WIN) - -namespace mozilla { - -MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, PR_Close) - -namespace { - -// Utilities for safely manipulating ArrayBuffer contents even in the -// absence of a JSContext. - -/** - * The C buffer underlying to an ArrayBuffer. Throughout the code, we manipulate - * this instead of a void* buffer, as this lets us transfer data across threads - * and into JavaScript without copy. - */ -struct ArrayBufferContents { - /** - * The header of the ArrayBuffer. This is the pointer actually used by JSAPI. - */ - void* header; - /** - * The data of the ArrayBuffer. This is the pointer manipulated to - * read/write the contents of the buffer. - */ - uint8_t* data; - /** - * The number of bytes in the ArrayBuffer. - */ - size_t nbytes; -}; - -/** - * RAII for ArrayBufferContents. - */ -struct ScopedArrayBufferContentsTraits { - typedef ArrayBufferContents type; - const static type empty() { - type result = {0, 0, 0}; - return result; - } - const static void release(type ptr) { - js_free(ptr.header); - ptr.header = nullptr; - ptr.data = nullptr; - ptr.nbytes = 0; - } -}; - -struct ScopedArrayBufferContents: public Scoped { - ScopedArrayBufferContents(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM): - Scoped(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_TO_PARENT) - { } - ScopedArrayBufferContents(const ArrayBufferContents& v - MOZ_GUARD_OBJECT_NOTIFIER_PARAM): - Scoped(v MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT) - { } - ScopedArrayBufferContents& operator=(ArrayBufferContents ptr) { - Scoped::operator=(ptr); - return *this; - } - - /** - * Request memory for this ArrayBufferContent. This memory may later - * be used to create an ArrayBuffer object (possibly on another - * thread) without copy. - * - * @return true In case of success, false otherwise. - */ - bool Allocate(uint32_t length) { - dispose(); - ArrayBufferContents& value = rwget(); - if (JS_AllocateArrayBufferContents(/*no context available*/nullptr, - length, - &value.header, - &value.data)) { - value.nbytes = length; - return true; - } - return false; - } -private: - explicit ScopedArrayBufferContents(ScopedArrayBufferContents& source) MOZ_DELETE; - ScopedArrayBufferContents& operator=(ScopedArrayBufferContents& source) MOZ_DELETE; -}; - -///////// Cross-platform issues - -// Platform specific constants. As OS.File always uses OS-level -// errors, we need to map a few high-level errors to OS-level -// constants. -#if defined(XP_UNIX) -#define OS_ERROR_NOMEM ENOMEM -#define OS_ERROR_INVAL EINVAL -#define OS_ERROR_TOO_LARGE EFBIG -#define OS_ERROR_RACE EIO -#elif defined(XP_WIN) -#define OS_ERROR_NOMEM ERROR_NOT_ENOUGH_MEMORY -#define OS_ERROR_INVAL ERROR_BAD_ARGUMENTS -#define OS_ERROR_TOO_LARGE ERROR_FILE_TOO_LARGE -#define OS_ERROR_RACE ERROR_SHARING_VIOLATION -#else -#error "We do not have platform-specific constants for this platform" -#endif - -///////// Results of OS.File operations - -/** - * Base class for results passed to the callbacks. - * - * This base class implements caching of JS values returned to the client. - * We make use of this caching in derived classes e.g. to avoid accidents - * when we transfer data allocated on another thread into JS. Note that - * this caching can lead to cycles (e.g. if a client adds a back-reference - * in the JS value), so we implement all Cycle Collector primitives in - * AbstractResult. - */ -class AbstractResult: public nsINativeOSFileResult { -public: - NS_DECL_NSINATIVEOSFILERESULT - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AbstractResult) - - /** - * Construct the result object. Must be called on the main thread - * as the AbstractResult is cycle-collected. - * - * @param aStartDate The instant at which the operation was - * requested. Used to collect Telemetry statistics. - */ - AbstractResult(TimeStamp aStartDate) - : mStartDate(aStartDate) - { - MOZ_ASSERT(NS_IsMainThread()); - mozilla::HoldJSObjects(this); - } - virtual ~AbstractResult() { - MOZ_ASSERT(NS_IsMainThread()); - DropJSData(); - mozilla::DropJSObjects(this); - } - - /** - * Setup the AbstractResult once data is available. - * - * @param aDispatchDate The instant at which the IO thread received - * the operation request. Used to collect Telemetry statistics. - * @param aExecutionDuration The duration of the operation on the - * IO thread. - */ - void Init(TimeStamp aDispatchDate, - TimeDuration aExecutionDuration) { - MOZ_ASSERT(!NS_IsMainThread()); - - mDispatchDuration = (aDispatchDate - mStartDate); - mExecutionDuration = aExecutionDuration; - } - - /** - * Drop any data that could lead to a cycle. - */ - void DropJSData() { - mCachedResult = JS::UndefinedValue(); - } - -protected: - virtual nsresult GetCacheableResult(JSContext *cx, JS::MutableHandleValue aResult) = 0; - -private: - TimeStamp mStartDate; - TimeDuration mDispatchDuration; - TimeDuration mExecutionDuration; - JS::Heap mCachedResult; -}; - -NS_IMPL_CYCLE_COLLECTING_ADDREF(AbstractResult) -NS_IMPL_CYCLE_COLLECTING_RELEASE(AbstractResult) - -NS_IMPL_CYCLE_COLLECTION_CLASS(AbstractResult) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AbstractResult) - NS_INTERFACE_MAP_ENTRY(nsINativeOSFileResult) - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AbstractResult) - NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedResult) -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AbstractResult) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AbstractResult) - tmp->DropJSData(); -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMETHODIMP -AbstractResult::GetDispatchDurationMS(double *aDispatchDuration) -{ - *aDispatchDuration = mDispatchDuration.ToMilliseconds(); - return NS_OK; -} - -NS_IMETHODIMP -AbstractResult::GetExecutionDurationMS(double *aExecutionDuration) -{ - *aExecutionDuration = mExecutionDuration.ToMilliseconds(); - return NS_OK; -} - -NS_IMETHODIMP -AbstractResult::GetResult(JSContext *cx, JS::MutableHandleValue aResult) -{ - if (mCachedResult.isUndefined()) { - nsresult rv = GetCacheableResult(cx, aResult); - if (NS_FAILED(rv)) { - return rv; - } - mCachedResult = aResult; - return NS_OK; - } - aResult.set(mCachedResult); - return NS_OK; -} - -/** - * Return a result as a string. - * - * In this implementation, attribute |result| is a string. Strings are - * passed to JS without copy. - */ -class StringResult MOZ_FINAL : public AbstractResult -{ -public: - StringResult(TimeStamp aStartDate) - : AbstractResult(aStartDate) - { - } - - /** - * Initialize the object once the contents of the result as available. - * - * @param aContents The string to pass to JavaScript. Ownership of the - * string and its contents is passed to StringResult. The string must - * be valid UTF-16. - */ - void Init(TimeStamp aDispatchDate, - TimeDuration aExecutionDuration, - nsString& aContents) { - AbstractResult::Init(aDispatchDate, aExecutionDuration); - mContents = aContents; - } - -protected: - nsresult GetCacheableResult(JSContext* cx, JS::MutableHandleValue aResult) MOZ_OVERRIDE; - -private: - nsString mContents; -}; - -nsresult -StringResult::GetCacheableResult(JSContext* cx, JS::MutableHandleValue aResult) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mContents.get()); - - // Convert mContents to a js string without copy. Note that this - // may have the side-effect of stealing the contents of the string - // from XPCOM and into JS. - if (!xpc::StringToJsval(cx, mContents, aResult)) { - return NS_ERROR_FAILURE; - } - return NS_OK; -} - - -/** - * Return a result as a Uint8Array. - * - * In this implementation, attribute |result| is a Uint8Array. The array - * is passed to JS without memory copy. - */ -class TypedArrayResult MOZ_FINAL : public AbstractResult -{ -public: - TypedArrayResult(TimeStamp aStartDate) - : AbstractResult(aStartDate) - { - } - - /** - * @param aContents The contents to pass to JS. Calling this method. - * transmits ownership of the ArrayBufferContents to the TypedArrayResult. - * Do not reuse this value anywhere else. - */ - void Init(TimeStamp aDispatchDate, - TimeDuration aExecutionDuration, - ArrayBufferContents aContents) { - AbstractResult::Init(aDispatchDate, aExecutionDuration); - mContents = aContents; - } - -protected: - nsresult GetCacheableResult(JSContext* cx, JS::MutableHandleValue aResult) MOZ_OVERRIDE; -private: - ScopedArrayBufferContents mContents; -}; - -nsresult -TypedArrayResult::GetCacheableResult(JSContext* cx, JS::MutableHandle aResult) -{ - MOZ_ASSERT(NS_IsMainThread()); - // We cannot simply construct a typed array using contents.header as - // this would allow us to have several otherwise unrelated - // ArrayBuffers with the same underlying C buffer. As this would be - // very unsafe, we need to cache the result once we have it. - - const ArrayBufferContents& contents = mContents.get(); - MOZ_ASSERT(contents.data); - - JS::Rooted - arrayBuffer(cx, JS_NewArrayBufferWithContents(cx, contents.header)); - if (!arrayBuffer) { - return NS_ERROR_OUT_OF_MEMORY; - } - - JS::Rooted - result(cx, JS_NewUint8ArrayWithBuffer(cx, arrayBuffer, - 0, contents.nbytes)); - if (!result) { - return NS_ERROR_OUT_OF_MEMORY; - } - // The memory of contents has been allocated on a thread that - // doesn't have a JSRuntime, hence without a context. Now that we - // have a context, attach the memory to where it belongs. - JS_updateMallocCounter(cx, contents.nbytes); - mContents.forget(); - - aResult.setObject(*result); - return NS_OK; -} - -//////// Callback events - -/** - * An event used to notify asynchronously of an error. - */ -class ErrorEvent MOZ_FINAL : public nsRunnable { -public: - /** - * @param aOnSuccess The success callback. - * @param aOnError The error callback. - * @param aDiscardedResult The discarded result. - * @param aOperation The name of the operation, used for error reporting. - * @param aOSError The OS error of the operation, as returned by errno/ - * GetLastError(). - * - * Note that we pass both the success callback and the error - * callback, as well as the discarded result to ensure that they are - * all released on the main thread, rather than on the IO thread - * (which would hopefully segfault). Also, we pass the callbacks as - * alread_AddRefed to ensure that we do not manipulate main-thread - * only refcounters off the main thread. - */ - ErrorEvent(already_AddRefed aOnSuccess, - already_AddRefed aOnError, - already_AddRefed aDiscardedResult, - const nsACString& aOperation, - int32_t aOSError) - : mOnSuccess(aOnSuccess) - , mOnError(aOnError) - , mDiscardedResult(aDiscardedResult) - , mOSError(aOSError) - , mOperation(aOperation) - { - MOZ_ASSERT(!NS_IsMainThread()); - } - - NS_METHOD Run() { - MOZ_ASSERT(NS_IsMainThread()); - (void)mOnError->Complete(mOperation, mOSError); - - // Ensure that the callbacks are released on the main thread. - mOnSuccess = nullptr; - mOnError = nullptr; - mDiscardedResult = nullptr; - - return NS_OK; - } - private: - // The callbacks. Maintained as nsRefPtr as they are generally - // xpconnect values, which cannot be manipulated with nsCOMPtr off - // the main thread. We store both the success callback and the - // error callback to ensure that they are safely released on the - // main thread. - nsRefPtr mOnSuccess; - nsRefPtr mOnError; - nsRefPtr mDiscardedResult; - int32_t mOSError; - nsCString mOperation; -}; - -/** - * An event used to notify of a success. - */ -class SuccessEvent MOZ_FINAL : public nsRunnable { -public: - /** - * @param aOnSuccess The success callback. - * @param aOnError The error callback. - * - * Note that we pass both the success callback and the error - * callback to ensure that they are both released on the main - * thread, rather than on the IO thread (which would hopefully - * segfault). Also, we pass them as alread_AddRefed to ensure that - * we do not manipulate xpconnect refcounters off the main thread - * (which is illegal). - */ - SuccessEvent(already_AddRefed aOnSuccess, - already_AddRefed aOnError, - already_AddRefed aResult) - : mOnSuccess(aOnSuccess) - , mOnError(aOnError) - , mResult(aResult) - { - MOZ_ASSERT(!NS_IsMainThread()); - } - - NS_METHOD Run() { - MOZ_ASSERT(NS_IsMainThread()); - (void)mOnSuccess->Complete(mResult); - - // Ensure that the callbacks are released on the main thread. - mOnSuccess = nullptr; - mOnError = nullptr; - mResult = nullptr; - - return NS_OK; - } - private: - // The callbacks. Maintained as nsRefPtr as they are generally - // xpconnect values, which cannot be manipulated with nsCOMPtr off - // the main thread. We store both the success callback and the - // error callback to ensure that they are safely released on the - // main thread. - nsRefPtr mOnSuccess; - nsRefPtr mOnError; - nsRefPtr mResult; -}; - - -//////// Action events - -/** - * Base class shared by actions. - */ -class AbstractDoEvent: public nsRunnable { -public: - AbstractDoEvent(already_AddRefed aOnSuccess, - already_AddRefed aOnError) - : mOnSuccess(aOnSuccess) - , mOnError(aOnError) -#if defined(DEBUG) - , mResolved(false) -#endif // defined(DEBUG) - { - MOZ_ASSERT(NS_IsMainThread()); - } - - /** - * Fail, asynchronously. - */ - void Fail(const nsACString& aOperation, - already_AddRefed aDiscardedResult, - int32_t aOSError = 0) { - Resolve(); - nsRefPtr event = new ErrorEvent(mOnSuccess.forget(), - mOnError.forget(), - aDiscardedResult, - aOperation, - aOSError); - nsresult rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); - if (NS_FAILED(rv)) { - // Last ditch attempt to release on the main thread - some of - // the members of event are not thread-safe, so letting the - // pointer go out of scope would cause a crash. - nsCOMPtr main = do_GetMainThread(); - NS_ProxyRelease(main, event); - } - } - - /** - * Succeed, asynchronously. - */ - void Succeed(already_AddRefed aResult) { - Resolve(); - nsRefPtr event = new SuccessEvent(mOnSuccess.forget(), - mOnError.forget(), - aResult); - nsresult rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); - if (NS_FAILED(rv)) { - // Last ditch attempt to release on the main thread - some of - // the members of event are not thread-safe, so letting the - // pointer go out of scope would cause a crash. - nsCOMPtr main = do_GetMainThread(); - NS_ProxyRelease(main, event); - } - - } - -private: - - /** - * Mark the event as complete, for debugging purposes. - */ - void Resolve() { -#if defined(DEBUG) - MOZ_ASSERT(!mResolved); - mResolved = true; -#endif // defined(DEBUG) - } - -private: - nsRefPtr mOnSuccess; - nsRefPtr mOnError; -#if defined(DEBUG) - // |true| once the action is complete - bool mResolved; -#endif // defined(DEBUG) -}; - -/** - * An abstract event implementing reading from a file. - * - * Concrete subclasses are responsible for handling the - * data obtained from the file and possibly post-processing it. - */ -class AbstractReadEvent: public AbstractDoEvent { -public: - /** - * @param aPath The path of the file. - */ - AbstractReadEvent(const nsAString& aPath, - const uint64_t aBytes, - already_AddRefed aOnSuccess, - already_AddRefed aOnError) - : AbstractDoEvent(aOnSuccess, aOnError) - , mPath(aPath) - , mBytes(aBytes) - { - MOZ_ASSERT(NS_IsMainThread()); - } - - NS_METHOD Run() MOZ_OVERRIDE { - MOZ_ASSERT(!NS_IsMainThread()); - TimeStamp dispatchDate = TimeStamp::Now(); - - nsresult rv = BeforeRead(); - if (NS_FAILED(rv)) { - // Error reporting is handled by BeforeRead(); - return NS_OK; - } - - ScopedArrayBufferContents buffer; - rv = Read(buffer); - if (NS_FAILED(rv)) { - // Error reporting is handled by Read(); - return NS_OK; - } - - AfterRead(dispatchDate, buffer); - return NS_OK; - } - - private: - /** - * Read synchronously. - * - * Must be called off the main thread. - * - * @param aBuffer The destination buffer. - */ - nsresult Read(ScopedArrayBufferContents& aBuffer) - { - MOZ_ASSERT(!NS_IsMainThread()); - - ScopedPRFileDesc file; -#if defined(XP_WIN) - // On Windows, we can't use PR_OpenFile because it doesn't - // handle UTF-16 encoding, which is pretty bad. In addition, - // PR_OpenFile opens files without sharing, which is not the - // general semantics of OS.File. - HANDLE handle = - ::CreateFileW(mPath.get(), - GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - /*Security attributes*/nullptr, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, - /*Template file*/ nullptr); - - if (handle == INVALID_HANDLE_VALUE) { - Fail(NS_LITERAL_CSTRING("open"), nullptr, ::GetLastError()); - return NS_ERROR_FAILURE; - } - - file = PR_ImportFile((PROsfd)handle); - if (!file) { - // |file| is closed by PR_ImportFile - Fail(NS_LITERAL_CSTRING("ImportFile"), nullptr, PR_GetOSError()); - return NS_ERROR_FAILURE; - } - -#else - // On other platforms, PR_OpenFile will do. - NS_ConvertUTF16toUTF8 path(mPath); - file = PR_OpenFile(path.get(), PR_RDONLY, 0); - if (!file) { - Fail(NS_LITERAL_CSTRING("open"), nullptr, PR_GetOSError()); - return NS_ERROR_FAILURE; - } - -#endif // defined(XP_XIN) - - PRFileInfo64 stat; - if (PR_GetOpenFileInfo64(file, &stat) != PR_SUCCESS) { - Fail(NS_LITERAL_CSTRING("stat"), nullptr, PR_GetOSError()); - return NS_ERROR_FAILURE; - } - - uint64_t bytes = std::min((uint64_t)stat.size, mBytes); - if (bytes > UINT32_MAX) { - Fail(NS_LITERAL_CSTRING("Arithmetics"), nullptr, OS_ERROR_INVAL); - return NS_ERROR_FAILURE; - } - - if (!aBuffer.Allocate(bytes)) { - Fail(NS_LITERAL_CSTRING("allocate"), nullptr, OS_ERROR_NOMEM); - return NS_ERROR_FAILURE; - } - - uint64_t total_read = 0; - int32_t just_read = 0; - char* dest_chars = reinterpret_cast(aBuffer.rwget().data); - do { - just_read = PR_Read(file, dest_chars + total_read, - std::min(uint64_t(PR_INT32_MAX), bytes - total_read)); - if (just_read == -1) { - Fail(NS_LITERAL_CSTRING("read"), nullptr, PR_GetOSError()); - return NS_ERROR_FAILURE; - } - total_read += just_read; - } while (just_read != 0 && total_read < bytes); - if (total_read != bytes) { - // We seem to have a race condition here. - Fail(NS_LITERAL_CSTRING("read"), nullptr, OS_ERROR_RACE); - return NS_ERROR_FAILURE; - } - - return NS_OK; - } - -protected: - /** - * Any steps that need to be taken before reading. - * - * In case of error, this method should call Fail() and return - * a failure code. - */ - virtual - nsresult BeforeRead() { - return NS_OK; - } - - /** - * Proceed after reading. - */ - virtual - void AfterRead(TimeStamp aDispatchDate, ScopedArrayBufferContents& aBuffer) = 0; - - protected: - const nsString mPath; - const uint64_t mBytes; -}; - -/** - * An implementation of a Read event that provides the data - * as a TypedArray. - */ -class DoReadToTypedArrayEvent MOZ_FINAL : public AbstractReadEvent { -public: - DoReadToTypedArrayEvent(const nsAString& aPath, - const uint32_t aBytes, - already_AddRefed aOnSuccess, - already_AddRefed aOnError) - : AbstractReadEvent(aPath, aBytes, - aOnSuccess, aOnError) - , mResult(new TypedArrayResult(TimeStamp::Now())) - { } - - ~DoReadToTypedArrayEvent() { - // If AbstractReadEvent::Run() has bailed out, we may need to cleanup - // mResult, which is main-thread only data - if (!mResult) { - return; - } - nsCOMPtr main = do_GetMainThread(); - (void)NS_ProxyRelease(main, mResult); - } - -protected: - void AfterRead(TimeStamp aDispatchDate, - ScopedArrayBufferContents& aBuffer) MOZ_OVERRIDE { - MOZ_ASSERT(!NS_IsMainThread()); - mResult->Init(aDispatchDate, TimeStamp::Now() - aDispatchDate, aBuffer.forget()); - Succeed(mResult.forget()); - } - - private: - nsRefPtr mResult; -}; - -/** - * An implementation of a Read event that provides the data - * as a JavaScript string. - */ -class DoReadToStringEvent MOZ_FINAL : public AbstractReadEvent { -public: - DoReadToStringEvent(const nsAString& aPath, - const nsACString& aEncoding, - const uint32_t aBytes, - already_AddRefed aOnSuccess, - already_AddRefed aOnError) - : AbstractReadEvent(aPath, aBytes, aOnSuccess, aOnError) - , mEncoding(aEncoding) - , mResult(new StringResult(TimeStamp::Now())) - { } - - ~DoReadToStringEvent() { - // If AbstraactReadEvent::Run() has bailed out, we may need to cleanup - // mResult, which is main-thread only data - if (!mResult) { - return; - } - nsCOMPtr main = do_GetMainThread(); - (void)NS_ProxyRelease(main, mResult); - } - -protected: - nsresult BeforeRead() MOZ_OVERRIDE { - // Obtain the decoder. We do this before reading to avoid doing - // any unnecessary I/O in case the name of the encoding is incorrect. - MOZ_ASSERT(!NS_IsMainThread()); - nsAutoCString encodingName; - if (!dom::EncodingUtils::FindEncodingForLabel(mEncoding, encodingName)) { - Fail(NS_LITERAL_CSTRING("Decode"), mResult.forget(), OS_ERROR_INVAL); - return NS_ERROR_FAILURE; - } - mDecoder = dom::EncodingUtils::DecoderForEncoding(encodingName); - if (!mDecoder) { - Fail(NS_LITERAL_CSTRING("DecoderForEncoding"), mResult.forget(), OS_ERROR_INVAL); - return NS_ERROR_FAILURE; - } - - return NS_OK; - } - - void AfterRead(TimeStamp aDispatchDate, - ScopedArrayBufferContents& aBuffer) MOZ_OVERRIDE { - MOZ_ASSERT(!NS_IsMainThread()); - - int32_t maxChars; - const char* sourceChars = reinterpret_cast(aBuffer.get().data); - int32_t sourceBytes = aBuffer.get().nbytes; - if (sourceBytes < 0) { - Fail(NS_LITERAL_CSTRING("arithmetics"), mResult.forget(), OS_ERROR_TOO_LARGE); - return; - } - - nsresult rv = mDecoder->GetMaxLength(sourceChars, sourceBytes, &maxChars); - if (NS_FAILED(rv)) { - Fail(NS_LITERAL_CSTRING("GetMaxLength"), mResult.forget(), OS_ERROR_INVAL); - return; - } - - if (maxChars < 0) { - Fail(NS_LITERAL_CSTRING("arithmetics"), mResult.forget(), OS_ERROR_TOO_LARGE); - return; - } - - nsString resultString; - resultString.SetLength(maxChars); - if (resultString.Length() != (nsString::size_type)maxChars) { - Fail(NS_LITERAL_CSTRING("allocation"), mResult.forget(), OS_ERROR_TOO_LARGE); - return; - } - - - rv = mDecoder->Convert(sourceChars, &sourceBytes, - resultString.BeginWriting(), &maxChars); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - resultString.SetLength(maxChars); - - mResult->Init(aDispatchDate, TimeStamp::Now() - aDispatchDate, resultString); - Succeed(mResult.forget()); - } - - private: - nsCString mEncoding; - nsCOMPtr mDecoder; - nsRefPtr mResult; -}; - -} // osfile - -// The OS.File service - -NS_IMPL_ISUPPORTS1(NativeOSFileInternalsService, nsINativeOSFileInternalsService); - -NS_IMETHODIMP -NativeOSFileInternalsService::Read(const nsAString& aPath, - JS::HandleValue aOptions, - nsINativeOSFileSuccessCallback *aOnSuccess, - nsINativeOSFileErrorCallback *aOnError, - JSContext* cx) -{ - // Extract options - nsCString encoding; - uint64_t bytes = UINT64_MAX; - - if (aOptions.isObject()) { - dom::NativeOSFileReadOptions dict; - if (!dict.Init(cx, aOptions)) { - return NS_ERROR_INVALID_ARG; - } - - if (dict.mEncoding.WasPassed()) { - CopyUTF16toUTF8(dict.mEncoding.Value(), encoding); - } - - if (dict.mBytes.WasPassed() && !dict.mBytes.Value().IsNull()) { - bytes = dict.mBytes.Value().Value(); - } - } - - // Prepare the off main thread event and dispatch it - nsCOMPtr onSuccess(aOnSuccess); - nsCOMPtr onError(aOnError); - - nsRefPtr event; - if (encoding.IsEmpty()) { - event = new DoReadToTypedArrayEvent(aPath, bytes, - onSuccess.forget(), - onError.forget()); - } else { - event = new DoReadToStringEvent(aPath, encoding, bytes, - onSuccess.forget(), - onError.forget()); - } - - nsresult rv; - nsCOMPtr target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv); - - if (NS_FAILED(rv)) { - return rv; - } - return target->Dispatch(event, NS_DISPATCH_NORMAL); -} - -} // namespace mozilla - diff --git a/toolkit/components/osfile/NativeOSFileInternals.h b/toolkit/components/osfile/NativeOSFileInternals.h deleted file mode 100644 index 9bc97c8f1959..000000000000 --- a/toolkit/components/osfile/NativeOSFileInternals.h +++ /dev/null @@ -1,24 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_nativeosfileinternalservice_h__ -#define mozilla_nativeosfileinternalservice_h__ - -#include "nsINativeOSFileInternals.h" -#include "mozilla/Attributes.h" - -namespace mozilla { - -class NativeOSFileInternalsService MOZ_FINAL : public nsINativeOSFileInternalsService { -public: - NS_DECL_ISUPPORTS - NS_DECL_NSINATIVEOSFILEINTERNALSSERVICE -private: - // Avoid accidental use of built-in operator= - void operator=(const NativeOSFileInternalsService& other) MOZ_DELETE; -}; - -} // namespace mozilla - -#endif // mozilla_finalizationwitnessservice_h__ diff --git a/toolkit/components/osfile/modules/moz.build b/toolkit/components/osfile/modules/moz.build index c2407cad4582..34c1b53a1647 100644 --- a/toolkit/components/osfile/modules/moz.build +++ b/toolkit/components/osfile/modules/moz.build @@ -10,7 +10,6 @@ EXTRA_JS_MODULES += [ '_PromiseWorker.jsm', 'osfile_async_front.jsm', 'osfile_async_worker.js', - 'osfile_native.jsm', 'osfile_shared_allthreads.jsm', 'osfile_shared_front.jsm', 'osfile_unix_allthreads.jsm', diff --git a/toolkit/components/osfile/modules/osfile_async_front.jsm b/toolkit/components/osfile/modules/osfile_async_front.jsm index 6bd0d86e7694..f755a98d0335 100644 --- a/toolkit/components/osfile/modules/osfile_async_front.jsm +++ b/toolkit/components/osfile/modules/osfile_async_front.jsm @@ -58,7 +58,6 @@ Cu.import("resource://gre/modules/osfile/_PromiseWorker.jsm", this); Cu.import("resource://gre/modules/Services.jsm", this); Cu.import("resource://gre/modules/TelemetryStopwatch.jsm", this); Cu.import("resource://gre/modules/AsyncShutdown.jsm", this); -let Native = Cu.import("resource://gre/modules/osfile/osfile_native.jsm", {}); /** * Constructors for decoding standard exceptions @@ -349,7 +348,7 @@ const PREF_OSFILE_LOG_REDIRECT = "toolkit.osfile.log.redirect"; * @param bool oldPref * An optional value that the DEBUG flag was set to previously. */ -function readDebugPref(prefName, oldPref = false) { +let readDebugPref = function readDebugPref(prefName, oldPref = false) { let pref = oldPref; try { pref = Services.prefs.getBoolPref(prefName); @@ -380,19 +379,6 @@ Services.prefs.addObserver(PREF_OSFILE_LOG_REDIRECT, }, false); SharedAll.Config.TEST = readDebugPref(PREF_OSFILE_LOG_REDIRECT, false); - -/** - * If |true|, use the native implementaiton of OS.File methods - * whenever possible. Otherwise, force the use of the JS version. - */ -let nativeWheneverAvailable = true; -const PREF_OSFILE_NATIVE = "toolkit.osfile.native"; -Services.prefs.addObserver(PREF_OSFILE_NATIVE, - function prefObserver(aSubject, aTopic, aData) { - nativeWheneverAvailable = readDebugPref(PREF_OSFILE_NATIVE, nativeWheneverAvailable); - }, false); - - // Update worker's DEBUG flag if it's true. // Don't start the worker just for this, though. if (SharedAll.Config.DEBUG && Scheduler.launched) { @@ -927,32 +913,12 @@ File.makeDir = function makeDir(path, options) { * read from the file. */ File.read = function read(path, bytes, options = {}) { - if (typeof bytes == "object") { - // Passing |bytes| as an argument is deprecated. - // We should now be passing it as a field of |options|. - options = bytes || {}; - } else { - options = clone(options, ["outExecutionDuration"]); - if (typeof bytes != "undefined") { - options.bytes = bytes; - } - } - - if (options.compression || !nativeWheneverAvailable) { - // We need to use the JS implementation. - let promise = Scheduler.post("read", - [Type.path.toMsg(path), bytes, options], path); - return promise.then( - function onSuccess(data) { - if (typeof data == "string") { - return data; - } - return new Uint8Array(data.buffer, data.byteOffset, data.byteLength); - }); - } - - // Otherwise, use the native implementation. - return Scheduler.push(() => Native.read(path, options)); + let promise = Scheduler.post("read", + [Type.path.toMsg(path), bytes, options], path); + return promise.then( + function onSuccess(data) { + return new Uint8Array(data.buffer, data.byteOffset, data.byteLength); + }); }; /** diff --git a/toolkit/components/osfile/modules/osfile_async_worker.js b/toolkit/components/osfile/modules/osfile_async_worker.js index 562b43e751ef..4d4ff5cf62ae 100644 --- a/toolkit/components/osfile/modules/osfile_async_worker.js +++ b/toolkit/components/osfile/modules/osfile_async_worker.js @@ -362,9 +362,6 @@ const EXCEPTION_NAMES = { }, read: function read(path, bytes, options) { let data = File.read(Type.path.fromMsg(path), bytes, options); - if (typeof data == "string") { - return data; - } return new Meta({ buffer: data.buffer, byteOffset: data.byteOffset, diff --git a/toolkit/components/osfile/modules/osfile_native.jsm b/toolkit/components/osfile/modules/osfile_native.jsm deleted file mode 100644 index 09e33feded6b..000000000000 --- a/toolkit/components/osfile/modules/osfile_native.jsm +++ /dev/null @@ -1,70 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Native (xpcom) implementation of key OS.File functions - */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["read"]; - -let {results: Cr, utils: Cu, interfaces: Ci} = Components; - -let SharedAll = Cu.import("resource://gre/modules/osfile/osfile_shared_allthreads.jsm", {}); - -let SysAll = {}; -if (SharedAll.Constants.Win) { - Cu.import("resource://gre/modules/osfile/osfile_win_allthreads.jsm", SysAll); -} else if (SharedAll.Constants.libc) { - Cu.import("resource://gre/modules/osfile/osfile_unix_allthreads.jsm", SysAll); -} else { - throw new Error("I am neither under Windows nor under a Posix system"); -} -let {Promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); -let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); - -/** - * The native service holding the implementation of the functions. - */ -XPCOMUtils.defineLazyServiceGetter(this, - "Internals", - "@mozilla.org/toolkit/osfile/native-internals;1", - "nsINativeOSFileInternalsService"); - -/** - * Native implementation of OS.File.read - * - * This implementation does not handle option |compression|. - */ -this.read = function(path, options = {}) { - // Sanity check on types of options - if ("encoding" in options && typeof options.encoding != "string") { - return Promise.reject(new TypeError("Invalid type for option encoding")); - } - if ("compression" in options && typeof options.compression != "string") { - return Promise.reject(new TypeError("Invalid type for option compression")); - } - if ("bytes" in options && typeof options.bytes != "number") { - return Promise.reject(new TypeError("Invalid type for option bytes")); - } - - let deferred = Promise.defer(); - Internals.read(path, - options, - function onSuccess(success) { - success.QueryInterface(Ci.nsINativeOSFileResult); - if ("outExecutionDuration" in options) { - options.outExecutionDuration = - success.executionDurationMS + - (options.outExecutionDuration || 0); - } - deferred.resolve(success.result); - }, - function onError(operation, oserror) { - deferred.reject(new SysAll.Error(operation, oserror, path)); - } - ); - return deferred.promise; -}; diff --git a/toolkit/components/osfile/modules/osfile_shared_front.jsm b/toolkit/components/osfile/modules/osfile_shared_front.jsm index d4325a6df0a7..5c88177ddde8 100644 --- a/toolkit/components/osfile/modules/osfile_shared_front.jsm +++ b/toolkit/components/osfile/modules/osfile_shared_front.jsm @@ -331,35 +331,14 @@ AbstractFile.read = function read(path, bytes, options = {}) { options = bytes; bytes = options.bytes || null; } - if ("encoding" in options && typeof options.encoding != "string") { - throw new TypeError("Invalid type for option encoding"); - } - if ("compression" in options && typeof options.compression != "string") { - throw new TypeError("Invalid type for option compression: " + options.compression); - } - if ("bytes" in options && typeof options.bytes != "number") { - throw new TypeError("Invalid type for option bytes"); - } let file = exports.OS.File.open(path); try { let buffer = file.read(bytes, options); - if ("compression" in options) { - if (options.compression == "lz4") { - buffer = Lz4.decompressFileContent(buffer, options); - } else { - throw OS.File.Error.invalidArgument("Compression"); - } - } - if (!("encoding" in options)) { + if ("compression" in options && options.compression == "lz4") { + return Lz4.decompressFileContent(buffer, options); + } else { return buffer; } - let decoder; - try { - decoder = new TextDecoder(options.encoding); - } catch (ex if ex instanceof TypeError) { - throw OS.File.Error.invalidArgument("Decode"); - } - return decoder.decode(buffer); } finally { file.close(); } diff --git a/toolkit/components/osfile/modules/osfile_unix_allthreads.jsm b/toolkit/components/osfile/modules/osfile_unix_allthreads.jsm index 8be2927662cf..5202c5fe080a 100644 --- a/toolkit/components/osfile/modules/osfile_unix_allthreads.jsm +++ b/toolkit/components/osfile/modules/osfile_unix_allthreads.jsm @@ -139,15 +139,6 @@ Object.defineProperty(OSError.prototype, "becauseAccessDenied", { return this.unixErrno == Const.EACCES; } }); -/** - * |true| if the error was raised because some invalid argument was passed, - * |false| otherwise. - */ -Object.defineProperty(OSError.prototype, "becauseInvalidArgument", { - get: function becauseInvalidArgument() { - return this.unixErrno == Const.EINVAL; - } -}); /** * Serialize an instance of OSError to something that can be @@ -340,10 +331,6 @@ OSError.noSuchFile = function noSuchFile(operation, path) { return new OSError(operation, Const.ENOENT, path); }; -OSError.invalidArgument = function invalidArgument(operation) { - return new OSError(operation, Const.EINVAL); -}; - let EXPORTED_SYMBOLS = [ "declareFFI", "libc", diff --git a/toolkit/components/osfile/modules/osfile_win_allthreads.jsm b/toolkit/components/osfile/modules/osfile_win_allthreads.jsm index 3ac28e8ecc32..3ef7d610a2e4 100644 --- a/toolkit/components/osfile/modules/osfile_win_allthreads.jsm +++ b/toolkit/components/osfile/modules/osfile_win_allthreads.jsm @@ -161,15 +161,6 @@ Object.defineProperty(OSError.prototype, "becauseAccessDenied", { return this.winLastError == Const.ERROR_ACCESS_DENIED; } }); -/** - * |true| if the error was raised because some invalid argument was passed, - * |false| otherwise. - */ -Object.defineProperty(OSError.prototype, "becauseInvalidArgument", { - get: function becauseInvalidArgument() { - return this.winLastError == Const.ERROR_NOT_SUPPORTED; - } -}); /** * Serialize an instance of OSError to something that can be @@ -377,10 +368,6 @@ OSError.noSuchFile = function noSuchFile(operation, path) { return new OSError(operation, Const.ERROR_FILE_NOT_FOUND, path); }; -OSError.invalidArgument = function invalidArgument(operation) { - return new OSError(operation, Const.ERROR_NOT_SUPPORTED); -}; - let EXPORTED_SYMBOLS = [ "declareFFI", "libc", diff --git a/toolkit/components/osfile/moz.build b/toolkit/components/osfile/moz.build index 65138b4dcb03..2a3c9873fe86 100644 --- a/toolkit/components/osfile/moz.build +++ b/toolkit/components/osfile/moz.build @@ -11,22 +11,7 @@ DIRS += [ MOCHITEST_CHROME_MANIFESTS += ['tests/mochi/chrome.ini'] XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini'] -SOURCES += [ - 'NativeOSFileInternals.cpp', -] - -XPIDL_MODULE = 'toolkit_osfile' - -XPIDL_SOURCES += [ - 'nsINativeOSFileInternals.idl', -] - -EXPORTS.mozilla += [ - 'NativeOSFileInternals.h', -] - EXTRA_PP_JS_MODULES += [ 'osfile.jsm', ] -FINAL_LIBRARY = 'toolkitcomps' diff --git a/toolkit/components/osfile/nsINativeOSFileInternals.idl b/toolkit/components/osfile/nsINativeOSFileInternals.idl deleted file mode 100644 index c1bf8be147f1..000000000000 --- a/toolkit/components/osfile/nsINativeOSFileInternals.idl +++ /dev/null @@ -1,93 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* vim: set ts=2 et sw=2 tw=40: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -/** - * The result of a successful asynchronous operation. - */ -[scriptable, builtinclass, uuid(08B4CF29-3D65-4E79-B522-A694C322ED07)] -interface nsINativeOSFileResult: nsISupports -{ - /** - * The actual value produced by the operation. - * - * Actual type of this value depends on the options passed to the - * operation. - */ - [implicit_jscontext] - readonly attribute jsval result; - - /** - * Delay between when the operation was requested on the main thread and - * when the operation was started off main thread. - */ - readonly attribute double dispatchDurationMS; - - /** - * Duration of the off main thread execution. - */ - readonly attribute double executionDurationMS; -}; - -/** - * A callback invoked in case of success. - */ -[scriptable, function, uuid(2C1922CA-CA1B-4099-8B61-EC23CFF49412)] -interface nsINativeOSFileSuccessCallback: nsISupports -{ - void complete(in nsINativeOSFileResult result); -}; - -/** - * A callback invoked in case of error. - */ -[scriptable, function, uuid(F612E0FC-6736-4D24-AA50-FD661B3B40B6)] -interface nsINativeOSFileErrorCallback: nsISupports -{ - /** - * @param operation The name of the failed operation. Provided to aid - * debugging only, may change without notice. - * @param OSstatus The OS status of the operation (errno under Unix, - * GetLastError under Windows). - */ - void complete(in ACString operation, in long OSstatus); -}; - -/** - * A service providing native implementations of some of the features - * of OS.File. - */ -[scriptable, builtinclass, uuid(913362AD-1526-4623-9E6B-A2EB08AFBBB9)] -interface nsINativeOSFileInternalsService: nsISupports -{ - /** - * Implementation of OS.File.read - * - * @param path The absolute path to the file to read. - * @param options An object that may contain some of the following fields - * - {number} bytes The maximal number of bytes to read. - * - {string} encoding If provided, return the result as a string, decoded - * using this encoding. Otherwise, pass the result as an ArrayBuffer. - * Invalid encodings cause onError to be called with the platform-specific - * "invalid argument" constant. - * - {string} compression Unimplemented at the moment. - * @param onSuccess The success callback. - * @param onError The error callback. - */ - [implicit_jscontext] - void read(in AString path, in jsval options, - in nsINativeOSFileSuccessCallback onSuccess, - in nsINativeOSFileErrorCallback onError); -}; - - -%{ C++ - -#define NATIVE_OSFILE_INTERNALS_SERVICE_CID {0x63A69303,0x8A64,0x45A9,{0x84, 0x8C, 0xD4, 0xE2, 0x79, 0x27, 0x94, 0xE6}} -#define NATIVE_OSFILE_INTERNALS_SERVICE_CONTRACTID "@mozilla.org/toolkit/osfile/native-internals;1" - -%} diff --git a/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js b/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js index 062d2d57e829..85519cb7de2b 100644 --- a/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js +++ b/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js @@ -155,6 +155,7 @@ let test = maketest("Main", function main(test) { yield test_debug(); yield test_info_features_detect(); yield test_read_write(); + yield test_read_write_all(); yield test_position(); yield test_iter(); yield test_exists(); @@ -302,6 +303,92 @@ let test_read_write = maketest("read_write", function read_write(test) { }); }); +/** + * Test OS.File.writeAtomic + */ +let test_read_write_all = maketest("read_write_all", function read_write_all(test) { + return Task.spawn(function() { + let pathDest = OS.Path.join(OS.Constants.Path.tmpDir, + "osfile async test read writeAtomic.tmp"); + let tmpPath = pathDest + ".tmp"; + + let test_with_options = function(options, suffix) { + return Task.spawn(function() { + let optionsBackup = JSON.parse(JSON.stringify(options)); + + // Check that read + writeAtomic performs a correct copy + let currentDir = yield OS.File.getCurrentDirectory(); + let pathSource = OS.Path.join(currentDir, EXISTING_FILE); + let contents = yield OS.File.read(pathSource); + test.ok(contents, "Obtained contents"); + let bytesWritten = yield OS.File.writeAtomic(pathDest, contents, options); + test.is(contents.byteLength, bytesWritten, "Wrote the correct number of bytes (" + suffix + ")"); + + // Check that options are not altered + test.is(Object.keys(options).length, Object.keys(optionsBackup).length, + "The number of options was not changed"); + for (let k in options) { + test.is(options[k], optionsBackup[k], "Option was not changed (" + suffix + ")"); + } + yield reference_compare_files(pathSource, pathDest, test); + + // Check that temporary file was removed or doesn't exist + test.info("Compare complete"); + test.ok(!(new FileUtils.File(tmpPath).exists()), "No temporary file at the end of the run (" + suffix + ")"); + + // Check that writeAtomic fails if noOverwrite is true and the destination + // file already exists! + let view = new Uint8Array(contents.buffer, 10, 200); + try { + let opt = JSON.parse(JSON.stringify(options)); + opt.noOverwrite = true; + yield OS.File.writeAtomic(pathDest, view, opt); + test.fail("With noOverwrite, writeAtomic should have refused to overwrite file (" + suffix + ")"); + } catch (err) { + test.info("With noOverwrite, writeAtomic correctly failed (" + suffix + ")"); + test.ok(err instanceof OS.File.Error, "writeAtomic correctly failed with a file error (" + suffix + ")"); + test.ok(err.becauseExists, "writeAtomic file error confirmed that the file already exists (" + suffix + ")"); + } + yield reference_compare_files(pathSource, pathDest, test); + test.ok(!(new FileUtils.File(tmpPath).exists()), "Temporary file was removed"); + + // Now write a subset + let START = 10; + let LENGTH = 100; + view = new Uint8Array(contents.buffer, START, LENGTH); + bytesWritten = yield OS.File.writeAtomic(pathDest, view, options); + test.is(bytesWritten, LENGTH, "Partial write wrote the correct number of bytes (" + suffix + ")"); + let array2 = yield OS.File.read(pathDest); + let view1 = new Uint8Array(contents.buffer, START, LENGTH); + test.is(view1.length, array2.length, "Re-read partial write with the correct number of bytes (" + suffix + ")"); + let decoder = new TextDecoder(); + test.is(decoder.decode(view1), decoder.decode(array2), "Comparing re-read of partial write (" + suffix + ")"); + + // Write strings, default encoding + let ARBITRARY_STRING = "aeiouyâêîôûçß•"; + yield OS.File.writeAtomic(pathDest, ARBITRARY_STRING, options); + let array = yield OS.File.read(pathDest); + let IN_STRING = decoder.decode(array); + test.is(ARBITRARY_STRING, IN_STRING, "String write + read with default encoding works (" + suffix + ")"); + + let opt16 = JSON.parse(JSON.stringify(options)); + opt16.encoding = "utf-16"; + yield OS.File.writeAtomic(pathDest, ARBITRARY_STRING, opt16); + array = yield OS.File.read(pathDest); + IN_STRING = (new TextDecoder("utf-16")).decode(array); + test.is(ARBITRARY_STRING, IN_STRING, "String write + read with utf-16 encoding works (" + suffix + ")"); + + // Cleanup. + OS.File.remove(pathDest); + }); + }; + + yield test_with_options({tmpPath: tmpPath}, "Renaming, not flushing"); + yield test_with_options({tmpPath: tmpPath, flush: true}, "Renaming, flushing"); + yield test_with_options({}, "Not renaming, not flushing"); + yield test_with_options({flush: true}, "Not renaming, flushing"); + }); +}); /** * Test file.{getPosition, setPosition} diff --git a/toolkit/components/osfile/tests/xpcshell/head.js b/toolkit/components/osfile/tests/xpcshell/head.js deleted file mode 100644 index d72f1a17ec55..000000000000 --- a/toolkit/components/osfile/tests/xpcshell/head.js +++ /dev/null @@ -1,86 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -let {utils: Cu, interfaces: Ci} = Components; - -let {OS} = Cu.import("resource://gre/modules/osfile.jsm", {}); -let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); -let {Promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); -let {Task} = Cu.import("resource://gre/modules/Task.jsm", {}); -let {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {}); -let {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {}); - -Services.prefs.setBoolPref("toolkit.osfile.log", true); - -/** - * As add_task, but execute the test both with native operations and - * without. - */ -function add_test_pair(generator) { - add_task(function*() { - do_print("Executing test " + generator.name + " with native operations"); - Services.prefs.setBoolPref("toolkit.osfile.native", true); - return Task.spawn(generator); - }); - add_task(function*() { - do_print("Executing test " + generator.name + " without native operations"); - Services.prefs.setBoolPref("toolkit.osfile.native", false); - return Task.spawn(generator); - }); -} - -/** - * Fetch asynchronously the contents of a file using xpcom. - * - * Used for comparing xpcom-based results to os.file-based results. - * - * @param {string} path The _absolute_ path to the file. - * @return {promise} - * @resolves {string} The contents of the file. - */ -function reference_fetch_file(path, test) { - do_print("Fetching file " + path); - let deferred = Promise.defer(); - let file = new FileUtils.File(path); - NetUtil.asyncFetch(file, - function(stream, status) { - if (!Components.isSuccessCode(status)) { - deferred.reject(status); - return; - } - let result, reject; - try { - result = NetUtil.readInputStreamToString(stream, stream.available()); - } catch (x) { - reject = x; - } - stream.close(); - if (reject) { - deferred.reject(reject); - } else { - deferred.resolve(result); - } - }); - return deferred.promise; -}; - -/** - * Compare asynchronously the contents two files using xpcom. - * - * Used for comparing xpcom-based results to os.file-based results. - * - * @param {string} a The _absolute_ path to the first file. - * @param {string} b The _absolute_ path to the second file. - * - * @resolves {null} - */ -function reference_compare_files(a, b, test) { - return Task.spawn(function*() { - do_print("Comparing files " + a + " and " + b); - let a_contents = yield reference_fetch_file(a, test); - let b_contents = yield reference_fetch_file(b, test); - do_check_eq(a_contents, b_contents); - }); -}; diff --git a/toolkit/components/osfile/tests/xpcshell/test_creationDate.js b/toolkit/components/osfile/tests/xpcshell/test_creationDate.js index 9c4fa1dfc491..0d93b597c86a 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_creationDate.js +++ b/toolkit/components/osfile/tests/xpcshell/test_creationDate.js @@ -1,5 +1,13 @@ "use strict"; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/osfile.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js"); + function run_test() { do_test_pending(); run_next_test(); diff --git a/toolkit/components/osfile/tests/xpcshell/test_exception.js b/toolkit/components/osfile/tests/xpcshell/test_exception.js index 976d04b95bb7..e4ed10c79e8b 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_exception.js +++ b/toolkit/components/osfile/tests/xpcshell/test_exception.js @@ -1,18 +1,16 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/** - * Test that functions throw the appropriate exceptions. - */ - "use strict"; -let EXISTING_FILE = do_get_file("xpcshell.ini").path; +Components.utils.import("resource://gre/modules/osfile.jsm"); +function run_test() { + do_test_pending(); + run_next_test(); +} -// Tests on |open| - -add_test_pair(function test_typeerror() { +add_task(function test_typeerror() { let exn; try { let fd = yield OS.File.open("/tmp", {no_such_key: 1}); @@ -24,66 +22,6 @@ add_test_pair(function test_typeerror() { do_check_true(exn.constructor.name == "TypeError"); }); -// Tests on |read| - -add_test_pair(function* test_bad_encoding() { - do_print("Testing with a wrong encoding"); - try { - yield OS.File.read(EXISTING_FILE, { encoding: "baby-speak-encoded" }); - do_throw("Should have thrown with an ex.becauseInvalidArgument"); - } catch (ex if ex.becauseInvalidArgument) { - do_print("Wrong encoding caused the correct exception"); - } - - try { - yield OS.File.read(EXISTING_FILE, { encoding: 4 }); - do_throw("Should have thrown a TypeError"); - } catch (ex if ex.constructor.name == "TypeError") { - // Note that TypeError doesn't carry across compartments - do_print("Non-string encoding caused the correct exception"); - } - }); - -add_test_pair(function* test_bad_compression() { - do_print("Testing with a non-existing compression"); - try { - yield OS.File.read(EXISTING_FILE, { compression: "mmmh-crunchy" }); - do_throw("Should have thrown with an ex.becauseInvalidArgument"); - } catch (ex if ex.becauseInvalidArgument) { - do_print("Wrong encoding caused the correct exception"); - } - - do_print("Testing with a bad type for option compression"); - try { - yield OS.File.read(EXISTING_FILE, { compression: 5 }); - do_throw("Should have thrown a TypeError"); - } catch (ex if ex.constructor.name == "TypeError") { - // Note that TypeError doesn't carry across compartments - do_print("Non-string encoding caused the correct exception"); - } +add_task(function() { + do_test_finished(); }); - -add_test_pair(function* test_bad_bytes() { - do_print("Testing with a bad type for option bytes"); - try { - yield OS.File.read(EXISTING_FILE, { bytes: "five" }); - do_throw("Should have thrown a TypeError"); - } catch (ex if ex.constructor.name == "TypeError") { - // Note that TypeError doesn't carry across compartments - do_print("Non-number bytes caused the correct exception"); - } -}); - -add_test_pair(function* read_non_existent() { - do_print("Testing with a non-existent file"); - try { - yield OS.File.read("I/do/not/exist"); - do_throw("Should have thrown with an ex.becauseNoSuchFile"); - } catch (ex if ex.becauseNoSuchFile) { - do_print("Correct exceptions"); - } -}); - -function run_test() { - run_next_test(); -} diff --git a/toolkit/components/osfile/tests/xpcshell/test_open.js b/toolkit/components/osfile/tests/xpcshell/test_open.js index 78772ad09ae1..8d5470eab3bd 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_open.js +++ b/toolkit/components/osfile/tests/xpcshell/test_open.js @@ -6,6 +6,7 @@ Components.utils.import("resource://gre/modules/osfile.jsm"); function run_test() { + do_test_pending(); run_next_test(); } @@ -68,3 +69,7 @@ add_task(function test_error_attributes () { do_check_true(err.becauseNoSuchFile); } }); + +add_task(function() { + do_test_finished(); +}); diff --git a/toolkit/components/osfile/tests/xpcshell/test_path_constants.js b/toolkit/components/osfile/tests/xpcshell/test_path_constants.js index d099931b0ccd..6c75ed239195 100644 --- a/toolkit/components/osfile/tests/xpcshell/test_path_constants.js +++ b/toolkit/components/osfile/tests/xpcshell/test_path_constants.js @@ -4,6 +4,10 @@ "use strict"; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/osfile.jsm", this); +Cu.import("resource://gre/modules/Services.jsm", this); Cu.import("resource://gre/modules/ctypes.jsm", this); Cu.import("resource://testing-common/AppData.jsm", this); diff --git a/toolkit/components/osfile/tests/xpcshell/test_read_write.js b/toolkit/components/osfile/tests/xpcshell/test_read_write.js deleted file mode 100644 index aa969d669be0..000000000000 --- a/toolkit/components/osfile/tests/xpcshell/test_read_write.js +++ /dev/null @@ -1,100 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -let {utils: Cu} = Components; - -let SHARED_PATH; - -let EXISTING_FILE = do_get_file("xpcshell.ini").path; - -add_task(function* init() { - do_get_profile(); - SHARED_PATH = OS.Path.join(OS.Constants.Path.profileDir, "test_osfile_read.tmp"); -}); - - -// Check that OS.File.read() is executed after the previous operation -add_test_pair(function* ordering() { - let string1 = "Initial state " + Math.random(); - let string2 = "After writing " + Math.random(); - yield OS.File.writeAtomic(SHARED_PATH, string1); - OS.File.writeAtomic(SHARED_PATH, string2); - let string3 = yield OS.File.read(SHARED_PATH, { encoding: "utf-8" }); - do_check_eq(string3, string2); -}); - -add_test_pair(function* read_write_all() { - let DEST_PATH = SHARED_PATH + Math.random(); - let TMP_PATH = DEST_PATH + ".tmp"; - - let test_with_options = function(options, suffix) { - return Task.spawn(function*() { - do_print("Running test read_write_all with options " + JSON.stringify(options)); - let TEST = "read_write_all " + suffix; - - let optionsBackup = JSON.parse(JSON.stringify(options)); - - // Check that read + writeAtomic performs a correct copy - let currentDir = yield OS.File.getCurrentDirectory(); - let pathSource = OS.Path.join(currentDir, EXISTING_FILE); - let contents = yield OS.File.read(pathSource); - do_check_true(!!contents); // Content is not empty - - let bytesWritten = yield OS.File.writeAtomic(DEST_PATH, contents, options); - do_check_eq(contents.byteLength, bytesWritten); // Correct number of bytes written - - // Check that options are not altered - do_check_eq(JSON.stringify(options), JSON.stringify(optionsBackup)); - yield reference_compare_files(pathSource, DEST_PATH, TEST); - - // Check that temporary file was removed or never created exist - do_check_false(new FileUtils.File(TMP_PATH).exists()); - - // Check that writeAtomic fails if noOverwrite is true and the destination - // file already exists! - let view = new Uint8Array(contents.buffer, 10, 200); - try { - let opt = JSON.parse(JSON.stringify(options)); - opt.noOverwrite = true; - yield OS.File.writeAtomic(DEST_PATH, view, opt); - do_throw("With noOverwrite, writeAtomic should have refused to overwrite file (" + suffix + ")"); - } catch (err if err instanceof OS.File.Error && err.becauseExists) { - do_print("With noOverwrite, writeAtomic correctly failed (" + suffix + ")"); - } - yield reference_compare_files(pathSource, DEST_PATH, TEST); - - // Check that temporary file was removed or never created - do_check_false(new FileUtils.File(TMP_PATH).exists()); - - // Now write a subset - let START = 10; - let LENGTH = 100; - view = new Uint8Array(contents.buffer, START, LENGTH); - bytesWritten = yield OS.File.writeAtomic(DEST_PATH, view, options); - do_check_eq(bytesWritten, LENGTH); - - let array2 = yield OS.File.read(DEST_PATH); - let view1 = new Uint8Array(contents.buffer, START, LENGTH); - do_check_eq(view1.length, array2.length); - let decoder = new TextDecoder(); - do_check_eq(decoder.decode(view1), decoder.decode(array2)); - - - // Cleanup. - yield OS.File.remove(DEST_PATH); - yield OS.File.remove(TMP_PATH); - }); - }; - - yield test_with_options({tmpPath: TMP_PATH}, "Renaming, not flushing"); - yield test_with_options({tmpPath: TMP_PATH, flush: true}, "Renaming, flushing"); - yield test_with_options({}, "Not renaming, not flushing"); - yield test_with_options({flush: true}, "Not renaming, flushing"); -}); - - -function run_test() { - run_next_test(); -} diff --git a/toolkit/components/osfile/tests/xpcshell/xpcshell.ini b/toolkit/components/osfile/tests/xpcshell/xpcshell.ini index 8fc1bc3dd476..67c88548284d 100644 --- a/toolkit/components/osfile/tests/xpcshell/xpcshell.ini +++ b/toolkit/components/osfile/tests/xpcshell/xpcshell.ini @@ -1,5 +1,5 @@ [DEFAULT] -head = head.js +head = tail = [test_available_free_space.js] @@ -25,7 +25,6 @@ tail = [test_open.js] [test_telemetry.js] [test_duration.js] -[test_read_write.js] [test_compression.js] [test_osfile_writeAtomic_backupTo_option.js] [test_osfile_error.js] diff --git a/toolkit/crashreporter/test/unit/test_crash_AsyncShutdown.js b/toolkit/crashreporter/test/unit/test_crash_AsyncShutdown.js index ffcf02314ab5..78e084a73649 100644 --- a/toolkit/crashreporter/test/unit/test_crash_AsyncShutdown.js +++ b/toolkit/crashreporter/test/unit/test_crash_AsyncShutdown.js @@ -40,7 +40,6 @@ function setup_osfile_crash_noerror() { Services.prefs.setBoolPref("toolkit.osfile.debug.failshutdown", true); Services.prefs.setIntPref("toolkit.asyncshutdown.crash_timeout", 1); - Services.prefs.setBoolPref("toolkit.osfile.native", false); OS.File.getCurrentDirectory(); Services.obs.notifyObservers(null, "profile-before-change", null); @@ -69,7 +68,6 @@ function setup_osfile_crash_exn() { Services.prefs.setBoolPref("toolkit.osfile.debug.failshutdown", true); Services.prefs.setIntPref("toolkit.asyncshutdown.crash_timeout", 1); - Services.prefs.setBoolPref("toolkit.osfile.native", false); OS.File.read("I do not exist"); Services.obs.notifyObservers(null, "profile-before-change", null); @@ -82,6 +80,7 @@ function after_osfile_crash_exn(mdump, extra) { let state = info.conditions[0].state; do_print("Keys: " + Object.keys(state).join(", ")); do_check_eq(info.phase, "profile-before-change"); + do_check_true(state.launched); do_check_false(state.shutdown); do_check_true(state.worker); do_check_true(!!state.latestSent); diff --git a/xpcom/io/nsLocalFileWin.cpp b/xpcom/io/nsLocalFileWin.cpp index 6609b5eb5483..7badaa32f8e6 100644 --- a/xpcom/io/nsLocalFileWin.cpp +++ b/xpcom/io/nsLocalFileWin.cpp @@ -592,7 +592,7 @@ struct PRFilePrivate { // copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} : // PR_Open and _PR_MD_OPEN -nsresult +static nsresult OpenFile(const nsAFlatString &name, int osflags, int mode, PRFileDesc **fd) {