summaryrefslogtreecommitdiff
path: root/xpcom/io
diff options
context:
space:
mode:
authorwolfbeast <mcwerewolf@gmail.com>2014-05-21 11:38:25 +0200
committerwolfbeast <mcwerewolf@gmail.com>2014-05-21 11:38:25 +0200
commitd25ba7d760b017b038e5aa6c0a605b4a330eb68d (patch)
tree16ec27edc7d5f83986f16236d3a36a2682a0f37e /xpcom/io
parenta942906574671868daf122284a9c4689e6924f74 (diff)
downloadpalemoon-gre-d25ba7d760b017b038e5aa6c0a605b4a330eb68d.tar.gz
Recommit working copy to repo with proper line endings.
Diffstat (limited to 'xpcom/io')
-rw-r--r--xpcom/io/Base64.cpp346
-rw-r--r--xpcom/io/Base64.h38
-rw-r--r--xpcom/io/CocoaFileUtils.h27
-rw-r--r--xpcom/io/CocoaFileUtils.mm120
-rw-r--r--xpcom/io/Makefile.in62
-rw-r--r--xpcom/io/SpecialSystemDirectory.cpp958
-rw-r--r--xpcom/io/SpecialSystemDirectory.h108
-rw-r--r--xpcom/io/moz.build122
-rw-r--r--xpcom/io/nsAnonymousTemporaryFile.cpp240
-rw-r--r--xpcom/io/nsAnonymousTemporaryFile.h31
-rw-r--r--xpcom/io/nsAppDirectoryServiceDefs.h93
-rw-r--r--xpcom/io/nsAppFileLocationProvider.cpp578
-rw-r--r--xpcom/io/nsAppFileLocationProvider.h47
-rw-r--r--xpcom/io/nsBinaryStream.cpp823
-rw-r--r--xpcom/io/nsBinaryStream.h90
-rw-r--r--xpcom/io/nsDirectoryService.cpp933
-rw-r--r--xpcom/io/nsDirectoryService.h65
-rw-r--r--xpcom/io/nsDirectoryServiceAtomList.h95
-rw-r--r--xpcom/io/nsDirectoryServiceDefs.h158
-rw-r--r--xpcom/io/nsDirectoryServiceUtils.h28
-rw-r--r--xpcom/io/nsEscape.cpp497
-rw-r--r--xpcom/io/nsEscape.h180
-rw-r--r--xpcom/io/nsIAsyncInputStream.idl104
-rw-r--r--xpcom/io/nsIAsyncOutputStream.idl104
-rw-r--r--xpcom/io/nsIBinaryInputStream.idl120
-rw-r--r--xpcom/io/nsIBinaryOutputStream.idl91
-rw-r--r--xpcom/io/nsIConverterInputStream.idl40
-rw-r--r--xpcom/io/nsIConverterOutputStream.idl44
-rw-r--r--xpcom/io/nsIDirectoryEnumerator.idl34
-rw-r--r--xpcom/io/nsIDirectoryService.idl101
-rw-r--r--xpcom/io/nsIFile.idl465
-rw-r--r--xpcom/io/nsIIOUtil.idl34
-rw-r--r--xpcom/io/nsIInputStream.idl147
-rw-r--r--xpcom/io/nsIInputStreamTee.idl42
-rw-r--r--xpcom/io/nsILineInputStream.idl28
-rw-r--r--xpcom/io/nsILocalFile.idl17
-rw-r--r--xpcom/io/nsILocalFileMac.idl179
-rw-r--r--xpcom/io/nsILocalFileOS2.idl57
-rw-r--r--xpcom/io/nsILocalFileWin.idl98
-rw-r--r--xpcom/io/nsIMultiplexInputStream.idl55
-rw-r--r--xpcom/io/nsIOUtil.cpp27
-rw-r--r--xpcom/io/nsIOUtil.h24
-rw-r--r--xpcom/io/nsIObjectInputStream.idl54
-rw-r--r--xpcom/io/nsIObjectOutputStream.idl98
-rw-r--r--xpcom/io/nsIOutputStream.idl143
-rw-r--r--xpcom/io/nsIPipe.idl178
-rw-r--r--xpcom/io/nsISafeOutputStream.idl39
-rw-r--r--xpcom/io/nsIScriptableBase64Encoder.idl32
-rw-r--r--xpcom/io/nsIScriptableInputStream.idl67
-rw-r--r--xpcom/io/nsISeekableStream.idl74
-rw-r--r--xpcom/io/nsISimpleUnicharStreamFactory.idl26
-rw-r--r--xpcom/io/nsIStorageStream.idl74
-rw-r--r--xpcom/io/nsIStreamBufferAccess.idl89
-rw-r--r--xpcom/io/nsIStringStream.idl57
-rw-r--r--xpcom/io/nsIUnicharInputStream.idl95
-rw-r--r--xpcom/io/nsIUnicharLineInputStream.idl26
-rw-r--r--xpcom/io/nsIUnicharOutputStream.idl47
-rw-r--r--xpcom/io/nsInputStreamTee.cpp347
-rw-r--r--xpcom/io/nsLinebreakConverter.cpp465
-rw-r--r--xpcom/io/nsLinebreakConverter.h122
-rw-r--r--xpcom/io/nsLocalFile.h92
-rw-r--r--xpcom/io/nsLocalFileCommon.cpp290
-rw-r--r--xpcom/io/nsLocalFileOS2.cpp2572
-rw-r--r--xpcom/io/nsLocalFileOS2.h91
-rw-r--r--xpcom/io/nsLocalFileUnix.cpp2478
-rw-r--r--xpcom/io/nsLocalFileUnix.h131
-rw-r--r--xpcom/io/nsLocalFileWin.cpp3455
-rw-r--r--xpcom/io/nsLocalFileWin.h102
-rw-r--r--xpcom/io/nsMultiplexInputStream.cpp683
-rw-r--r--xpcom/io/nsMultiplexInputStream.h29
-rw-r--r--xpcom/io/nsNativeCharsetUtils.cpp1136
-rw-r--r--xpcom/io/nsNativeCharsetUtils.h60
-rw-r--r--xpcom/io/nsPipe.h24
-rw-r--r--xpcom/io/nsPipe3.cpp1308
-rw-r--r--xpcom/io/nsScriptableBase64Encoder.cpp31
-rw-r--r--xpcom/io/nsScriptableBase64Encoder.h27
-rw-r--r--xpcom/io/nsScriptableInputStream.cpp108
-rw-r--r--xpcom/io/nsScriptableInputStream.h39
-rw-r--r--xpcom/io/nsSegmentedBuffer.cpp175
-rw-r--r--xpcom/io/nsSegmentedBuffer.h93
-rw-r--r--xpcom/io/nsStorageStream.cpp537
-rw-r--r--xpcom/io/nsStorageStream.h65
-rw-r--r--xpcom/io/nsStreamUtils.cpp775
-rw-r--r--xpcom/io/nsStreamUtils.h235
-rw-r--r--xpcom/io/nsStringStream.cpp400
-rw-r--r--xpcom/io/nsStringStream.h74
-rw-r--r--xpcom/io/nsUnicharInputStream.cpp434
-rw-r--r--xpcom/io/nsUnicharInputStream.h29
-rw-r--r--xpcom/io/nsWildCard.cpp439
-rw-r--r--xpcom/io/nsWildCard.h64
90 files changed, 25459 insertions, 0 deletions
diff --git a/xpcom/io/Base64.cpp b/xpcom/io/Base64.cpp
new file mode 100644
index 000000000..5d8dfc531
--- /dev/null
+++ b/xpcom/io/Base64.cpp
@@ -0,0 +1,346 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "Base64.h"
+
+#include "nsIInputStream.h"
+#include "nsStringGlue.h"
+
+#include "plbase64.h"
+
+namespace {
+
+// BEGIN base64 encode code copied and modified from NSPR
+const unsigned char *base = (unsigned char *)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+template <typename T>
+static void
+Encode3to4(const unsigned char *src, T *dest)
+{
+ uint32_t b32 = (uint32_t)0;
+ int i, j = 18;
+
+ for( i = 0; i < 3; i++ )
+ {
+ b32 <<= 8;
+ b32 |= (uint32_t)src[i];
+ }
+
+ for( i = 0; i < 4; i++ )
+ {
+ dest[i] = base[ (uint32_t)((b32>>j) & 0x3F) ];
+ j -= 6;
+ }
+}
+
+template <typename T>
+static void
+Encode2to4(const unsigned char *src, T *dest)
+{
+ dest[0] = base[ (uint32_t)((src[0]>>2) & 0x3F) ];
+ dest[1] = base[ (uint32_t)(((src[0] & 0x03) << 4) | ((src[1] >> 4) & 0x0F)) ];
+ dest[2] = base[ (uint32_t)((src[1] & 0x0F) << 2) ];
+ dest[3] = (unsigned char)'=';
+}
+
+template <typename T>
+static void
+Encode1to4(const unsigned char *src, T *dest)
+{
+ dest[0] = base[ (uint32_t)((src[0]>>2) & 0x3F) ];
+ dest[1] = base[ (uint32_t)((src[0] & 0x03) << 4) ];
+ dest[2] = (unsigned char)'=';
+ dest[3] = (unsigned char)'=';
+}
+
+template <typename T>
+static void
+Encode(const unsigned char *src, uint32_t srclen, T *dest)
+{
+ while( srclen >= 3 )
+ {
+ Encode3to4(src, dest);
+ src += 3;
+ dest += 4;
+ srclen -= 3;
+ }
+
+ switch( srclen )
+ {
+ case 2:
+ Encode2to4(src, dest);
+ break;
+ case 1:
+ Encode1to4(src, dest);
+ break;
+ case 0:
+ break;
+ default:
+ NS_NOTREACHED("coding error");
+ }
+}
+
+// END base64 encode code copied and modified from NSPR.
+
+template <typename T>
+struct EncodeInputStream_State {
+ unsigned char c[3];
+ uint8_t charsOnStack;
+ typename T::char_type* buffer;
+};
+
+template <typename T>
+NS_METHOD
+EncodeInputStream_Encoder(nsIInputStream *aStream,
+ void *aClosure,
+ const char *aFromSegment,
+ uint32_t aToOffset,
+ uint32_t aCount,
+ uint32_t *aWriteCount)
+{
+ NS_ASSERTION(aCount > 0, "Er, what?");
+
+ EncodeInputStream_State<T>* state =
+ static_cast<EncodeInputStream_State<T>*>(aClosure);
+
+ // If we have any data left from last time, encode it now.
+ uint32_t countRemaining = aCount;
+ const unsigned char *src = (const unsigned char*)aFromSegment;
+ if (state->charsOnStack) {
+ unsigned char firstSet[4];
+ if (state->charsOnStack == 1) {
+ firstSet[0] = state->c[0];
+ firstSet[1] = src[0];
+ firstSet[2] = (countRemaining > 1) ? src[1] : '\0';
+ firstSet[3] = '\0';
+ } else /* state->charsOnStack == 2 */ {
+ firstSet[0] = state->c[0];
+ firstSet[1] = state->c[1];
+ firstSet[2] = src[0];
+ firstSet[3] = '\0';
+ }
+ Encode(firstSet, 3, state->buffer);
+ state->buffer += 4;
+ countRemaining -= (3 - state->charsOnStack);
+ src += (3 - state->charsOnStack);
+ state->charsOnStack = 0;
+ }
+
+ // Encode the bulk of the
+ uint32_t encodeLength = countRemaining - countRemaining % 3;
+ NS_ABORT_IF_FALSE(encodeLength % 3 == 0,
+ "Should have an exact number of triplets!");
+ Encode(src, encodeLength, state->buffer);
+ state->buffer += (encodeLength / 3) * 4;
+ src += encodeLength;
+ countRemaining -= encodeLength;
+
+ // We must consume all data, so if there's some data left stash it
+ *aWriteCount = aCount;
+
+ if (countRemaining) {
+ // We should never have a full triplet left at this point.
+ NS_ABORT_IF_FALSE(countRemaining < 3, "We should have encoded more!");
+ state->c[0] = src[0];
+ state->c[1] = (countRemaining == 2) ? src[1] : '\0';
+ state->charsOnStack = countRemaining;
+ }
+
+ return NS_OK;
+}
+
+template <typename T>
+nsresult
+EncodeInputStream(nsIInputStream *aInputStream,
+ T &aDest,
+ uint32_t aCount,
+ uint32_t aOffset)
+{
+ nsresult rv;
+ uint64_t count64 = aCount;
+
+ if (!aCount) {
+ rv = aInputStream->Available(&count64);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // if count64 is over 4GB, it will be failed at the below condition,
+ // then will return NS_ERROR_OUT_OF_MEMORY
+ aCount = (uint32_t)count64;
+ }
+
+ uint64_t countlong =
+ (count64 + 2) / 3 * 4; // +2 due to integer math.
+ if (countlong + aOffset > UINT32_MAX)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ uint32_t count = uint32_t(countlong);
+
+ aDest.SetLength(count + aOffset);
+ if (aDest.Length() != count + aOffset)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ EncodeInputStream_State<T> state;
+ state.charsOnStack = 0;
+ state.c[2] = '\0';
+ state.buffer = aOffset + aDest.BeginWriting();
+
+ while (1) {
+ uint32_t read = 0;
+
+ rv = aInputStream->ReadSegments(&EncodeInputStream_Encoder<T>,
+ (void*)&state,
+ aCount,
+ &read);
+ if (NS_FAILED(rv)) {
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK)
+ NS_RUNTIMEABORT("Not implemented for async streams!");
+ if (rv == NS_ERROR_NOT_IMPLEMENTED)
+ NS_RUNTIMEABORT("Requires a stream that implements ReadSegments!");
+ return rv;
+ }
+
+ if (!read)
+ break;
+ }
+
+ // Finish encoding if anything is left
+ if (state.charsOnStack)
+ Encode(state.c, state.charsOnStack, state.buffer);
+
+ *aDest.EndWriting() = '\0';
+
+ return NS_OK;
+}
+
+} // namespace (anonymous)
+
+namespace mozilla {
+
+nsresult
+Base64EncodeInputStream(nsIInputStream *aInputStream,
+ nsACString &aDest,
+ uint32_t aCount,
+ uint32_t aOffset)
+{
+ return EncodeInputStream<nsACString>(aInputStream, aDest, aCount, aOffset);
+}
+
+nsresult
+Base64EncodeInputStream(nsIInputStream *aInputStream,
+ nsAString &aDest,
+ uint32_t aCount,
+ uint32_t aOffset)
+{
+ return EncodeInputStream<nsAString>(aInputStream, aDest, aCount, aOffset);
+}
+
+nsresult
+Base64Encode(const nsACString &aBinaryData, nsACString &aString)
+{
+ // Check for overflow.
+ if (aBinaryData.Length() > (UINT32_MAX / 4) * 3) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Don't ask PR_Base64Encode to encode empty strings
+ if (aBinaryData.IsEmpty()) {
+ aString.Truncate();
+ return NS_OK;
+ }
+
+ uint32_t stringLen = ((aBinaryData.Length() + 2) / 3) * 4;
+
+ char *buffer;
+
+ // Add one byte for null termination.
+ if (aString.SetCapacity(stringLen + 1, fallible_t()) &&
+ (buffer = aString.BeginWriting()) &&
+ PL_Base64Encode(aBinaryData.BeginReading(), aBinaryData.Length(), buffer)) {
+ // PL_Base64Encode doesn't null terminate the buffer for us when we pass
+ // the buffer in. Do that manually.
+ buffer[stringLen] = '\0';
+
+ aString.SetLength(stringLen);
+ return NS_OK;
+ }
+
+ aString.Truncate();
+ return NS_ERROR_INVALID_ARG;
+}
+
+nsresult
+Base64Encode(const nsAString &aString, nsAString &aBinaryData)
+{
+ NS_LossyConvertUTF16toASCII string(aString);
+ nsAutoCString binaryData;
+
+ nsresult rv = Base64Encode(string, binaryData);
+ if (NS_SUCCEEDED(rv)) {
+ CopyASCIItoUTF16(binaryData, aBinaryData);
+ } else {
+ aBinaryData.Truncate();
+ }
+
+ return rv;
+}
+
+nsresult
+Base64Decode(const nsACString &aString, nsACString &aBinaryData)
+{
+ // Check for overflow.
+ if (aString.Length() > UINT32_MAX / 3) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Don't ask PR_Base64Decode to decode the empty string
+ if (aString.IsEmpty()) {
+ aBinaryData.Truncate();
+ return NS_OK;
+ }
+
+ uint32_t binaryDataLen = ((aString.Length() * 3) / 4);
+
+ char *buffer;
+
+ // Add one byte for null termination.
+ if (aBinaryData.SetCapacity(binaryDataLen + 1, fallible_t()) &&
+ (buffer = aBinaryData.BeginWriting()) &&
+ PL_Base64Decode(aString.BeginReading(), aString.Length(), buffer)) {
+ // PL_Base64Decode doesn't null terminate the buffer for us when we pass
+ // the buffer in. Do that manually, taking into account the number of '='
+ // characters we were passed.
+ if (!aString.IsEmpty() && aString[aString.Length() - 1] == '=') {
+ if (aString.Length() > 1 && aString[aString.Length() - 2] == '=') {
+ binaryDataLen -= 2;
+ } else {
+ binaryDataLen -= 1;
+ }
+ }
+ buffer[binaryDataLen] = '\0';
+
+ aBinaryData.SetLength(binaryDataLen);
+ return NS_OK;
+ }
+
+ aBinaryData.Truncate();
+ return NS_ERROR_INVALID_ARG;
+}
+
+nsresult
+Base64Decode(const nsAString &aBinaryData, nsAString &aString)
+{
+ NS_LossyConvertUTF16toASCII binaryData(aBinaryData);
+ nsAutoCString string;
+
+ nsresult rv = Base64Decode(binaryData, string);
+ if (NS_SUCCEEDED(rv)) {
+ CopyASCIItoUTF16(string, aString);
+ } else {
+ aString.Truncate();
+ }
+
+ return rv;
+}
+
+} // namespace mozilla
diff --git a/xpcom/io/Base64.h b/xpcom/io/Base64.h
new file mode 100644
index 000000000..39fe0766a
--- /dev/null
+++ b/xpcom/io/Base64.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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_Base64_h__
+#define mozilla_Base64_h__
+
+#include "nsString.h"
+
+class nsIInputStream;
+
+namespace mozilla {
+
+nsresult
+Base64EncodeInputStream(nsIInputStream *aInputStream,
+ nsACString &aDest,
+ uint32_t aCount,
+ uint32_t aOffset = 0);
+nsresult
+Base64EncodeInputStream(nsIInputStream *aInputStream,
+ nsAString &aDest,
+ uint32_t aCount,
+ uint32_t aOffset = 0);
+
+nsresult
+Base64Encode(const nsACString &aString, nsACString &aBinary);
+nsresult
+Base64Encode(const nsAString &aString, nsAString &aBinaryData);
+
+nsresult
+Base64Decode(const nsACString &aBinaryData, nsACString &aString);
+nsresult
+Base64Decode(const nsAString &aBinaryData, nsAString &aString);
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/io/CocoaFileUtils.h b/xpcom/io/CocoaFileUtils.h
new file mode 100644
index 000000000..e007b5875
--- /dev/null
+++ b/xpcom/io/CocoaFileUtils.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:set ts=2 sts=2 sw=2 et cin:
+/* 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/. */
+
+// This namespace contains methods with Obj-C/Cocoa implementations. The header
+// is C/C++ for inclusion in C/C++-only files.
+
+#ifndef CocoaFileUtils_h_
+#define CocoaFileUtils_h_
+
+#include "nscore.h"
+#include <CoreFoundation/CoreFoundation.h>
+
+namespace CocoaFileUtils {
+
+nsresult RevealFileInFinder(CFURLRef url);
+nsresult OpenURL(CFURLRef url);
+nsresult GetFileCreatorCode(CFURLRef url, OSType *creatorCode);
+nsresult SetFileCreatorCode(CFURLRef url, OSType creatorCode);
+nsresult GetFileTypeCode(CFURLRef url, OSType *typeCode);
+nsresult SetFileTypeCode(CFURLRef url, OSType typeCode);
+
+} // namespace CocoaFileUtils
+
+#endif
diff --git a/xpcom/io/CocoaFileUtils.mm b/xpcom/io/CocoaFileUtils.mm
new file mode 100644
index 000000000..793183655
--- /dev/null
+++ b/xpcom/io/CocoaFileUtils.mm
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:set ts=2 sts=2 sw=2 et cin:
+/* 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 "CocoaFileUtils.h"
+#include <Cocoa/Cocoa.h>
+#include "nsObjCExceptions.h"
+#include "nsDebug.h"
+
+namespace CocoaFileUtils {
+
+nsresult RevealFileInFinder(CFURLRef url)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ NS_ENSURE_ARG_POINTER(url);
+
+ NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
+ BOOL success = [[NSWorkspace sharedWorkspace] selectFile:[(NSURL*)url path] inFileViewerRootedAtPath:@""];
+ [ap release];
+
+ return (success ? NS_OK : NS_ERROR_FAILURE);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsresult OpenURL(CFURLRef url)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ NS_ENSURE_ARG_POINTER(url);
+
+ NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
+ BOOL success = [[NSWorkspace sharedWorkspace] openURL:(NSURL*)url];
+ [ap release];
+
+ return (success ? NS_OK : NS_ERROR_FAILURE);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsresult GetFileCreatorCode(CFURLRef url, OSType *creatorCode)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ NS_ENSURE_ARG_POINTER(url);
+ NS_ENSURE_ARG_POINTER(creatorCode);
+
+ nsresult rv = NS_ERROR_FAILURE;
+
+ NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
+ NSDictionary* dict = [[NSFileManager defaultManager] fileAttributesAtPath:[(NSURL*)url path] traverseLink:YES];
+ NSNumber* creatorNum = (NSNumber*)[dict objectForKey:NSFileHFSCreatorCode];
+ if (creatorNum) {
+ *creatorCode = [creatorNum unsignedLongValue];
+ rv = NS_OK;
+ }
+ [ap release];
+
+ return rv;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsresult SetFileCreatorCode(CFURLRef url, OSType creatorCode)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ NS_ENSURE_ARG_POINTER(url);
+
+ NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
+ NSDictionary* dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:creatorCode] forKey:NSFileHFSCreatorCode];
+ BOOL success = [[NSFileManager defaultManager] setAttributes:dict ofItemAtPath:[(NSURL*)url path] error:nil];
+ [ap release];
+ return (success ? NS_OK : NS_ERROR_FAILURE);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsresult GetFileTypeCode(CFURLRef url, OSType *typeCode)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ NS_ENSURE_ARG_POINTER(url);
+ NS_ENSURE_ARG_POINTER(typeCode);
+
+ nsresult rv = NS_ERROR_FAILURE;
+
+ NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
+ NSDictionary* dict = [[NSFileManager defaultManager] fileAttributesAtPath:[(NSURL*)url path] traverseLink:YES];
+ NSNumber* typeNum = (NSNumber*)[dict objectForKey:NSFileHFSTypeCode];
+ if (typeNum) {
+ *typeCode = [typeNum unsignedLongValue];
+ rv = NS_OK;
+ }
+ [ap release];
+
+ return rv;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsresult SetFileTypeCode(CFURLRef url, OSType typeCode)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ NS_ENSURE_ARG_POINTER(url);
+
+ NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
+ NSDictionary* dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:typeCode] forKey:NSFileHFSTypeCode];
+ BOOL success = [[NSFileManager defaultManager] setAttributes:dict ofItemAtPath:[(NSURL*)url path] error:nil];
+ [ap release];
+ return (success ? NS_OK : NS_ERROR_FAILURE);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+} // namespace CocoaFileUtils
diff --git a/xpcom/io/Makefile.in b/xpcom/io/Makefile.in
new file mode 100644
index 000000000..e9a965bdd
--- /dev/null
+++ b/xpcom/io/Makefile.in
@@ -0,0 +1,62 @@
+#
+# 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/.
+
+DEPTH = @DEPTH@
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+LIBRARY_NAME = xpcomio_s
+MSVC_ENABLE_PGO := 1
+MOZILLA_INTERNAL_API = 1
+LIBXUL_LIBRARY = 1
+
+# work around bug 408258
+ifdef GNU_CC
+ifneq ($(OS_ARCH), Darwin)
+MODULE_OPTIMIZE_FLAGS = $(MOZ_OPTIMIZE_FLAGS) -fno-strict-aliasing
+endif
+endif
+
+ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
+DISABLED_CMMSRCS += \
+ CocoaFileUtils.mm \
+ $(NULL)
+endif
+
+SDK_HEADERS = \
+ nsDirectoryServiceDefs.h \
+ nsDirectoryServiceUtils.h \
+ $(NULL)
+
+# we don't want the shared lib, but we want to force the creation of a static lib.
+FORCE_STATIC_LIB = 1
+
+
+include $(topsrcdir)/config/rules.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
+
+DEFINES += -D_IMPL_NS_COM
+
+ifeq ($(OS_ARCH),Linux)
+ifneq (,$(findstring lib64,$(libdir)))
+DEFINES += -DHAVE_USR_LIB64_DIR
+endif
+endif
+
+LOCAL_INCLUDES += -I..
+
+ifeq ($(MOZ_PLATFORM_MAEMO),5)
+CFLAGS += $(MOZ_DBUS_CFLAGS)
+CXXFLAGS += $(MOZ_DBUS_CFLAGS)
+endif
+
+ifdef MOZ_PLATFORM_MAEMO
+CFLAGS += $(MOZ_PLATFORM_MAEMO_CFLAGS) $(MOZ_QT_CFLAGS)
+CXXFLAGS += $(MOZ_PLATFORM_MAEMO_CFLAGS) $(MOZ_QT_CFLAGS)
+endif
+
diff --git a/xpcom/io/SpecialSystemDirectory.cpp b/xpcom/io/SpecialSystemDirectory.cpp
new file mode 100644
index 000000000..38834a69f
--- /dev/null
+++ b/xpcom/io/SpecialSystemDirectory.cpp
@@ -0,0 +1,958 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "SpecialSystemDirectory.h"
+#include "nsString.h"
+#include "nsDependentString.h"
+#include "nsAutoPtr.h"
+
+#if defined(XP_WIN)
+
+#include <windows.h>
+#include <shlobj.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <direct.h>
+#include <shlobj.h>
+#include <knownfolders.h>
+#include <guiddef.h>
+
+#elif defined(XP_OS2)
+
+#define MAX_PATH _MAX_PATH
+#define INCL_WINWORKPLACE
+#define INCL_DOSMISC
+#define INCL_DOSMODULEMGR
+#define INCL_DOSPROCESS
+#define INCL_WINSHELLDATA
+#include <os2.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "prenv.h"
+
+#elif defined(XP_UNIX)
+
+#include <limits.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include "prenv.h"
+
+#endif
+
+#if defined(VMS)
+#include <unixlib.h>
+#endif
+
+#ifndef MAXPATHLEN
+#ifdef PATH_MAX
+#define MAXPATHLEN PATH_MAX
+#elif defined(MAX_PATH)
+#define MAXPATHLEN MAX_PATH
+#elif defined(_MAX_PATH)
+#define MAXPATHLEN _MAX_PATH
+#elif defined(CCHMAXPATH)
+#define MAXPATHLEN CCHMAXPATH
+#else
+#define MAXPATHLEN 1024
+#endif
+#endif
+
+#ifdef XP_WIN
+typedef HRESULT (WINAPI* nsGetKnownFolderPath)(GUID& rfid,
+ DWORD dwFlags,
+ HANDLE hToken,
+ PWSTR *ppszPath);
+
+static nsGetKnownFolderPath gGetKnownFolderPath = NULL;
+#endif
+
+void StartupSpecialSystemDirectory()
+{
+#if defined (XP_WIN)
+ // SHGetKnownFolderPath is only available on Windows Vista
+ // so that we need to use GetProcAddress to get the pointer.
+ HMODULE hShell32DLLInst = GetModuleHandleW(L"shell32.dll");
+ if(hShell32DLLInst)
+ {
+ gGetKnownFolderPath = (nsGetKnownFolderPath)
+ GetProcAddress(hShell32DLLInst, "SHGetKnownFolderPath");
+ }
+#endif
+}
+
+#if defined (XP_WIN)
+
+static nsresult GetKnownFolder(GUID* guid, nsIFile** aFile)
+{
+ if (!guid || !gGetKnownFolderPath)
+ return NS_ERROR_FAILURE;
+
+ PWSTR path = NULL;
+ gGetKnownFolderPath(*guid, 0, NULL, &path);
+
+ if (!path)
+ return NS_ERROR_FAILURE;
+
+ nsresult rv = NS_NewLocalFile(nsDependentString(path),
+ true,
+ aFile);
+
+ CoTaskMemFree(path);
+ return rv;
+}
+
+static nsresult
+GetWindowsFolder(int folder, nsIFile** aFile)
+{
+ WCHAR path_orig[MAX_PATH + 3];
+ WCHAR *path = path_orig+1;
+ HRESULT result = SHGetSpecialFolderPathW(NULL, path, folder, true);
+
+ if (!SUCCEEDED(result))
+ return NS_ERROR_FAILURE;
+
+ // Append the trailing slash
+ int len = wcslen(path);
+ if (len > 1 && path[len - 1] != L'\\')
+ {
+ path[len] = L'\\';
+ path[++len] = L'\0';
+ }
+
+ return NS_NewLocalFile(nsDependentString(path, len), true, aFile);
+}
+
+__inline HRESULT
+SHLoadLibraryFromKnownFolder(REFKNOWNFOLDERID aFolderId, DWORD aMode,
+ REFIID riid, void **ppv)
+{
+ *ppv = NULL;
+ IShellLibrary *plib;
+ HRESULT hr = CoCreateInstance(CLSID_ShellLibrary, NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_PPV_ARGS(&plib));
+ if (SUCCEEDED(hr)) {
+ hr = plib->LoadLibraryFromKnownFolder(aFolderId, aMode);
+ if (SUCCEEDED(hr)) {
+ hr = plib->QueryInterface(riid, ppv);
+ }
+ plib->Release();
+ }
+ return hr;
+}
+
+/*
+ * Check to see if we're on Win7 and up, and if so, returns the default
+ * save-to location for the Windows Library passed in through aFolderId.
+ * Otherwise falls back on pre-win7 GetWindowsFolder.
+ */
+static nsresult
+GetLibrarySaveToPath(int aFallbackFolderId, REFKNOWNFOLDERID aFolderId,
+ nsIFile** aFile)
+{
+ // Skip off checking for library support if the os is Vista or lower.
+ DWORD dwVersion = GetVersion();
+ if ((DWORD)(LOBYTE(LOWORD(dwVersion))) < 6 ||
+ ((DWORD)(LOBYTE(LOWORD(dwVersion))) == 6 &&
+ (DWORD)(HIBYTE(LOWORD(dwVersion))) == 0))
+ return GetWindowsFolder(aFallbackFolderId, aFile);
+
+ nsRefPtr<IShellLibrary> shellLib;
+ nsRefPtr<IShellItem> savePath;
+ HRESULT hr =
+ SHLoadLibraryFromKnownFolder(aFolderId, STGM_READ,
+ IID_IShellLibrary, getter_AddRefs(shellLib));
+
+ if (shellLib &&
+ SUCCEEDED(shellLib->GetDefaultSaveFolder(DSFT_DETECT, IID_IShellItem,
+ getter_AddRefs(savePath)))) {
+ PRUnichar* str = nullptr;
+ if (SUCCEEDED(savePath->GetDisplayName(SIGDN_FILESYSPATH, &str))) {
+ nsAutoString path;
+ path.Assign(str);
+ path.AppendLiteral("\\");
+ nsresult rv =
+ NS_NewLocalFile(path, false, aFile);
+ CoTaskMemFree(str);
+ return rv;
+ }
+ }
+
+ return GetWindowsFolder(aFallbackFolderId, aFile);
+}
+
+/**
+ * Provides a fallback for getting the path to APPDATA or LOCALAPPDATA by
+ * querying the registry when the call to SHGetSpecialFolderPathW is unable to
+ * provide these paths (Bug 513958).
+ */
+static nsresult GetRegWindowsAppDataFolder(bool aLocal, nsIFile** aFile)
+{
+ HKEY key;
+ NS_NAMED_LITERAL_STRING(keyName,
+ "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
+ DWORD res = ::RegOpenKeyExW(HKEY_CURRENT_USER, keyName.get(), 0, KEY_READ,
+ &key);
+ if (res != ERROR_SUCCESS)
+ return NS_ERROR_FAILURE;
+
+ WCHAR path[MAX_PATH + 2];
+ DWORD type, size;
+ res = RegQueryValueExW(key, (aLocal ? L"Local AppData" : L"AppData"), NULL,
+ &type, (LPBYTE)&path, &size);
+ ::RegCloseKey(key);
+ // The call to RegQueryValueExW must succeed, the type must be REG_SZ, the
+ // buffer size must not equal 0, and the buffer size be a multiple of 2.
+ if (res != ERROR_SUCCESS || type != REG_SZ || size == 0 || size % 2 != 0)
+ return NS_ERROR_FAILURE;
+
+ // Append the trailing slash
+ int len = wcslen(path);
+ if (len > 1 && path[len - 1] != L'\\')
+ {
+ path[len] = L'\\';
+ path[++len] = L'\0';
+ }
+
+ return NS_NewLocalFile(nsDependentString(path, len), true, aFile);
+}
+
+#endif // XP_WIN
+
+#if defined(XP_UNIX)
+static nsresult
+GetUnixHomeDir(nsIFile** aFile)
+{
+#ifdef VMS
+ char *pHome;
+ pHome = getenv("HOME");
+ if (*pHome == '/') {
+ return NS_NewNativeLocalFile(nsDependentCString(pHome),
+ true,
+ aFile);
+ } else {
+ return NS_NewNativeLocalFile(nsDependentCString(decc$translate_vms(pHome)),
+ true,
+ aFile);
+ }
+#elif defined(ANDROID)
+ // XXX no home dir on android; maybe we should return the sdcard if present?
+ return NS_ERROR_FAILURE;
+#else
+ return NS_NewNativeLocalFile(nsDependentCString(PR_GetEnv("HOME")),
+ true, aFile);
+#endif
+}
+
+/*
+ The following license applies to the xdg_user_dir_lookup function:
+
+ Copyright (c) 2007 Red Hat, Inc.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+
+static char *
+xdg_user_dir_lookup (const char *type)
+{
+ FILE *file;
+ char *home_dir, *config_home, *config_file;
+ char buffer[512];
+ char *user_dir;
+ char *p, *d;
+ int len;
+ int relative;
+
+ home_dir = getenv ("HOME");
+
+ if (home_dir == NULL)
+ goto error;
+
+ config_home = getenv ("XDG_CONFIG_HOME");
+ if (config_home == NULL || config_home[0] == 0)
+ {
+ config_file = (char*) malloc (strlen (home_dir) + strlen ("/.config/user-dirs.dirs") + 1);
+ if (config_file == NULL)
+ goto error;
+
+ strcpy (config_file, home_dir);
+ strcat (config_file, "/.config/user-dirs.dirs");
+ }
+ else
+ {
+ config_file = (char*) malloc (strlen (config_home) + strlen ("/user-dirs.dirs") + 1);
+ if (config_file == NULL)
+ goto error;
+
+ strcpy (config_file, config_home);
+ strcat (config_file, "/user-dirs.dirs");
+ }
+
+ file = fopen (config_file, "r");
+ free (config_file);
+ if (file == NULL)
+ goto error;
+
+ user_dir = NULL;
+ while (fgets (buffer, sizeof (buffer), file))
+ {
+ /* Remove newline at end */
+ len = strlen (buffer);
+ if (len > 0 && buffer[len-1] == '\n')
+ buffer[len-1] = 0;
+
+ p = buffer;
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (strncmp (p, "XDG_", 4) != 0)
+ continue;
+ p += 4;
+ if (strncmp (p, type, strlen (type)) != 0)
+ continue;
+ p += strlen (type);
+ if (strncmp (p, "_DIR", 4) != 0)
+ continue;
+ p += 4;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (*p != '=')
+ continue;
+ p++;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (*p != '"')
+ continue;
+ p++;
+
+ relative = 0;
+ if (strncmp (p, "$HOME/", 6) == 0)
+ {
+ p += 6;
+ relative = 1;
+ }
+ else if (*p != '/')
+ continue;
+
+ if (relative)
+ {
+ user_dir = (char*) malloc (strlen (home_dir) + 1 + strlen (p) + 1);
+ if (user_dir == NULL)
+ goto error2;
+
+ strcpy (user_dir, home_dir);
+ strcat (user_dir, "/");
+ }
+ else
+ {
+ user_dir = (char*) malloc (strlen (p) + 1);
+ if (user_dir == NULL)
+ goto error2;
+
+ *user_dir = 0;
+ }
+
+ d = user_dir + strlen (user_dir);
+ while (*p && *p != '"')
+ {
+ if ((*p == '\\') && (*(p+1) != 0))
+ p++;
+ *d++ = *p++;
+ }
+ *d = 0;
+ }
+error2:
+ fclose (file);
+
+ if (user_dir)
+ return user_dir;
+
+ error:
+ return NULL;
+}
+
+static const char xdg_user_dirs[] =
+ "DESKTOP\0"
+ "DOCUMENTS\0"
+ "DOWNLOAD\0"
+ "MUSIC\0"
+ "PICTURES\0"
+ "PUBLICSHARE\0"
+ "TEMPLATES\0"
+ "VIDEOS";
+
+static const uint8_t xdg_user_dir_offsets[] = {
+ 0,
+ 8,
+ 18,
+ 27,
+ 33,
+ 42,
+ 54,
+ 64
+};
+
+static nsresult
+GetUnixXDGUserDirectory(SystemDirectories aSystemDirectory,
+ nsIFile** aFile)
+{
+ char *dir = xdg_user_dir_lookup
+ (xdg_user_dirs + xdg_user_dir_offsets[aSystemDirectory -
+ Unix_XDG_Desktop]);
+
+ nsresult rv;
+ nsCOMPtr<nsIFile> file;
+ if (dir) {
+ rv = NS_NewNativeLocalFile(nsDependentCString(dir), true,
+ getter_AddRefs(file));
+ free(dir);
+ } else if (Unix_XDG_Desktop == aSystemDirectory) {
+ // for the XDG desktop dir, fall back to HOME/Desktop
+ // (for historical compatibility)
+ rv = GetUnixHomeDir(getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = file->AppendNative(NS_LITERAL_CSTRING("Desktop"));
+ }
+#if defined(MOZ_PLATFORM_MAEMO)
+ // "MYDOCSDIR" is exported to point to "/home/user/MyDocs" in maemo.
+ else if (Unix_XDG_Documents == aSystemDirectory) {
+
+ char *myDocs = PR_GetEnv("MYDOCSDIR");
+ if (!myDocs || !*myDocs)
+ return NS_ERROR_FAILURE;
+
+ rv = NS_NewNativeLocalFile(nsDependentCString(myDocs), true,
+ getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = file->AppendNative(NS_LITERAL_CSTRING(".documents"));
+ }
+#endif
+ else {
+ // no fallback for the other XDG dirs
+ rv = NS_ERROR_FAILURE;
+ }
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ bool exists;
+ rv = file->Exists(&exists);
+ if (NS_FAILED(rv))
+ return rv;
+ if (!exists) {
+ rv = file->Create(nsIFile::DIRECTORY_TYPE, 0755);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ *aFile = nullptr;
+ file.swap(*aFile);
+
+ return NS_OK;
+}
+#endif
+
+nsresult
+GetSpecialSystemDirectory(SystemDirectories aSystemSystemDirectory,
+ nsIFile** aFile)
+{
+#if defined(XP_WIN)
+ WCHAR path[MAX_PATH];
+#else
+ char path[MAXPATHLEN];
+#endif
+
+ switch (aSystemSystemDirectory)
+ {
+ case OS_CurrentWorkingDirectory:
+#if defined(XP_WIN)
+ if (!_wgetcwd(path, MAX_PATH))
+ return NS_ERROR_FAILURE;
+ return NS_NewLocalFile(nsDependentString(path),
+ true,
+ aFile);
+#elif defined(XP_OS2)
+ if (DosQueryPathInfo( ".", FIL_QUERYFULLNAME, path, MAXPATHLEN))
+ return NS_ERROR_FAILURE;
+#else
+ if(!getcwd(path, MAXPATHLEN))
+ return NS_ERROR_FAILURE;
+#endif
+
+#if !defined(XP_WIN)
+ return NS_NewNativeLocalFile(nsDependentCString(path),
+ true,
+ aFile);
+#endif
+
+ case OS_DriveDirectory:
+#if defined (XP_WIN)
+ {
+ int32_t len = ::GetWindowsDirectoryW(path, MAX_PATH);
+ if (len == 0)
+ break;
+ if (path[1] == PRUnichar(':') && path[2] == PRUnichar('\\'))
+ path[3] = 0;
+
+ return NS_NewLocalFile(nsDependentString(path),
+ true,
+ aFile);
+ }
+#elif defined(XP_OS2)
+ {
+ ULONG ulBootDrive = 0;
+ char buffer[] = " :\\OS2\\";
+ DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
+ &ulBootDrive, sizeof ulBootDrive);
+ buffer[0] = 'A' - 1 + ulBootDrive; // duh, 1-based index...
+
+ return NS_NewNativeLocalFile(nsDependentCString(buffer),
+ true,
+ aFile);
+ }
+#else
+ return NS_NewNativeLocalFile(nsDependentCString("/"),
+ true,
+ aFile);
+
+#endif
+
+ case OS_TemporaryDirectory:
+#if defined (XP_WIN)
+ {
+ DWORD len = ::GetTempPathW(MAX_PATH, path);
+ if (len == 0)
+ break;
+ return NS_NewLocalFile(nsDependentString(path, len),
+ true,
+ aFile);
+ }
+#elif defined(XP_OS2)
+ {
+ char *tPath = PR_GetEnv("TMP");
+ if (!tPath || !*tPath) {
+ tPath = PR_GetEnv("TEMP");
+ if (!tPath || !*tPath) {
+ // if an OS/2 system has neither TMP nor TEMP defined
+ // then it is severely broken, so this will never happen.
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+ nsCString tString = nsDependentCString(tPath);
+ if (tString.Find("/", false, 0, -1)) {
+ tString.ReplaceChar('/','\\');
+ }
+ return NS_NewNativeLocalFile(tString, true, aFile);
+ }
+#elif defined(MOZ_WIDGET_COCOA)
+ {
+ return GetOSXFolderType(kUserDomain, kTemporaryFolderType, aFile);
+ }
+
+#elif defined(XP_UNIX)
+ {
+ static const char *tPath = nullptr;
+ if (!tPath) {
+ tPath = PR_GetEnv("TMPDIR");
+ if (!tPath || !*tPath) {
+ tPath = PR_GetEnv("TMP");
+ if (!tPath || !*tPath) {
+ tPath = PR_GetEnv("TEMP");
+ if (!tPath || !*tPath) {
+ tPath = "/tmp/";
+ }
+ }
+ }
+ }
+ return NS_NewNativeLocalFile(nsDependentCString(tPath),
+ true,
+ aFile);
+ }
+#else
+ break;
+#endif
+#if defined (XP_WIN)
+ case Win_SystemDirectory:
+ {
+ int32_t len = ::GetSystemDirectoryW(path, MAX_PATH);
+
+ // Need enough space to add the trailing backslash
+ if (!len || len > MAX_PATH - 2)
+ break;
+ path[len] = L'\\';
+ path[++len] = L'\0';
+
+ return NS_NewLocalFile(nsDependentString(path, len),
+ true,
+ aFile);
+ }
+
+ case Win_WindowsDirectory:
+ {
+ int32_t len = ::GetWindowsDirectoryW(path, MAX_PATH);
+
+ // Need enough space to add the trailing backslash
+ if (!len || len > MAX_PATH - 2)
+ break;
+
+ path[len] = L'\\';
+ path[++len] = L'\0';
+
+ return NS_NewLocalFile(nsDependentString(path, len),
+ true,
+ aFile);
+ }
+
+ case Win_ProgramFiles:
+ {
+ return GetWindowsFolder(CSIDL_PROGRAM_FILES, aFile);
+ }
+
+ case Win_HomeDirectory:
+ {
+ nsresult rv = GetWindowsFolder(CSIDL_PROFILE, aFile);
+ if (NS_SUCCEEDED(rv))
+ return rv;
+
+ int32_t len;
+ if ((len = ::GetEnvironmentVariableW(L"HOME", path, MAX_PATH)) > 0)
+ {
+ // Need enough space to add the trailing backslash
+ if (len > MAX_PATH - 2)
+ break;
+
+ path[len] = L'\\';
+ path[++len] = L'\0';
+
+ rv = NS_NewLocalFile(nsDependentString(path, len),
+ true,
+ aFile);
+ if (NS_SUCCEEDED(rv))
+ return rv;
+ }
+
+ len = ::GetEnvironmentVariableW(L"HOMEDRIVE", path, MAX_PATH);
+ if (0 < len && len < MAX_PATH)
+ {
+ WCHAR temp[MAX_PATH];
+ DWORD len2 = ::GetEnvironmentVariableW(L"HOMEPATH", temp, MAX_PATH);
+ if (0 < len2 && len + len2 < MAX_PATH)
+ wcsncat(path, temp, len2);
+
+ len = wcslen(path);
+
+ // Need enough space to add the trailing backslash
+ if (len > MAX_PATH - 2)
+ break;
+
+ path[len] = L'\\';
+ path[++len] = L'\0';
+
+ return NS_NewLocalFile(nsDependentString(path, len),
+ true,
+ aFile);
+ }
+ }
+ case Win_Desktop:
+ {
+ return GetWindowsFolder(CSIDL_DESKTOP, aFile);
+ }
+ case Win_Programs:
+ {
+ return GetWindowsFolder(CSIDL_PROGRAMS, aFile);
+ }
+
+ case Win_Downloads:
+ {
+ // Defined in KnownFolders.h.
+ GUID folderid_downloads = {0x374de290, 0x123f, 0x4565, {0x91, 0x64,
+ 0x39, 0xc4, 0x92, 0x5e, 0x46, 0x7b}};
+ nsresult rv = GetKnownFolder(&folderid_downloads, aFile);
+ // On WinXP, there is no downloads folder, default
+ // to 'Desktop'.
+ if(NS_ERROR_FAILURE == rv)
+ {
+ rv = GetWindowsFolder(CSIDL_DESKTOP, aFile);
+ }
+ return rv;
+ }
+
+ case Win_Controls:
+ {
+ return GetWindowsFolder(CSIDL_CONTROLS, aFile);
+ }
+ case Win_Printers:
+ {
+ return GetWindowsFolder(CSIDL_PRINTERS, aFile);
+ }
+ case Win_Personal:
+ {
+ return GetWindowsFolder(CSIDL_PERSONAL, aFile);
+ }
+ case Win_Favorites:
+ {
+ return GetWindowsFolder(CSIDL_FAVORITES, aFile);
+ }
+ case Win_Startup:
+ {
+ return GetWindowsFolder(CSIDL_STARTUP, aFile);
+ }
+ case Win_Recent:
+ {
+ return GetWindowsFolder(CSIDL_RECENT, aFile);
+ }
+ case Win_Sendto:
+ {
+ return GetWindowsFolder(CSIDL_SENDTO, aFile);
+ }
+ case Win_Bitbucket:
+ {
+ return GetWindowsFolder(CSIDL_BITBUCKET, aFile);
+ }
+ case Win_Startmenu:
+ {
+ return GetWindowsFolder(CSIDL_STARTMENU, aFile);
+ }
+ case Win_Desktopdirectory:
+ {
+ return GetWindowsFolder(CSIDL_DESKTOPDIRECTORY, aFile);
+ }
+ case Win_Drives:
+ {
+ return GetWindowsFolder(CSIDL_DRIVES, aFile);
+ }
+ case Win_Network:
+ {
+ return GetWindowsFolder(CSIDL_NETWORK, aFile);
+ }
+ case Win_Nethood:
+ {
+ return GetWindowsFolder(CSIDL_NETHOOD, aFile);
+ }
+ case Win_Fonts:
+ {
+ return GetWindowsFolder(CSIDL_FONTS, aFile);
+ }
+ case Win_Templates:
+ {
+ return GetWindowsFolder(CSIDL_TEMPLATES, aFile);
+ }
+ case Win_Common_Startmenu:
+ {
+ return GetWindowsFolder(CSIDL_COMMON_STARTMENU, aFile);
+ }
+ case Win_Common_Programs:
+ {
+ return GetWindowsFolder(CSIDL_COMMON_PROGRAMS, aFile);
+ }
+ case Win_Common_Startup:
+ {
+ return GetWindowsFolder(CSIDL_COMMON_STARTUP, aFile);
+ }
+ case Win_Common_Desktopdirectory:
+ {
+ return GetWindowsFolder(CSIDL_COMMON_DESKTOPDIRECTORY, aFile);
+ }
+ case Win_Common_AppData:
+ {
+ return GetWindowsFolder(CSIDL_COMMON_APPDATA, aFile);
+ }
+ case Win_Printhood:
+ {
+ return GetWindowsFolder(CSIDL_PRINTHOOD, aFile);
+ }
+ case Win_Cookies:
+ {
+ return GetWindowsFolder(CSIDL_COOKIES, aFile);
+ }
+ case Win_Appdata:
+ {
+ nsresult rv = GetWindowsFolder(CSIDL_APPDATA, aFile);
+ if (NS_FAILED(rv))
+ rv = GetRegWindowsAppDataFolder(false, aFile);
+ return rv;
+ }
+ case Win_LocalAppdata:
+ {
+ nsresult rv = GetWindowsFolder(CSIDL_LOCAL_APPDATA, aFile);
+ if (NS_FAILED(rv))
+ rv = GetRegWindowsAppDataFolder(true, aFile);
+ return rv;
+ }
+ case Win_Documents:
+ {
+ return GetLibrarySaveToPath(CSIDL_MYDOCUMENTS,
+ FOLDERID_DocumentsLibrary,
+ aFile);
+ }
+ case Win_Pictures:
+ {
+ return GetLibrarySaveToPath(CSIDL_MYPICTURES,
+ FOLDERID_PicturesLibrary,
+ aFile);
+ }
+ case Win_Music:
+ {
+ return GetLibrarySaveToPath(CSIDL_MYMUSIC,
+ FOLDERID_MusicLibrary,
+ aFile);
+ }
+ case Win_Videos:
+ {
+ return GetLibrarySaveToPath(CSIDL_MYVIDEO,
+ FOLDERID_VideosLibrary,
+ aFile);
+ }
+#endif // XP_WIN
+
+#if defined(XP_UNIX)
+ case Unix_LocalDirectory:
+ return NS_NewNativeLocalFile(nsDependentCString("/usr/local/netscape/"),
+ true,
+ aFile);
+ case Unix_LibDirectory:
+ return NS_NewNativeLocalFile(nsDependentCString("/usr/local/lib/netscape/"),
+ true,
+ aFile);
+
+ case Unix_HomeDirectory:
+ return GetUnixHomeDir(aFile);
+
+ case Unix_XDG_Desktop:
+ case Unix_XDG_Documents:
+ case Unix_XDG_Download:
+ case Unix_XDG_Music:
+ case Unix_XDG_Pictures:
+ case Unix_XDG_PublicShare:
+ case Unix_XDG_Templates:
+ case Unix_XDG_Videos:
+ return GetUnixXDGUserDirectory(aSystemSystemDirectory, aFile);
+#endif
+
+#ifdef XP_OS2
+ case OS2_SystemDirectory:
+ {
+ ULONG ulBootDrive = 0;
+ char buffer[] = " :\\OS2\\System\\";
+ DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
+ &ulBootDrive, sizeof ulBootDrive);
+ buffer[0] = 'A' - 1 + ulBootDrive; // duh, 1-based index...
+
+ return NS_NewNativeLocalFile(nsDependentCString(buffer),
+ true,
+ aFile);
+ }
+
+ case OS2_OS2Directory:
+ {
+ ULONG ulBootDrive = 0;
+ char buffer[] = " :\\OS2\\";
+ DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
+ &ulBootDrive, sizeof ulBootDrive);
+ buffer[0] = 'A' - 1 + ulBootDrive; // duh, 1-based index...
+
+ return NS_NewNativeLocalFile(nsDependentCString(buffer),
+ true,
+ aFile);
+ }
+
+ case OS2_HomeDirectory:
+ {
+ nsresult rv;
+ char *tPath = PR_GetEnv("MOZILLA_HOME");
+ char buffer[CCHMAXPATH];
+ /* If MOZILLA_HOME is not set, use GetCurrentProcessDirectory */
+ /* To ensure we get a long filename system */
+ if (!tPath || !*tPath) {
+ PPIB ppib;
+ PTIB ptib;
+ DosGetInfoBlocks( &ptib, &ppib);
+ DosQueryModuleName( ppib->pib_hmte, CCHMAXPATH, buffer);
+ *strrchr( buffer, '\\') = '\0'; // XXX DBCS misery
+ tPath = buffer;
+ }
+ rv = NS_NewNativeLocalFile(nsDependentCString(tPath),
+ true,
+ aFile);
+
+ PrfWriteProfileString(HINI_USERPROFILE, "Mozilla", "Home", tPath);
+ return rv;
+ }
+
+ case OS2_DesktopDirectory:
+ {
+ char szPath[CCHMAXPATH + 1];
+ BOOL fSuccess;
+ fSuccess = WinQueryActiveDesktopPathname (szPath, sizeof(szPath));
+ if (!fSuccess) {
+ // this could happen if we are running without the WPS, return
+ // the Home directory instead
+ return GetSpecialSystemDirectory(OS2_HomeDirectory, aFile);
+ }
+ int len = strlen (szPath);
+ if (len > CCHMAXPATH -1)
+ break;
+ szPath[len] = '\\';
+ szPath[len + 1] = '\0';
+
+ return NS_NewNativeLocalFile(nsDependentCString(szPath),
+ true,
+ aFile);
+ }
+#endif
+ default:
+ break;
+ }
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+#if defined (MOZ_WIDGET_COCOA)
+nsresult
+GetOSXFolderType(short aDomain, OSType aFolderType, nsIFile **localFile)
+{
+ OSErr err;
+ FSRef fsRef;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ err = ::FSFindFolder(aDomain, aFolderType, kCreateFolder, &fsRef);
+ if (err == noErr)
+ {
+ NS_NewLocalFile(EmptyString(), true, localFile);
+ nsCOMPtr<nsILocalFileMac> localMacFile(do_QueryInterface(*localFile));
+ if (localMacFile)
+ rv = localMacFile->InitWithFSRef(&fsRef);
+ }
+ return rv;
+}
+#endif
+
diff --git a/xpcom/io/SpecialSystemDirectory.h b/xpcom/io/SpecialSystemDirectory.h
new file mode 100644
index 000000000..c78d4b233
--- /dev/null
+++ b/xpcom/io/SpecialSystemDirectory.h
@@ -0,0 +1,108 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 _SPECIALSYSTEMDIRECTORY_H_
+#define _SPECIALSYSTEMDIRECTORY_H_
+
+#include "nscore.h"
+#include "nsIFile.h"
+
+#ifdef MOZ_WIDGET_COCOA
+#include <Carbon/Carbon.h>
+#include "nsILocalFileMac.h"
+#include "prenv.h"
+#endif
+
+extern void StartupSpecialSystemDirectory();
+
+
+enum SystemDirectories {
+ OS_DriveDirectory = 1,
+ OS_TemporaryDirectory = 2,
+ OS_CurrentProcessDirectory= 3,
+ OS_CurrentWorkingDirectory= 4,
+ XPCOM_CurrentProcessComponentDirectory= 5,
+ XPCOM_CurrentProcessComponentRegistry= 6,
+
+ Moz_BinDirectory = 100 ,
+ Mac_SystemDirectory = 101,
+ Mac_DesktopDirectory = 102,
+ Mac_TrashDirectory = 103,
+ Mac_StartupDirectory = 104,
+ Mac_ShutdownDirectory = 105,
+ Mac_AppleMenuDirectory = 106,
+ Mac_ControlPanelDirectory = 107,
+ Mac_ExtensionDirectory = 108,
+ Mac_FontsDirectory = 109,
+ Mac_ClassicPreferencesDirectory = 110,
+ Mac_DocumentsDirectory = 111,
+ Mac_InternetSearchDirectory = 112,
+ Mac_DefaultDownloadDirectory = 113,
+ Mac_UserLibDirectory = 114,
+ Mac_PreferencesDirectory = 115,
+
+ Win_SystemDirectory = 201,
+ Win_WindowsDirectory = 202,
+ Win_HomeDirectory = 203,
+ Win_Desktop = 204,
+ Win_Programs = 205,
+ Win_Controls = 206,
+ Win_Printers = 207,
+ Win_Personal = 208,
+ Win_Favorites = 209,
+ Win_Startup = 210,
+ Win_Recent = 211,
+ Win_Sendto = 212,
+ Win_Bitbucket = 213,
+ Win_Startmenu = 214,
+ Win_Desktopdirectory = 215,
+ Win_Drives = 216,
+ Win_Network = 217,
+ Win_Nethood = 218,
+ Win_Fonts = 219,
+ Win_Templates = 220,
+ Win_Common_Startmenu = 221,
+ Win_Common_Programs = 222,
+ Win_Common_Startup = 223,
+ Win_Common_Desktopdirectory = 224,
+ Win_Appdata = 225,
+ Win_Printhood = 226,
+ Win_Cookies = 227,
+ Win_LocalAppdata = 228,
+ Win_ProgramFiles = 229,
+ Win_Downloads = 230,
+ Win_Common_AppData = 231,
+ Win_Documents = 232,
+ Win_Pictures = 233,
+ Win_Music = 234,
+ Win_Videos = 235,
+
+ Unix_LocalDirectory = 301,
+ Unix_LibDirectory = 302,
+ Unix_HomeDirectory = 303,
+ Unix_XDG_Desktop = 304,
+ Unix_XDG_Documents = 305,
+ Unix_XDG_Download = 306,
+ Unix_XDG_Music = 307,
+ Unix_XDG_Pictures = 308,
+ Unix_XDG_PublicShare = 309,
+ Unix_XDG_Templates = 310,
+ Unix_XDG_Videos = 311,
+
+ OS2_SystemDirectory = 401,
+ OS2_OS2Directory = 402,
+ OS2_DesktopDirectory = 403,
+ OS2_HomeDirectory = 404
+};
+
+nsresult
+GetSpecialSystemDirectory(SystemDirectories aSystemSystemDirectory,
+ nsIFile** aFile);
+#ifdef MOZ_WIDGET_COCOA
+nsresult
+GetOSXFolderType(short aDomain, OSType aFolderType, nsIFile **localFile);
+#endif
+
+#endif
diff --git a/xpcom/io/moz.build b/xpcom/io/moz.build
new file mode 100644
index 000000000..b17bca770
--- /dev/null
+++ b/xpcom/io/moz.build
@@ -0,0 +1,122 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+XPIDL_SOURCES += [
+ 'nsIAsyncInputStream.idl',
+ 'nsIAsyncOutputStream.idl',
+ 'nsIBinaryInputStream.idl',
+ 'nsIBinaryOutputStream.idl',
+ 'nsIConverterInputStream.idl',
+ 'nsIConverterOutputStream.idl',
+ 'nsIDirectoryEnumerator.idl',
+ 'nsIDirectoryService.idl',
+ 'nsIFile.idl',
+ 'nsIIOUtil.idl',
+ 'nsIInputStream.idl',
+ 'nsIInputStreamTee.idl',
+ 'nsILineInputStream.idl',
+ 'nsILocalFile.idl',
+ 'nsILocalFileWin.idl',
+ 'nsIMultiplexInputStream.idl',
+ 'nsIObjectInputStream.idl',
+ 'nsIObjectOutputStream.idl',
+ 'nsIOutputStream.idl',
+ 'nsIPipe.idl',
+ 'nsISafeOutputStream.idl',
+ 'nsIScriptableBase64Encoder.idl',
+ 'nsIScriptableInputStream.idl',
+ 'nsISeekableStream.idl',
+ 'nsISimpleUnicharStreamFactory.idl',
+ 'nsIStorageStream.idl',
+ 'nsIStreamBufferAccess.idl',
+ 'nsIStringStream.idl',
+ 'nsIUnicharInputStream.idl',
+ 'nsIUnicharLineInputStream.idl',
+ 'nsIUnicharOutputStream.idl',
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'os2':
+ XPIDL_SOURCES += [
+ 'nsILocalFileOS2.idl',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ XPIDL_SOURCES += [
+ 'nsILocalFileMac.idl',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'os2':
+ EXPORTS += ['nsLocalFileOS2.h']
+ CPP_SOURCES += [
+ 'nsLocalFileOS2.cpp',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ EXPORTS += ['nsLocalFileWin.h']
+ CPP_SOURCES += [
+ 'nsLocalFileWin.cpp',
+ ]
+else:
+ EXPORTS += ['nsLocalFileUnix.h']
+ CPP_SOURCES += [
+ 'nsLocalFileUnix.cpp',
+ ]
+
+XPIDL_MODULE = 'xpcom_io'
+
+MODULE = 'xpcom'
+
+EXPORTS += [
+ 'SpecialSystemDirectory.h',
+ 'nsAnonymousTemporaryFile.h',
+ 'nsAppDirectoryServiceDefs.h',
+ 'nsDirectoryService.h',
+ 'nsDirectoryServiceAtomList.h',
+ 'nsEscape.h',
+ 'nsLinebreakConverter.h',
+ 'nsLocalFile.h',
+ 'nsMultiplexInputStream.h',
+ 'nsNativeCharsetUtils.h',
+ 'nsScriptableInputStream.h',
+ 'nsStorageStream.h',
+ 'nsStreamUtils.h',
+ 'nsStringStream.h',
+ 'nsUnicharInputStream.h',
+ 'nsWildCard.h',
+]
+
+EXPORTS.mozilla += [
+ 'Base64.h',
+]
+
+CPP_SOURCES += [
+ 'Base64.cpp',
+ 'SpecialSystemDirectory.cpp',
+ 'nsAnonymousTemporaryFile.cpp',
+ 'nsAppFileLocationProvider.cpp',
+ 'nsBinaryStream.cpp',
+ 'nsDirectoryService.cpp',
+ 'nsEscape.cpp',
+ 'nsIOUtil.cpp',
+ 'nsInputStreamTee.cpp',
+ 'nsLinebreakConverter.cpp',
+ 'nsLocalFileCommon.cpp',
+ 'nsMultiplexInputStream.cpp',
+ 'nsNativeCharsetUtils.cpp',
+ 'nsPipe3.cpp',
+ 'nsScriptableBase64Encoder.cpp',
+ 'nsScriptableInputStream.cpp',
+ 'nsSegmentedBuffer.cpp',
+ 'nsStorageStream.cpp',
+ 'nsStreamUtils.cpp',
+ 'nsStringStream.cpp',
+ 'nsUnicharInputStream.cpp',
+ 'nsWildCard.cpp',
+]
+
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ CMMSRCS += [
+ 'CocoaFileUtils.mm',
+ ]
diff --git a/xpcom/io/nsAnonymousTemporaryFile.cpp b/xpcom/io/nsAnonymousTemporaryFile.cpp
new file mode 100644
index 000000000..aaac18958
--- /dev/null
+++ b/xpcom/io/nsAnonymousTemporaryFile.cpp
@@ -0,0 +1,240 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsAnonymousTemporaryFile.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsXULAppAPI.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsAppDirectoryServiceDefs.h"
+
+#ifdef XP_WIN
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "mozilla/Services.h"
+#include "nsIIdleService.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIFile.h"
+#include "nsAutoPtr.h"
+#include "nsITimer.h"
+#include "nsCRT.h"
+
+using namespace mozilla;
+#endif
+
+
+// We store the temp files in the system temp dir.
+//
+// On Windows systems in particular we use a sub-directory of the temp
+// directory, because:
+// 1. DELETE_ON_CLOSE is unreliable on Windows, in particular if we power
+// cycle (and perhaps if we crash) the files are not deleted. We store
+// the temporary files in a known sub-dir so that we can find and delete
+// them easily and quickly.
+// 2. On Windows NT the system temp dir is in the user's $HomeDir/AppData,
+// so we can be sure the user always has write privileges to that directory;
+// if the sub-dir for our temp files was in some shared location and
+// was created by a privileged user, it's possible that other users
+// wouldn't have write access to that sub-dir. (Non-Windows systems
+// don't store their temp files in a sub-dir, so this isn't an issue on
+// those platforms).
+// 3. Content processes can access the system temp dir
+// (NS_GetSpecialDirectory fails on NS_APP_USER_PROFILE_LOCAL_50_DIR
+// for content process for example, which is where we previously stored
+// temp files on Windows). This argument applies to all platforms, not
+// just Windows.
+static nsresult
+GetTempDir(nsIFile** aTempDir)
+{
+ NS_ENSURE_ARG(aTempDir);
+ nsCOMPtr<nsIFile> tmpFile;
+ nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+#ifdef XP_WIN
+ // On windows DELETE_ON_CLOSE is unreliable, so we store temporary files
+ // in a subdir of the temp dir and delete that in an idle service observer
+ // to ensure it's been cleared.
+ rv = tmpFile->AppendNative(nsDependentCString("mozilla-temp-files"));
+ NS_ENSURE_SUCCESS(rv,rv);
+ rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700);
+ NS_ENSURE_TRUE(rv == NS_ERROR_FILE_ALREADY_EXISTS || NS_SUCCEEDED(rv), rv);
+#endif
+
+ tmpFile.forget(aTempDir);
+
+ return NS_OK;
+}
+
+nsresult
+NS_OpenAnonymousTemporaryFile(PRFileDesc** aOutFileDesc)
+{
+ NS_ENSURE_ARG(aOutFileDesc);
+ nsresult rv;
+
+ nsCOMPtr<nsIFile> tmpFile;
+ rv = GetTempDir(getter_AddRefs(tmpFile));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // Give the temp file a name with a random element. CreateUnique will also
+ // append a counter to the name if it encounters a name collision. Adding
+ // a random element to the name reduces the likelihood of a name collision,
+ // so that CreateUnique() doesn't end up trying a lot of name variants in
+ // its "try appending an incrementing counter" loop, as file IO can be
+ // expensive on some mobile flash drives.
+ nsAutoCString name("mozilla-temp-");
+ name.AppendInt(rand());
+
+ rv = tmpFile->AppendNative(name);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0700);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = tmpFile->OpenNSPRFileDesc(PR_RDWR | nsIFile::DELETE_ON_CLOSE,
+ PR_IRWXU, aOutFileDesc);
+
+ return rv;
+}
+
+#ifdef XP_WIN
+
+// On Windows we have an idle service observer that runs some time after
+// startup and deletes any stray anonymous temporary files...
+
+// Duration of idle time before we'll get a callback whereupon we attempt to
+// remove any stray and unused anonymous temp files.
+#define TEMP_FILE_IDLE_TIME_S 30
+
+// The nsAnonTempFileRemover is created in a timer, which sets an idle observer.
+// This is expiration time (in ms) which initial timer is set for (3 minutes).
+#define SCHEDULE_TIMEOUT_MS 3 * 60 * 1000
+
+#define XPCOM_SHUTDOWN_TOPIC "xpcom-shutdown"
+
+// This class adds itself as an idle observer. When the application has
+// been idle for about 30 seconds we'll get a notification, whereupon we'll
+// attempt to delete ${TempDir}/mozilla-temp-files/. This is to ensure all
+// temp files that were supposed to be deleted on application exit were actually
+// deleted, as they may not be if we previously crashed. See bugs 572579 and
+// 785662. This is only needed on some versions of Windows,
+// nsIFile::DELETE_ON_CLOSE works on other platforms.
+// This class adds itself as a shutdown observer so that it can cancel the
+// idle observer and its timer on shutdown. Note: the observer and idle
+// services hold references to instances of this object, and those references
+// are what keep this object alive.
+class nsAnonTempFileRemover MOZ_FINAL : public nsIObserver {
+public:
+ NS_DECL_ISUPPORTS
+
+ nsAnonTempFileRemover() {
+ MOZ_COUNT_CTOR(nsAnonTempFileRemover);
+ }
+
+ ~nsAnonTempFileRemover() {
+ MOZ_COUNT_DTOR(nsAnonTempFileRemover);
+ }
+
+ nsresult Init() {
+ // We add the idle observer in a timer, so that the app has enough
+ // time to start up before we add the idle observer. If we register the
+ // idle observer too early, it will be registered before the fake idle
+ // service is installed when running in xpcshell, and this interferes with
+ // the fake idle service, causing xpcshell-test failures.
+ mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+ NS_ENSURE_TRUE(mTimer != nullptr, NS_ERROR_FAILURE);
+ nsresult rv = mTimer->Init(this,
+ SCHEDULE_TIMEOUT_MS,
+ nsITimer::TYPE_ONE_SHOT);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Register shutdown observer so we can cancel the timer if we shutdown before
+ // the timer runs.
+ nsCOMPtr<nsIObserverService> obsSrv = services::GetObserverService();
+ NS_ENSURE_TRUE(obsSrv != nullptr, NS_ERROR_FAILURE);
+ return obsSrv->AddObserver(this, XPCOM_SHUTDOWN_TOPIC, false);
+ }
+
+ void Cleanup() {
+ // Cancel timer.
+ if (mTimer) {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+ // Remove idle service observer.
+ nsCOMPtr<nsIIdleService> idleSvc =
+ do_GetService("@mozilla.org/widget/idleservice;1");
+ if (idleSvc) {
+ idleSvc->RemoveIdleObserver(this, TEMP_FILE_IDLE_TIME_S);
+ }
+ // Remove shutdown observer.
+ nsCOMPtr<nsIObserverService> obsSrv = services::GetObserverService();
+ if (obsSrv) {
+ obsSrv->RemoveObserver(this, XPCOM_SHUTDOWN_TOPIC);
+ }
+ }
+
+ NS_IMETHODIMP Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const PRUnichar *aData)
+ {
+ if (nsCRT::strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC) == 0 &&
+ NS_FAILED(RegisterIdleObserver())) {
+ Cleanup();
+ } else if (nsCRT::strcmp(aTopic, OBSERVER_TOPIC_IDLE) == 0) {
+ // The user has been idle for a while, clean up the temp files.
+ // The idle service will drop its reference to this object after
+ // we exit, destroying this object.
+ RemoveAnonTempFileFiles();
+ Cleanup();
+ } else if (nsCRT::strcmp(aTopic, XPCOM_SHUTDOWN_TOPIC) == 0) {
+ Cleanup();
+ }
+ return NS_OK;
+ }
+
+ nsresult RegisterIdleObserver() {
+ // Add this as an idle observer. When we've been idle for
+ // TEMP_FILE_IDLE_TIME_S seconds, we'll get a notification, and we'll then
+ // try to delete any stray temp files.
+ nsCOMPtr<nsIIdleService> idleSvc =
+ do_GetService("@mozilla.org/widget/idleservice;1");
+ if (!idleSvc)
+ return NS_ERROR_FAILURE;
+ return idleSvc->AddIdleObserver(this, TEMP_FILE_IDLE_TIME_S);
+ }
+
+ void RemoveAnonTempFileFiles() {
+ nsCOMPtr<nsIFile> tmpDir;
+ nsresult rv = GetTempDir(getter_AddRefs(tmpDir));
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ // Remove the directory recursively.
+ tmpDir->Remove(true);
+ }
+
+private:
+ nsCOMPtr<nsITimer> mTimer;
+};
+
+NS_IMPL_ISUPPORTS1(nsAnonTempFileRemover, nsIObserver)
+
+nsresult CreateAnonTempFileRemover() {
+ // Create a temp file remover. If Init() succeeds, the temp file remover is kept
+ // alive by a reference held by the observer service, since the temp file remover
+ // is a shutdown observer. We only create the temp file remover if we're running
+ // in the main process; there's no point in doing the temp file removal multiple
+ // times per startup.
+ if (XRE_GetProcessType() != GeckoProcessType_Default) {
+ return NS_OK;
+ }
+ nsRefPtr<nsAnonTempFileRemover> tempRemover = new nsAnonTempFileRemover();
+ return tempRemover->Init();
+}
+
+#endif
+
diff --git a/xpcom/io/nsAnonymousTemporaryFile.h b/xpcom/io/nsAnonymousTemporaryFile.h
new file mode 100644
index 000000000..2a0968a41
--- /dev/null
+++ b/xpcom/io/nsAnonymousTemporaryFile.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#pragma once
+
+#include "prio.h"
+#include "nscore.h"
+
+/**
+ * OpenAnonymousTemporaryFile
+ *
+ * Creates and opens a temporary file which has a random name. Callers have no
+ * control over the file name, and the file is opened in a temporary location
+ * which is appropriate for the platform.
+ *
+ * Upon success, aOutFileDesc contains an opened handle to the temporary file.
+ * The caller is responsible for closing the file when they're finished with it.
+ *
+ * The file will be deleted when the file handle is closed. On non-Windows
+ * platforms the file will be unlinked before this function returns. On Windows
+ * the OS supplied delete-on-close mechanism is unreliable if the application
+ * crashes or the computer power cycles unexpectedly, so unopened temporary
+ * files are purged at some time after application startup.
+ *
+ */
+nsresult
+NS_OpenAnonymousTemporaryFile(PRFileDesc** aOutFileDesc);
+
diff --git a/xpcom/io/nsAppDirectoryServiceDefs.h b/xpcom/io/nsAppDirectoryServiceDefs.h
new file mode 100644
index 000000000..542662aa8
--- /dev/null
+++ b/xpcom/io/nsAppDirectoryServiceDefs.h
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 nsAppDirectoryServiceDefs_h___
+#define nsAppDirectoryServiceDefs_h___
+
+//========================================================================================
+//
+// Defines property names for directories available from standard nsIDirectoryServiceProviders.
+// These keys are not guaranteed to exist because the nsIDirectoryServiceProviders which
+// provide them are optional.
+//
+// Keys whose definition ends in "DIR" or "FILE" return a single nsIFile (or subclass).
+// Keys whose definition ends in "LIST" return an nsISimpleEnumerator which enumerates a
+// list of file objects.
+//
+// System and XPCOM level properties are defined in nsDirectoryServiceDefs.h.
+//
+//========================================================================================
+
+
+// --------------------------------------------------------------------------------------
+// Files and directories which exist on a per-product basis
+// --------------------------------------------------------------------------------------
+
+#define NS_APP_APPLICATION_REGISTRY_FILE "AppRegF"
+#define NS_APP_APPLICATION_REGISTRY_DIR "AppRegD"
+
+#define NS_APP_DEFAULTS_50_DIR "DefRt" // The root dir of all defaults dirs
+#define NS_APP_PREF_DEFAULTS_50_DIR "PrfDef"
+#define NS_APP_PROFILE_DEFAULTS_50_DIR "profDef" // The profile defaults of the "current"
+ // locale. Should be first choice.
+#define NS_APP_PROFILE_DEFAULTS_NLOC_50_DIR "ProfDefNoLoc" // The profile defaults of the "default"
+ // installed locale. Second choice
+ // when above is not available.
+
+#define NS_APP_USER_PROFILES_ROOT_DIR "DefProfRt" // The dir where user profile dirs live.
+#define NS_APP_USER_PROFILES_LOCAL_ROOT_DIR "DefProfLRt" // The dir where user profile temp dirs live.
+
+#define NS_APP_RES_DIR "ARes"
+#define NS_APP_CHROME_DIR "AChrom"
+#define NS_APP_PLUGINS_DIR "APlugns" // Deprecated - use NS_APP_PLUGINS_DIR_LIST
+#define NS_APP_SEARCH_DIR "SrchPlugns"
+
+#define NS_APP_CHROME_DIR_LIST "AChromDL"
+#define NS_APP_PLUGINS_DIR_LIST "APluginsDL"
+#define NS_APP_SEARCH_DIR_LIST "SrchPluginsDL"
+
+// --------------------------------------------------------------------------------------
+// Files and directories which exist on a per-profile basis
+// These locations are typically provided by the profile mgr
+// --------------------------------------------------------------------------------------
+
+// In a shared profile environment, prefixing a profile-relative
+// key with NS_SHARED returns a location that is shared by
+// other users of the profile. Without this prefix, the consumer
+// has exclusive access to this location.
+
+#define NS_SHARED "SHARED"
+
+#define NS_APP_PREFS_50_DIR "PrefD" // Directory which contains user prefs
+#define NS_APP_PREFS_50_FILE "PrefF"
+#define NS_APP_PREFS_DEFAULTS_DIR_LIST "PrefDL"
+#define NS_EXT_PREFS_DEFAULTS_DIR_LIST "ExtPrefDL"
+#define NS_APP_PREFS_OVERRIDE_DIR "PrefDOverride" // Directory for per-profile defaults
+
+#define NS_APP_USER_PROFILE_50_DIR "ProfD"
+#define NS_APP_USER_PROFILE_LOCAL_50_DIR "ProfLD"
+
+#define NS_APP_USER_CHROME_DIR "UChrm"
+#define NS_APP_USER_SEARCH_DIR "UsrSrchPlugns"
+
+#define NS_APP_LOCALSTORE_50_FILE "LclSt"
+#define NS_APP_USER_PANELS_50_FILE "UPnls"
+#define NS_APP_USER_MIMETYPES_50_FILE "UMimTyp"
+#define NS_APP_CACHE_PARENT_DIR "cachePDir"
+
+#define NS_APP_BOOKMARKS_50_FILE "BMarks"
+
+#define NS_APP_DOWNLOADS_50_FILE "DLoads"
+
+#define NS_APP_SEARCH_50_FILE "SrchF"
+
+#define NS_APP_INSTALL_CLEANUP_DIR "XPIClnupD" //location of xpicleanup.dat xpicleanup.exe
+
+#define NS_APP_STORAGE_50_FILE "UStor" // sqlite database used as mozStorage profile db
+
+#define NS_APP_INDEXEDDB_PARENT_DIR "indexedDBPDir"
+
+#define NS_APP_PERMISSION_PARENT_DIR "permissionDBPDir"
+#endif
diff --git a/xpcom/io/nsAppFileLocationProvider.cpp b/xpcom/io/nsAppFileLocationProvider.cpp
new file mode 100644
index 000000000..ba497572a
--- /dev/null
+++ b/xpcom/io/nsAppFileLocationProvider.cpp
@@ -0,0 +1,578 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsAppFileLocationProvider.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIAtom.h"
+#include "nsIFile.h"
+#include "nsString.h"
+#include "nsXPIDLString.h"
+#include "nsISimpleEnumerator.h"
+#include "prenv.h"
+#include "nsCRT.h"
+
+#if defined(MOZ_WIDGET_COCOA)
+#include <Carbon/Carbon.h>
+#include "nsILocalFileMac.h"
+#elif defined(XP_OS2)
+#define INCL_DOSPROCESS
+#define INCL_DOSMODULEMGR
+#include <os2.h>
+#elif defined(XP_WIN)
+#include <windows.h>
+#include <shlobj.h>
+#elif defined(XP_UNIX)
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#endif
+
+
+// WARNING: These hard coded names need to go away. They need to
+// come from localizable resources
+
+#if defined(MOZ_WIDGET_COCOA)
+#define APP_REGISTRY_NAME NS_LITERAL_CSTRING("Application Registry")
+#define ESSENTIAL_FILES NS_LITERAL_CSTRING("Essential Files")
+#elif defined(XP_WIN) || defined(XP_OS2)
+#define APP_REGISTRY_NAME NS_LITERAL_CSTRING("registry.dat")
+#else
+#define APP_REGISTRY_NAME NS_LITERAL_CSTRING("appreg")
+#endif
+
+// define default product directory
+#define DEFAULT_PRODUCT_DIR NS_LITERAL_CSTRING(MOZ_USER_DIR)
+
+// Locally defined keys used by nsAppDirectoryEnumerator
+#define NS_ENV_PLUGINS_DIR "EnvPlugins" // env var MOZ_PLUGIN_PATH
+#define NS_USER_PLUGINS_DIR "UserPlugins"
+
+#ifdef MOZ_WIDGET_COCOA
+#define NS_MACOSX_USER_PLUGIN_DIR "OSXUserPlugins"
+#define NS_MACOSX_LOCAL_PLUGIN_DIR "OSXLocalPlugins"
+#define NS_MACOSX_JAVA2_PLUGIN_DIR "OSXJavaPlugins"
+#elif XP_UNIX
+#define NS_SYSTEM_PLUGINS_DIR "SysPlugins"
+#endif
+
+#define DEFAULTS_DIR_NAME NS_LITERAL_CSTRING("defaults")
+#define DEFAULTS_PREF_DIR_NAME NS_LITERAL_CSTRING("pref")
+#define DEFAULTS_PROFILE_DIR_NAME NS_LITERAL_CSTRING("profile")
+#define RES_DIR_NAME NS_LITERAL_CSTRING("res")
+#define CHROME_DIR_NAME NS_LITERAL_CSTRING("chrome")
+#define PLUGINS_DIR_NAME NS_LITERAL_CSTRING("plugins")
+#define SEARCH_DIR_NAME NS_LITERAL_CSTRING("searchplugins")
+
+//*****************************************************************************
+// nsAppFileLocationProvider::Constructor/Destructor
+//*****************************************************************************
+
+nsAppFileLocationProvider::nsAppFileLocationProvider()
+{
+}
+
+//*****************************************************************************
+// nsAppFileLocationProvider::nsISupports
+//*****************************************************************************
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsAppFileLocationProvider, nsIDirectoryServiceProvider, nsIDirectoryServiceProvider2)
+
+//*****************************************************************************
+// nsAppFileLocationProvider::nsIDirectoryServiceProvider
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsAppFileLocationProvider::GetFile(const char *prop, bool *persistent, nsIFile **_retval)
+{
+ nsCOMPtr<nsIFile> localFile;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ NS_ENSURE_ARG(prop);
+ *_retval = nullptr;
+ *persistent = true;
+
+#ifdef MOZ_WIDGET_COCOA
+ FSRef fileRef;
+ nsCOMPtr<nsILocalFileMac> macFile;
+#endif
+
+ if (nsCRT::strcmp(prop, NS_APP_APPLICATION_REGISTRY_DIR) == 0)
+ {
+ rv = GetProductDirectory(getter_AddRefs(localFile));
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_APPLICATION_REGISTRY_FILE) == 0)
+ {
+ rv = GetProductDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv))
+ rv = localFile->AppendNative(APP_REGISTRY_NAME);
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_DEFAULTS_50_DIR) == 0)
+ {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv))
+ rv = localFile->AppendRelativeNativePath(DEFAULTS_DIR_NAME);
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_PREF_DEFAULTS_50_DIR) == 0)
+ {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv)) {
+ rv = localFile->AppendRelativeNativePath(DEFAULTS_DIR_NAME);
+ if (NS_SUCCEEDED(rv))
+ rv = localFile->AppendRelativeNativePath(DEFAULTS_PREF_DIR_NAME);
+ }
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_PROFILE_DEFAULTS_50_DIR) == 0 ||
+ nsCRT::strcmp(prop, NS_APP_PROFILE_DEFAULTS_NLOC_50_DIR) == 0)
+ {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv)) {
+ rv = localFile->AppendRelativeNativePath(DEFAULTS_DIR_NAME);
+ if (NS_SUCCEEDED(rv))
+ rv = localFile->AppendRelativeNativePath(DEFAULTS_PROFILE_DIR_NAME);
+ }
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_USER_PROFILES_ROOT_DIR) == 0)
+ {
+ rv = GetDefaultUserProfileRoot(getter_AddRefs(localFile));
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_USER_PROFILES_LOCAL_ROOT_DIR) == 0)
+ {
+ rv = GetDefaultUserProfileRoot(getter_AddRefs(localFile), true);
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_RES_DIR) == 0)
+ {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv))
+ rv = localFile->AppendRelativeNativePath(RES_DIR_NAME);
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_CHROME_DIR) == 0)
+ {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv))
+ rv = localFile->AppendRelativeNativePath(CHROME_DIR_NAME);
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_PLUGINS_DIR) == 0)
+ {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv))
+ rv = localFile->AppendRelativeNativePath(PLUGINS_DIR_NAME);
+ }
+#ifdef MOZ_WIDGET_COCOA
+ else if (nsCRT::strcmp(prop, NS_MACOSX_USER_PLUGIN_DIR) == 0)
+ {
+ if (::FSFindFolder(kUserDomain, kInternetPlugInFolderType, false, &fileRef) == noErr) {
+ rv = NS_NewLocalFileWithFSRef(&fileRef, true, getter_AddRefs(macFile));
+ if (NS_SUCCEEDED(rv))
+ localFile = macFile;
+ }
+ }
+ else if (nsCRT::strcmp(prop, NS_MACOSX_LOCAL_PLUGIN_DIR) == 0)
+ {
+ if (::FSFindFolder(kLocalDomain, kInternetPlugInFolderType, false, &fileRef) == noErr) {
+ rv = NS_NewLocalFileWithFSRef(&fileRef, true, getter_AddRefs(macFile));
+ if (NS_SUCCEEDED(rv))
+ localFile = macFile;
+ }
+ }
+ else if (nsCRT::strcmp(prop, NS_MACOSX_JAVA2_PLUGIN_DIR) == 0)
+ {
+ static const char *const java2PluginDirPath =
+ "/System/Library/Java/Support/Deploy.bundle/Contents/Resources/";
+ rv = NS_NewNativeLocalFile(nsDependentCString(java2PluginDirPath), true, getter_AddRefs(localFile));
+ }
+#else
+ else if (nsCRT::strcmp(prop, NS_ENV_PLUGINS_DIR) == 0)
+ {
+ NS_ERROR("Don't use nsAppFileLocationProvider::GetFile(NS_ENV_PLUGINS_DIR, ...). "
+ "Use nsAppFileLocationProvider::GetFiles(...).");
+ const char *pathVar = PR_GetEnv("MOZ_PLUGIN_PATH");
+ if (pathVar && *pathVar)
+ rv = NS_NewNativeLocalFile(nsDependentCString(pathVar), true,
+ getter_AddRefs(localFile));
+ }
+ else if (nsCRT::strcmp(prop, NS_USER_PLUGINS_DIR) == 0)
+ {
+#ifdef ENABLE_SYSTEM_EXTENSION_DIRS
+ rv = GetProductDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv))
+ rv = localFile->AppendRelativeNativePath(PLUGINS_DIR_NAME);
+#else
+ rv = NS_ERROR_FAILURE;
+#endif
+ }
+#ifdef XP_UNIX
+ else if (nsCRT::strcmp(prop, NS_SYSTEM_PLUGINS_DIR) == 0) {
+#ifdef ENABLE_SYSTEM_EXTENSION_DIRS
+ static const char *const sysLPlgDir =
+#if defined(HAVE_USR_LIB64_DIR) && defined(__LP64__)
+ "/usr/lib64/mozilla/plugins";
+#elif defined(__OpenBSD__) || defined (__FreeBSD__)
+ "/usr/local/lib/mozilla/plugins";
+#else
+ "/usr/lib/mozilla/plugins";
+#endif
+ rv = NS_NewNativeLocalFile(nsDependentCString(sysLPlgDir),
+ false, getter_AddRefs(localFile));
+#else
+ rv = NS_ERROR_FAILURE;
+#endif
+ }
+#endif
+#endif
+ else if (nsCRT::strcmp(prop, NS_APP_SEARCH_DIR) == 0)
+ {
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ if (NS_SUCCEEDED(rv))
+ rv = localFile->AppendRelativeNativePath(SEARCH_DIR_NAME);
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_USER_SEARCH_DIR) == 0)
+ {
+ rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, _retval);
+ if (NS_SUCCEEDED(rv))
+ rv = (*_retval)->AppendNative(SEARCH_DIR_NAME);
+ }
+ else if (nsCRT::strcmp(prop, NS_APP_INSTALL_CLEANUP_DIR) == 0)
+ {
+ // This is cloned so that embeddors will have a hook to override
+ // with their own cleanup dir. See bugzilla bug #105087
+ rv = CloneMozBinDirectory(getter_AddRefs(localFile));
+ }
+
+ if (localFile && NS_SUCCEEDED(rv))
+ return localFile->QueryInterface(NS_GET_IID(nsIFile), (void**)_retval);
+
+ return rv;
+}
+
+
+NS_METHOD nsAppFileLocationProvider::CloneMozBinDirectory(nsIFile **aLocalFile)
+{
+ NS_ENSURE_ARG_POINTER(aLocalFile);
+ nsresult rv;
+
+ if (!mMozBinDirectory)
+ {
+ // Get the mozilla bin directory
+ // 1. Check the directory service first for NS_XPCOM_CURRENT_PROCESS_DIR
+ // This will be set if a directory was passed to NS_InitXPCOM
+ // 2. If that doesn't work, set it to be the current process directory
+ nsCOMPtr<nsIProperties>
+ directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(mMozBinDirectory));
+ if (NS_FAILED(rv)) {
+ rv = directoryService->Get(NS_OS_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(mMozBinDirectory));
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ }
+
+ nsCOMPtr<nsIFile> aFile;
+ rv = mMozBinDirectory->Clone(getter_AddRefs(aFile));
+ if (NS_FAILED(rv))
+ return rv;
+
+ NS_IF_ADDREF(*aLocalFile = aFile);
+ return NS_OK;
+}
+
+
+//----------------------------------------------------------------------------------------
+// GetProductDirectory - Gets the directory which contains the application data folder
+//
+// UNIX : ~/.mozilla/
+// WIN : <Application Data folder on user's machine>\Mozilla
+// Mac : :Documents:Mozilla:
+//----------------------------------------------------------------------------------------
+NS_METHOD nsAppFileLocationProvider::GetProductDirectory(nsIFile **aLocalFile, bool aLocal)
+{
+ NS_ENSURE_ARG_POINTER(aLocalFile);
+
+ nsresult rv;
+ bool exists;
+ nsCOMPtr<nsIFile> localDir;
+
+#if defined(MOZ_WIDGET_COCOA)
+ FSRef fsRef;
+ OSType folderType = aLocal ? (OSType) kCachedDataFolderType : (OSType) kDomainLibraryFolderType;
+ OSErr err = ::FSFindFolder(kUserDomain, folderType, kCreateFolder, &fsRef);
+ if (err) return NS_ERROR_FAILURE;
+ NS_NewLocalFile(EmptyString(), true, getter_AddRefs(localDir));
+ if (!localDir) return NS_ERROR_FAILURE;
+ nsCOMPtr<nsILocalFileMac> localDirMac(do_QueryInterface(localDir));
+ rv = localDirMac->InitWithFSRef(&fsRef);
+ if (NS_FAILED(rv)) return rv;
+#elif defined(XP_OS2)
+ nsCOMPtr<nsIProperties> directoryService =
+ do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+ rv = directoryService->Get(NS_OS2_HOME_DIR, NS_GET_IID(nsIFile), getter_AddRefs(localDir));
+ if (NS_FAILED(rv)) return rv;
+#elif defined(XP_WIN)
+ nsCOMPtr<nsIProperties> directoryService =
+ do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+ const char* prop = aLocal ? NS_WIN_LOCAL_APPDATA_DIR : NS_WIN_APPDATA_DIR;
+ rv = directoryService->Get(prop, NS_GET_IID(nsIFile), getter_AddRefs(localDir));
+ if (NS_FAILED(rv)) return rv;
+#elif defined(XP_UNIX)
+ rv = NS_NewNativeLocalFile(nsDependentCString(PR_GetEnv("HOME")), true, getter_AddRefs(localDir));
+ if (NS_FAILED(rv)) return rv;
+#else
+#error dont_know_how_to_get_product_dir_on_your_platform
+#endif
+
+ rv = localDir->AppendRelativeNativePath(DEFAULT_PRODUCT_DIR);
+ if (NS_FAILED(rv)) return rv;
+ rv = localDir->Exists(&exists);
+
+ if (NS_SUCCEEDED(rv) && !exists)
+ rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
+
+ if (NS_FAILED(rv)) return rv;
+
+ *aLocalFile = localDir;
+ NS_ADDREF(*aLocalFile);
+
+ return rv;
+}
+
+
+//----------------------------------------------------------------------------------------
+// GetDefaultUserProfileRoot - Gets the directory which contains each user profile dir
+//
+// UNIX : ~/.mozilla/
+// WIN : <Application Data folder on user's machine>\Mozilla\Profiles
+// Mac : :Documents:Mozilla:Profiles:
+//----------------------------------------------------------------------------------------
+NS_METHOD nsAppFileLocationProvider::GetDefaultUserProfileRoot(nsIFile **aLocalFile, bool aLocal)
+{
+ NS_ENSURE_ARG_POINTER(aLocalFile);
+
+ nsresult rv;
+ nsCOMPtr<nsIFile> localDir;
+
+ rv = GetProductDirectory(getter_AddRefs(localDir), aLocal);
+ if (NS_FAILED(rv)) return rv;
+
+#if defined(MOZ_WIDGET_COCOA) || defined(XP_OS2) || defined(XP_WIN)
+ // These 3 platforms share this part of the path - do them as one
+ rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("Profiles"));
+ if (NS_FAILED(rv)) return rv;
+
+ bool exists;
+ rv = localDir->Exists(&exists);
+ if (NS_SUCCEEDED(rv) && !exists)
+ rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0775);
+ if (NS_FAILED(rv)) return rv;
+#endif
+
+ *aLocalFile = localDir;
+ NS_ADDREF(*aLocalFile);
+
+ return rv;
+}
+
+//*****************************************************************************
+// nsAppFileLocationProvider::nsIDirectoryServiceProvider2
+//*****************************************************************************
+
+class nsAppDirectoryEnumerator : public nsISimpleEnumerator
+{
+ public:
+ NS_DECL_ISUPPORTS
+
+ /**
+ * aKeyList is a null-terminated list of properties which are provided by aProvider
+ * They do not need to be publicly defined keys.
+ */
+ nsAppDirectoryEnumerator(nsIDirectoryServiceProvider *aProvider,
+ const char* aKeyList[]) :
+ mProvider(aProvider),
+ mCurrentKey(aKeyList)
+ {
+ }
+
+ NS_IMETHOD HasMoreElements(bool *result)
+ {
+ while (!mNext && *mCurrentKey)
+ {
+ bool dontCare;
+ nsCOMPtr<nsIFile> testFile;
+ (void)mProvider->GetFile(*mCurrentKey++, &dontCare, getter_AddRefs(testFile));
+ // Don't return a file which does not exist.
+ bool exists;
+ if (testFile && NS_SUCCEEDED(testFile->Exists(&exists)) && exists)
+ mNext = testFile;
+ }
+ *result = mNext != nullptr;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetNext(nsISupports **result)
+ {
+ NS_ENSURE_ARG_POINTER(result);
+ *result = nullptr;
+
+ bool hasMore;
+ HasMoreElements(&hasMore);
+ if (!hasMore)
+ return NS_ERROR_FAILURE;
+
+ *result = mNext;
+ NS_IF_ADDREF(*result);
+ mNext = nullptr;
+
+ return *result ? NS_OK : NS_ERROR_FAILURE;
+ }
+
+ // Virtual destructor since subclass nsPathsDirectoryEnumerator
+ // does not re-implement Release()
+
+ virtual ~nsAppDirectoryEnumerator()
+ {
+ }
+
+ protected:
+ nsIDirectoryServiceProvider *mProvider;
+ const char** mCurrentKey;
+ nsCOMPtr<nsIFile> mNext;
+};
+
+NS_IMPL_ISUPPORTS1(nsAppDirectoryEnumerator, nsISimpleEnumerator)
+
+/* nsPathsDirectoryEnumerator and PATH_SEPARATOR
+ * are not used on MacOS/X. */
+
+#if defined(XP_WIN) || defined(XP_OS2) /* Win32 and OS/2 */
+#define PATH_SEPARATOR ';'
+#else
+#define PATH_SEPARATOR ':'
+#endif
+
+class nsPathsDirectoryEnumerator : public nsAppDirectoryEnumerator
+{
+ public:
+ /**
+ * aKeyList is a null-terminated list.
+ * The first element is a path list.
+ * The remainder are properties provided by aProvider.
+ * They do not need to be publicly defined keys.
+ */
+ nsPathsDirectoryEnumerator(nsIDirectoryServiceProvider *aProvider,
+ const char* aKeyList[]) :
+ nsAppDirectoryEnumerator(aProvider, aKeyList+1),
+ mEndPath(aKeyList[0])
+ {
+ }
+
+ NS_IMETHOD HasMoreElements(bool *result)
+ {
+ if (mEndPath)
+ while (!mNext && *mEndPath)
+ {
+ const char *pathVar = mEndPath;
+
+ // skip PATH_SEPARATORs at the begining of the mEndPath
+ while (*pathVar == PATH_SEPARATOR) pathVar++;
+
+ do { ++mEndPath; } while (*mEndPath && *mEndPath != PATH_SEPARATOR);
+
+ nsCOMPtr<nsIFile> localFile;
+ NS_NewNativeLocalFile(Substring(pathVar, mEndPath),
+ true,
+ getter_AddRefs(localFile));
+ if (*mEndPath == PATH_SEPARATOR)
+ ++mEndPath;
+ // Don't return a "file" (directory) which does not exist.
+ bool exists;
+ if (localFile &&
+ NS_SUCCEEDED(localFile->Exists(&exists)) &&
+ exists)
+ mNext = localFile;
+ }
+ if (mNext)
+ *result = true;
+ else
+ nsAppDirectoryEnumerator::HasMoreElements(result);
+
+ return NS_OK;
+ }
+
+ protected:
+ const char *mEndPath;
+};
+
+NS_IMETHODIMP
+nsAppFileLocationProvider::GetFiles(const char *prop, nsISimpleEnumerator **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = nullptr;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ if (!nsCRT::strcmp(prop, NS_APP_PLUGINS_DIR_LIST))
+ {
+#ifdef MOZ_WIDGET_COCOA
+ // As of Java for Mac OS X 10.5 Update 10, Apple has (in effect) deprecated Java Plugin2 on
+ // on OS X 10.5, and removed the soft link to it from /Library/Internet Plug-Ins/. Java
+ // Plugin2 is still present and usable, but there are no longer any links to it in the
+ // "normal" locations. So we won't be able to find it unless we look in the "non-normal"
+ // location where it actually is. Safari can use the WebKit-specific JavaPluginCocoa.bundle,
+ // which (of course) is still fully supported on OS X 10.5. But we have no alternative to
+ // using Java Plugin2. For more information see bug 668639.
+ static const char* keys[] = { NS_APP_PLUGINS_DIR, NS_MACOSX_USER_PLUGIN_DIR,
+ NS_MACOSX_LOCAL_PLUGIN_DIR,
+ IsOSXLeopard() ? NS_MACOSX_JAVA2_PLUGIN_DIR : nullptr, nullptr };
+ *_retval = new nsAppDirectoryEnumerator(this, keys);
+#else
+#ifdef XP_UNIX
+ static const char* keys[] = { nullptr, NS_USER_PLUGINS_DIR, NS_APP_PLUGINS_DIR, NS_SYSTEM_PLUGINS_DIR, nullptr };
+#else
+ static const char* keys[] = { nullptr, NS_USER_PLUGINS_DIR, NS_APP_PLUGINS_DIR, nullptr };
+#endif
+ if (!keys[0] && !(keys[0] = PR_GetEnv("MOZ_PLUGIN_PATH"))) {
+ static const char nullstr = 0;
+ keys[0] = &nullstr;
+ }
+ *_retval = new nsPathsDirectoryEnumerator(this, keys);
+#endif
+ NS_IF_ADDREF(*_retval);
+ rv = *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+ }
+ if (!nsCRT::strcmp(prop, NS_APP_SEARCH_DIR_LIST))
+ {
+ static const char* keys[] = { nullptr, NS_APP_SEARCH_DIR, NS_APP_USER_SEARCH_DIR, nullptr };
+ if (!keys[0] && !(keys[0] = PR_GetEnv("MOZ_SEARCH_ENGINE_PATH"))) {
+ static const char nullstr = 0;
+ keys[0] = &nullstr;
+ }
+ *_retval = new nsPathsDirectoryEnumerator(this, keys);
+ NS_IF_ADDREF(*_retval);
+ rv = *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+ }
+ return rv;
+}
+
+#if defined(MOZ_WIDGET_COCOA)
+bool
+nsAppFileLocationProvider::IsOSXLeopard()
+{
+ static SInt32 version = 0;
+
+ if (!version) {
+ OSErr err = ::Gestalt(gestaltSystemVersion, &version);
+ if (err != noErr) {
+ version = 0;
+ } else {
+ version &= 0xFFFF; // The system version is in the low order word
+ }
+ }
+
+ return ((version >= 0x1050) && (version < 0x1060));
+}
+#endif
diff --git a/xpcom/io/nsAppFileLocationProvider.h b/xpcom/io/nsAppFileLocationProvider.h
new file mode 100644
index 000000000..8ecf78d53
--- /dev/null
+++ b/xpcom/io/nsAppFileLocationProvider.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIDirectoryService.h"
+#include "nsIFile.h"
+#include "mozilla/Attributes.h"
+
+class nsIFile;
+
+//*****************************************************************************
+// class nsAppFileLocationProvider
+//*****************************************************************************
+
+class nsAppFileLocationProvider MOZ_FINAL : public nsIDirectoryServiceProvider2
+{
+public:
+ nsAppFileLocationProvider();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER2
+
+private:
+ ~nsAppFileLocationProvider() {}
+
+protected:
+ NS_METHOD CloneMozBinDirectory(nsIFile **aLocalFile);
+ /**
+ * Get the product directory. This is a user-specific directory for storing
+ * application settings (e.g. the Application Data directory on windows
+ * systems).
+ * @param aLocal If true, should try to get a directory that is only stored
+ * locally (ie not transferred with roaming profiles)
+ */
+ NS_METHOD GetProductDirectory(nsIFile **aLocalFile,
+ bool aLocal = false);
+ NS_METHOD GetDefaultUserProfileRoot(nsIFile **aLocalFile,
+ bool aLocal = false);
+
+#if defined(MOZ_WIDGET_COCOA)
+ static bool IsOSXLeopard();
+#endif
+
+ nsCOMPtr<nsIFile> mMozBinDirectory;
+};
diff --git a/xpcom/io/nsBinaryStream.cpp b/xpcom/io/nsBinaryStream.cpp
new file mode 100644
index 000000000..8c2cc2397
--- /dev/null
+++ b/xpcom/io/nsBinaryStream.cpp
@@ -0,0 +1,823 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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/. */
+
+/**
+ * This file contains implementations of the nsIBinaryInputStream and
+ * nsIBinaryOutputStream interfaces. Together, these interfaces allows reading
+ * and writing of primitive data types (integers, floating-point values,
+ * booleans, etc.) to a stream in a binary, untagged, fixed-endianness format.
+ * This might be used, for example, to implement network protocols or to
+ * produce architecture-neutral binary disk files, i.e. ones that can be read
+ * and written by both big-endian and little-endian platforms. Output is
+ * written in big-endian order (high-order byte first), as this is traditional
+ * network order.
+ *
+ * @See nsIBinaryInputStream
+ * @See nsIBinaryOutputStream
+ */
+#include <string.h>
+#include "nsBinaryStream.h"
+#include "nsCRT.h"
+#include "prlong.h"
+#include "nsString.h"
+#include "nsISerializable.h"
+#include "nsIClassInfo.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIURI.h" // for NS_IURI_IID
+#include "mozilla/Endian.h"
+
+#include "jsapi.h"
+#include "jsfriendapi.h"
+
+NS_IMPL_ISUPPORTS3(nsBinaryOutputStream, nsIObjectOutputStream, nsIBinaryOutputStream, nsIOutputStream)
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Flush()
+{
+ NS_ENSURE_STATE(mOutputStream);
+ return mOutputStream->Flush();
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Close()
+{
+ NS_ENSURE_STATE(mOutputStream);
+ return mOutputStream->Close();
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Write(const char *aBuf, uint32_t aCount, uint32_t *aActualBytes)
+{
+ NS_ENSURE_STATE(mOutputStream);
+ return mOutputStream->Write(aBuf, aCount, aActualBytes);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval)
+{
+ NS_NOTREACHED("WriteFrom");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval)
+{
+ NS_NOTREACHED("WriteSegments");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::IsNonBlocking(bool *aNonBlocking)
+{
+ NS_ENSURE_STATE(mOutputStream);
+ return mOutputStream->IsNonBlocking(aNonBlocking);
+}
+
+nsresult
+nsBinaryOutputStream::WriteFully(const char *aBuf, uint32_t aCount)
+{
+ NS_ENSURE_STATE(mOutputStream);
+
+ nsresult rv;
+ uint32_t bytesWritten;
+
+ rv = mOutputStream->Write(aBuf, aCount, &bytesWritten);
+ if (NS_FAILED(rv)) return rv;
+ if (bytesWritten != aCount)
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::SetOutputStream(nsIOutputStream *aOutputStream)
+{
+ NS_ENSURE_ARG_POINTER(aOutputStream);
+ mOutputStream = aOutputStream;
+ mBufferAccess = do_QueryInterface(aOutputStream);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteBoolean(bool aBoolean)
+{
+ return Write8(aBoolean);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Write8(uint8_t aByte)
+{
+ return WriteFully((const char*)&aByte, sizeof aByte);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Write16(uint16_t a16)
+{
+ a16 = mozilla::NativeEndian::swapToBigEndian(a16);
+ return WriteFully((const char*)&a16, sizeof a16);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Write32(uint32_t a32)
+{
+ a32 = mozilla::NativeEndian::swapToBigEndian(a32);
+ return WriteFully((const char*)&a32, sizeof a32);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::Write64(uint64_t a64)
+{
+ nsresult rv;
+ uint32_t bytesWritten;
+
+ a64 = mozilla::NativeEndian::swapToBigEndian(a64);
+ rv = Write(reinterpret_cast<char*>(&a64), sizeof a64, &bytesWritten);
+ if (NS_FAILED(rv)) return rv;
+ if (bytesWritten != sizeof a64)
+ return NS_ERROR_FAILURE;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteFloat(float aFloat)
+{
+ NS_ASSERTION(sizeof(float) == sizeof (uint32_t),
+ "False assumption about sizeof(float)");
+ return Write32(*reinterpret_cast<uint32_t*>(&aFloat));
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteDouble(double aDouble)
+{
+ NS_ASSERTION(sizeof(double) == sizeof(uint64_t),
+ "False assumption about sizeof(double)");
+ return Write64(*reinterpret_cast<uint64_t*>(&aDouble));
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteStringZ(const char *aString)
+{
+ uint32_t length;
+ nsresult rv;
+
+ length = strlen(aString);
+ rv = Write32(length);
+ if (NS_FAILED(rv)) return rv;
+ return WriteFully(aString, length);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteWStringZ(const PRUnichar* aString)
+{
+ uint32_t length, byteCount;
+ nsresult rv;
+
+ length = NS_strlen(aString);
+ rv = Write32(length);
+ if (NS_FAILED(rv)) return rv;
+
+ if (length == 0)
+ return NS_OK;
+ byteCount = length * sizeof(PRUnichar);
+
+#ifdef IS_BIG_ENDIAN
+ rv = WriteBytes(reinterpret_cast<const char*>(aString), byteCount);
+#else
+ // XXX use WriteSegments here to avoid copy!
+ PRUnichar *copy, temp[64];
+ if (length <= 64) {
+ copy = temp;
+ } else {
+ copy = reinterpret_cast<PRUnichar*>(moz_malloc(byteCount));
+ if (!copy)
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ NS_ASSERTION((uintptr_t(aString) & 0x1) == 0, "aString not properly aligned");
+ mozilla::NativeEndian::copyAndSwapToBigEndian(copy, aString, length);
+ rv = WriteBytes(reinterpret_cast<const char*>(copy), byteCount);
+ if (copy != temp)
+ moz_free(copy);
+#endif
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteUtf8Z(const PRUnichar* aString)
+{
+ return WriteStringZ(NS_ConvertUTF16toUTF8(aString).get());
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteBytes(const char *aString, uint32_t aLength)
+{
+ nsresult rv;
+ uint32_t bytesWritten;
+
+ rv = Write(aString, aLength, &bytesWritten);
+ if (NS_FAILED(rv)) return rv;
+ if (bytesWritten != aLength)
+ return NS_ERROR_FAILURE;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteByteArray(uint8_t *aBytes, uint32_t aLength)
+{
+ return WriteBytes(reinterpret_cast<char *>(aBytes), aLength);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteObject(nsISupports* aObject, bool aIsStrongRef)
+{
+ return WriteCompoundObject(aObject, NS_GET_IID(nsISupports),
+ aIsStrongRef);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteSingleRefObject(nsISupports* aObject)
+{
+ return WriteCompoundObject(aObject, NS_GET_IID(nsISupports),
+ true);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteCompoundObject(nsISupports* aObject,
+ const nsIID& aIID,
+ bool aIsStrongRef)
+{
+ // Can't deal with weak refs
+ NS_ENSURE_TRUE(aIsStrongRef, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(aObject);
+ NS_ENSURE_TRUE(classInfo, NS_ERROR_NOT_AVAILABLE);
+
+ nsCOMPtr<nsISerializable> serializable = do_QueryInterface(aObject);
+ NS_ENSURE_TRUE(serializable, NS_ERROR_NOT_AVAILABLE);
+
+ nsCID cid;
+ classInfo->GetClassIDNoAlloc(&cid);
+
+ nsresult rv = WriteID(cid);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = WriteID(aIID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return serializable->Write(this);
+}
+
+NS_IMETHODIMP
+nsBinaryOutputStream::WriteID(const nsIID& aIID)
+{
+ nsresult rv = Write32(aIID.m0);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = Write16(aIID.m1);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = Write16(aIID.m2);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (int i = 0; i < 8; ++i) {
+ rv = Write8(aIID.m3[i]);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP_(char*)
+nsBinaryOutputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
+{
+ if (mBufferAccess)
+ return mBufferAccess->GetBuffer(aLength, aAlignMask);
+ return nullptr;
+}
+
+NS_IMETHODIMP_(void)
+nsBinaryOutputStream::PutBuffer(char* aBuffer, uint32_t aLength)
+{
+ if (mBufferAccess)
+ mBufferAccess->PutBuffer(aBuffer, aLength);
+}
+
+NS_IMPL_ISUPPORTS3(nsBinaryInputStream, nsIObjectInputStream, nsIBinaryInputStream, nsIInputStream)
+
+NS_IMETHODIMP
+nsBinaryInputStream::Available(uint64_t* aResult)
+{
+ NS_ENSURE_STATE(mInputStream);
+ return mInputStream->Available(aResult);
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t *aNumRead)
+{
+ NS_ENSURE_STATE(mInputStream);
+
+ // mInputStream might give us short reads, so deal with that.
+ uint32_t totalRead = 0;
+
+ uint32_t bytesRead;
+ do {
+ nsresult rv = mInputStream->Read(aBuffer, aCount, &bytesRead);
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK && totalRead != 0) {
+ // We already read some data. Return it.
+ break;
+ }
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ totalRead += bytesRead;
+ aBuffer += bytesRead;
+ aCount -= bytesRead;
+ } while (aCount != 0 && bytesRead != 0);
+
+ *aNumRead = totalRead;
+
+ return NS_OK;
+}
+
+
+// when forwarding ReadSegments to mInputStream, we need to make sure
+// 'this' is being passed to the writer each time. To do this, we need
+// a thunking function which keeps the real input stream around.
+
+// the closure wrapper
+struct ReadSegmentsClosure {
+ nsIInputStream* mRealInputStream;
+ void* mRealClosure;
+ nsWriteSegmentFun mRealWriter;
+ nsresult mRealResult;
+ uint32_t mBytesRead; // to properly implement aToOffset
+};
+
+// the thunking function
+static NS_METHOD
+ReadSegmentForwardingThunk(nsIInputStream* aStream,
+ void *aClosure,
+ const char* aFromSegment,
+ uint32_t aToOffset,
+ uint32_t aCount,
+ uint32_t *aWriteCount)
+{
+ ReadSegmentsClosure* thunkClosure =
+ reinterpret_cast<ReadSegmentsClosure*>(aClosure);
+
+ NS_ASSERTION(NS_SUCCEEDED(thunkClosure->mRealResult),
+ "How did this get to be a failure status?");
+
+ thunkClosure->mRealResult =
+ thunkClosure->mRealWriter(thunkClosure->mRealInputStream,
+ thunkClosure->mRealClosure,
+ aFromSegment,
+ thunkClosure->mBytesRead + aToOffset,
+ aCount, aWriteCount);
+
+ return thunkClosure->mRealResult;
+}
+
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure, uint32_t count, uint32_t *_retval)
+{
+ NS_ENSURE_STATE(mInputStream);
+
+ ReadSegmentsClosure thunkClosure = { this, closure, writer, NS_OK, 0 };
+
+ // mInputStream might give us short reads, so deal with that.
+ uint32_t bytesRead;
+ do {
+ nsresult rv = mInputStream->ReadSegments(ReadSegmentForwardingThunk,
+ &thunkClosure,
+ count, &bytesRead);
+
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK && thunkClosure.mBytesRead != 0) {
+ // We already read some data. Return it.
+ break;
+ }
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ thunkClosure.mBytesRead += bytesRead;
+ count -= bytesRead;
+ } while (count != 0 && bytesRead != 0 &&
+ NS_SUCCEEDED(thunkClosure.mRealResult));
+
+ *_retval = thunkClosure.mBytesRead;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::IsNonBlocking(bool *aNonBlocking)
+{
+ NS_ENSURE_STATE(mInputStream);
+ return mInputStream->IsNonBlocking(aNonBlocking);
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::Close()
+{
+ NS_ENSURE_STATE(mInputStream);
+ return mInputStream->Close();
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::SetInputStream(nsIInputStream *aInputStream)
+{
+ NS_ENSURE_ARG_POINTER(aInputStream);
+ mInputStream = aInputStream;
+ mBufferAccess = do_QueryInterface(aInputStream);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadBoolean(bool* aBoolean)
+{
+ uint8_t byteResult;
+ nsresult rv = Read8(&byteResult);
+ if (NS_FAILED(rv)) return rv;
+ *aBoolean = !!byteResult;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::Read8(uint8_t* aByte)
+{
+ nsresult rv;
+ uint32_t bytesRead;
+
+ rv = Read(reinterpret_cast<char*>(aByte), sizeof(*aByte), &bytesRead);
+ if (NS_FAILED(rv)) return rv;
+ if (bytesRead != 1)
+ return NS_ERROR_FAILURE;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::Read16(uint16_t* a16)
+{
+ nsresult rv;
+ uint32_t bytesRead;
+
+ rv = Read(reinterpret_cast<char*>(a16), sizeof *a16, &bytesRead);
+ if (NS_FAILED(rv)) return rv;
+ if (bytesRead != sizeof *a16)
+ return NS_ERROR_FAILURE;
+ *a16 = mozilla::NativeEndian::swapFromBigEndian(*a16);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::Read32(uint32_t* a32)
+{
+ nsresult rv;
+ uint32_t bytesRead;
+
+ rv = Read(reinterpret_cast<char*>(a32), sizeof *a32, &bytesRead);
+ if (NS_FAILED(rv)) return rv;
+ if (bytesRead != sizeof *a32)
+ return NS_ERROR_FAILURE;
+ *a32 = mozilla::NativeEndian::swapFromBigEndian(*a32);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::Read64(uint64_t* a64)
+{
+ nsresult rv;
+ uint32_t bytesRead;
+
+ rv = Read(reinterpret_cast<char*>(a64), sizeof *a64, &bytesRead);
+ if (NS_FAILED(rv)) return rv;
+ if (bytesRead != sizeof *a64)
+ return NS_ERROR_FAILURE;
+ *a64 = mozilla::NativeEndian::swapFromBigEndian(*a64);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadFloat(float* aFloat)
+{
+ NS_ASSERTION(sizeof(float) == sizeof (uint32_t),
+ "False assumption about sizeof(float)");
+ return Read32(reinterpret_cast<uint32_t*>(aFloat));
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadDouble(double* aDouble)
+{
+ NS_ASSERTION(sizeof(double) == sizeof(uint64_t),
+ "False assumption about sizeof(double)");
+ return Read64(reinterpret_cast<uint64_t*>(aDouble));
+}
+
+static NS_METHOD
+WriteSegmentToCString(nsIInputStream* aStream,
+ void *aClosure,
+ const char* aFromSegment,
+ uint32_t aToOffset,
+ uint32_t aCount,
+ uint32_t *aWriteCount)
+{
+ nsACString* outString = static_cast<nsACString*>(aClosure);
+
+ outString->Append(aFromSegment, aCount);
+
+ *aWriteCount = aCount;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadCString(nsACString& aString)
+{
+ nsresult rv;
+ uint32_t length, bytesRead;
+
+ rv = Read32(&length);
+ if (NS_FAILED(rv)) return rv;
+
+ aString.Truncate();
+ rv = ReadSegments(WriteSegmentToCString, &aString, length, &bytesRead);
+ if (NS_FAILED(rv)) return rv;
+
+ if (bytesRead != length)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+
+// sometimes, WriteSegmentToString will be handed an odd-number of
+// bytes, which means we only have half of the last PRUnichar
+struct WriteStringClosure {
+ PRUnichar *mWriteCursor;
+ bool mHasCarryoverByte;
+ char mCarryoverByte;
+};
+
+// there are a few cases we have to account for here:
+// * even length buffer, no carryover - easy, just append
+// * odd length buffer, no carryover - the last byte needs to be saved
+// for carryover
+// * odd length buffer, with carryover - first byte needs to be used
+// with the carryover byte, and
+// the rest of the even length
+// buffer is appended as normal
+// * even length buffer, with carryover - the first byte needs to be
+// used with the previous carryover byte.
+// this gives you an odd length buffer,
+// so you have to save the last byte for
+// the next carryover
+
+
+// same version of the above, but with correct casting and endian swapping
+static NS_METHOD
+WriteSegmentToString(nsIInputStream* aStream,
+ void *aClosure,
+ const char* aFromSegment,
+ uint32_t aToOffset,
+ uint32_t aCount,
+ uint32_t *aWriteCount)
+{
+ NS_PRECONDITION(aCount > 0, "Why are we being told to write 0 bytes?");
+ NS_PRECONDITION(sizeof(PRUnichar) == 2, "We can't handle other sizes!");
+
+ WriteStringClosure* closure = static_cast<WriteStringClosure*>(aClosure);
+ PRUnichar *cursor = closure->mWriteCursor;
+
+ // we're always going to consume the whole buffer no matter what
+ // happens, so take care of that right now.. that allows us to
+ // tweak aCount later. Do NOT move this!
+ *aWriteCount = aCount;
+
+ // if the last Write had an odd-number of bytes read, then
+ if (closure->mHasCarryoverByte) {
+ // re-create the two-byte sequence we want to work with
+ char bytes[2] = { closure->mCarryoverByte, *aFromSegment };
+ *cursor = *(PRUnichar*)bytes;
+ // Now the little endianness dance
+ mozilla::NativeEndian::swapToBigEndianInPlace(cursor, 1);
+ ++cursor;
+
+ // now skip past the first byte of the buffer.. code from here
+ // can assume normal operations, but should not assume aCount
+ // is relative to the ORIGINAL buffer
+ ++aFromSegment;
+ --aCount;
+
+ closure->mHasCarryoverByte = false;
+ }
+
+ // this array is possibly unaligned... be careful how we access it!
+ const PRUnichar *unicodeSegment =
+ reinterpret_cast<const PRUnichar*>(aFromSegment);
+
+ // calculate number of full characters in segment (aCount could be odd!)
+ uint32_t segmentLength = aCount / sizeof(PRUnichar);
+
+ // copy all data into our aligned buffer. byte swap if necessary.
+ // cursor may be unaligned, so we cannot use copyAndSwapToBigEndian directly
+ memcpy(cursor, unicodeSegment, segmentLength * sizeof(PRUnichar));
+ PRUnichar *end = cursor + segmentLength;
+ mozilla::NativeEndian::swapToBigEndianInPlace(cursor, segmentLength);
+ closure->mWriteCursor = end;
+
+ // remember this is the modifed aCount and aFromSegment,
+ // so that will take into account the fact that we might have
+ // skipped the first byte in the buffer
+ if (aCount % sizeof(PRUnichar) != 0) {
+ // we must have had a carryover byte, that we'll need the next
+ // time around
+ closure->mCarryoverByte = aFromSegment[aCount - 1];
+ closure->mHasCarryoverByte = true;
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadString(nsAString& aString)
+{
+ nsresult rv;
+ uint32_t length, bytesRead;
+
+ rv = Read32(&length);
+ if (NS_FAILED(rv)) return rv;
+
+ if (length == 0) {
+ aString.Truncate();
+ return NS_OK;
+ }
+
+ // pre-allocate output buffer, and get direct access to buffer...
+ if (!aString.SetLength(length, mozilla::fallible_t()))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsAString::iterator start;
+ aString.BeginWriting(start);
+
+ WriteStringClosure closure;
+ closure.mWriteCursor = start.get();
+ closure.mHasCarryoverByte = false;
+
+ rv = ReadSegments(WriteSegmentToString, &closure,
+ length*sizeof(PRUnichar), &bytesRead);
+ if (NS_FAILED(rv)) return rv;
+
+ NS_ASSERTION(!closure.mHasCarryoverByte, "some strange stream corruption!");
+
+ if (bytesRead != length*sizeof(PRUnichar))
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadBytes(uint32_t aLength, char* *_rval)
+{
+ nsresult rv;
+ uint32_t bytesRead;
+ char* s;
+
+ s = reinterpret_cast<char*>(moz_malloc(aLength));
+ if (!s)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = Read(s, aLength, &bytesRead);
+ if (NS_FAILED(rv)) {
+ moz_free(s);
+ return rv;
+ }
+ if (bytesRead != aLength) {
+ moz_free(s);
+ return NS_ERROR_FAILURE;
+ }
+
+ *_rval = s;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadByteArray(uint32_t aLength, uint8_t* *_rval)
+{
+ return ReadBytes(aLength, reinterpret_cast<char **>(_rval));
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadArrayBuffer(uint32_t aLength, const JS::Value& aBuffer, JSContext* cx)
+{
+ if (!aBuffer.isObject()) {
+ return NS_ERROR_FAILURE;
+ }
+ JS::RootedObject buffer(cx, &aBuffer.toObject());
+ if (!JS_IsArrayBufferObject(buffer) ||
+ JS_GetArrayBufferByteLength(buffer) < aLength) {
+ return NS_ERROR_FAILURE;
+ }
+ uint8_t* data = JS_GetArrayBufferData(&aBuffer.toObject());
+ if (!data) {
+ return NS_ERROR_FAILURE;
+ }
+
+ uint32_t bytesRead;
+ nsresult rv = Read(reinterpret_cast<char*>(data), aLength, &bytesRead);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (bytesRead != aLength) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadObject(bool aIsStrongRef, nsISupports* *aObject)
+{
+ nsCID cid;
+ nsIID iid;
+ nsresult rv = ReadID(&cid);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ReadID(&iid);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // HACK: Intercept old (pre-gecko6) nsIURI IID, and replace with
+ // the updated IID, so that we're QI'ing to an actual interface.
+ // (As soon as we drop support for upgrading from pre-gecko6, we can
+ // remove this chunk.)
+ static const nsIID oldURIiid =
+ { 0x7a22cc0, 0xce5, 0x11d3,
+ { 0x93, 0x31, 0x0, 0x10, 0x4b, 0xa0, 0xfd, 0x40 }};
+
+ // hackaround for bug 670542
+ static const nsIID oldURIiid2 =
+ { 0xd6d04c36, 0x0fa4, 0x4db3,
+ { 0xbe, 0x05, 0x4a, 0x18, 0x39, 0x71, 0x03, 0xe2 }};
+
+ // hackaround for bug 682031
+ static const nsIID oldURIiid3 =
+ { 0x12120b20, 0x0929, 0x40e9,
+ { 0x88, 0xcf, 0x6e, 0x08, 0x76, 0x6e, 0x8b, 0x23 }};
+
+ if (iid.Equals(oldURIiid) ||
+ iid.Equals(oldURIiid2) ||
+ iid.Equals(oldURIiid3)) {
+ const nsIID newURIiid = NS_IURI_IID;
+ iid = newURIiid;
+ }
+ // END HACK
+
+ nsCOMPtr<nsISupports> object = do_CreateInstance(cid, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISerializable> serializable = do_QueryInterface(object);
+ NS_ENSURE_TRUE(serializable, NS_ERROR_UNEXPECTED);
+
+ rv = serializable->Read(this);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return object->QueryInterface(iid, reinterpret_cast<void**>(aObject));
+}
+
+NS_IMETHODIMP
+nsBinaryInputStream::ReadID(nsID *aResult)
+{
+ nsresult rv = Read32(&aResult->m0);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = Read16(&aResult->m1);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = Read16(&aResult->m2);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (int i = 0; i < 8; ++i) {
+ rv = Read8(&aResult->m3[i]);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP_(char*)
+nsBinaryInputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask)
+{
+ if (mBufferAccess)
+ return mBufferAccess->GetBuffer(aLength, aAlignMask);
+ return nullptr;
+}
+
+NS_IMETHODIMP_(void)
+nsBinaryInputStream::PutBuffer(char* aBuffer, uint32_t aLength)
+{
+ if (mBufferAccess)
+ mBufferAccess->PutBuffer(aBuffer, aLength);
+}
diff --git a/xpcom/io/nsBinaryStream.h b/xpcom/io/nsBinaryStream.h
new file mode 100644
index 000000000..3748baf8f
--- /dev/null
+++ b/xpcom/io/nsBinaryStream.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 nsBinaryStream_h___
+#define nsBinaryStream_h___
+
+#include "nsCOMPtr.h"
+#include "nsAString.h"
+#include "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.h"
+#include "nsIStreamBufferAccess.h"
+
+#define NS_BINARYOUTPUTSTREAM_CID \
+{ /* 86c37b9a-74e7-4672-844e-6e7dd83ba484 */ \
+ 0x86c37b9a, \
+ 0x74e7, \
+ 0x4672, \
+ {0x84, 0x4e, 0x6e, 0x7d, 0xd8, 0x3b, 0xa4, 0x84} \
+}
+
+#define NS_BINARYOUTPUTSTREAM_CONTRACTID "@mozilla.org/binaryoutputstream;1"
+
+// Derive from nsIObjectOutputStream so this class can be used as a superclass
+// by nsObjectOutputStream.
+class nsBinaryOutputStream : public nsIObjectOutputStream
+{
+public:
+ nsBinaryOutputStream() {}
+ // virtual dtor since subclasses call our Release()
+ virtual ~nsBinaryOutputStream() {}
+
+protected:
+ // nsISupports methods
+ NS_DECL_ISUPPORTS
+
+ // nsIOutputStream methods
+ NS_DECL_NSIOUTPUTSTREAM
+
+ // nsIBinaryOutputStream methods
+ NS_DECL_NSIBINARYOUTPUTSTREAM
+
+ // nsIObjectOutputStream methods
+ NS_DECL_NSIOBJECTOUTPUTSTREAM
+
+ // Call Write(), ensuring that all proffered data is written
+ nsresult WriteFully(const char *aBuf, uint32_t aCount);
+
+ nsCOMPtr<nsIOutputStream> mOutputStream;
+ nsCOMPtr<nsIStreamBufferAccess> mBufferAccess;
+};
+
+#define NS_BINARYINPUTSTREAM_CID \
+{ /* c521a612-2aad-46db-b6ab-3b821fb150b1 */ \
+ 0xc521a612, \
+ 0x2aad, \
+ 0x46db, \
+ {0xb6, 0xab, 0x3b, 0x82, 0x1f, 0xb1, 0x50, 0xb1} \
+}
+
+#define NS_BINARYINPUTSTREAM_CONTRACTID "@mozilla.org/binaryinputstream;1"
+
+// Derive from nsIObjectInputStream so this class can be used as a superclass
+// by nsObjectInputStream.
+class nsBinaryInputStream : public nsIObjectInputStream
+{
+public:
+ nsBinaryInputStream() {}
+ // virtual dtor since subclasses call our Release()
+ virtual ~nsBinaryInputStream() {}
+
+protected:
+ // nsISupports methods
+ NS_DECL_ISUPPORTS
+
+ // nsIInputStream methods
+ NS_DECL_NSIINPUTSTREAM
+
+ // nsIBinaryInputStream methods
+ NS_DECL_NSIBINARYINPUTSTREAM
+
+ // nsIObjectInputStream methods
+ NS_DECL_NSIOBJECTINPUTSTREAM
+
+ nsCOMPtr<nsIInputStream> mInputStream;
+ nsCOMPtr<nsIStreamBufferAccess> mBufferAccess;
+};
+
+#endif // nsBinaryStream_h___
diff --git a/xpcom/io/nsDirectoryService.cpp b/xpcom/io/nsDirectoryService.cpp
new file mode 100644
index 000000000..f013a4f11
--- /dev/null
+++ b/xpcom/io/nsDirectoryService.cpp
@@ -0,0 +1,933 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 sts=4 et
+ * 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 "mozilla/Util.h"
+
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsLocalFile.h"
+#include "nsDebug.h"
+#include "nsStaticAtom.h"
+#include "nsEnumeratorUtils.h"
+
+#include "nsICategoryManager.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIStringEnumerator.h"
+
+#if defined(XP_WIN)
+#include <windows.h>
+#include <shlobj.h>
+#include <stdlib.h>
+#include <stdio.h>
+#elif defined(XP_UNIX)
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include "prenv.h"
+#ifdef MOZ_WIDGET_COCOA
+#include <CoreServices/CoreServices.h>
+#include <Carbon/Carbon.h>
+#endif
+#elif defined(XP_OS2)
+#define MAX_PATH _MAX_PATH
+#endif
+
+#include "SpecialSystemDirectory.h"
+#include "nsAppFileLocationProvider.h"
+
+using namespace mozilla;
+
+#define COMPONENT_DIRECTORY NS_LITERAL_CSTRING("components")
+
+// define home directory
+// For Windows platform, We are choosing Appdata folder as HOME
+#if defined (XP_WIN)
+#define HOME_DIR NS_WIN_APPDATA_DIR
+#elif defined (MOZ_WIDGET_COCOA)
+#define HOME_DIR NS_OSX_HOME_DIR
+#elif defined (XP_UNIX)
+#define HOME_DIR NS_UNIX_HOME_DIR
+#elif defined (XP_OS2)
+#define HOME_DIR NS_OS2_HOME_DIR
+#endif
+
+//----------------------------------------------------------------------------------------
+nsresult
+nsDirectoryService::GetCurrentProcessDirectory(nsIFile** aFile)
+//----------------------------------------------------------------------------------------
+{
+ NS_ENSURE_ARG_POINTER(aFile);
+ *aFile = nullptr;
+
+ // Set the component registry location:
+ if (!gService)
+ return NS_ERROR_FAILURE;
+
+ nsresult rv;
+
+ nsCOMPtr<nsIProperties> dirService;
+ rv = nsDirectoryService::Create(nullptr,
+ NS_GET_IID(nsIProperties),
+ getter_AddRefs(dirService)); // needs to be around for life of product
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (dirService)
+ {
+ nsCOMPtr <nsIFile> aLocalFile;
+ dirService->Get(NS_XPCOM_INIT_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(aLocalFile));
+ if (aLocalFile)
+ {
+ *aFile = aLocalFile;
+ NS_ADDREF(*aFile);
+ return NS_OK;
+ }
+ }
+
+ nsLocalFile* localFile = new nsLocalFile;
+
+ if (localFile == nullptr)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(localFile);
+
+
+
+#ifdef XP_WIN
+ PRUnichar buf[MAX_PATH + 1];
+ SetLastError(ERROR_SUCCESS);
+ if (GetModuleFileNameW(0, buf, mozilla::ArrayLength(buf)) &&
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ // chop off the executable name by finding the rightmost backslash
+ PRUnichar* lastSlash = wcsrchr(buf, L'\\');
+ if (lastSlash)
+ *(lastSlash + 1) = L'\0';
+
+ localFile->InitWithPath(nsDependentString(buf));
+ *aFile = localFile;
+ return NS_OK;
+ }
+
+#elif defined(MOZ_WIDGET_COCOA)
+ // Works even if we're not bundled.
+ CFBundleRef appBundle = CFBundleGetMainBundle();
+ if (appBundle != nullptr)
+ {
+ CFURLRef bundleURL = CFBundleCopyExecutableURL(appBundle);
+ if (bundleURL != nullptr)
+ {
+ CFURLRef parentURL = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, bundleURL);
+ if (parentURL)
+ {
+ // Pass true for the "resolveAgainstBase" arg to CFURLGetFileSystemRepresentation.
+ // This will resolve the relative portion of the CFURL against it base, giving a full
+ // path, which CFURLCopyFileSystemPath doesn't do.
+ char buffer[PATH_MAX];
+ if (CFURLGetFileSystemRepresentation(parentURL, true, (UInt8 *)buffer, sizeof(buffer)))
+ {
+#ifdef DEBUG_conrad
+ printf("nsDirectoryService - CurrentProcessDir is: %s\n", buffer);
+#endif
+ rv = localFile->InitWithNativePath(nsDependentCString(buffer));
+ if (NS_SUCCEEDED(rv))
+ *aFile = localFile;
+ }
+ CFRelease(parentURL);
+ }
+ CFRelease(bundleURL);
+ }
+ }
+
+ NS_ASSERTION(*aFile, "nsDirectoryService - Could not determine CurrentProcessDir.\n");
+ if (*aFile)
+ return NS_OK;
+
+#elif defined(XP_UNIX)
+
+ // In the absence of a good way to get the executable directory let
+ // us try this for unix:
+ // - if MOZILLA_FIVE_HOME is defined, that is it
+ // - else give the current directory
+ char buf[MAXPATHLEN];
+
+ // The MOZ_DEFAULT_MOZILLA_FIVE_HOME variable can be set at configure time with
+ // a --with-default-mozilla-five-home=foo autoconf flag.
+ //
+ // The idea here is to allow for builds that have a default MOZILLA_FIVE_HOME
+ // regardless of the environment. This makes it easier to write apps that
+ // embed mozilla without having to worry about setting up the environment
+ //
+ // We do this by putenv()ing the default value into the environment. Note that
+ // we only do this if it is not already set.
+#ifdef MOZ_DEFAULT_MOZILLA_FIVE_HOME
+ const char *home = PR_GetEnv("MOZILLA_FIVE_HOME");
+ if (!home || !*home)
+ {
+ putenv("MOZILLA_FIVE_HOME=" MOZ_DEFAULT_MOZILLA_FIVE_HOME);
+ }
+#endif
+
+ char *moz5 = PR_GetEnv("MOZILLA_FIVE_HOME");
+ if (moz5 && *moz5)
+ {
+ if (realpath(moz5, buf)) {
+ localFile->InitWithNativePath(nsDependentCString(buf));
+ *aFile = localFile;
+ return NS_OK;
+ }
+ }
+#if defined(DEBUG)
+ static bool firstWarning = true;
+
+ if((!moz5 || !*moz5) && firstWarning) {
+ // Warn that MOZILLA_FIVE_HOME not set, once.
+ printf("Warning: MOZILLA_FIVE_HOME not set.\n");
+ firstWarning = false;
+ }
+#endif /* DEBUG */
+
+ // Fall back to current directory.
+ if (getcwd(buf, sizeof(buf)))
+ {
+ localFile->InitWithNativePath(nsDependentCString(buf));
+ *aFile = localFile;
+ return NS_OK;
+ }
+
+#elif defined(XP_OS2)
+ PPIB ppib;
+ PTIB ptib;
+ char buffer[CCHMAXPATH];
+ DosGetInfoBlocks( &ptib, &ppib);
+ DosQueryModuleName( ppib->pib_hmte, CCHMAXPATH, buffer);
+ *strrchr( buffer, '\\') = '\0'; // XXX DBCS misery
+ localFile->InitWithNativePath(nsDependentCString(buffer));
+ *aFile = localFile;
+ return NS_OK;
+
+#endif
+
+ NS_RELEASE(localFile);
+
+ NS_ERROR("unable to get current process directory");
+ return NS_ERROR_FAILURE;
+} // GetCurrentProcessDirectory()
+
+nsDirectoryService* nsDirectoryService::gService = nullptr;
+
+nsDirectoryService::nsDirectoryService() :
+ mHashtable(256, true)
+{
+}
+
+nsresult
+nsDirectoryService::Create(nsISupports *outer, REFNSIID aIID, void **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ NS_ENSURE_NO_AGGREGATION(outer);
+
+ if (!gService)
+ {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return gService->QueryInterface(aIID, aResult);
+}
+
+#define DIR_ATOM(name_, value_) nsIAtom* nsDirectoryService::name_ = nullptr;
+#include "nsDirectoryServiceAtomList.h"
+#undef DIR_ATOM
+
+#define DIR_ATOM(name_, value_) NS_STATIC_ATOM_BUFFER(name_##_buffer, value_)
+#include "nsDirectoryServiceAtomList.h"
+#undef DIR_ATOM
+
+static const nsStaticAtom directory_atoms[] = {
+#define DIR_ATOM(name_, value_) NS_STATIC_ATOM(name_##_buffer, &nsDirectoryService::name_),
+#include "nsDirectoryServiceAtomList.h"
+#undef DIR_ATOM
+};
+
+NS_IMETHODIMP
+nsDirectoryService::Init()
+{
+ NS_NOTREACHED("nsDirectoryService::Init() for internal use only!");
+ return NS_OK;
+}
+
+void
+nsDirectoryService::RealInit()
+{
+ NS_ASSERTION(!gService,
+ "nsDirectoryService::RealInit Mustn't initialize twice!");
+
+ nsRefPtr<nsDirectoryService> self = new nsDirectoryService();
+
+ NS_RegisterStaticAtoms(directory_atoms);
+
+ // Let the list hold the only reference to the provider.
+ nsAppFileLocationProvider *defaultProvider = new nsAppFileLocationProvider;
+ self->mProviders.AppendElement(defaultProvider);
+
+ self.swap(gService);
+}
+
+bool
+nsDirectoryService::ReleaseValues(nsHashKey* key, void* data, void* closure)
+{
+ nsISupports* value = (nsISupports*)data;
+ NS_IF_RELEASE(value);
+ return true;
+}
+
+nsDirectoryService::~nsDirectoryService()
+{
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS4(nsDirectoryService, nsIProperties, nsIDirectoryService, nsIDirectoryServiceProvider, nsIDirectoryServiceProvider2)
+
+
+NS_IMETHODIMP
+nsDirectoryService::Undefine(const char* prop)
+{
+ NS_ENSURE_ARG(prop);
+
+ nsCStringKey key(prop);
+ if (!mHashtable.Exists(&key))
+ return NS_ERROR_FAILURE;
+
+ mHashtable.Remove (&key);
+ return NS_OK;
+ }
+
+NS_IMETHODIMP
+nsDirectoryService::GetKeys(uint32_t *count, char ***keys)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+struct FileData
+{
+ FileData(const char* aProperty,
+ const nsIID& aUUID) :
+ property(aProperty),
+ data(nullptr),
+ persistent(true),
+ uuid(aUUID) {}
+
+ const char* property;
+ nsISupports* data;
+ bool persistent;
+ const nsIID& uuid;
+};
+
+static bool FindProviderFile(nsIDirectoryServiceProvider* aElement,
+ FileData* aData)
+{
+ nsresult rv;
+ if (aData->uuid.Equals(NS_GET_IID(nsISimpleEnumerator))) {
+ // Not all providers implement this iface
+ nsCOMPtr<nsIDirectoryServiceProvider2> prov2 = do_QueryInterface(aElement);
+ if (prov2)
+ {
+ nsCOMPtr<nsISimpleEnumerator> newFiles;
+ rv = prov2->GetFiles(aData->property, getter_AddRefs(newFiles));
+ if (NS_SUCCEEDED(rv) && newFiles) {
+ if (aData->data) {
+ nsCOMPtr<nsISimpleEnumerator> unionFiles;
+
+ NS_NewUnionEnumerator(getter_AddRefs(unionFiles),
+ (nsISimpleEnumerator*) aData->data, newFiles);
+
+ if (unionFiles)
+ unionFiles.swap(* (nsISimpleEnumerator**) &aData->data);
+ }
+ else
+ {
+ NS_ADDREF(aData->data = newFiles);
+ }
+
+ aData->persistent = false; // Enumerators can never be persistent
+ return rv == NS_SUCCESS_AGGREGATE_RESULT;
+ }
+ }
+ }
+ else
+ {
+ rv = aElement->GetFile(aData->property, &aData->persistent,
+ (nsIFile **)&aData->data);
+ if (NS_SUCCEEDED(rv) && aData->data)
+ return false;
+ }
+
+ return true;
+}
+
+NS_IMETHODIMP
+nsDirectoryService::Get(const char* prop, const nsIID & uuid, void* *result)
+{
+ NS_ENSURE_ARG(prop);
+
+ nsCStringKey key(prop);
+
+ nsCOMPtr<nsISupports> value = dont_AddRef(mHashtable.Get(&key));
+
+ if (value)
+ {
+ nsCOMPtr<nsIFile> cloneFile;
+ nsCOMPtr<nsIFile> cachedFile = do_QueryInterface(value);
+ NS_ASSERTION(cachedFile,
+ "nsDirectoryService::Get nsIFile expected");
+
+ cachedFile->Clone(getter_AddRefs(cloneFile));
+ return cloneFile->QueryInterface(uuid, result);
+ }
+
+ // it is not one of our defaults, lets check any providers
+ FileData fileData(prop, uuid);
+
+ for (int32_t i = mProviders.Length() - 1; i >= 0; i--) {
+ if (!FindProviderFile(mProviders[i], &fileData)) {
+ break;
+ }
+ }
+ if (fileData.data)
+ {
+ if (fileData.persistent)
+ {
+ Set(prop, static_cast<nsIFile*>(fileData.data));
+ }
+ nsresult rv = (fileData.data)->QueryInterface(uuid, result);
+ NS_RELEASE(fileData.data); // addref occurs in FindProviderFile()
+ return rv;
+ }
+
+ FindProviderFile(static_cast<nsIDirectoryServiceProvider*>(this), &fileData);
+ if (fileData.data)
+ {
+ if (fileData.persistent)
+ {
+ Set(prop, static_cast<nsIFile*>(fileData.data));
+ }
+ nsresult rv = (fileData.data)->QueryInterface(uuid, result);
+ NS_RELEASE(fileData.data); // addref occurs in FindProviderFile()
+ return rv;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDirectoryService::Set(const char* prop, nsISupports* value)
+{
+ NS_ENSURE_ARG(prop);
+
+ nsCStringKey key(prop);
+ if (mHashtable.Exists(&key) || value == nullptr)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIFile> ourFile;
+ value->QueryInterface(NS_GET_IID(nsIFile), getter_AddRefs(ourFile));
+ if (ourFile)
+ {
+ nsCOMPtr<nsIFile> cloneFile;
+ ourFile->Clone (getter_AddRefs (cloneFile));
+ mHashtable.Put(&key, cloneFile);
+
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDirectoryService::Has(const char *prop, bool *_retval)
+{
+ NS_ENSURE_ARG(prop);
+
+ *_retval = false;
+ nsCOMPtr<nsIFile> value;
+ nsresult rv = Get(prop, NS_GET_IID(nsIFile), getter_AddRefs(value));
+ if (NS_FAILED(rv))
+ return NS_OK;
+
+ if (value)
+ {
+ *_retval = true;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDirectoryService::RegisterProvider(nsIDirectoryServiceProvider *prov)
+{
+ if (!prov)
+ return NS_ERROR_FAILURE;
+
+ mProviders.AppendElement(prov);
+ return NS_OK;
+}
+
+void
+nsDirectoryService::RegisterCategoryProviders()
+{
+ nsCOMPtr<nsICategoryManager> catman
+ (do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
+ if (!catman)
+ return;
+
+ nsCOMPtr<nsISimpleEnumerator> entries;
+ catman->EnumerateCategory(XPCOM_DIRECTORY_PROVIDER_CATEGORY,
+ getter_AddRefs(entries));
+
+ nsCOMPtr<nsIUTF8StringEnumerator> strings(do_QueryInterface(entries));
+ if (!strings)
+ return;
+
+ bool more;
+ while (NS_SUCCEEDED(strings->HasMore(&more)) && more) {
+ nsAutoCString entry;
+ strings->GetNext(entry);
+
+ nsXPIDLCString contractID;
+ catman->GetCategoryEntry(XPCOM_DIRECTORY_PROVIDER_CATEGORY, entry.get(), getter_Copies(contractID));
+
+ if (contractID) {
+ nsCOMPtr<nsIDirectoryServiceProvider> provider = do_GetService(contractID.get());
+ if (provider)
+ RegisterProvider(provider);
+ }
+ }
+}
+
+NS_IMETHODIMP
+nsDirectoryService::UnregisterProvider(nsIDirectoryServiceProvider *prov)
+{
+ if (!prov)
+ return NS_ERROR_FAILURE;
+
+ mProviders.RemoveElement(prov);
+ return NS_OK;
+}
+
+// DO NOT ADD ANY LOCATIONS TO THIS FUNCTION UNTIL YOU TALK TO: dougt@netscape.com.
+// This is meant to be a place of xpcom or system specific file locations, not
+// application specific locations. If you need the later, register a callback for
+// your application.
+
+NS_IMETHODIMP
+nsDirectoryService::GetFile(const char *prop, bool *persistent, nsIFile **_retval)
+{
+ nsCOMPtr<nsIFile> localFile;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ *_retval = nullptr;
+ *persistent = true;
+
+ nsCOMPtr<nsIAtom> inAtom = do_GetAtom(prop);
+
+ // check to see if it is one of our defaults
+
+ if (inAtom == nsDirectoryService::sCurrentProcess ||
+ inAtom == nsDirectoryService::sOS_CurrentProcessDirectory )
+ {
+ rv = GetCurrentProcessDirectory(getter_AddRefs(localFile));
+ }
+
+ // Unless otherwise set, the core pieces of the GRE exist
+ // in the current process directory.
+ else if (inAtom == nsDirectoryService::sGRE_Directory)
+ {
+ rv = GetCurrentProcessDirectory(getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sOS_DriveDirectory)
+ {
+ rv = GetSpecialSystemDirectory(OS_DriveDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sOS_TemporaryDirectory)
+ {
+ rv = GetSpecialSystemDirectory(OS_TemporaryDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sOS_CurrentProcessDirectory)
+ {
+ rv = GetSpecialSystemDirectory(OS_CurrentProcessDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sOS_CurrentWorkingDirectory)
+ {
+ rv = GetSpecialSystemDirectory(OS_CurrentWorkingDirectory, getter_AddRefs(localFile));
+ }
+
+#if defined(MOZ_WIDGET_COCOA)
+ else if (inAtom == nsDirectoryService::sDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kSystemFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sTrashDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kTrashFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sStartupDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kStartupFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sShutdownDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kShutdownFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sAppleMenuDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kAppleMenuFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sControlPanelDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kControlPanelFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sExtensionDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kExtensionFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sFontsDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kFontsFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sPreferencesDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kPreferencesFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sDocumentsDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kDocumentsFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sInternetSearchDirectory)
+ {
+ rv = GetOSXFolderType(kClassicDomain, kInternetSearchSitesFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sUserLibDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kDomainLibraryFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sOS_HomeDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kDomainTopLevelFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sDefaultDownloadDirectory)
+ {
+ // 10.5 and later, we can use kDownloadsFolderType which is defined in
+ // Folders.h as "down". However, in order to support 10.4 still, we
+ // cannot use the named constant. We'll use it's value, and if it
+ // fails, fall back to the desktop.
+#ifndef kDownloadsFolderType
+#define kDownloadsFolderType 'down'
+#endif
+
+ rv = GetOSXFolderType(kUserDomain, kDownloadsFolderType,
+ getter_AddRefs(localFile));
+ if (NS_FAILED(rv)) {
+ rv = GetOSXFolderType(kUserDomain, kDesktopFolderType,
+ getter_AddRefs(localFile));
+ }
+ }
+ else if (inAtom == nsDirectoryService::sUserDesktopDirectory ||
+ inAtom == nsDirectoryService::sOS_DesktopDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kDesktopFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sLocalDesktopDirectory)
+ {
+ rv = GetOSXFolderType(kLocalDomain, kDesktopFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sUserApplicationsDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kApplicationsFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sLocalApplicationsDirectory)
+ {
+ rv = GetOSXFolderType(kLocalDomain, kApplicationsFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sUserDocumentsDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kDocumentsFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sLocalDocumentsDirectory)
+ {
+ rv = GetOSXFolderType(kLocalDomain, kDocumentsFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sUserInternetPlugInDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kInternetPlugInFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sLocalInternetPlugInDirectory)
+ {
+ rv = GetOSXFolderType(kLocalDomain, kInternetPlugInFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sUserFrameworksDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kFrameworksFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sLocalFrameworksDirectory)
+ {
+ rv = GetOSXFolderType(kLocalDomain, kFrameworksFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sUserPreferencesDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kPreferencesFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sLocalPreferencesDirectory)
+ {
+ rv = GetOSXFolderType(kLocalDomain, kPreferencesFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sPictureDocumentsDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kPictureDocumentsFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sMovieDocumentsDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kMovieDocumentsFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sMusicDocumentsDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kMusicDocumentsFolderType, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sInternetSitesDirectory)
+ {
+ rv = GetOSXFolderType(kUserDomain, kInternetSitesFolderType, getter_AddRefs(localFile));
+ }
+#elif defined (XP_WIN)
+ else if (inAtom == nsDirectoryService::sSystemDirectory)
+ {
+ rv = GetSpecialSystemDirectory(Win_SystemDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sWindowsDirectory)
+ {
+ rv = GetSpecialSystemDirectory(Win_WindowsDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sWindowsProgramFiles)
+ {
+ rv = GetSpecialSystemDirectory(Win_ProgramFiles, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sOS_HomeDirectory)
+ {
+ rv = GetSpecialSystemDirectory(Win_HomeDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sDesktop)
+ {
+ rv = GetSpecialSystemDirectory(Win_Desktop, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sPrograms)
+ {
+ rv = GetSpecialSystemDirectory(Win_Programs, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sControls)
+ {
+ rv = GetSpecialSystemDirectory(Win_Controls, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sPrinters)
+ {
+ rv = GetSpecialSystemDirectory(Win_Printers, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sPersonal)
+ {
+ rv = GetSpecialSystemDirectory(Win_Personal, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sFavorites)
+ {
+ rv = GetSpecialSystemDirectory(Win_Favorites, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sStartup)
+ {
+ rv = GetSpecialSystemDirectory(Win_Startup, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sRecent)
+ {
+ rv = GetSpecialSystemDirectory(Win_Recent, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sSendto)
+ {
+ rv = GetSpecialSystemDirectory(Win_Sendto, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sBitbucket)
+ {
+ rv = GetSpecialSystemDirectory(Win_Bitbucket, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sStartmenu)
+ {
+ rv = GetSpecialSystemDirectory(Win_Startmenu, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sDesktopdirectory ||
+ inAtom == nsDirectoryService::sOS_DesktopDirectory)
+ {
+ rv = GetSpecialSystemDirectory(Win_Desktopdirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sDrives)
+ {
+ rv = GetSpecialSystemDirectory(Win_Drives, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sNetwork)
+ {
+ rv = GetSpecialSystemDirectory(Win_Network, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sNethood)
+ {
+ rv = GetSpecialSystemDirectory(Win_Nethood, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sFonts)
+ {
+ rv = GetSpecialSystemDirectory(Win_Fonts, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sTemplates)
+ {
+ rv = GetSpecialSystemDirectory(Win_Templates, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sCommon_Startmenu)
+ {
+ rv = GetSpecialSystemDirectory(Win_Common_Startmenu, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sCommon_Programs)
+ {
+ rv = GetSpecialSystemDirectory(Win_Common_Programs, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sCommon_Startup)
+ {
+ rv = GetSpecialSystemDirectory(Win_Common_Startup, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sCommon_Desktopdirectory)
+ {
+ rv = GetSpecialSystemDirectory(Win_Common_Desktopdirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sCommon_AppData)
+ {
+ rv = GetSpecialSystemDirectory(Win_Common_AppData, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sAppdata)
+ {
+ rv = GetSpecialSystemDirectory(Win_Appdata, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sLocalAppdata)
+ {
+ rv = GetSpecialSystemDirectory(Win_LocalAppdata, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sPrinthood)
+ {
+ rv = GetSpecialSystemDirectory(Win_Printhood, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sWinCookiesDirectory)
+ {
+ rv = GetSpecialSystemDirectory(Win_Cookies, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sDefaultDownloadDirectory)
+ {
+ rv = GetSpecialSystemDirectory(Win_Downloads, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sDocs)
+ {
+ rv = GetSpecialSystemDirectory(Win_Documents, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sPictures)
+ {
+ rv = GetSpecialSystemDirectory(Win_Pictures, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sMusic)
+ {
+ rv = GetSpecialSystemDirectory(Win_Music, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sVideos)
+ {
+ rv = GetSpecialSystemDirectory(Win_Videos, getter_AddRefs(localFile));
+ }
+#elif defined (XP_UNIX)
+
+ else if (inAtom == nsDirectoryService::sLocalDirectory)
+ {
+ rv = GetSpecialSystemDirectory(Unix_LocalDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sLibDirectory)
+ {
+ rv = GetSpecialSystemDirectory(Unix_LibDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sOS_HomeDirectory)
+ {
+ rv = GetSpecialSystemDirectory(Unix_HomeDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sXDGDesktop ||
+ inAtom == nsDirectoryService::sOS_DesktopDirectory)
+ {
+ rv = GetSpecialSystemDirectory(Unix_XDG_Desktop, getter_AddRefs(localFile));
+ *persistent = false;
+ }
+ else if (inAtom == nsDirectoryService::sXDGDocuments)
+ {
+ rv = GetSpecialSystemDirectory(Unix_XDG_Documents, getter_AddRefs(localFile));
+ *persistent = false;
+ }
+ else if (inAtom == nsDirectoryService::sXDGDownload ||
+ inAtom == nsDirectoryService::sDefaultDownloadDirectory)
+ {
+ rv = GetSpecialSystemDirectory(Unix_XDG_Download, getter_AddRefs(localFile));
+ *persistent = false;
+ }
+ else if (inAtom == nsDirectoryService::sXDGMusic)
+ {
+ rv = GetSpecialSystemDirectory(Unix_XDG_Music, getter_AddRefs(localFile));
+ *persistent = false;
+ }
+ else if (inAtom == nsDirectoryService::sXDGPictures)
+ {
+ rv = GetSpecialSystemDirectory(Unix_XDG_Pictures, getter_AddRefs(localFile));
+ *persistent = false;
+ }
+ else if (inAtom == nsDirectoryService::sXDGPublicShare)
+ {
+ rv = GetSpecialSystemDirectory(Unix_XDG_PublicShare, getter_AddRefs(localFile));
+ *persistent = false;
+ }
+ else if (inAtom == nsDirectoryService::sXDGTemplates)
+ {
+ rv = GetSpecialSystemDirectory(Unix_XDG_Templates, getter_AddRefs(localFile));
+ *persistent = false;
+ }
+ else if (inAtom == nsDirectoryService::sXDGVideos)
+ {
+ rv = GetSpecialSystemDirectory(Unix_XDG_Videos, getter_AddRefs(localFile));
+ *persistent = false;
+ }
+#elif defined (XP_OS2)
+ else if (inAtom == nsDirectoryService::sSystemDirectory)
+ {
+ rv = GetSpecialSystemDirectory(OS2_SystemDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sOS2Directory)
+ {
+ rv = GetSpecialSystemDirectory(OS2_OS2Directory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sOS_HomeDirectory)
+ {
+ rv = GetSpecialSystemDirectory(OS2_HomeDirectory, getter_AddRefs(localFile));
+ }
+ else if (inAtom == nsDirectoryService::sOS_DesktopDirectory)
+ {
+ rv = GetSpecialSystemDirectory(OS2_DesktopDirectory, getter_AddRefs(localFile));
+ }
+#endif
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!localFile)
+ return NS_ERROR_FAILURE;
+
+ return CallQueryInterface(localFile, _retval);
+}
+
+NS_IMETHODIMP
+nsDirectoryService::GetFiles(const char *prop, nsISimpleEnumerator **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = nullptr;
+
+ return NS_ERROR_FAILURE;
+}
diff --git a/xpcom/io/nsDirectoryService.h b/xpcom/io/nsDirectoryService.h
new file mode 100644
index 000000000..ed9408b82
--- /dev/null
+++ b/xpcom/io/nsDirectoryService.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 nsDirectoryService_h___
+#define nsDirectoryService_h___
+
+#include "nsIDirectoryService.h"
+#include "nsHashtable.h"
+#include "nsIFile.h"
+#include "nsIAtom.h"
+#include "nsTArray.h"
+#include "mozilla/Attributes.h"
+
+#define NS_XPCOM_INIT_CURRENT_PROCESS_DIR "MozBinD" // Can be used to set NS_XPCOM_CURRENT_PROCESS_DIR
+ // CANNOT be used to GET a location
+#define NS_DIRECTORY_SERVICE_CID {0xf00152d0,0xb40b,0x11d3,{0x8c, 0x9c, 0x00, 0x00, 0x64, 0x65, 0x73, 0x74}}
+
+class nsDirectoryService MOZ_FINAL : public nsIDirectoryService,
+ public nsIProperties,
+ public nsIDirectoryServiceProvider2
+{
+ public:
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ NS_DECL_NSIPROPERTIES
+
+ NS_DECL_NSIDIRECTORYSERVICE
+
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER
+
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER2
+
+ nsDirectoryService();
+ ~nsDirectoryService();
+
+ static void RealInit();
+ void RegisterCategoryProviders();
+
+ static nsresult
+ Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
+
+ static nsDirectoryService* gService;
+
+private:
+ nsresult GetCurrentProcessDirectory(nsIFile** aFile);
+
+ static bool ReleaseValues(nsHashKey* key, void* data, void* closure);
+ nsSupportsHashtable mHashtable;
+ nsTArray<nsCOMPtr<nsIDirectoryServiceProvider> > mProviders;
+
+public:
+
+#define DIR_ATOM(name_, value_) static nsIAtom* name_;
+#include "nsDirectoryServiceAtomList.h"
+#undef DIR_ATOM
+
+};
+
+
+#endif
+
diff --git a/xpcom/io/nsDirectoryServiceAtomList.h b/xpcom/io/nsDirectoryServiceAtomList.h
new file mode 100644
index 000000000..d46e57343
--- /dev/null
+++ b/xpcom/io/nsDirectoryServiceAtomList.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+DIR_ATOM(sCurrentProcess, NS_XPCOM_CURRENT_PROCESS_DIR)
+DIR_ATOM(sGRE_Directory, NS_GRE_DIR)
+DIR_ATOM(sOS_DriveDirectory, NS_OS_DRIVE_DIR)
+DIR_ATOM(sOS_TemporaryDirectory, NS_OS_TEMP_DIR)
+DIR_ATOM(sOS_CurrentProcessDirectory, NS_OS_CURRENT_PROCESS_DIR)
+DIR_ATOM(sOS_CurrentWorkingDirectory, NS_OS_CURRENT_WORKING_DIR)
+DIR_ATOM(sOS_HomeDirectory, NS_OS_HOME_DIR)
+DIR_ATOM(sOS_DesktopDirectory, NS_OS_DESKTOP_DIR)
+DIR_ATOM(sInitCurrentProcess_dummy, NS_XPCOM_INIT_CURRENT_PROCESS_DIR)
+#if defined (MOZ_WIDGET_COCOA)
+DIR_ATOM(sDirectory, NS_OS_SYSTEM_DIR)
+DIR_ATOM(sTrashDirectory, NS_MAC_TRASH_DIR)
+DIR_ATOM(sStartupDirectory, NS_MAC_STARTUP_DIR)
+DIR_ATOM(sShutdownDirectory, NS_MAC_SHUTDOWN_DIR)
+DIR_ATOM(sAppleMenuDirectory, NS_MAC_APPLE_MENU_DIR)
+DIR_ATOM(sControlPanelDirectory, NS_MAC_CONTROL_PANELS_DIR)
+DIR_ATOM(sExtensionDirectory, NS_MAC_EXTENSIONS_DIR)
+DIR_ATOM(sFontsDirectory, NS_MAC_FONTS_DIR)
+DIR_ATOM(sPreferencesDirectory, NS_MAC_PREFS_DIR)
+DIR_ATOM(sDocumentsDirectory, NS_MAC_DOCUMENTS_DIR)
+DIR_ATOM(sInternetSearchDirectory, NS_MAC_INTERNET_SEARCH_DIR)
+DIR_ATOM(sUserLibDirectory, NS_MAC_USER_LIB_DIR)
+DIR_ATOM(sDefaultDownloadDirectory, NS_OSX_DEFAULT_DOWNLOAD_DIR)
+DIR_ATOM(sUserDesktopDirectory, NS_OSX_USER_DESKTOP_DIR)
+DIR_ATOM(sLocalDesktopDirectory, NS_OSX_LOCAL_DESKTOP_DIR)
+DIR_ATOM(sUserApplicationsDirectory, NS_OSX_USER_APPLICATIONS_DIR)
+DIR_ATOM(sLocalApplicationsDirectory, NS_OSX_LOCAL_APPLICATIONS_DIR)
+DIR_ATOM(sUserDocumentsDirectory, NS_OSX_USER_DOCUMENTS_DIR)
+DIR_ATOM(sLocalDocumentsDirectory, NS_OSX_LOCAL_DOCUMENTS_DIR)
+DIR_ATOM(sUserInternetPlugInDirectory, NS_OSX_USER_INTERNET_PLUGIN_DIR)
+DIR_ATOM(sLocalInternetPlugInDirectory, NS_OSX_LOCAL_INTERNET_PLUGIN_DIR)
+DIR_ATOM(sUserFrameworksDirectory, NS_OSX_USER_FRAMEWORKS_DIR)
+DIR_ATOM(sLocalFrameworksDirectory, NS_OSX_LOCAL_FRAMEWORKS_DIR)
+DIR_ATOM(sUserPreferencesDirectory, NS_OSX_USER_PREFERENCES_DIR)
+DIR_ATOM(sLocalPreferencesDirectory, NS_OSX_LOCAL_PREFERENCES_DIR)
+DIR_ATOM(sPictureDocumentsDirectory, NS_OSX_PICTURE_DOCUMENTS_DIR)
+DIR_ATOM(sMovieDocumentsDirectory, NS_OSX_MOVIE_DOCUMENTS_DIR)
+DIR_ATOM(sMusicDocumentsDirectory, NS_OSX_MUSIC_DOCUMENTS_DIR)
+DIR_ATOM(sInternetSitesDirectory, NS_OSX_INTERNET_SITES_DIR)
+#elif defined (XP_WIN)
+DIR_ATOM(sSystemDirectory, NS_OS_SYSTEM_DIR)
+DIR_ATOM(sWindowsDirectory, NS_WIN_WINDOWS_DIR)
+DIR_ATOM(sWindowsProgramFiles, NS_WIN_PROGRAM_FILES_DIR)
+DIR_ATOM(sDesktop, NS_WIN_DESKTOP_DIR)
+DIR_ATOM(sPrograms, NS_WIN_PROGRAMS_DIR)
+DIR_ATOM(sControls, NS_WIN_CONTROLS_DIR)
+DIR_ATOM(sPrinters, NS_WIN_PRINTERS_DIR)
+DIR_ATOM(sPersonal, NS_WIN_PERSONAL_DIR)
+DIR_ATOM(sFavorites, NS_WIN_FAVORITES_DIR)
+DIR_ATOM(sStartup, NS_WIN_STARTUP_DIR)
+DIR_ATOM(sRecent, NS_WIN_RECENT_DIR)
+DIR_ATOM(sSendto, NS_WIN_SEND_TO_DIR)
+DIR_ATOM(sBitbucket, NS_WIN_BITBUCKET_DIR)
+DIR_ATOM(sStartmenu, NS_WIN_STARTMENU_DIR)
+DIR_ATOM(sDesktopdirectory, NS_WIN_DESKTOP_DIRECTORY)
+DIR_ATOM(sDrives, NS_WIN_DRIVES_DIR)
+DIR_ATOM(sNetwork, NS_WIN_NETWORK_DIR)
+DIR_ATOM(sNethood, NS_WIN_NETHOOD_DIR)
+DIR_ATOM(sFonts, NS_WIN_FONTS_DIR)
+DIR_ATOM(sTemplates, NS_WIN_TEMPLATES_DIR)
+DIR_ATOM(sCommon_Startmenu, NS_WIN_COMMON_STARTMENU_DIR)
+DIR_ATOM(sCommon_Programs, NS_WIN_COMMON_PROGRAMS_DIR)
+DIR_ATOM(sCommon_Startup, NS_WIN_COMMON_STARTUP_DIR)
+DIR_ATOM(sCommon_Desktopdirectory, NS_WIN_COMMON_DESKTOP_DIRECTORY)
+DIR_ATOM(sCommon_AppData, NS_WIN_COMMON_APPDATA_DIR)
+DIR_ATOM(sAppdata, NS_WIN_APPDATA_DIR)
+DIR_ATOM(sLocalAppdata, NS_WIN_LOCAL_APPDATA_DIR)
+DIR_ATOM(sPrinthood, NS_WIN_PRINTHOOD)
+DIR_ATOM(sWinCookiesDirectory, NS_WIN_COOKIES_DIR)
+DIR_ATOM(sDefaultDownloadDirectory, NS_WIN_DEFAULT_DOWNLOAD_DIR)
+DIR_ATOM(sDocs, NS_WIN_DOCUMENTS_DIR)
+DIR_ATOM(sPictures, NS_WIN_PICTURES_DIR)
+DIR_ATOM(sMusic, NS_WIN_MUSIC_DIR)
+DIR_ATOM(sVideos, NS_WIN_VIDEOS_DIR)
+#elif defined (XP_UNIX)
+DIR_ATOM(sLocalDirectory, NS_UNIX_LOCAL_DIR)
+DIR_ATOM(sLibDirectory, NS_UNIX_LIB_DIR)
+DIR_ATOM(sDefaultDownloadDirectory, NS_UNIX_DEFAULT_DOWNLOAD_DIR)
+DIR_ATOM(sXDGDesktop, NS_UNIX_XDG_DESKTOP_DIR)
+DIR_ATOM(sXDGDocuments, NS_UNIX_XDG_DOCUMENTS_DIR)
+DIR_ATOM(sXDGDownload, NS_UNIX_XDG_DOWNLOAD_DIR)
+DIR_ATOM(sXDGMusic, NS_UNIX_XDG_MUSIC_DIR)
+DIR_ATOM(sXDGPictures, NS_UNIX_XDG_PICTURES_DIR)
+DIR_ATOM(sXDGPublicShare, NS_UNIX_XDG_PUBLIC_SHARE_DIR)
+DIR_ATOM(sXDGTemplates, NS_UNIX_XDG_TEMPLATES_DIR)
+DIR_ATOM(sXDGVideos, NS_UNIX_XDG_VIDEOS_DIR)
+#elif defined (XP_OS2)
+DIR_ATOM(sSystemDirectory, NS_OS_SYSTEM_DIR)
+DIR_ATOM(sOS2Directory, NS_OS2_DIR)
+#endif
diff --git a/xpcom/io/nsDirectoryServiceDefs.h b/xpcom/io/nsDirectoryServiceDefs.h
new file mode 100644
index 000000000..418c428a5
--- /dev/null
+++ b/xpcom/io/nsDirectoryServiceDefs.h
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/**
+ * Defines the property names for directories available from
+ * nsIDirectoryService. These dirs are always available even if no
+ * nsIDirectoryServiceProviders have been registered with the service.
+ * Application level keys are defined in nsAppDirectoryServiceDefs.h.
+ *
+ * Keys whose definition ends in "DIR" or "FILE" return a single nsIFile (or
+ * subclass). Keys whose definition ends in "LIST" return an nsISimpleEnumerator
+ * which enumerates a list of file objects.
+ *
+ * Defines listed in this file are FROZEN. This list may grow.
+ */
+
+#ifndef nsDirectoryServiceDefs_h___
+#define nsDirectoryServiceDefs_h___
+
+/* General OS specific locations */
+
+#define NS_OS_HOME_DIR "Home"
+#define NS_OS_TEMP_DIR "TmpD"
+#define NS_OS_CURRENT_WORKING_DIR "CurWorkD"
+/* Files stored in this directory will appear on the user's desktop,
+ * if there is one, otherwise it's just the same as "Home"
+ */
+#define NS_OS_DESKTOP_DIR "Desk"
+
+/* Property returns the directory in which the procces was started from.
+ * On Unix this will be the path in the MOZILLA_FIVE_HOME env var and if
+ * unset will be the current working directory.
+ */
+#define NS_OS_CURRENT_PROCESS_DIR "CurProcD"
+
+/* This location is similar to NS_OS_CURRENT_PROCESS_DIR, however,
+ * NS_XPCOM_CURRENT_PROCESS_DIR can be overriden by passing a "bin
+ * directory" to NS_InitXPCOM2().
+ */
+#define NS_XPCOM_CURRENT_PROCESS_DIR "XCurProcD"
+
+/* Property will return the location of the the XPCOM Shared Library.
+ */
+#define NS_XPCOM_LIBRARY_FILE "XpcomLib"
+
+/* Property will return the current location of the the GRE directory.
+ * If no GRE is used, this propery will behave like
+ * NS_XPCOM_CURRENT_PROCESS_DIR.
+ */
+#define NS_GRE_DIR "GreD"
+
+/* Platform Specific Locations */
+
+#if !defined (XP_UNIX) || defined(MOZ_WIDGET_COCOA)
+ #define NS_OS_SYSTEM_DIR "SysD"
+#endif
+
+#if defined (MOZ_WIDGET_COCOA)
+ #define NS_MAC_DESKTOP_DIR NS_OS_DESKTOP_DIR
+ #define NS_MAC_TRASH_DIR "Trsh"
+ #define NS_MAC_STARTUP_DIR "Strt"
+ #define NS_MAC_SHUTDOWN_DIR "Shdwn"
+ #define NS_MAC_APPLE_MENU_DIR "ApplMenu"
+ #define NS_MAC_CONTROL_PANELS_DIR "CntlPnl"
+ #define NS_MAC_EXTENSIONS_DIR "Exts"
+ #define NS_MAC_FONTS_DIR "Fnts"
+ #define NS_MAC_PREFS_DIR "Prfs"
+ #define NS_MAC_DOCUMENTS_DIR "Docs"
+ #define NS_MAC_INTERNET_SEARCH_DIR "ISrch"
+ #define NS_OSX_HOME_DIR NS_OS_HOME_DIR
+ #define NS_MAC_HOME_DIR NS_OS_HOME_DIR
+ #define NS_MAC_DEFAULT_DOWNLOAD_DIR "DfltDwnld"
+ #define NS_MAC_USER_LIB_DIR "ULibDir" // Only available under OS X
+ #define NS_OSX_DEFAULT_DOWNLOAD_DIR NS_MAC_DEFAULT_DOWNLOAD_DIR
+ #define NS_OSX_USER_DESKTOP_DIR "UsrDsk"
+ #define NS_OSX_LOCAL_DESKTOP_DIR "LocDsk"
+ #define NS_OSX_USER_APPLICATIONS_DIR "UsrApp"
+ #define NS_OSX_LOCAL_APPLICATIONS_DIR "LocApp"
+ #define NS_OSX_USER_DOCUMENTS_DIR "UsrDocs"
+ #define NS_OSX_LOCAL_DOCUMENTS_DIR "LocDocs"
+ #define NS_OSX_USER_INTERNET_PLUGIN_DIR "UsrIntrntPlgn"
+ #define NS_OSX_LOCAL_INTERNET_PLUGIN_DIR "LoclIntrntPlgn"
+ #define NS_OSX_USER_FRAMEWORKS_DIR "UsrFrmwrks"
+ #define NS_OSX_LOCAL_FRAMEWORKS_DIR "LocFrmwrks"
+ #define NS_OSX_USER_PREFERENCES_DIR "UsrPrfs"
+ #define NS_OSX_LOCAL_PREFERENCES_DIR "LocPrfs"
+ #define NS_OSX_PICTURE_DOCUMENTS_DIR "Pct"
+ #define NS_OSX_MOVIE_DOCUMENTS_DIR "Mov"
+ #define NS_OSX_MUSIC_DOCUMENTS_DIR "Music"
+ #define NS_OSX_INTERNET_SITES_DIR "IntrntSts"
+#elif defined (XP_WIN)
+ #define NS_WIN_WINDOWS_DIR "WinD"
+ #define NS_WIN_PROGRAM_FILES_DIR "ProgF"
+ #define NS_WIN_HOME_DIR NS_OS_HOME_DIR
+ #define NS_WIN_DESKTOP_DIR "DeskV" // virtual folder at the root of the namespace
+ #define NS_WIN_PROGRAMS_DIR "Progs" // User start menu programs directory!
+ #define NS_WIN_CONTROLS_DIR "Cntls"
+ #define NS_WIN_PRINTERS_DIR "Prnts"
+ #define NS_WIN_PERSONAL_DIR "Pers"
+ #define NS_WIN_FAVORITES_DIR "Favs"
+ #define NS_WIN_STARTUP_DIR "Strt"
+ #define NS_WIN_RECENT_DIR "Rcnt"
+ #define NS_WIN_SEND_TO_DIR "SndTo"
+ #define NS_WIN_BITBUCKET_DIR "Buckt"
+ #define NS_WIN_STARTMENU_DIR "Strt"
+// This gives the same thing as NS_OS_DESKTOP_DIR
+ #define NS_WIN_DESKTOP_DIRECTORY "DeskP" // file sys dir which physically stores objects on desktop
+ #define NS_WIN_DRIVES_DIR "Drivs"
+ #define NS_WIN_NETWORK_DIR "NetW"
+ #define NS_WIN_NETHOOD_DIR "netH"
+ #define NS_WIN_FONTS_DIR "Fnts"
+ #define NS_WIN_TEMPLATES_DIR "Tmpls"
+ #define NS_WIN_COMMON_STARTMENU_DIR "CmStrt"
+ #define NS_WIN_COMMON_PROGRAMS_DIR "CmPrgs"
+ #define NS_WIN_COMMON_STARTUP_DIR "CmStrt"
+ #define NS_WIN_COMMON_DESKTOP_DIRECTORY "CmDeskP"
+ #define NS_WIN_COMMON_APPDATA_DIR "CmAppData"
+ #define NS_WIN_APPDATA_DIR "AppData"
+ #define NS_WIN_LOCAL_APPDATA_DIR "LocalAppData"
+ #define NS_WIN_PRINTHOOD "PrntHd"
+ #define NS_WIN_COOKIES_DIR "CookD"
+ #define NS_WIN_DEFAULT_DOWNLOAD_DIR "DfltDwnld"
+ // On Win7 and up these ids will return the default save-to location for
+ // Windows Libraries associated with the specific content type. For other
+ // os they return the local user folder. Note these can return network file
+ // paths which can jank the ui thread so be careful how you access them.
+ #define NS_WIN_DOCUMENTS_DIR "Docs"
+ #define NS_WIN_PICTURES_DIR "Pict"
+ #define NS_WIN_MUSIC_DIR "Music"
+ #define NS_WIN_VIDEOS_DIR "Vids"
+#elif defined (XP_UNIX)
+ #define NS_UNIX_LOCAL_DIR "Locl"
+ #define NS_UNIX_LIB_DIR "LibD"
+ #define NS_UNIX_HOME_DIR NS_OS_HOME_DIR
+ #define NS_UNIX_XDG_DESKTOP_DIR "XDGDesk"
+ #define NS_UNIX_XDG_DOCUMENTS_DIR "XDGDocs"
+ #define NS_UNIX_XDG_DOWNLOAD_DIR "XDGDwnld"
+ #define NS_UNIX_XDG_MUSIC_DIR "XDGMusic"
+ #define NS_UNIX_XDG_PICTURES_DIR "XDGPict"
+ #define NS_UNIX_XDG_PUBLIC_SHARE_DIR "XDGPubSh"
+ #define NS_UNIX_XDG_TEMPLATES_DIR "XDGTempl"
+ #define NS_UNIX_XDG_VIDEOS_DIR "XDGVids"
+ #define NS_UNIX_DEFAULT_DOWNLOAD_DIR "DfltDwnld"
+#elif defined (XP_OS2)
+ #define NS_OS2_DIR "OS2Dir"
+ #define NS_OS2_HOME_DIR NS_OS_HOME_DIR
+ #define NS_OS2_DESKTOP_DIR NS_OS_DESKTOP_DIR
+#endif
+
+/* Deprecated */
+
+#define NS_OS_DRIVE_DIR "DrvD"
+
+
+
+#endif
diff --git a/xpcom/io/nsDirectoryServiceUtils.h b/xpcom/io/nsDirectoryServiceUtils.h
new file mode 100644
index 000000000..ff8e85c98
--- /dev/null
+++ b/xpcom/io/nsDirectoryServiceUtils.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 nsDirectoryServiceUtils_h___
+#define nsDirectoryServiceUtils_h___
+
+#include "nsIServiceManager.h"
+#include "nsIProperties.h"
+#include "nsServiceManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsXPCOMCID.h"
+#include "nsIFile.h"
+
+inline nsresult
+NS_GetSpecialDirectory(const char* specialDirName, nsIFile* *result)
+{
+ nsresult rv;
+ nsCOMPtr<nsIProperties> serv(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+
+ return serv->Get(specialDirName, NS_GET_IID(nsIFile),
+ reinterpret_cast<void**>(result));
+}
+
+#endif
diff --git a/xpcom/io/nsEscape.cpp b/xpcom/io/nsEscape.cpp
new file mode 100644
index 000000000..8453e6a06
--- /dev/null
+++ b/xpcom/io/nsEscape.cpp
@@ -0,0 +1,497 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+// First checked in on 98/12/03 by John R. McMullen, derived from net.h/mkparse.c.
+
+#include "nsEscape.h"
+#include "nsMemory.h"
+#include "nsCRT.h"
+#include "nsReadableUtils.h"
+
+const int netCharType[256] =
+/* Bit 0 xalpha -- the alphas
+** Bit 1 xpalpha -- as xalpha but
+** converts spaces to plus and plus to %2B
+** Bit 3 ... path -- as xalphas but doesn't escape '/'
+*/
+ /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */
+ 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */
+ 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */
+ 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */
+ /* bits for '@' changed from 7 to 0 so '@' can be escaped */
+ /* in usernames and passwords in publishing. */
+ 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */
+ 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */
+ 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */
+ 0, };
+
+/* decode % escaped hex codes into character values
+ */
+#define UNHEX(C) \
+ ((C >= '0' && C <= '9') ? C - '0' : \
+ ((C >= 'A' && C <= 'F') ? C - 'A' + 10 : \
+ ((C >= 'a' && C <= 'f') ? C - 'a' + 10 : 0)))
+
+
+#define IS_OK(C) (netCharType[((unsigned int) (C))] & (flags))
+#define HEX_ESCAPE '%'
+
+//----------------------------------------------------------------------------------------
+static char* nsEscapeCount(
+ const char * str,
+ nsEscapeMask flags,
+ size_t* out_len)
+//----------------------------------------------------------------------------------------
+{
+ if (!str)
+ return 0;
+
+ size_t i, len = 0, charsToEscape = 0;
+ static const char hexChars[] = "0123456789ABCDEF";
+
+ register const unsigned char* src = (const unsigned char *) str;
+ while (*src)
+ {
+ len++;
+ if (!IS_OK(*src++))
+ charsToEscape++;
+ }
+
+ // calculate how much memory should be allocated
+ // original length + 2 bytes for each escaped character + terminating '\0'
+ // do the sum in steps to check for overflow
+ size_t dstSize = len + 1 + charsToEscape;
+ if (dstSize <= len)
+ return 0;
+ dstSize += charsToEscape;
+ if (dstSize < len)
+ return 0;
+
+ // fail if we need more than 4GB
+ // size_t is likely to be long unsigned int but nsMemory::Alloc(size_t)
+ // calls NS_Alloc_P(size_t) which calls PR_Malloc(uint32_t), so there is
+ // no chance to allocate more than 4GB using nsMemory::Alloc()
+ if (dstSize > UINT32_MAX)
+ return 0;
+
+ char* result = (char *)nsMemory::Alloc(dstSize);
+ if (!result)
+ return 0;
+
+ register unsigned char* dst = (unsigned char *) result;
+ src = (const unsigned char *) str;
+ if (flags == url_XPAlphas)
+ {
+ for (i = 0; i < len; i++)
+ {
+ unsigned char c = *src++;
+ if (IS_OK(c))
+ *dst++ = c;
+ else if (c == ' ')
+ *dst++ = '+'; /* convert spaces to pluses */
+ else
+ {
+ *dst++ = HEX_ESCAPE;
+ *dst++ = hexChars[c >> 4]; /* high nibble */
+ *dst++ = hexChars[c & 0x0f]; /* low nibble */
+ }
+ }
+ }
+ else
+ {
+ for (i = 0; i < len; i++)
+ {
+ unsigned char c = *src++;
+ if (IS_OK(c))
+ *dst++ = c;
+ else
+ {
+ *dst++ = HEX_ESCAPE;
+ *dst++ = hexChars[c >> 4]; /* high nibble */
+ *dst++ = hexChars[c & 0x0f]; /* low nibble */
+ }
+ }
+ }
+
+ *dst = '\0'; /* tack on eos */
+ if(out_len)
+ *out_len = dst - (unsigned char *) result;
+ return result;
+}
+
+//----------------------------------------------------------------------------------------
+char* nsEscape(const char * str, nsEscapeMask flags)
+//----------------------------------------------------------------------------------------
+{
+ if(!str)
+ return NULL;
+ return nsEscapeCount(str, flags, NULL);
+}
+
+//----------------------------------------------------------------------------------------
+char* nsUnescape(char * str)
+//----------------------------------------------------------------------------------------
+{
+ nsUnescapeCount(str);
+ return str;
+}
+
+//----------------------------------------------------------------------------------------
+int32_t nsUnescapeCount(char * str)
+//----------------------------------------------------------------------------------------
+{
+ register char *src = str;
+ register char *dst = str;
+ static const char hexChars[] = "0123456789ABCDEFabcdef";
+
+ char c1[] = " ";
+ char c2[] = " ";
+ char* const pc1 = c1;
+ char* const pc2 = c2;
+
+ while (*src)
+ {
+ c1[0] = *(src+1);
+ if (*(src+1) == '\0')
+ c2[0] = '\0';
+ else
+ c2[0] = *(src+2);
+
+ if (*src != HEX_ESCAPE || PL_strpbrk(pc1, hexChars) == 0 ||
+ PL_strpbrk(pc2, hexChars) == 0 )
+ *dst++ = *src++;
+ else
+ {
+ src++; /* walk over escape */
+ if (*src)
+ {
+ *dst = UNHEX(*src) << 4;
+ src++;
+ }
+ if (*src)
+ {
+ *dst = (*dst + UNHEX(*src));
+ src++;
+ }
+ dst++;
+ }
+ }
+
+ *dst = 0;
+ return (int)(dst - str);
+
+} /* NET_UnEscapeCnt */
+
+
+char *
+nsEscapeHTML(const char * string)
+{
+ char *rv = nullptr;
+ /* XXX Hardcoded max entity len. The +1 is for the trailing null. */
+ uint32_t len = strlen(string);
+ if (len >= (UINT32_MAX / 6))
+ return nullptr;
+
+ rv = (char *)NS_Alloc( (6 * len) + 1 );
+ char *ptr = rv;
+
+ if(rv)
+ {
+ for(; *string != '\0'; string++)
+ {
+ if(*string == '<')
+ {
+ *ptr++ = '&';
+ *ptr++ = 'l';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ }
+ else if(*string == '>')
+ {
+ *ptr++ = '&';
+ *ptr++ = 'g';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ }
+ else if(*string == '&')
+ {
+ *ptr++ = '&';
+ *ptr++ = 'a';
+ *ptr++ = 'm';
+ *ptr++ = 'p';
+ *ptr++ = ';';
+ }
+ else if (*string == '"')
+ {
+ *ptr++ = '&';
+ *ptr++ = 'q';
+ *ptr++ = 'u';
+ *ptr++ = 'o';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ }
+ else if (*string == '\'')
+ {
+ *ptr++ = '&';
+ *ptr++ = '#';
+ *ptr++ = '3';
+ *ptr++ = '9';
+ *ptr++ = ';';
+ }
+ else
+ {
+ *ptr++ = *string;
+ }
+ }
+ *ptr = '\0';
+ }
+
+ return(rv);
+}
+
+PRUnichar *
+nsEscapeHTML2(const PRUnichar *aSourceBuffer, int32_t aSourceBufferLen)
+{
+ // Calculate the length, if the caller didn't.
+ if (aSourceBufferLen < 0) {
+ aSourceBufferLen = NS_strlen(aSourceBuffer);
+ }
+
+ /* XXX Hardcoded max entity len. */
+ if (uint32_t(aSourceBufferLen) >=
+ ((UINT32_MAX - sizeof(PRUnichar)) / (6 * sizeof(PRUnichar))) )
+ return nullptr;
+
+ PRUnichar *resultBuffer = (PRUnichar *)nsMemory::Alloc(aSourceBufferLen *
+ 6 * sizeof(PRUnichar) + sizeof(PRUnichar('\0')));
+ PRUnichar *ptr = resultBuffer;
+
+ if (resultBuffer) {
+ int32_t i;
+
+ for(i = 0; i < aSourceBufferLen; i++) {
+ if(aSourceBuffer[i] == '<') {
+ *ptr++ = '&';
+ *ptr++ = 'l';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ } else if(aSourceBuffer[i] == '>') {
+ *ptr++ = '&';
+ *ptr++ = 'g';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ } else if(aSourceBuffer[i] == '&') {
+ *ptr++ = '&';
+ *ptr++ = 'a';
+ *ptr++ = 'm';
+ *ptr++ = 'p';
+ *ptr++ = ';';
+ } else if (aSourceBuffer[i] == '"') {
+ *ptr++ = '&';
+ *ptr++ = 'q';
+ *ptr++ = 'u';
+ *ptr++ = 'o';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ } else if (aSourceBuffer[i] == '\'') {
+ *ptr++ = '&';
+ *ptr++ = '#';
+ *ptr++ = '3';
+ *ptr++ = '9';
+ *ptr++ = ';';
+ } else {
+ *ptr++ = aSourceBuffer[i];
+ }
+ }
+ *ptr = 0;
+ }
+
+ return resultBuffer;
+}
+
+//----------------------------------------------------------------------------------------
+
+const int EscapeChars[256] =
+/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */
+ 0,1023, 0, 512,1023, 0,1023, 0,1023,1023,1023,1023,1023,1023, 953, 784, /* 2x !"#$%&'()*+,-./ */
+ 1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1008,1008, 0,1008, 0, 768, /* 3x 0123456789:;<=>? */
+ 1008,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, /* 4x @ABCDEFGHIJKLMNO */
+ 1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, 896, 896, 896, 896,1023, /* 5x PQRSTUVWXYZ[\]^_ */
+ 0,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, /* 6x `abcdefghijklmno */
+ 1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, 896,1012, 896,1023, 0, /* 7x pqrstuvwxyz{|}~ */
+ 0 /* 8x DEL */
+};
+
+#define NO_NEED_ESC(C) (EscapeChars[((unsigned int) (C))] & (flags))
+
+//----------------------------------------------------------------------------------------
+
+/* returns an escaped string */
+
+/* use the following flags to specify which
+ part of an URL you want to escape:
+
+ esc_Scheme = 1
+ esc_Username = 2
+ esc_Password = 4
+ esc_Host = 8
+ esc_Directory = 16
+ esc_FileBaseName = 32
+ esc_FileExtension = 64
+ esc_Param = 128
+ esc_Query = 256
+ esc_Ref = 512
+*/
+
+/* by default this function will not escape parts of a string
+ that already look escaped, which means it already includes
+ a valid hexcode. This is done to avoid multiple escapes of
+ a string. Use the following flags to force escaping of a
+ string:
+
+ esc_Forced = 1024
+*/
+
+bool NS_EscapeURL(const char *part,
+ int32_t partLen,
+ uint32_t flags,
+ nsACString &result)
+{
+ if (!part) {
+ NS_NOTREACHED("null pointer");
+ return false;
+ }
+
+ int i = 0;
+ static const char hexChars[] = "0123456789ABCDEF";
+ if (partLen < 0)
+ partLen = strlen(part);
+ bool forced = !!(flags & esc_Forced);
+ bool ignoreNonAscii = !!(flags & esc_OnlyASCII);
+ bool ignoreAscii = !!(flags & esc_OnlyNonASCII);
+ bool writing = !!(flags & esc_AlwaysCopy);
+ bool colon = !!(flags & esc_Colon);
+
+ register const unsigned char* src = (const unsigned char *) part;
+
+ char tempBuffer[100];
+ unsigned int tempBufferPos = 0;
+
+ bool previousIsNonASCII = false;
+ for (i = 0; i < partLen; i++)
+ {
+ unsigned char c = *src++;
+
+ // if the char has not to be escaped or whatever follows % is
+ // a valid escaped string, just copy the char.
+ //
+ // Also the % will not be escaped until forced
+ // See bugzilla bug 61269 for details why we changed this
+ //
+ // And, we will not escape non-ascii characters if requested.
+ // On special request we will also escape the colon even when
+ // not covered by the matrix.
+ // ignoreAscii is not honored for control characters (C0 and DEL)
+ //
+ // And, we should escape the '|' character when it occurs after any
+ // non-ASCII character as it may be part of a multi-byte character.
+ //
+ // 0x20..0x7e are the valid ASCII characters. We also escape spaces
+ // (0x20) since they are not legal in URLs.
+ if ((NO_NEED_ESC(c) || (c == HEX_ESCAPE && !forced)
+ || (c > 0x7f && ignoreNonAscii)
+ || (c > 0x20 && c < 0x7f && ignoreAscii))
+ && !(c == ':' && colon)
+ && !(previousIsNonASCII && c == '|' && !ignoreNonAscii))
+ {
+ if (writing)
+ tempBuffer[tempBufferPos++] = c;
+ }
+ else /* do the escape magic */
+ {
+ if (!writing)
+ {
+ result.Append(part, i);
+ writing = true;
+ }
+ tempBuffer[tempBufferPos++] = HEX_ESCAPE;
+ tempBuffer[tempBufferPos++] = hexChars[c >> 4]; /* high nibble */
+ tempBuffer[tempBufferPos++] = hexChars[c & 0x0f]; /* low nibble */
+ }
+
+ if (tempBufferPos >= sizeof(tempBuffer) - 4)
+ {
+ NS_ASSERTION(writing, "should be writing");
+ tempBuffer[tempBufferPos] = '\0';
+ result += tempBuffer;
+ tempBufferPos = 0;
+ }
+
+ previousIsNonASCII = (c > 0x7f);
+ }
+ if (writing) {
+ tempBuffer[tempBufferPos] = '\0';
+ result += tempBuffer;
+ }
+ return writing;
+}
+
+#define ISHEX(c) memchr(hexChars, c, sizeof(hexChars)-1)
+
+bool NS_UnescapeURL(const char *str, int32_t len, uint32_t flags, nsACString &result)
+{
+ if (!str) {
+ NS_NOTREACHED("null pointer");
+ return false;
+ }
+
+ if (len < 0)
+ len = strlen(str);
+
+ bool ignoreNonAscii = !!(flags & esc_OnlyASCII);
+ bool ignoreAscii = !!(flags & esc_OnlyNonASCII);
+ bool writing = !!(flags & esc_AlwaysCopy);
+ bool skipControl = !!(flags & esc_SkipControl);
+
+ static const char hexChars[] = "0123456789ABCDEFabcdef";
+
+ const char *last = str;
+ const char *p = str;
+
+ for (int i=0; i<len; ++i, ++p) {
+ //printf("%c [i=%d of len=%d]\n", *p, i, len);
+ if (*p == HEX_ESCAPE && i < len-2) {
+ unsigned char *p1 = ((unsigned char *) p) + 1;
+ unsigned char *p2 = ((unsigned char *) p) + 2;
+ if (ISHEX(*p1) && ISHEX(*p2) &&
+ ((*p1 < '8' && !ignoreAscii) || (*p1 >= '8' && !ignoreNonAscii)) &&
+ !(skipControl &&
+ (*p1 < '2' || (*p1 == '7' && (*p2 == 'f' || *p2 == 'F'))))) {
+ //printf("- p1=%c p2=%c\n", *p1, *p2);
+ writing = true;
+ if (p > last) {
+ //printf("- p=%p, last=%p\n", p, last);
+ result.Append(last, p - last);
+ last = p;
+ }
+ char u = (UNHEX(*p1) << 4) + UNHEX(*p2);
+ //printf("- u=%c\n", u);
+ result.Append(u);
+ i += 2;
+ p += 2;
+ last += 3;
+ }
+ }
+ }
+ if (writing && last < str + len)
+ result.Append(last, str + len - last);
+
+ return writing;
+}
diff --git a/xpcom/io/nsEscape.h b/xpcom/io/nsEscape.h
new file mode 100644
index 000000000..e66ac28f6
--- /dev/null
+++ b/xpcom/io/nsEscape.h
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/* First checked in on 98/12/03 by John R. McMullen, derived from net.h/mkparse.c. */
+
+#ifndef _ESCAPE_H_
+#define _ESCAPE_H_
+
+#include "prtypes.h"
+#include "nscore.h"
+#include "nsError.h"
+#include "nsString.h"
+
+/**
+ * Valid mask values for nsEscape
+ * Note: these values are copied in nsINetUtil.idl. Any changes should be kept
+ * in sync.
+ */
+typedef enum {
+ url_All = 0 /**< %-escape every byte unconditionally */
+, url_XAlphas = 1u << 0 /**< Normal escape - leave alphas intact, escape the rest */
+, url_XPAlphas = 1u << 1 /**< As url_XAlphas, but convert spaces (0x20) to '+' and plus to %2B */
+, url_Path = 1u << 2 /**< As url_XAlphas, but don't escape slash ('/') */
+} nsEscapeMask;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Escape the given string according to mask
+ * @param str The string to escape
+ * @param mask How to escape the string
+ * @return A newly allocated escaped string that must be free'd with
+ * nsCRT::free, or null on failure
+ */
+char * nsEscape(const char * str, nsEscapeMask mask);
+
+char * nsUnescape(char * str);
+ /* decode % escaped hex codes into character values,
+ * modifies the parameter, returns the same buffer
+ */
+
+int32_t nsUnescapeCount (char * str);
+ /* decode % escaped hex codes into character values,
+ * modifies the parameter buffer, returns the length of the result
+ * (result may contain \0's).
+ */
+
+char *
+nsEscapeHTML(const char * string);
+
+PRUnichar *
+nsEscapeHTML2(const PRUnichar *aSourceBuffer,
+ int32_t aSourceBufferLen = -1);
+ /*
+ * Escape problem char's for HTML display
+ */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/**
+ * NS_EscapeURL/NS_UnescapeURL constants for |flags| parameter:
+ *
+ * Note: These values are copied to nsINetUtil.idl
+ * Any changes should be kept in sync
+ */
+enum EscapeMask {
+ /** url components **/
+ esc_Scheme = 1u << 0,
+ esc_Username = 1u << 1,
+ esc_Password = 1u << 2,
+ esc_Host = 1u << 3,
+ esc_Directory = 1u << 4,
+ esc_FileBaseName = 1u << 5,
+ esc_FileExtension = 1u << 6,
+ esc_FilePath = esc_Directory | esc_FileBaseName | esc_FileExtension,
+ esc_Param = 1u << 7,
+ esc_Query = 1u << 8,
+ esc_Ref = 1u << 9,
+ /** special flags **/
+ esc_Minimal = esc_Scheme | esc_Username | esc_Password | esc_Host | esc_FilePath | esc_Param | esc_Query | esc_Ref,
+ esc_Forced = 1u << 10, /* forces escaping of existing escape sequences */
+ esc_OnlyASCII = 1u << 11, /* causes non-ascii octets to be skipped */
+ esc_OnlyNonASCII = 1u << 12, /* causes _graphic_ ascii octets (0x20-0x7E)
+ * to be skipped when escaping. causes all
+ * ascii octets (<= 0x7F) to be skipped when unescaping */
+ esc_AlwaysCopy = 1u << 13, /* copy input to result buf even if escaping is unnecessary */
+ esc_Colon = 1u << 14, /* forces escape of colon */
+ esc_SkipControl = 1u << 15 /* skips C0 and DEL from unescaping */
+};
+
+/**
+ * NS_EscapeURL
+ *
+ * Escapes invalid char's in an URL segment. Has no side-effect if the URL
+ * segment is already escaped. Otherwise, the escaped URL segment is appended
+ * to |result|.
+ *
+ * @param str url segment string
+ * @param len url segment string length (-1 if unknown)
+ * @param flags url segment type flag
+ * @param result result buffer, untouched if part is already escaped
+ *
+ * @return TRUE if escaping was performed, FALSE otherwise.
+ */
+bool NS_EscapeURL(const char *str,
+ int32_t len,
+ uint32_t flags,
+ nsACString &result);
+
+/**
+ * Expands URL escape sequences... beware embedded null bytes!
+ *
+ * @param str url string to unescape
+ * @param len length of |str|
+ * @param flags only esc_OnlyNonASCII, esc_SkipControl and esc_AlwaysCopy
+ * are recognized
+ * @param result result buffer, untouched if |str| is already unescaped
+ *
+ * @return TRUE if unescaping was performed, FALSE otherwise.
+ */
+bool NS_UnescapeURL(const char *str,
+ int32_t len,
+ uint32_t flags,
+ nsACString &result);
+
+/** returns resultant string length **/
+inline int32_t NS_UnescapeURL(char *str) {
+ return nsUnescapeCount(str);
+}
+
+/**
+ * String friendly versions...
+ */
+inline const nsCSubstring &
+NS_EscapeURL(const nsCSubstring &str, uint32_t flags, nsCSubstring &result) {
+ if (NS_EscapeURL(str.Data(), str.Length(), flags, result))
+ return result;
+ return str;
+}
+inline const nsCSubstring &
+NS_UnescapeURL(const nsCSubstring &str, uint32_t flags, nsCSubstring &result) {
+ if (NS_UnescapeURL(str.Data(), str.Length(), flags, result))
+ return result;
+ return str;
+}
+
+/**
+ * CString version of nsEscape. Returns true on success, false
+ * on out of memory. To reverse this function, use NS_UnescapeURL.
+ */
+inline bool
+NS_Escape(const nsCString& aOriginal, nsCString& aEscaped,
+ nsEscapeMask aMask)
+{
+ char* esc = nsEscape(aOriginal.get(), aMask);
+ if (! esc)
+ return false;
+ aEscaped.Adopt(esc);
+ return true;
+}
+
+/**
+ * Inline unescape of mutable string object.
+ */
+inline nsCString &
+NS_UnescapeURL(nsCString &str)
+{
+ str.SetLength(nsUnescapeCount(str.BeginWriting()));
+ return str;
+}
+
+#endif // _ESCAPE_H_
diff --git a/xpcom/io/nsIAsyncInputStream.idl b/xpcom/io/nsIAsyncInputStream.idl
new file mode 100644
index 000000000..5570817dd
--- /dev/null
+++ b/xpcom/io/nsIAsyncInputStream.idl
@@ -0,0 +1,104 @@
+/* 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 "nsIInputStream.idl"
+
+interface nsIInputStreamCallback;
+interface nsIEventTarget;
+
+/**
+ * If an input stream is non-blocking, it may return NS_BASE_STREAM_WOULD_BLOCK
+ * when read. The caller must then wait for the stream to have some data to
+ * read. If the stream implements nsIAsyncInputStream, then the caller can use
+ * this interface to request an asynchronous notification when the stream
+ * becomes readable or closed (via the AsyncWait method).
+ *
+ * While this interface is almost exclusively used with non-blocking streams, it
+ * is not necessary that nsIInputStream::isNonBlocking return true. Nor is it
+ * necessary that a non-blocking nsIInputStream implementation also implement
+ * nsIAsyncInputStream.
+ */
+[scriptable, uuid(a5f255ab-4801-4161-8816-277ac92f6ad1)]
+interface nsIAsyncInputStream : nsIInputStream
+{
+ /**
+ * This method closes the stream and sets its internal status. If the
+ * stream is already closed, then this method is ignored. Once the stream
+ * is closed, the stream's status cannot be changed. Any successful status
+ * code passed to this method is treated as NS_BASE_STREAM_CLOSED, which
+ * has an effect equivalent to nsIInputStream::close.
+ *
+ * NOTE: this method exists in part to support pipes, which have both an
+ * input end and an output end. If the input end of a pipe is closed, then
+ * writes to the output end of the pipe will fail. The error code returned
+ * when an attempt is made to write to a "broken" pipe corresponds to the
+ * status code passed in when the input end of the pipe was closed, which
+ * greatly simplifies working with pipes in some cases.
+ *
+ * @param aStatus
+ * The error that will be reported if this stream is accessed after
+ * it has been closed.
+ */
+ void closeWithStatus(in nsresult aStatus);
+
+ /**
+ * Asynchronously wait for the stream to be readable or closed. The
+ * notification is one-shot, meaning that each asyncWait call will result
+ * in exactly one notification callback. After the OnInputStreamReady event
+ * is dispatched, the stream releases its reference to the
+ * nsIInputStreamCallback object. It is safe to call asyncWait again from the
+ * notification handler.
+ *
+ * This method may be called at any time (even if read has not been called).
+ * In other words, this method may be called when the stream already has
+ * data to read. It may also be called when the stream is closed. If the
+ * stream is already readable or closed when AsyncWait is called, then the
+ * OnInputStreamReady event will be dispatched immediately. Otherwise, the
+ * event will be dispatched when the stream becomes readable or closed.
+ *
+ * @param aCallback
+ * This object is notified when the stream becomes ready. This
+ * parameter may be null to clear an existing callback.
+ * @param aFlags
+ * This parameter specifies optional flags passed in to configure
+ * the behavior of this method. Pass zero to specify no flags.
+ * @param aRequestedCount
+ * Wait until at least this many bytes can be read. This is only
+ * a suggestion to the underlying stream; it may be ignored. The
+ * caller may pass zero to indicate no preference.
+ * @param aEventTarget
+ * Specify NULL to receive notification on ANY thread (possibly even
+ * recursively on the calling thread -- i.e., synchronously), or
+ * specify that the notification be delivered to a specific event
+ * target.
+ */
+ void asyncWait(in nsIInputStreamCallback aCallback,
+ in unsigned long aFlags,
+ in unsigned long aRequestedCount,
+ in nsIEventTarget aEventTarget);
+
+ /**
+ * If passed to asyncWait, this flag overrides the default behavior,
+ * causing the OnInputStreamReady notification to be suppressed until the
+ * stream becomes closed (either as a result of closeWithStatus/close being
+ * called on the stream or possibly due to some error in the underlying
+ * stream).
+ */
+ const unsigned long WAIT_CLOSURE_ONLY = (1<<0);
+};
+
+/**
+ * This is a companion interface for nsIAsyncInputStream::asyncWait.
+ */
+[function, scriptable, uuid(d1f28e94-3a6e-4050-a5f5-2e81b1fc2a43)]
+interface nsIInputStreamCallback : nsISupports
+{
+ /**
+ * Called to indicate that the stream is either readable or closed.
+ *
+ * @param aStream
+ * The stream whose asyncWait method was called.
+ */
+ void onInputStreamReady(in nsIAsyncInputStream aStream);
+};
diff --git a/xpcom/io/nsIAsyncOutputStream.idl b/xpcom/io/nsIAsyncOutputStream.idl
new file mode 100644
index 000000000..68d0464b1
--- /dev/null
+++ b/xpcom/io/nsIAsyncOutputStream.idl
@@ -0,0 +1,104 @@
+/* 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 "nsIOutputStream.idl"
+
+interface nsIOutputStreamCallback;
+interface nsIEventTarget;
+
+/**
+ * If an output stream is non-blocking, it may return NS_BASE_STREAM_WOULD_BLOCK
+ * when written to. The caller must then wait for the stream to become
+ * writable. If the stream implements nsIAsyncOutputStream, then the caller can
+ * use this interface to request an asynchronous notification when the stream
+ * becomes writable or closed (via the AsyncWait method).
+ *
+ * While this interface is almost exclusively used with non-blocking streams, it
+ * is not necessary that nsIOutputStream::isNonBlocking return true. Nor is it
+ * necessary that a non-blocking nsIOutputStream implementation also implement
+ * nsIAsyncOutputStream.
+ */
+[scriptable, uuid(beb632d3-d77a-4e90-9134-f9ece69e8200)]
+interface nsIAsyncOutputStream : nsIOutputStream
+{
+ /**
+ * This method closes the stream and sets its internal status. If the
+ * stream is already closed, then this method is ignored. Once the stream
+ * is closed, the stream's status cannot be changed. Any successful status
+ * code passed to this method is treated as NS_BASE_STREAM_CLOSED, which
+ * is equivalent to nsIInputStream::close.
+ *
+ * NOTE: this method exists in part to support pipes, which have both an
+ * input end and an output end. If the output end of a pipe is closed, then
+ * reads from the input end of the pipe will fail. The error code returned
+ * when an attempt is made to read from a "closed" pipe corresponds to the
+ * status code passed in when the output end of the pipe is closed, which
+ * greatly simplifies working with pipes in some cases.
+ *
+ * @param aStatus
+ * The error that will be reported if this stream is accessed after
+ * it has been closed.
+ */
+ void closeWithStatus(in nsresult reason);
+
+ /**
+ * Asynchronously wait for the stream to be writable or closed. The
+ * notification is one-shot, meaning that each asyncWait call will result
+ * in exactly one notification callback. After the OnOutputStreamReady event
+ * is dispatched, the stream releases its reference to the
+ * nsIOutputStreamCallback object. It is safe to call asyncWait again from the
+ * notification handler.
+ *
+ * This method may be called at any time (even if write has not been called).
+ * In other words, this method may be called when the stream already has
+ * room for more data. It may also be called when the stream is closed. If
+ * the stream is already writable or closed when AsyncWait is called, then the
+ * OnOutputStreamReady event will be dispatched immediately. Otherwise, the
+ * event will be dispatched when the stream becomes writable or closed.
+ *
+ * @param aCallback
+ * This object is notified when the stream becomes ready. This
+ * parameter may be null to clear an existing callback.
+ * @param aFlags
+ * This parameter specifies optional flags passed in to configure
+ * the behavior of this method. Pass zero to specify no flags.
+ * @param aRequestedCount
+ * Wait until at least this many bytes can be written. This is only
+ * a suggestion to the underlying stream; it may be ignored. The
+ * caller may pass zero to indicate no preference.
+ * @param aEventTarget
+ * Specify NULL to receive notification on ANY thread (possibly even
+ * recursively on the calling thread -- i.e., synchronously), or
+ * specify that the notification be delivered to a specific event
+ * target.
+ */
+ void asyncWait(in nsIOutputStreamCallback aCallback,
+ in unsigned long aFlags,
+ in unsigned long aRequestedCount,
+ in nsIEventTarget aEventTarget);
+
+ /**
+ * If passed to asyncWait, this flag overrides the default behavior,
+ * causing the OnOutputStreamReady notification to be suppressed until the
+ * stream becomes closed (either as a result of closeWithStatus/close being
+ * called on the stream or possibly due to some error in the underlying
+ * stream).
+ */
+ const unsigned long WAIT_CLOSURE_ONLY = (1<<0);
+};
+
+/**
+ * This is a companion interface for nsIAsyncOutputStream::asyncWait.
+ */
+[scriptable, uuid(40dbcdff-9053-42c5-a57c-3ec910d0f148)]
+interface nsIOutputStreamCallback : nsISupports
+{
+ /**
+ * Called to indicate that the stream is either writable or closed.
+ *
+ * @param aStream
+ * The stream whose asyncWait method was called.
+ */
+ void onOutputStreamReady(in nsIAsyncOutputStream aStream);
+};
diff --git a/xpcom/io/nsIBinaryInputStream.idl b/xpcom/io/nsIBinaryInputStream.idl
new file mode 100644
index 000000000..5d228dee4
--- /dev/null
+++ b/xpcom/io/nsIBinaryInputStream.idl
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "nsIInputStream.idl"
+#include "nsrootidl.idl"
+
+/**
+ * This interface allows consumption of primitive data types from a "binary
+ * stream" containing untagged, big-endian binary data, i.e. as produced by an
+ * implementation of nsIBinaryOutputStream. This might be used, for example,
+ * to implement network protocols or to read from architecture-neutral disk
+ * files, i.e. ones that can be read and written by both big-endian and
+ * little-endian platforms.
+ *
+ * @See nsIBinaryOutputStream
+ */
+
+[scriptable, uuid(42084755-fedc-4310-831c-4f43e7b42e20)]
+interface nsIBinaryInputStream : nsIInputStream {
+ void setInputStream(in nsIInputStream aInputStream);
+
+ /**
+ * Read 8-bits from the stream.
+ *
+ * @return that byte to be treated as a boolean.
+ */
+ boolean readBoolean();
+
+ uint8_t read8();
+ uint16_t read16();
+ uint32_t read32();
+ uint64_t read64();
+
+ float readFloat();
+ double readDouble();
+
+ /**
+ * Read an 8-bit pascal style string from the stream.
+ * 32-bit length field, followed by length 8-bit chars.
+ */
+ ACString readCString();
+
+ /**
+ * Read an 16-bit pascal style string from the stream.
+ * 32-bit length field, followed by length PRUnichars.
+ */
+ AString readString();
+
+ /**
+ * Read an opaque byte array from the stream.
+ *
+ * @param aLength the number of bytes that must be read.
+ *
+ * @throws NS_ERROR_FAILURE if it can't read aLength bytes
+ */
+ void readBytes(in uint32_t aLength,
+ [size_is(aLength), retval] out string aString);
+
+ /**
+ * Read an opaque byte array from the stream, storing the results
+ * as an array of PRUint8s.
+ *
+ * @param aLength the number of bytes that must be read.
+ *
+ * @throws NS_ERROR_FAILURE if it can't read aLength bytes
+ */
+ void readByteArray(in uint32_t aLength,
+ [array, size_is(aLength), retval] out uint8_t aBytes);
+
+ /**
+ * Read opaque bytes from the stream, storing the results in an ArrayBuffer.
+ *
+ * @param aLength the number of bytes that must be read
+ * @param aArrayBuffer the arraybuffer in which to store the results
+ * Note: passing view.buffer, where view is an ArrayBufferView of an
+ * ArrayBuffer, is not valid unless view.byteOffset == 0.
+ *
+ * @throws NS_ERROR_FAILURE if it can't read aLength bytes
+ */
+ [implicit_jscontext]
+ void readArrayBuffer(in uint32_t aLength, in jsval aArrayBuffer);
+};
+
+%{C++
+
+#ifdef MOZILLA_INTERNAL_API
+#include "nsString.h"
+
+inline nsresult
+NS_ReadOptionalCString(nsIBinaryInputStream* aStream, nsACString& aResult)
+{
+ bool nonnull;
+ nsresult rv = aStream->ReadBoolean(&nonnull);
+ if (NS_SUCCEEDED(rv)) {
+ if (nonnull)
+ rv = aStream->ReadCString(aResult);
+ else
+ aResult.Truncate();
+ }
+ return rv;
+}
+
+inline nsresult
+NS_ReadOptionalString(nsIBinaryInputStream* aStream, nsAString& aResult)
+{
+ bool nonnull;
+ nsresult rv = aStream->ReadBoolean(&nonnull);
+ if (NS_SUCCEEDED(rv)) {
+ if (nonnull)
+ rv = aStream->ReadString(aResult);
+ else
+ aResult.Truncate();
+ }
+ return rv;
+}
+#endif
+
+%}
diff --git a/xpcom/io/nsIBinaryOutputStream.idl b/xpcom/io/nsIBinaryOutputStream.idl
new file mode 100644
index 000000000..7711aecd1
--- /dev/null
+++ b/xpcom/io/nsIBinaryOutputStream.idl
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "nsIOutputStream.idl"
+#include "nsrootidl.idl"
+
+/**
+ * This interface allows writing of primitive data types (integers,
+ * floating-point values, booleans, etc.) to a stream in a binary, untagged,
+ * fixed-endianness format. This might be used, for example, to implement
+ * network protocols or to produce architecture-neutral binary disk files,
+ * i.e. ones that can be read and written by both big-endian and little-endian
+ * platforms. Output is written in big-endian order (high-order byte first),
+ * as this is traditional network order.
+ *
+ * @See nsIBinaryInputStream
+ */
+
+[scriptable, uuid(204ee610-8765-11d3-90cf-0040056a906e)]
+interface nsIBinaryOutputStream : nsIOutputStream {
+ void setOutputStream(in nsIOutputStream aOutputStream);
+
+ /**
+ * Write a boolean as an 8-bit char to the stream.
+ */
+ void writeBoolean(in boolean aBoolean);
+
+ void write8(in uint8_t aByte);
+ void write16(in uint16_t a16);
+ void write32(in uint32_t a32);
+ void write64(in uint64_t a64);
+
+ void writeFloat(in float aFloat);
+ void writeDouble(in double aDouble);
+
+ /**
+ * Write an 8-bit pascal style string to the stream.
+ * 32-bit length field, followed by length 8-bit chars.
+ */
+ void writeStringZ(in string aString);
+
+ /**
+ * Write a 16-bit pascal style string to the stream.
+ * 32-bit length field, followed by length PRUnichars.
+ */
+ void writeWStringZ(in wstring aString);
+
+ /**
+ * Write an 8-bit pascal style string (UTF8-encoded) to the stream.
+ * 32-bit length field, followed by length 8-bit chars.
+ */
+ void writeUtf8Z(in wstring aString);
+
+ /**
+ * Write an opaque byte array to the stream.
+ */
+ void writeBytes([size_is(aLength)] in string aString, in uint32_t aLength);
+
+ /**
+ * Write an opaque byte array to the stream.
+ */
+ void writeByteArray([array, size_is(aLength)] in uint8_t aBytes,
+ in uint32_t aLength);
+
+};
+
+%{C++
+
+inline nsresult
+NS_WriteOptionalStringZ(nsIBinaryOutputStream* aStream, const char* aString)
+{
+ bool nonnull = (aString != nullptr);
+ nsresult rv = aStream->WriteBoolean(nonnull);
+ if (NS_SUCCEEDED(rv) && nonnull)
+ rv = aStream->WriteStringZ(aString);
+ return rv;
+}
+
+inline nsresult
+NS_WriteOptionalWStringZ(nsIBinaryOutputStream* aStream, const PRUnichar* aString)
+{
+ bool nonnull = (aString != nullptr);
+ nsresult rv = aStream->WriteBoolean(nonnull);
+ if (NS_SUCCEEDED(rv) && nonnull)
+ rv = aStream->WriteWStringZ(aString);
+ return rv;
+}
+
+%}
diff --git a/xpcom/io/nsIConverterInputStream.idl b/xpcom/io/nsIConverterInputStream.idl
new file mode 100644
index 000000000..6f3d01071
--- /dev/null
+++ b/xpcom/io/nsIConverterInputStream.idl
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIUnicharInputStream.idl"
+
+interface nsIInputStream;
+
+/**
+ * A unichar input stream that wraps an input stream.
+ * This allows reading unicode strings from a stream, automatically converting
+ * the bytes from a selected character encoding.
+ */
+[scriptable, uuid(FC66FFB6-5404-4908-A4A3-27F92FA0579D)]
+interface nsIConverterInputStream : nsIUnicharInputStream {
+ /**
+ * Default replacement char value, U+FFFD REPLACEMENT CHARACTER.
+ */
+ const PRUnichar DEFAULT_REPLACEMENT_CHARACTER = 0xFFFD;
+
+ /**
+ * Initialize this stream.
+ * @param aStream
+ * The underlying stream to read from.
+ * @param aCharset
+ * The character encoding to use for converting the bytes of the
+ * stream. A null charset will be interpreted as UTF-8.
+ * @param aBufferSize
+ * How many bytes to buffer.
+ * @param aReplacementChar
+ * The character to replace unknown byte sequences in the stream
+ * with. The standard replacement character is U+FFFD.
+ * A value of 0x0000 will cause an exception to be thrown if unknown
+ * byte sequences are encountered in the stream.
+ */
+ void init (in nsIInputStream aStream, in string aCharset,
+ in long aBufferSize, in PRUnichar aReplacementChar);
+};
+
diff --git a/xpcom/io/nsIConverterOutputStream.idl b/xpcom/io/nsIConverterOutputStream.idl
new file mode 100644
index 000000000..66c30f0bc
--- /dev/null
+++ b/xpcom/io/nsIConverterOutputStream.idl
@@ -0,0 +1,44 @@
+/* vim:set expandtab ts=4 sw=4 sts=4 cin: */
+/* 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 "nsIUnicharOutputStream.idl"
+
+interface nsIOutputStream;
+
+/**
+ * This interface allows writing strings to a stream, doing automatic
+ * character encoding conversion.
+ */
+[scriptable, uuid(4b71113a-cb0d-479f-8ed5-01daeba2e8d4)]
+interface nsIConverterOutputStream : nsIUnicharOutputStream
+{
+ /**
+ * Initialize this stream. Must be called before any other method on this
+ * interface, or you will crash. The output stream passed to this method
+ * must not be null, or you will crash.
+ *
+ * @param aOutStream
+ * The underlying output stream to which the converted strings will
+ * be written.
+ * @param aCharset
+ * The character set to use for encoding the characters. A null
+ * charset will be interpreted as UTF-8.
+ * @param aBufferSize
+ * How many bytes to buffer. A value of 0 means that no bytes will be
+ * buffered. Implementations not supporting buffering may ignore
+ * this parameter.
+ * @param aReplacementCharacter
+ * The replacement character to use when an unsupported character is found.
+ * The character must be encodable in the selected character
+ * encoding; otherwise, attempts to write an unsupported character
+ * will throw NS_ERROR_LOSS_OF_SIGNIFICANT_DATA.
+ *
+ * A value of 0x0000 will cause an exception to be thrown upon
+ * attempts to write unsupported characters.
+ */
+ void init(in nsIOutputStream aOutStream, in string aCharset,
+ in unsigned long aBufferSize,
+ in PRUnichar aReplacementCharacter);
+};
diff --git a/xpcom/io/nsIDirectoryEnumerator.idl b/xpcom/io/nsIDirectoryEnumerator.idl
new file mode 100644
index 000000000..7a1135fda
--- /dev/null
+++ b/xpcom/io/nsIDirectoryEnumerator.idl
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+interface nsIFile;
+
+/**
+ * This interface provides a means for enumerating the contents of a directory.
+ * It is similar to nsISimpleEnumerator except the retrieved entries are QI'ed
+ * to nsIFile, and there is a mechanism for closing the directory when the
+ * enumeration is complete.
+ */
+[scriptable, uuid(31f7f4ae-6916-4f2d-a81e-926a4e3022ee)]
+interface nsIDirectoryEnumerator : nsISupports
+{
+ /**
+ * Retrieves the next file in the sequence. The "nextFile" element is the
+ * first element upon the first call. This attribute is null if there is no
+ * next element.
+ */
+ readonly attribute nsIFile nextFile;
+
+ /**
+ * Closes the directory being enumerated, releasing the system resource.
+ * @throws NS_OK if the call succeeded and the directory was closed.
+ * NS_ERROR_FAILURE if the directory close failed.
+ * It is safe to call this function many times.
+ */
+ void close();
+};
+
diff --git a/xpcom/io/nsIDirectoryService.idl b/xpcom/io/nsIDirectoryService.idl
new file mode 100644
index 000000000..520c9bc11
--- /dev/null
+++ b/xpcom/io/nsIDirectoryService.idl
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+#include "nsIFile.idl"
+
+/**
+ * nsIDirectoryServiceProvider
+ *
+ * Used by Directory Service to get file locations.
+ */
+
+[scriptable, uuid(bbf8cab0-d43a-11d3-8cc2-00609792278c)]
+interface nsIDirectoryServiceProvider: nsISupports
+{
+ /**
+ * getFile
+ *
+ * Directory Service calls this when it gets the first request for
+ * a prop or on every request if the prop is not persistent.
+ *
+ * @param prop The symbolic name of the file.
+ * @param persistent TRUE - The returned file will be cached by Directory
+ * Service. Subsequent requests for this prop will
+ * bypass the provider and use the cache.
+ * FALSE - The provider will be asked for this prop
+ * each time it is requested.
+ *
+ * @return The file represented by the property.
+ *
+ */
+ nsIFile getFile(in string prop, out boolean persistent);
+};
+
+/**
+ * nsIDirectoryServiceProvider2
+ *
+ * An extension of nsIDirectoryServiceProvider which allows
+ * multiple files to be returned for the given key.
+ */
+
+[scriptable, uuid(2f977d4b-5485-11d4-87e2-0010a4e75ef2)]
+interface nsIDirectoryServiceProvider2: nsIDirectoryServiceProvider
+{
+ /**
+ * getFiles
+ *
+ * Directory Service calls this when it gets a request for
+ * a prop and the requested type is nsISimpleEnumerator.
+ *
+ * @param prop The symbolic name of the file list.
+ *
+ * @return An enumerator for a list of file locations.
+ * The elements in the enumeration are nsIFile
+ * @returnCode NS_SUCCESS_AGGREGATE_RESULT if this result should be
+ * aggregated with other "lower" providers.
+ */
+ nsISimpleEnumerator getFiles(in string prop);
+};
+
+/**
+ * nsIDirectoryService
+ */
+
+[scriptable, uuid(57a66a60-d43a-11d3-8cc2-00609792278c)]
+interface nsIDirectoryService: nsISupports
+{
+ /**
+ * init
+ *
+ * Must be called. Used internally by XPCOM initialization.
+ *
+ */
+ void init();
+
+ /**
+ * registerProvider
+ *
+ * Register a provider with the service.
+ *
+ * @param prov The service will keep a strong reference
+ * to this object. It will be released when
+ * the service is released.
+ *
+ */
+ void registerProvider(in nsIDirectoryServiceProvider prov);
+
+ /**
+ * unregisterProvider
+ *
+ * Unregister a provider with the service.
+ *
+ * @param prov
+ *
+ */
+ void unregisterProvider(in nsIDirectoryServiceProvider prov);
+};
+
+
diff --git a/xpcom/io/nsIFile.idl b/xpcom/io/nsIFile.idl
new file mode 100644
index 000000000..0f353ecb4
--- /dev/null
+++ b/xpcom/io/nsIFile.idl
@@ -0,0 +1,465 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+%{C++
+#include "prio.h"
+#include "prlink.h"
+#include <stdio.h>
+%}
+
+[ptr] native PRFileDescStar(PRFileDesc);
+[ptr] native PRLibraryStar(PRLibrary);
+[ptr] native FILE(FILE);
+
+interface nsISimpleEnumerator;
+
+/**
+ * This is the only correct cross-platform way to specify a file.
+ * Strings are not such a way. If you grew up on windows or unix, you
+ * may think they are. Welcome to reality.
+ *
+ * All methods with string parameters have two forms. The preferred
+ * form operates on UCS-2 encoded characters strings. An alternate
+ * form operates on characters strings encoded in the "native" charset.
+ *
+ * A string containing characters encoded in the native charset cannot
+ * be safely passed to javascript via xpconnect. Therefore, the "native
+ * methods" are not scriptable.
+ */
+[scriptable, uuid(272a5020-64f5-485c-a8c4-44b2882ae0a2), builtinclass]
+interface nsIFile : nsISupports
+{
+ /**
+ * Create Types
+ *
+ * NORMAL_FILE_TYPE - A normal file.
+ * DIRECTORY_TYPE - A directory/folder.
+ */
+ const unsigned long NORMAL_FILE_TYPE = 0;
+ const unsigned long DIRECTORY_TYPE = 1;
+
+ /**
+ * append[Native]
+ *
+ * This function is used for constructing a descendent of the
+ * current nsIFile.
+ *
+ * @param node
+ * A string which is intended to be a child node of the nsIFile.
+ * For the |appendNative| method, the node must be in the native
+ * filesystem charset.
+ */
+ void append(in AString node);
+ [noscript] void appendNative(in ACString node);
+
+ /**
+ * Normalize the pathName (e.g. removing .. and . components on Unix).
+ */
+ void normalize();
+
+ /**
+ * create
+ *
+ * This function will create a new file or directory in the
+ * file system. Any nodes that have not been created or
+ * resolved, will be. If the file or directory already
+ * exists create() will return NS_ERROR_FILE_ALREADY_EXISTS.
+ *
+ * @param type
+ * This specifies the type of file system object
+ * to be made. The only two types at this time
+ * are file and directory which are defined above.
+ * If the type is unrecongnized, we will return an
+ * error (NS_ERROR_FILE_UNKNOWN_TYPE).
+ *
+ * @param permissions
+ * The unix style octal permissions. This may
+ * be ignored on systems that do not need to do
+ * permissions.
+ */
+ void create(in unsigned long type, in unsigned long permissions);
+
+ /**
+ * Accessor to the leaf name of the file itself.
+ * For the |nativeLeafName| method, the nativeLeafName must
+ * be in the native filesystem charset.
+ */
+ attribute AString leafName;
+ [noscript] attribute ACString nativeLeafName;
+
+ /**
+ * copyTo[Native]
+ *
+ * This will copy this file to the specified newParentDir.
+ * If a newName is specified, the file will be renamed.
+ * If 'this' is not created we will return an error
+ * (NS_ERROR_FILE_TARGET_DOES_NOT_EXIST).
+ *
+ * copyTo may fail if the file already exists in the destination
+ * directory.
+ *
+ * copyTo will NOT resolve aliases/shortcuts during the copy.
+ *
+ * @param newParentDir
+ * This param is the destination directory. If the
+ * newParentDir is null, copyTo() will use the parent
+ * directory of this file. If the newParentDir is not
+ * empty and is not a directory, an error will be
+ * returned (NS_ERROR_FILE_DESTINATION_NOT_DIR). For the
+ * |CopyToNative| method, the newName must be in the
+ * native filesystem charset.
+ *
+ * @param newName
+ * This param allows you to specify a new name for
+ * the file to be copied. This param may be empty, in
+ * which case the current leaf name will be used.
+ */
+ void copyTo(in nsIFile newParentDir, in AString newName);
+ [noscript] void CopyToNative(in nsIFile newParentDir, in ACString newName);
+
+ /**
+ * copyToFollowingLinks[Native]
+ *
+ * This function is identical to copyTo with the exception that,
+ * as the name implies, it follows symbolic links. The XP_UNIX
+ * implementation always follow symbolic links when copying. For
+ * the |CopyToFollowingLinks| method, the newName must be in the
+ * native filesystem charset.
+ */
+ void copyToFollowingLinks(in nsIFile newParentDir, in AString newName);
+ [noscript] void copyToFollowingLinksNative(in nsIFile newParentDir, in ACString newName);
+
+ /**
+ * moveTo[Native]
+ *
+ * A method to move this file or directory to newParentDir.
+ * If a newName is specified, the file or directory will be renamed.
+ * If 'this' is not created we will return an error
+ * (NS_ERROR_FILE_TARGET_DOES_NOT_EXIST).
+ * If 'this' is a file, and the destination file already exists, moveTo
+ * will replace the old file.
+ * This object is updated to refer to the new file.
+ *
+ * moveTo will NOT resolve aliases/shortcuts during the copy.
+ * moveTo will do the right thing and allow copies across volumes.
+ * moveTo will return an error (NS_ERROR_FILE_DIR_NOT_EMPTY) if 'this' is
+ * a directory and the destination directory is not empty.
+ * moveTo will return an error (NS_ERROR_FILE_ACCESS_DENIED) if 'this' is
+ * a directory and the destination directory is not writable.
+ *
+ * @param newParentDir
+ * This param is the destination directory. If the
+ * newParentDir is empty, moveTo() will rename the file
+ * within its current directory. If the newParentDir is
+ * not empty and does not name a directory, an error will
+ * be returned (NS_ERROR_FILE_DESTINATION_NOT_DIR). For
+ * the |moveToNative| method, the newName must be in the
+ * native filesystem charset.
+ *
+ * @param newName
+ * This param allows you to specify a new name for
+ * the file to be moved. This param may be empty, in
+ * which case the current leaf name will be used.
+ */
+ void moveTo(in nsIFile newParentDir, in AString newName);
+ [noscript] void moveToNative(in nsIFile newParentDir, in ACString newName);
+
+ /**
+ * This will try to delete this file. The 'recursive' flag
+ * must be PR_TRUE to delete directories which are not empty.
+ *
+ * This will not resolve any symlinks.
+ */
+ void remove(in boolean recursive);
+
+ /**
+ * Attributes of nsIFile.
+ */
+
+ attribute unsigned long permissions;
+ attribute unsigned long permissionsOfLink;
+
+ /**
+ * File Times are to be in milliseconds from
+ * midnight (00:00:00), January 1, 1970 Greenwich Mean
+ * Time (GMT).
+ */
+ attribute PRTime lastModifiedTime;
+ attribute PRTime lastModifiedTimeOfLink;
+
+ /**
+ * WARNING! On the Mac, getting/setting the file size with nsIFile
+ * only deals with the size of the data fork. If you need to
+ * know the size of the combined data and resource forks use the
+ * GetFileSizeWithResFork() method defined on nsILocalFileMac.
+ */
+ attribute int64_t fileSize;
+ readonly attribute int64_t fileSizeOfLink;
+
+ /**
+ * target & path
+ *
+ * Accessor to the string path. The native version of these
+ * strings are not guaranteed to be a usable path to pass to
+ * NSPR or the C stdlib. There are problems that affect
+ * platforms on which a path does not fully specify a file
+ * because two volumes can have the same name (e.g., mac).
+ * This is solved by holding "private", native data in the
+ * nsIFile implementation. This native data is lost when
+ * you convert to a string.
+ *
+ * DO NOT PASS TO USE WITH NSPR OR STDLIB!
+ *
+ * target
+ * Find out what the symlink points at. Will give error
+ * (NS_ERROR_FILE_INVALID_PATH) if not a symlink.
+ *
+ * path
+ * Find out what the nsIFile points at.
+ *
+ * Note that the ACString attributes are returned in the
+ * native filesystem charset.
+ *
+ */
+ readonly attribute AString target;
+ [noscript] readonly attribute ACString nativeTarget;
+ readonly attribute AString path;
+ [noscript] readonly attribute ACString nativePath;
+
+ boolean exists();
+ boolean isWritable();
+ boolean isReadable();
+ boolean isExecutable();
+ boolean isHidden();
+ boolean isDirectory();
+ boolean isFile();
+ boolean isSymlink();
+ /**
+ * Not a regular file, not a directory, not a symlink.
+ */
+ boolean isSpecial();
+
+ /**
+ * createUnique
+ *
+ * This function will create a new file or directory in the
+ * file system. Any nodes that have not been created or
+ * resolved, will be. If this file already exists, we try
+ * variations on the leaf name "suggestedName" until we find
+ * one that did not already exist.
+ *
+ * If the search for nonexistent files takes too long
+ * (thousands of the variants already exist), we give up and
+ * return NS_ERROR_FILE_TOO_BIG.
+ *
+ * @param type
+ * This specifies the type of file system object
+ * to be made. The only two types at this time
+ * are file and directory which are defined above.
+ * If the type is unrecongnized, we will return an
+ * error (NS_ERROR_FILE_UNKNOWN_TYPE).
+ *
+ * @param permissions
+ * The unix style octal permissions. This may
+ * be ignored on systems that do not need to do
+ * permissions.
+ */
+ void createUnique(in unsigned long type, in unsigned long permissions);
+
+ /**
+ * clone()
+ *
+ * This function will allocate and initialize a nsIFile object to the
+ * exact location of the |this| nsIFile.
+ *
+ * @param file
+ * A nsIFile which this object will be initialize
+ * with.
+ *
+ */
+ nsIFile clone();
+
+ /**
+ * Will determine if the inFile equals this.
+ */
+ boolean equals(in nsIFile inFile);
+
+ /**
+ * Will determine if inFile is a descendant of this file
+ * If |recur| is true, look in subdirectories too
+ */
+ boolean contains(in nsIFile inFile, in boolean recur);
+
+ /**
+ * Parent will be null when this is at the top of the volume.
+ */
+ readonly attribute nsIFile parent;
+
+ /**
+ * Returns an enumeration of the elements in a directory. Each
+ * element in the enumeration is an nsIFile.
+ *
+ * @throws NS_ERROR_FILE_NOT_DIRECTORY if the current nsIFile does
+ * not specify a directory.
+ */
+ readonly attribute nsISimpleEnumerator directoryEntries;
+
+ /**
+ * initWith[Native]Path
+ *
+ * This function will initialize the nsIFile object. Any
+ * internal state information will be reset.
+ *
+ * @param filePath
+ * A string which specifies a full file path to a
+ * location. Relative paths will be treated as an
+ * error (NS_ERROR_FILE_UNRECOGNIZED_PATH). For
+ * initWithNativePath, the filePath must be in the native
+ * filesystem charset.
+ */
+ void initWithPath(in AString filePath);
+ [noscript] void initWithNativePath(in ACString filePath);
+
+ /**
+ * initWithFile
+ *
+ * Initialize this object with another file
+ *
+ * @param aFile
+ * the file this becomes equivalent to
+ */
+ void initWithFile(in nsIFile aFile);
+
+ /**
+ * followLinks
+ *
+ * This attribute will determine if the nsLocalFile will auto
+ * resolve symbolic links. By default, this value will be false
+ * on all non unix systems. On unix, this attribute is effectively
+ * a noop.
+ */
+ attribute boolean followLinks;
+
+ /**
+ * Flag for openNSPRFileDesc(), to hint to the OS that the file will be
+ * read sequentially with agressive readahead.
+ */
+ const unsigned long OS_READAHEAD = 0x40000000;
+
+ /**
+ * Flag for openNSPRFileDesc(). Deprecated and unreliable!
+ * Instead use NS_OpenAnonymousTemporaryFile() to create a temporary
+ * file which will be deleted upon close!
+ */
+ const unsigned long DELETE_ON_CLOSE = 0x80000000;
+
+ /**
+ * Return the result of PR_Open on the file. The caller is
+ * responsible for calling PR_Close on the result.
+ *
+ * @param flags the PR_Open flags from prio.h, plus optionally
+ * OS_READAHEAD or DELETE_ON_CLOSE. OS_READAHEAD is a hint to the
+ * OS that the file will be read sequentially with agressive
+ * readahead. DELETE_ON_CLOSE is unreliable on Windows and is deprecated.
+ * Instead use NS_OpenAnonymousTemporaryFile() to create a temporary
+ * file which will be deleted upon close.
+ */
+ [noscript] PRFileDescStar openNSPRFileDesc(in long flags, in long mode);
+
+ /**
+ * Return the result of fopen on the file. The caller is
+ * responsible for calling fclose on the result.
+ */
+ [noscript] FILE openANSIFileDesc(in string mode);
+
+ /**
+ * Return the result of PR_LoadLibrary on the file. The caller is
+ * responsible for calling PR_UnloadLibrary on the result.
+ */
+ [noscript] PRLibraryStar load();
+
+ // number of bytes available on disk to non-superuser
+ readonly attribute int64_t diskSpaceAvailable;
+
+ /**
+ * appendRelative[Native]Path
+ *
+ * Append a relative path to the current path of the nsIFile object.
+ *
+ * @param relativeFilePath
+ * relativeFilePath is a native relative path. For security reasons,
+ * this cannot contain .. or cannot start with a directory separator.
+ * For the |appendRelativeNativePath| method, the relativeFilePath
+ * must be in the native filesystem charset.
+ */
+ void appendRelativePath(in AString relativeFilePath);
+ [noscript] void appendRelativeNativePath(in ACString relativeFilePath);
+
+ /**
+ * Accessor to a null terminated string which will specify
+ * the file in a persistent manner for disk storage.
+ *
+ * The character set of this attribute is undefined. DO NOT TRY TO
+ * INTERPRET IT AS HUMAN READABLE TEXT!
+ */
+ attribute ACString persistentDescriptor;
+
+ /**
+ * reveal
+ *
+ * Ask the operating system to open the folder which contains
+ * this file or folder. This routine only works on platforms which
+ * support the ability to open a folder and is run async on Windows.
+ * This routine must be called on the main.
+ */
+ void reveal();
+
+ /**
+ * launch
+ *
+ * Ask the operating system to attempt to open the file.
+ * this really just simulates "double clicking" the file on your platform.
+ * This routine only works on platforms which support this functionality
+ * and is run async on Windows. This routine must be called on the
+ * main thread.
+ */
+ void launch();
+
+ /**
+ * getRelativeDescriptor
+ *
+ * Returns a relative file path in an opaque, XP format. It is therefore
+ * not a native path.
+ *
+ * The character set of the string returned from this function is
+ * undefined. DO NOT TRY TO INTERPRET IT AS HUMAN READABLE TEXT!
+ *
+ * @param fromFile
+ * the file from which the descriptor is relative.
+ * There is no defined result if this param is null.
+ */
+ ACString getRelativeDescriptor(in nsIFile fromFile);
+
+ /**
+ * setRelativeDescriptor
+ *
+ * Initializes the file to the location relative to fromFile using
+ * a string returned by getRelativeDescriptor.
+ *
+ * @param fromFile
+ * the file to which the descriptor is relative
+ * @param relative
+ * the relative descriptor obtained from getRelativeDescriptor
+ */
+ void setRelativeDescriptor(in nsIFile fromFile, in ACString relativeDesc);
+};
+
+%{C++
+#ifdef MOZILLA_INTERNAL_API
+#include "nsDirectoryServiceUtils.h"
+#endif
+%}
diff --git a/xpcom/io/nsIIOUtil.idl b/xpcom/io/nsIIOUtil.idl
new file mode 100644
index 000000000..577ba4046
--- /dev/null
+++ b/xpcom/io/nsIIOUtil.idl
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+interface nsIInputStream;
+interface nsIOutputStream;
+
+/**
+ * nsIIOUtil provdes various xpcom/io-related utility methods.
+ */
+[scriptable, uuid(e8152f7f-4209-4c63-ad23-c3d2aa0c5a49)]
+interface nsIIOUtil : nsISupports
+{
+ /**
+ * Test whether an input stream is buffered. See nsStreamUtils.h
+ * documentation for NS_InputStreamIsBuffered for the definition of
+ * "buffered" used here and for edge-case behavior.
+ *
+ * @throws NS_ERROR_INVALID_POINTER if null is passed in.
+ */
+ boolean inputStreamIsBuffered(in nsIInputStream aStream);
+
+ /**
+ * Test whether an output stream is buffered. See nsStreamUtils.h
+ * documentation for NS_OutputStreamIsBuffered for the definition of
+ * "buffered" used here and for edge-case behavior.
+ *
+ * @throws NS_ERROR_INVALID_POINTER if null is passed in.
+ */
+ boolean outputStreamIsBuffered(in nsIOutputStream aStream);
+};
diff --git a/xpcom/io/nsIInputStream.idl b/xpcom/io/nsIInputStream.idl
new file mode 100644
index 000000000..b68cdbb7d
--- /dev/null
+++ b/xpcom/io/nsIInputStream.idl
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+interface nsIInputStream;
+
+%{C++
+/**
+ * The signature of the writer function passed to ReadSegments. This
+ * is the "consumer" of data that gets read from the stream's buffer.
+ *
+ * @param aInStream stream being read
+ * @param aClosure opaque parameter passed to ReadSegments
+ * @param aFromSegment pointer to memory owned by the input stream. This is
+ * where the writer function should start consuming data.
+ * @param aToOffset amount of data already consumed by this writer during this
+ * ReadSegments call. This is also the sum of the aWriteCount
+ * returns from this writer over the previous invocations of
+ * the writer by this ReadSegments call.
+ * @param aCount Number of bytes available to be read starting at aFromSegment
+ * @param [out] aWriteCount number of bytes read by this writer function call
+ *
+ * Implementers should return the following:
+ *
+ * @return NS_OK and (*aWriteCount > 0) if consumed some data
+ * @return <any-error> if not interested in consuming any data
+ *
+ * Errors are never passed to the caller of ReadSegments.
+ *
+ * NOTE: returning NS_OK and (*aWriteCount = 0) has undefined behavior.
+ */
+typedef NS_CALLBACK(nsWriteSegmentFun)(nsIInputStream *aInStream,
+ void *aClosure,
+ const char *aFromSegment,
+ uint32_t aToOffset,
+ uint32_t aCount,
+ uint32_t *aWriteCount);
+%}
+
+native nsWriteSegmentFun(nsWriteSegmentFun);
+
+/**
+ * nsIInputStream
+ *
+ * An interface describing a readable stream of data. An input stream may be
+ * "blocking" or "non-blocking" (see the IsNonBlocking method). A blocking
+ * input stream may suspend the calling thread in order to satisfy a call to
+ * Close, Available, Read, or ReadSegments. A non-blocking input stream, on
+ * the other hand, must not block the calling thread of execution.
+ *
+ * NOTE: blocking input streams are often read on a background thread to avoid
+ * locking up the main application thread. For this reason, it is generally
+ * the case that a blocking input stream should be implemented using thread-
+ * safe AddRef and Release.
+ */
+[scriptable, uuid(53cdbc97-c2d7-4e30-b2c3-45b2ee79db18)]
+interface nsIInputStream : nsISupports
+{
+ /**
+ * Close the stream. This method causes subsequent calls to Read and
+ * ReadSegments to return 0 bytes read to indicate end-of-file. Any
+ * subsequent calls to Available should throw NS_BASE_STREAM_CLOSED.
+ */
+ void close();
+
+ /**
+ * Determine number of bytes available in the stream. A non-blocking
+ * stream that does not yet have any data to read should return 0 bytes
+ * from this method (i.e., it must not throw the NS_BASE_STREAM_WOULD_BLOCK
+ * exception).
+ *
+ * In addition to the number of bytes available in the stream, this method
+ * also informs the caller of the current status of the stream. A stream
+ * that is closed will throw an exception when this method is called. That
+ * enables the caller to know the condition of the stream before attempting
+ * to read from it. If a stream is at end-of-file, but not closed, then
+ * this method returns 0 bytes available. (Note: some nsIInputStream
+ * implementations automatically close when eof is reached; some do not).
+ *
+ * @return number of bytes currently available in the stream.
+ *
+ * @throws NS_BASE_STREAM_CLOSED if the stream is closed normally.
+ * @throws <other-error> if the stream is closed due to some error
+ * condition
+ */
+ unsigned long long available();
+
+ /**
+ * Read data from the stream.
+ *
+ * @param aBuf the buffer into which the data is to be read
+ * @param aCount the maximum number of bytes to be read
+ *
+ * @return number of bytes read (may be less than aCount).
+ * @return 0 if reached end-of-file
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if reading from the input stream would
+ * block the calling thread (non-blocking mode only)
+ * @throws <other-error> on failure
+ *
+ * NOTE: this method should not throw NS_BASE_STREAM_CLOSED.
+ */
+ [noscript] unsigned long read(in charPtr aBuf, in unsigned long aCount);
+
+ /**
+ * Low-level read method that provides access to the stream's underlying
+ * buffer. The writer function may be called multiple times for segmented
+ * buffers. ReadSegments is expected to keep calling the writer until
+ * either there is nothing left to read or the writer returns an error.
+ * ReadSegments should not call the writer with zero bytes to consume.
+ *
+ * @param aWriter the "consumer" of the data to be read
+ * @param aClosure opaque parameter passed to writer
+ * @param aCount the maximum number of bytes to be read
+ *
+ * @return number of bytes read (may be less than aCount)
+ * @return 0 if reached end-of-file (or if aWriter refused to consume data)
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if reading from the input stream would
+ * block the calling thread (non-blocking mode only)
+ * @throws NS_ERROR_NOT_IMPLEMENTED if the stream has no underlying buffer
+ * @throws <other-error> on failure
+ *
+ * NOTE: this function may be unimplemented if a stream has no underlying
+ * buffer (e.g., socket input stream).
+ *
+ * NOTE: this method should not throw NS_BASE_STREAM_CLOSED.
+ */
+ [noscript] unsigned long readSegments(in nsWriteSegmentFun aWriter,
+ in voidPtr aClosure,
+ in unsigned long aCount);
+
+ /**
+ * @return true if stream is non-blocking
+ *
+ * NOTE: reading from a blocking input stream will block the calling thread
+ * until at least one byte of data can be extracted from the stream.
+ *
+ * NOTE: a non-blocking input stream may implement nsIAsyncInputStream to
+ * provide consumers with a way to wait for the stream to have more data
+ * once its read method is unable to return any data without blocking.
+ */
+ boolean isNonBlocking();
+};
diff --git a/xpcom/io/nsIInputStreamTee.idl b/xpcom/io/nsIInputStreamTee.idl
new file mode 100644
index 000000000..953be7a7c
--- /dev/null
+++ b/xpcom/io/nsIInputStreamTee.idl
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIInputStream.idl"
+
+interface nsIOutputStream;
+interface nsIEventTarget;
+
+/**
+ * A nsIInputStreamTee is a wrapper for an input stream, that when read
+ * reads the specified amount of data from its |source| and copies that
+ * data to its |sink|. |sink| must be a blocking output stream.
+ */
+[scriptable, uuid(90a9d790-3bca-421e-a73b-98f68e13c917)]
+interface nsIInputStreamTee : nsIInputStream
+{
+ attribute nsIInputStream source;
+ attribute nsIOutputStream sink;
+
+ /**
+ * If |eventTarget| is set, copying to sink is done asynchronously using
+ * the event-target (e.g. a thread). If |eventTarget| is not set, copying
+ * to sink happens synchronously while reading from the source.
+ */
+ attribute nsIEventTarget eventTarget;
+};
+
+%{C++
+// factory methods
+extern nsresult
+NS_NewInputStreamTee(nsIInputStream **tee, // read from this input stream
+ nsIInputStream *source,
+ nsIOutputStream *sink);
+
+extern nsresult
+NS_NewInputStreamTeeAsync(nsIInputStream **tee, // read from this input stream
+ nsIInputStream *source,
+ nsIOutputStream *sink,
+ nsIEventTarget *eventTarget);
+%}
diff --git a/xpcom/io/nsILineInputStream.idl b/xpcom/io/nsILineInputStream.idl
new file mode 100644
index 000000000..db53a456d
--- /dev/null
+++ b/xpcom/io/nsILineInputStream.idl
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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"
+
+interface nsILineInputStream;
+
+[scriptable, uuid(c97b466c-1e6e-4773-a4ab-2b2b3190a7a6)]
+interface nsILineInputStream : nsISupports
+{
+ /**
+ * Read a single line from the stream, where a line is a
+ * possibly zero length sequence of 8bit chars terminated by a
+ * CR, LF, CRLF, LFCR, or eof.
+ * The line terminator is not returned.
+ * @retval false
+ * End of file. This line is the last line of the file
+ * (aLine is valid).
+ * @retval true
+ * The file contains further lines.
+ * @note Do not mix readLine with other read functions.
+ * Doing so can cause various problems and is not supported.
+ */
+ boolean readLine(out ACString aLine);
+};
diff --git a/xpcom/io/nsILocalFile.idl b/xpcom/io/nsILocalFile.idl
new file mode 100644
index 000000000..bcfea14a9
--- /dev/null
+++ b/xpcom/io/nsILocalFile.idl
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIFile.idl"
+
+/**
+ * An empty interface to provide backwards compatibility for existing code.
+ *
+ * @see nsIFile
+ */
+[scriptable, builtinclass, uuid(ce4ef184-7660-445e-9e59-6731bdc65505)]
+interface nsILocalFile : nsIFile
+{
+};
+
diff --git a/xpcom/io/nsILocalFileMac.idl b/xpcom/io/nsILocalFileMac.idl
new file mode 100644
index 000000000..2a815eb20
--- /dev/null
+++ b/xpcom/io/nsILocalFileMac.idl
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsILocalFile.idl"
+
+%{C++
+#include <Carbon/Carbon.h>
+#include <CoreFoundation/CoreFoundation.h>
+%}
+
+ native OSType(OSType);
+ native FSSpec(FSSpec);
+ native FSRef(FSRef);
+[ptr] native FSRefPtr(FSRef);
+ native CFURLRef(CFURLRef);
+
+[scriptable, builtinclass, uuid(E5DE2CC9-BF06-4329-8F91-5D2D45284500)]
+interface nsILocalFileMac : nsILocalFile
+{
+ /**
+ * initWithCFURL
+ *
+ * Init this object with a CFURLRef
+ *
+ * NOTE: Supported only for XP_MACOSX
+ * NOTE: If the path of the CFURL is /a/b/c, at least a/b must exist beforehand.
+ *
+ * @param aCFURL the CoreFoundation URL
+ *
+ */
+ [noscript] void initWithCFURL(in CFURLRef aCFURL);
+
+ /**
+ * initWithFSRef
+ *
+ * Init this object with an FSRef
+ *
+ * NOTE: Supported only for XP_MACOSX
+ *
+ * @param aFSRef the native FSRef
+ *
+ */
+ [noscript] void initWithFSRef([const] in FSRefPtr aFSRef);
+
+ /**
+ * getCFURL
+ *
+ * Returns the CFURLRef of the file object. The caller is
+ * responsible for calling CFRelease() on it.
+ *
+ * NOTE: Observes the state of the followLinks attribute.
+ * If the file object is an alias and followLinks is TRUE, returns
+ * the target of the alias. If followLinks is FALSE, returns
+ * the unresolved alias file.
+ *
+ * NOTE: Supported only for XP_MACOSX
+ *
+ * @return
+ *
+ */
+ [noscript] CFURLRef getCFURL();
+
+ /**
+ * getFSRef
+ *
+ * Returns the FSRef of the file object.
+ *
+ * NOTE: Observes the state of the followLinks attribute.
+ * If the file object is an alias and followLinks is TRUE, returns
+ * the target of the alias. If followLinks is FALSE, returns
+ * the unresolved alias file.
+ *
+ * NOTE: Supported only for XP_MACOSX
+ *
+ * @return
+ *
+ */
+ [noscript] FSRef getFSRef();
+
+ /**
+ * getFSSpec
+ *
+ * Returns the FSSpec of the file object.
+ *
+ * NOTE: Observes the state of the followLinks attribute.
+ * If the file object is an alias and followLinks is TRUE, returns
+ * the target of the alias. If followLinks is FALSE, returns
+ * the unresolved alias file.
+ *
+ * @return
+ *
+ */
+ [noscript] FSSpec getFSSpec();
+
+ /**
+ * fileSizeWithResFork
+ *
+ * Returns the combined size of both the data fork and the resource
+ * fork (if present) rather than just the size of the data fork
+ * as returned by GetFileSize()
+ *
+ */
+ readonly attribute int64_t fileSizeWithResFork;
+
+ /**
+ * fileType, creator
+ *
+ * File type and creator attributes
+ *
+ */
+ [noscript] attribute OSType fileType;
+ [noscript] attribute OSType fileCreator;
+
+ /**
+ * launchWithDoc
+ *
+ * Launch the application that this file points to with a document.
+ *
+ * @param aDocToLoad Must not be NULL. If no document, use nsIFile::launch
+ * @param aLaunchInBackground TRUE if the application should not come to the front.
+ *
+ */
+ void launchWithDoc(in nsIFile aDocToLoad, in boolean aLaunchInBackground);
+
+ /**
+ * openDocWithApp
+ *
+ * Open the document that this file points to with the given application.
+ *
+ * @param aAppToOpenWith The application with which to open the document.
+ * If NULL, the creator code of the document is used
+ * to determine the application.
+ * @param aLaunchInBackground TRUE if the application should not come to the front.
+ *
+ */
+ void openDocWithApp(in nsIFile aAppToOpenWith, in boolean aLaunchInBackground);
+
+ /**
+ * isPackage
+ *
+ * returns true if a directory is determined to be a package under Mac OS 9/X
+ *
+ */
+ boolean isPackage();
+
+ /**
+ * bundleDisplayName
+ *
+ * returns the display name of the application bundle (usually the human
+ * readable name of the application)
+ */
+ readonly attribute AString bundleDisplayName;
+
+ /**
+ * bundleIdentifier
+ *
+ * returns the identifier of the bundle
+ */
+ readonly attribute AUTF8String bundleIdentifier;
+
+ /**
+ * Last modified time of a bundle's contents (as opposed to its package
+ * directory). Our convention is to make the bundle's Info.plist file
+ * stand in for the rest of its contents -- since this file contains the
+ * bundle's version information and other identifiers. For non-bundles
+ * this is the same as lastModifiedTime.
+ */
+ readonly attribute int64_t bundleContentsLastModifiedTime;
+};
+
+%{C++
+extern "C"
+{
+NS_EXPORT nsresult NS_NewLocalFileWithFSRef(const FSRef* aFSRef, bool aFollowSymlinks, nsILocalFileMac** result);
+NS_EXPORT nsresult NS_NewLocalFileWithCFURL(const CFURLRef aURL, bool aFollowSymlinks, nsILocalFileMac** result);
+}
+%}
diff --git a/xpcom/io/nsILocalFileOS2.idl b/xpcom/io/nsILocalFileOS2.idl
new file mode 100644
index 000000000..273715e89
--- /dev/null
+++ b/xpcom/io/nsILocalFileOS2.idl
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "nsILocalFile.idl"
+
+interface nsIArray;
+
+[scriptable, uuid(26de2089-239d-4697-818b-bae1fe8e8e0d)]
+interface nsILocalFileOS2 : nsILocalFile
+{
+ /**
+ * getFileTypes
+ *
+ * Returns the file's .TYPE extended attribute as an array of
+ * nsISupportsCStrings.
+ *
+ */
+ nsIArray getFileTypes( );
+
+ /**
+ * setFileTypes
+ *
+ * Sets the file's .TYPE extended attribute from a comma-separated
+ * list of types (this format is used because clients are unlikely
+ * to write more than a single type).
+ * @param fileTypes
+ * a string in the filesystem's native character set
+ *
+ */
+ void setFileTypes( in ACString fileTypes );
+
+ /**
+ * isFileType
+ *
+ * Returns TRUE if the file has a .TYPE extended attribute that
+ * matches the string passed in. The comparison is case-sensitive.
+ * @param fileType
+ * a string in the filesystem's native character set
+ *
+ */
+ boolean isFileType( in ACString fileType );
+
+ /**
+ * setFileSource
+ *
+ * Identifies the origin of a downloaded file by writing the
+ * source URI's spec to the .SUBJECT extended attribute.
+ *
+ * @param aURI
+ * the source URI
+ *
+ */
+ void setFileSource( in AUTF8String aURI );
+};
diff --git a/xpcom/io/nsILocalFileWin.idl b/xpcom/io/nsILocalFileWin.idl
new file mode 100644
index 000000000..150c31a1e
--- /dev/null
+++ b/xpcom/io/nsILocalFileWin.idl
@@ -0,0 +1,98 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "nsILocalFile.idl"
+
+[scriptable, builtinclass, uuid(c7b3fd13-30f2-46e5-a0d9-7a79a9b73c5b)]
+interface nsILocalFileWin : nsILocalFile
+{
+ /**
+ * getVersionInfoValue
+ *
+ * Retrieve a metadata field from the file's VERSIONINFO block.
+ * Throws NS_ERROR_FAILURE if no value is found, or the value is empty.
+ *
+ * @param aField The field to look up.
+ *
+ */
+ AString getVersionInfoField(in string aField);
+
+ /**
+ * The canonical path of the file, which avoids short/long
+ * pathname inconsistencies. The nsIFile persistent
+ * descriptor is not guaranteed to be canonicalized (it may
+ * persist either the long or the short path name). The format of
+ * the canonical path will vary with the underlying file system:
+ * it will typically be the short pathname on filesystems that
+ * support both short and long path forms.
+ */
+ readonly attribute AString canonicalPath;
+ [noscript] readonly attribute ACString nativeCanonicalPath;
+
+ /**
+ * Windows specific file attributes.
+ */
+
+ /*
+ * WFA_SEARCH_INDEXED: Generally the default on files in Windows except
+ * those created in temp locations. Valid on XP and up. When set the
+ * file or directory is marked to be indexed by desktop search services.
+ */
+ const unsigned long WFA_SEARCH_INDEXED = 1;
+
+ /*
+ * WFA_READONLY: Whether the file is readonly or not.
+ */
+ const unsigned long WFA_READONLY = 2;
+
+ /*
+ * WFA_READWRITE: Used to clear the readonly attribute.
+ */
+ const unsigned long WFA_READWRITE = 4;
+
+ /**
+ * fileAttributesWin
+ *
+ * Set or get windows specific file attributes.
+ *
+ * Throws NS_ERROR_FILE_INVALID_PATH for an invalid file.
+ * Throws NS_ERROR_FAILURE if the set or get fails.
+ */
+ attribute unsigned long fileAttributesWin;
+
+ /**
+ * setShortcut
+ *
+ * Creates the specified shortcut, or updates it if it already exists.
+ *
+ * If the shortcut is being updated (i.e. the shortcut already exists),
+ * any excluded parameters will remain unchanged in the shortcut file.
+ * For example, if you want to change the description of a specific
+ * shortcut but keep the target, working dir, args, and icon the same,
+ * pass null for those parameters and only pass in a value for the
+ * description.
+ *
+ * If the shortcut does not already exist and targetFile is not specified,
+ * setShortcut will throw NS_ERROR_FILE_TARGET_DOES_NOT_EXIST.
+ *
+ * @param targetFile the path that the shortcut should target
+ * @param workingDir the working dir that should be set for the shortcut
+ * @param args the args string that should be set for the shortcut
+ * @param description the description that should be set for the shortcut
+ * @param iconFile the file containing an icon to be used for this
+ shortcut
+ * @param iconIndex this value selects a specific icon from within
+ iconFile. If iconFile contains only one icon, this
+ value should be 0.
+ */
+ void setShortcut([optional] in nsIFile targetFile,
+ [optional] in nsIFile workingDir,
+ [optional] in wstring args,
+ [optional] in wstring description,
+ [optional] in nsIFile iconFile,
+ [optional] in long iconIndex);
+};
+
diff --git a/xpcom/io/nsIMultiplexInputStream.idl b/xpcom/io/nsIMultiplexInputStream.idl
new file mode 100644
index 000000000..d42adcbd4
--- /dev/null
+++ b/xpcom/io/nsIMultiplexInputStream.idl
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIInputStream.idl"
+
+/**
+ * The multiplex stream concatenates a list of input streams into a single
+ * stream.
+ */
+
+[scriptable, uuid(a076fd12-1dd1-11b2-b19a-d53b5dffaade)]
+interface nsIMultiplexInputStream : nsIInputStream
+{
+ /**
+ * Number of streams in this multiplex-stream
+ */
+ readonly attribute unsigned long count;
+
+ /**
+ * Appends a stream to the end of the streams. The cursor of the stream
+ * should be located at the beginning of the stream if the implementation
+ * of this nsIMultiplexInputStream also is used as an nsISeekableStream.
+ * @param stream stream to append
+ */
+ void appendStream(in nsIInputStream stream);
+
+ /**
+ * Insert a stream at specified index. If the cursor of this stream is at
+ * the beginning of the stream at index, the cursor will be placed at the
+ * beginning of the inserted stream instead.
+ * The cursor of the new stream should be located at the beginning of the
+ * stream if the implementation of this nsIMultiplexInputStream also is
+ * used as an nsISeekableStream.
+ * @param stream stream to insert
+ * @param index index to insert stream at, must be <= count
+ */
+ void insertStream(in nsIInputStream stream, in unsigned long index);
+
+ /**
+ * Remove stream at specified index. If this stream is the one currently
+ * being read the readcursor is moved to the beginning of the next
+ * stream
+ * @param index remove stream at this index, must be < count
+ */
+ void removeStream(in unsigned long index);
+
+ /**
+ * Get stream at specified index.
+ * @param index return stream at this index, must be < count
+ * @return stream at specified index
+ */
+ nsIInputStream getStream(in unsigned long index);
+};
diff --git a/xpcom/io/nsIOUtil.cpp b/xpcom/io/nsIOUtil.cpp
new file mode 100644
index 000000000..30bd4fe94
--- /dev/null
+++ b/xpcom/io/nsIOUtil.cpp
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIOUtil.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsStreamUtils.h"
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsIOUtil, nsIIOUtil)
+
+NS_IMETHODIMP
+nsIOUtil::InputStreamIsBuffered(nsIInputStream* aStream, bool* _retval)
+{
+ NS_ENSURE_ARG_POINTER(aStream);
+ *_retval = NS_InputStreamIsBuffered(aStream);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIOUtil::OutputStreamIsBuffered(nsIOutputStream* aStream, bool* _retval)
+{
+ NS_ENSURE_ARG_POINTER(aStream);
+ *_retval = NS_OutputStreamIsBuffered(aStream);
+ return NS_OK;
+}
diff --git a/xpcom/io/nsIOUtil.h b/xpcom/io/nsIOUtil.h
new file mode 100644
index 000000000..3e06ddb88
--- /dev/null
+++ b/xpcom/io/nsIOUtil.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 nsIOUtil_h__
+#define nsIOUtil_h__
+
+#define NS_IOUTIL_CID \
+{ 0xeb833911, 0x4f49, 0x4623, \
+ { 0x84, 0x5f, 0xe5, 0x8a, 0x8e, 0x6d, 0xe4, 0xc2 } }
+
+
+#include "nsIIOUtil.h"
+#include "mozilla/Attributes.h"
+
+class nsIOUtil MOZ_FINAL : public nsIIOUtil
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIIOUTIL
+};
+
+#endif /* nsIOUtil_h__ */
diff --git a/xpcom/io/nsIObjectInputStream.idl b/xpcom/io/nsIObjectInputStream.idl
new file mode 100644
index 000000000..70f2a55e8
--- /dev/null
+++ b/xpcom/io/nsIObjectInputStream.idl
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIBinaryInputStream.idl"
+#include "nsrootidl.idl"
+
+/**
+ * @see nsIObjectOutputStream
+ * @see nsIBinaryInputStream
+ */
+
+[scriptable, uuid(6c248606-4eae-46fa-9df0-ba58502368eb)]
+interface nsIObjectInputStream : nsIBinaryInputStream
+{
+ /**
+ * Read an object from this stream to satisfy a strong or weak reference
+ * to one of its interfaces. If the interface was not along the primary
+ * inheritance chain ending in the "root" or XPCOM-identity nsISupports,
+ * readObject will QueryInterface from the deserialized object root to the
+ * correct interface, which was specified when the object was serialized.
+ *
+ * @see nsIObjectOutputStream
+ */
+ nsISupports readObject(in boolean aIsStrongRef);
+
+ [notxpcom] nsresult readID(out nsID aID);
+
+ /**
+ * Optimized deserialization support -- see nsIStreamBufferAccess.idl.
+ */
+ [notxpcom] charPtr getBuffer(in uint32_t aLength, in uint32_t aAlignMask);
+ [notxpcom] void putBuffer(in charPtr aBuffer, in uint32_t aLength);
+};
+
+%{C++
+
+inline nsresult
+NS_ReadOptionalObject(nsIObjectInputStream* aStream, bool aIsStrongRef,
+ nsISupports* *aResult)
+{
+ bool nonnull;
+ nsresult rv = aStream->ReadBoolean(&nonnull);
+ if (NS_SUCCEEDED(rv)) {
+ if (nonnull)
+ rv = aStream->ReadObject(aIsStrongRef, aResult);
+ else
+ *aResult = nullptr;
+ }
+ return rv;
+}
+
+%}
diff --git a/xpcom/io/nsIObjectOutputStream.idl b/xpcom/io/nsIObjectOutputStream.idl
new file mode 100644
index 000000000..92c77b8b1
--- /dev/null
+++ b/xpcom/io/nsIObjectOutputStream.idl
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIBinaryOutputStream.idl"
+#include "nsrootidl.idl"
+
+/**
+ * @See nsIObjectInputStream
+ * @See nsIBinaryOutputStream
+ */
+
+[scriptable, uuid(92c898ac-5fde-4b99-87b3-5d486422094b)]
+interface nsIObjectOutputStream : nsIBinaryOutputStream
+{
+ /**
+ * Write the object whose "root" or XPCOM-identity nsISupports is aObject.
+ * The cause for writing this object is a strong or weak reference, so the
+ * aIsStrongRef argument must tell which kind of pointer is being followed
+ * here during serialization.
+ *
+ * If the object has only one strong reference in the serialization and no
+ * weak refs, use writeSingleRefObject. This is a valuable optimization:
+ * it saves space in the stream, and cycles on both ends of the process.
+ *
+ * If the reference being serialized is a pointer to an interface not on
+ * the primary inheritance chain ending in the root nsISupports, you must
+ * call writeCompoundObject instead of this method.
+ */
+ void writeObject(in nsISupports aObject, in boolean aIsStrongRef);
+
+ /**
+ * Write an object referenced singly and strongly via its root nsISupports
+ * or a subclass of its root nsISupports. There must not be other refs to
+ * aObject in memory, or in the serialization.
+ */
+ void writeSingleRefObject(in nsISupports aObject);
+
+ /**
+ * Write the object referenced by an interface pointer at aObject that
+ * inherits from a non-primary nsISupports, i.e., a reference to one of
+ * the multiply inherited interfaces derived from an nsISupports other
+ * than the root or XPCOM-identity nsISupports; or a reference to an
+ * inner object in the case of true XPCOM aggregation. aIID identifies
+ * this interface.
+ */
+ void writeCompoundObject(in nsISupports aObject,
+ in nsIIDRef aIID,
+ in boolean aIsStrongRef);
+
+ void writeID(in nsIDRef aID);
+
+ /**
+ * Optimized serialization support -- see nsIStreamBufferAccess.idl.
+ */
+ [notxpcom] charPtr getBuffer(in uint32_t aLength, in uint32_t aAlignMask);
+ [notxpcom] void putBuffer(in charPtr aBuffer, in uint32_t aLength);
+};
+
+%{C++
+
+inline nsresult
+NS_WriteOptionalObject(nsIObjectOutputStream* aStream, nsISupports* aObject,
+ bool aIsStrongRef)
+{
+ bool nonnull = (aObject != nullptr);
+ nsresult rv = aStream->WriteBoolean(nonnull);
+ if (NS_SUCCEEDED(rv) && nonnull)
+ rv = aStream->WriteObject(aObject, aIsStrongRef);
+ return rv;
+}
+
+inline nsresult
+NS_WriteOptionalSingleRefObject(nsIObjectOutputStream* aStream,
+ nsISupports* aObject)
+{
+ bool nonnull = (aObject != nullptr);
+ nsresult rv = aStream->WriteBoolean(nonnull);
+ if (NS_SUCCEEDED(rv) && nonnull)
+ rv = aStream->WriteSingleRefObject(aObject);
+ return rv;
+}
+
+inline nsresult
+NS_WriteOptionalCompoundObject(nsIObjectOutputStream* aStream,
+ nsISupports* aObject,
+ const nsIID& aIID,
+ bool aIsStrongRef)
+{
+ bool nonnull = (aObject != nullptr);
+ nsresult rv = aStream->WriteBoolean(nonnull);
+ if (NS_SUCCEEDED(rv) && nonnull)
+ rv = aStream->WriteCompoundObject(aObject, aIID, aIsStrongRef);
+ return rv;
+}
+
+%}
diff --git a/xpcom/io/nsIOutputStream.idl b/xpcom/io/nsIOutputStream.idl
new file mode 100644
index 000000000..d3181c517
--- /dev/null
+++ b/xpcom/io/nsIOutputStream.idl
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+interface nsIOutputStream;
+interface nsIInputStream;
+
+%{C++
+/**
+ * The signature for the reader function passed to WriteSegments. This
+ * is the "provider" of data that gets written into the stream's buffer.
+ *
+ * @param aOutStream stream being written to
+ * @param aClosure opaque parameter passed to WriteSegments
+ * @param aToSegment pointer to memory owned by the output stream
+ * @param aFromOffset amount already written (since WriteSegments was called)
+ * @param aCount length of toSegment
+ * @param aReadCount number of bytes written
+ *
+ * Implementers should return the following:
+ *
+ * @throws <any-error> if not interested in providing any data
+ *
+ * Errors are never passed to the caller of WriteSegments.
+ */
+typedef NS_CALLBACK(nsReadSegmentFun)(nsIOutputStream *aOutStream,
+ void *aClosure,
+ char *aToSegment,
+ uint32_t aFromOffset,
+ uint32_t aCount,
+ uint32_t *aReadCount);
+%}
+
+native nsReadSegmentFun(nsReadSegmentFun);
+
+/**
+ * nsIOutputStream
+ *
+ * An interface describing a writable stream of data. An output stream may be
+ * "blocking" or "non-blocking" (see the IsNonBlocking method). A blocking
+ * output stream may suspend the calling thread in order to satisfy a call to
+ * Close, Flush, Write, WriteFrom, or WriteSegments. A non-blocking output
+ * stream, on the other hand, must not block the calling thread of execution.
+ *
+ * NOTE: blocking output streams are often written to on a background thread to
+ * avoid locking up the main application thread. For this reason, it is
+ * generally the case that a blocking output stream should be implemented using
+ * thread- safe AddRef and Release.
+ */
+[scriptable, uuid(0d0acd2a-61b4-11d4-9877-00c04fa0cf4a)]
+interface nsIOutputStream : nsISupports
+{
+ /**
+ * Close the stream. Forces the output stream to flush any buffered data.
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if unable to flush without blocking
+ * the calling thread (non-blocking mode only)
+ */
+ void close();
+
+ /**
+ * Flush the stream.
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if unable to flush without blocking
+ * the calling thread (non-blocking mode only)
+ */
+ void flush();
+
+ /**
+ * Write data into the stream.
+ *
+ * @param aBuf the buffer containing the data to be written
+ * @param aCount the maximum number of bytes to be written
+ *
+ * @return number of bytes written (may be less than aCount)
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if writing to the output stream would
+ * block the calling thread (non-blocking mode only)
+ * @throws <other-error> on failure
+ */
+ unsigned long write(in string aBuf, in unsigned long aCount);
+
+ /**
+ * Writes data into the stream from an input stream.
+ *
+ * @param aFromStream the stream containing the data to be written
+ * @param aCount the maximum number of bytes to be written
+ *
+ * @return number of bytes written (may be less than aCount)
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if writing to the output stream would
+ * block the calling thread (non-blocking mode only)
+ * @throws <other-error> on failure
+ *
+ * NOTE: This method is defined by this interface in order to allow the
+ * output stream to efficiently copy the data from the input stream into
+ * its internal buffer (if any). If this method was provided as an external
+ * facility, a separate char* buffer would need to be used in order to call
+ * the output stream's other Write method.
+ */
+ unsigned long writeFrom(in nsIInputStream aFromStream,
+ in unsigned long aCount);
+
+ /**
+ * Low-level write method that has access to the stream's underlying buffer.
+ * The reader function may be called multiple times for segmented buffers.
+ * WriteSegments is expected to keep calling the reader until either there
+ * is nothing left to write or the reader returns an error. WriteSegments
+ * should not call the reader with zero bytes to provide.
+ *
+ * @param aReader the "provider" of the data to be written
+ * @param aClosure opaque parameter passed to reader
+ * @param aCount the maximum number of bytes to be written
+ *
+ * @return number of bytes written (may be less than aCount)
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if writing to the output stream would
+ * block the calling thread (non-blocking mode only)
+ * @throws NS_ERROR_NOT_IMPLEMENTED if the stream has no underlying buffer
+ * @throws <other-error> on failure
+ *
+ * NOTE: this function may be unimplemented if a stream has no underlying
+ * buffer (e.g., socket output stream).
+ */
+ [noscript] unsigned long writeSegments(in nsReadSegmentFun aReader,
+ in voidPtr aClosure,
+ in unsigned long aCount);
+
+ /**
+ * @return true if stream is non-blocking
+ *
+ * NOTE: writing to a blocking output stream will block the calling thread
+ * until all given data can be consumed by the stream.
+ *
+ * NOTE: a non-blocking output stream may implement nsIAsyncOutputStream to
+ * provide consumers with a way to wait for the stream to accept more data
+ * once its write method is unable to accept any data without blocking.
+ */
+ boolean isNonBlocking();
+};
diff --git a/xpcom/io/nsIPipe.idl b/xpcom/io/nsIPipe.idl
new file mode 100644
index 000000000..93951ffe5
--- /dev/null
+++ b/xpcom/io/nsIPipe.idl
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIAsyncInputStream.idl"
+#include "nsIAsyncOutputStream.idl"
+
+interface nsIMemory;
+
+/**
+ * nsIPipe represents an in-process buffer that can be read using nsIInputStream
+ * and written using nsIOutputStream. The reader and writer of a pipe do not
+ * have to be on the same thread. As a result, the pipe is an ideal mechanism
+ * to bridge data exchange between two threads. For example, a worker thread
+ * might write data to a pipe from which the main thread will read.
+ *
+ * Each end of the pipe can be either blocking or non-blocking. Recall that a
+ * non-blocking stream will return NS_BASE_STREAM_WOULD_BLOCK if it cannot be
+ * read or written to without blocking the calling thread. For example, if you
+ * try to read from an empty pipe that has not yet been closed, then if that
+ * pipe's input end is non-blocking, then the read call will fail immediately
+ * with NS_BASE_STREAM_WOULD_BLOCK as the error condition. However, if that
+ * pipe's input end is blocking, then the read call will not return until the
+ * pipe has data or until the pipe is closed. This example presumes that the
+ * pipe is being filled asynchronously on some background thread.
+ *
+ * The pipe supports nsIAsyncInputStream and nsIAsyncOutputStream, which give
+ * the user of a non-blocking pipe the ability to wait for the pipe to become
+ * ready again. For example, in the case of an empty non-blocking pipe, the
+ * user can call AsyncWait on the input end of the pipe to be notified when
+ * the pipe has data to read (or when the pipe becomes closed).
+ *
+ * NS_NewPipe2 and NS_NewPipe provide convenient pipe constructors. In most
+ * cases nsIPipe is not actually used. It is usually enough to just get
+ * references to the pipe's input and output end. In which case, the pipe is
+ * automatically closed when the respective pipe ends are released.
+ */
+[scriptable, uuid(f4211abc-61b3-11d4-9877-00c04fa0cf4a)]
+interface nsIPipe : nsISupports
+{
+ /**
+ * initialize this pipe
+ *
+ * @param nonBlockingInput
+ * true specifies non-blocking input stream behavior
+ * @param nonBlockingOutput
+ * true specifies non-blocking output stream behavior
+ * @param segmentSize
+ * specifies the segment size in bytes (pass 0 to use default value)
+ * @param segmentCount
+ * specifies the max number of segments (pass 0 to use default
+ * value). Passing UINT32_MAX here causes the pipe to have
+ * "infinite" space. This mode can be useful in some cases, but
+ * should always be used with caution. The default value for this
+ * parameter is a finite value.
+ * @param segmentAllocator
+ * pass reference to nsIMemory to have all pipe allocations use this
+ * allocator (pass null to use the default allocator)
+ */
+ void init(in boolean nonBlockingInput,
+ in boolean nonBlockingOutput,
+ in unsigned long segmentSize,
+ in unsigned long segmentCount,
+ in nsIMemory segmentAllocator);
+
+ /**
+ * The pipe's input end, which also implements nsISearchableInputStream.
+ */
+ readonly attribute nsIAsyncInputStream inputStream;
+
+ /**
+ * The pipe's output end.
+ */
+ readonly attribute nsIAsyncOutputStream outputStream;
+};
+
+/**
+ * XXX this interface doesn't really belong in here. It is here because
+ * currently nsPipeInputStream is the only implementation of this interface.
+ */
+[scriptable, uuid(8C39EF62-F7C9-11d4-98F5-001083010E9B)]
+interface nsISearchableInputStream : nsISupports
+{
+ /**
+ * Searches for a string in the input stream. Since the stream has a notion
+ * of EOF, it is possible that the string may at some time be in the
+ * buffer, but is is not currently found up to some offset. Consequently,
+ * both the found and not found cases return an offset:
+ * if found, return offset where it was found
+ * if not found, return offset of the first byte not searched
+ * In the case the stream is at EOF and the string is not found, the first
+ * byte not searched will correspond to the length of the buffer.
+ */
+ void search(in string forString,
+ in boolean ignoreCase,
+ out boolean found,
+ out unsigned long offsetSearchedTo);
+};
+
+%{C++
+
+/**
+ * NS_NewPipe2
+ *
+ * This function supersedes NS_NewPipe. It differs from NS_NewPipe in two
+ * major ways:
+ * (1) returns nsIAsyncInputStream and nsIAsyncOutputStream, so it is
+ * not necessary to QI in order to access these interfaces.
+ * (2) the size of the pipe is determined by the number of segments
+ * times the size of each segment.
+ *
+ * @param pipeIn
+ * resulting input end of the pipe
+ * @param pipeOut
+ * resulting output end of the pipe
+ * @param nonBlockingInput
+ * true specifies non-blocking input stream behavior
+ * @param nonBlockingOutput
+ * true specifies non-blocking output stream behavior
+ * @param segmentSize
+ * specifies the segment size in bytes (pass 0 to use default value)
+ * @param segmentCount
+ * specifies the max number of segments (pass 0 to use default value)
+ * passing UINT32_MAX here causes the pipe to have "infinite" space.
+ * this mode can be useful in some cases, but should always be used with
+ * caution. the default value for this parameter is a finite value.
+ * @param segmentAlloc
+ * pass reference to nsIMemory to have all pipe allocations use this
+ * allocator (pass null to use the default allocator)
+ */
+extern nsresult
+NS_NewPipe2(nsIAsyncInputStream **pipeIn,
+ nsIAsyncOutputStream **pipeOut,
+ bool nonBlockingInput = false,
+ bool nonBlockingOutput = false,
+ uint32_t segmentSize = 0,
+ uint32_t segmentCount = 0,
+ nsIMemory *segmentAlloc = nullptr);
+
+/**
+ * NS_NewPipe
+ *
+ * Preserved for backwards compatibility. Plus, this interface is more
+ * amiable in certain contexts (e.g., when you don't need the pipe's async
+ * capabilities).
+ *
+ * @param pipeIn
+ * resulting input end of the pipe
+ * @param pipeOut
+ * resulting output end of the pipe
+ * @param segmentSize
+ * specifies the segment size in bytes (pass 0 to use default value)
+ * @param maxSize
+ * specifies the max size of the pipe (pass 0 to use default value)
+ * number of segments is maxSize / segmentSize, and maxSize must be a
+ * multiple of segmentSize. passing UINT32_MAX here causes the
+ * pipe to have "infinite" space. this mode can be useful in some
+ * cases, but should always be used with caution. the default value
+ * for this parameter is a finite value.
+ * @param nonBlockingInput
+ * true specifies non-blocking input stream behavior
+ * @param nonBlockingOutput
+ * true specifies non-blocking output stream behavior
+ * @param segmentAlloc
+ * pass reference to nsIMemory to have all pipe allocations use this
+ * allocator (pass null to use the default allocator)
+ */
+extern nsresult
+NS_NewPipe(nsIInputStream **pipeIn,
+ nsIOutputStream **pipeOut,
+ uint32_t segmentSize = 0,
+ uint32_t maxSize = 0,
+ bool nonBlockingInput = false,
+ bool nonBlockingOutput = false,
+ nsIMemory *segmentAlloc = nullptr);
+
+%}
diff --git a/xpcom/io/nsISafeOutputStream.idl b/xpcom/io/nsISafeOutputStream.idl
new file mode 100644
index 000000000..c62d85f07
--- /dev/null
+++ b/xpcom/io/nsISafeOutputStream.idl
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+/**
+ * This interface provides a mechanism to control an output stream
+ * that takes care not to overwrite an existing target until it is known
+ * that all writes to the destination succeeded.
+ *
+ * An object that supports this interface is intended to also support
+ * nsIOutputStream.
+ *
+ * For example, a file output stream that supports this interface writes to
+ * a temporary file, and moves it over the original file when |finish| is
+ * called only if the stream can be successfully closed and all writes
+ * succeeded. If |finish| is called but something went wrong during
+ * writing, it will delete the temporary file and not touch the original.
+ * If the stream is closed by calling |close| directly, or the stream
+ * goes away, the original file will not be overwritten, and the temporary
+ * file will be deleted.
+ *
+ * Currently, this interface is implemented only for file output streams.
+ */
+[scriptable, uuid(5f914307-5c34-4e1f-8e32-ec749d25b27a)]
+interface nsISafeOutputStream : nsISupports
+{
+ /**
+ * Call this method to close the stream and cause the original target
+ * to be overwritten. Note: if any call to |write| failed to write out
+ * all of the data given to it, then calling this method will |close| the
+ * stream and return failure. Further, if closing the stream fails, this
+ * method will return failure. The original target will be overwritten only
+ * if all calls to |write| succeeded and the stream was successfully closed.
+ */
+ void finish();
+};
diff --git a/xpcom/io/nsIScriptableBase64Encoder.idl b/xpcom/io/nsIScriptableBase64Encoder.idl
new file mode 100644
index 000000000..641451805
--- /dev/null
+++ b/xpcom/io/nsIScriptableBase64Encoder.idl
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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"
+
+interface nsIInputStream;
+
+/**
+ * nsIScriptableBase64Encoder efficiently encodes the contents
+ * of a nsIInputStream to a Base64 string. This avoids the need
+ * to read the entire stream into a buffer, and only then do the
+ * Base64 encoding.
+ *
+ * If you already have a buffer full of data, you should use
+ * btoa instead!
+ */
+[scriptable, uuid(9479c864-d1f9-45ab-b7b9-28b907bd2ba9)]
+interface nsIScriptableBase64Encoder : nsISupports
+{
+ /**
+ * These methods take an nsIInputStream and return a narrow or wide
+ * string with the contents of the nsIInputStream base64 encoded.
+ *
+ * The stream passed in must support ReadSegments and must not be
+ * a non-blocking stream that will return NS_BASE_STREAM_WOULD_BLOCK.
+ * If either of these restrictions are violated we will abort.
+ */
+ ACString encodeToCString(in nsIInputStream stream, in unsigned long length);
+ AString encodeToString(in nsIInputStream stream, in unsigned long length);
+};
diff --git a/xpcom/io/nsIScriptableInputStream.idl b/xpcom/io/nsIScriptableInputStream.idl
new file mode 100644
index 000000000..d9248f425
--- /dev/null
+++ b/xpcom/io/nsIScriptableInputStream.idl
@@ -0,0 +1,67 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+
+interface nsIInputStream;
+
+/**
+ * nsIScriptableInputStream provides scriptable access to an nsIInputStream
+ * instance.
+ */
+[scriptable, uuid(3fce9015-472a-4080-ac3e-cd875dbe361e)]
+interface nsIScriptableInputStream : nsISupports
+{
+ /**
+ * Closes the stream.
+ */
+ void close();
+
+ /**
+ * Wrap the given nsIInputStream with this nsIScriptableInputStream.
+ *
+ * @param aInputStream parameter providing the stream to wrap
+ */
+ void init(in nsIInputStream aInputStream);
+
+ /**
+ * Return the number of bytes currently available in the stream
+ *
+ * @return the number of bytes
+ *
+ * @throws NS_BASE_STREAM_CLOSED if called after the stream has been closed
+ */
+ unsigned long long available();
+
+ /**
+ * Read data from the stream.
+ *
+ * WARNING: If the data contains a null byte, then this method will return
+ * a truncated string.
+ *
+ * @param aCount the maximum number of bytes to read
+ *
+ * @return the data, which will be an empty string if the stream is at EOF.
+ *
+ * @throws NS_BASE_STREAM_CLOSED if called after the stream has been closed
+ * @throws NS_ERROR_NOT_INITIALIZED if init was not called
+ */
+ string read(in unsigned long aCount);
+
+ /**
+ * Read data from the stream, including NULL bytes.
+ *
+ * @param aCount the maximum number of bytes to read.
+ *
+ * @return the data from the stream, which will be an empty string if EOF
+ * has been reached.
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if reading from the input stream
+ * would block the calling thread (non-blocking mode only).
+ * @throws NS_ERROR_FAILURE if there are not enough bytes available to read
+ * aCount amount of data.
+ */
+ ACString readBytes(in unsigned long aCount);
+};
diff --git a/xpcom/io/nsISeekableStream.idl b/xpcom/io/nsISeekableStream.idl
new file mode 100644
index 000000000..1be7ea60c
--- /dev/null
+++ b/xpcom/io/nsISeekableStream.idl
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+
+/*
+ * nsISeekableStream
+ *
+ * Note that a stream might not implement all methods (e.g., a readonly stream
+ * won't implement setEOF)
+ */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(8429d350-1040-4661-8b71-f2a6ba455980)]
+interface nsISeekableStream : nsISupports
+{
+ /*
+ * Sets the stream pointer to the value of the 'offset' parameter
+ */
+ const int32_t NS_SEEK_SET = 0;
+
+ /*
+ * Sets the stream pointer to its current location plus the value
+ * of the offset parameter.
+ */
+ const int32_t NS_SEEK_CUR = 1;
+
+ /*
+ * Sets the stream pointer to the size of the stream plus the value
+ * of the offset parameter.
+ */
+ const int32_t NS_SEEK_END = 2;
+
+ /**
+ * seek
+ *
+ * This method moves the stream offset of the steam implementing this
+ * interface.
+ *
+ * @param whence specifies how to interpret the 'offset' parameter in
+ * setting the stream offset associated with the implementing
+ * stream.
+ *
+ * @param offset specifies a value, in bytes, that is used in conjunction
+ * with the 'whence' parameter to set the stream offset of the
+ * implementing stream. A negative value causes seeking in
+ * the reverse direction.
+ *
+ * @throws NS_BASE_STREAM_CLOSED if called on a closed stream.
+ */
+ void seek(in long whence, in long long offset);
+
+ /**
+ * tell
+ *
+ * This method reports the current offset, in bytes, from the start of the
+ * stream.
+ *
+ * @throws NS_BASE_STREAM_CLOSED if called on a closed stream.
+ */
+ long long tell();
+
+
+ /**
+ * setEOF
+ *
+ * This method truncates the stream at the current offset.
+ *
+ * @throws NS_BASE_STREAM_CLOSED if called on a closed stream.
+ */
+ void setEOF();
+};
diff --git a/xpcom/io/nsISimpleUnicharStreamFactory.idl b/xpcom/io/nsISimpleUnicharStreamFactory.idl
new file mode 100644
index 000000000..188d887cc
--- /dev/null
+++ b/xpcom/io/nsISimpleUnicharStreamFactory.idl
@@ -0,0 +1,26 @@
+/* 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"
+
+interface nsIInputStream;
+interface nsIUnicharInputStream;
+
+/**
+ * Factory to create objects that implement nsIUnicharInputStream,
+ * converting from a unicode string or a UTF-8 stream.
+ */
+[scriptable, uuid(8238cd2e-e8e3-43e8-b556-56e21389c766)]
+interface nsISimpleUnicharStreamFactory : nsISupports
+{
+ /**
+ * Create a unicode input stream from a unicode string.
+ */
+ nsIUnicharInputStream createInstanceFromString(in AString aString);
+
+ /**
+ * Create a unicode stream from an input stream in UTF8.
+ */
+ nsIUnicharInputStream createInstanceFromUTF8Stream(in nsIInputStream aStream);
+};
diff --git a/xpcom/io/nsIStorageStream.idl b/xpcom/io/nsIStorageStream.idl
new file mode 100644
index 000000000..e49e80d00
--- /dev/null
+++ b/xpcom/io/nsIStorageStream.idl
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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"
+#include "nsrootidl.idl"
+
+interface nsIMemory;
+interface nsIInputStream;
+interface nsIOutputStream;
+
+/**
+ * The nsIStorageStream interface maintains an internal data buffer that can be
+ * filled using a single output stream. One or more independent input streams
+ * can be created to read the data from the buffer non-destructively.
+ */
+
+[scriptable, uuid(604ad9d0-753e-11d3-90ca-34278643278f)]
+interface nsIStorageStream : nsISupports
+{
+ /**
+ *
+ * Initialize the stream, setting up the amount of space that will be
+ * allocated for the stream's backing-store.
+ *
+ * @param segmentSize
+ * Size of each segment. Must be a power of two.
+ * @param maxSize
+ * Maximum total size of this stream. length will always be less
+ * than or equal to this value. Passing UINT32_MAX is safe.
+ * @param segmentAllocator
+ * Which allocator to use for the segments. May be null, in which
+ * case a default allocator will be used.
+ */
+ void init(in uint32_t segmentSize, in uint32_t maxSize, in nsIMemory segmentAllocator);
+
+ /**
+ * Get a reference to the one and only output stream for this instance.
+ * The zero-based startPosition argument is used is used to set the initial
+ * write cursor position. The startPosition cannot be set larger than the
+ * current buffer length. Calling this method has the side-effect of
+ * truncating the internal buffer to startPosition bytes.
+ */
+ nsIOutputStream getOutputStream(in int32_t startPosition);
+
+ /**
+ * Create a new input stream to read data (written by the singleton output
+ * stream) from the internal buffer. Multiple, independent input streams
+ * can be created.
+ */
+ nsIInputStream newInputStream(in int32_t startPosition);
+
+ /**
+ * The length attribute indicates the total number of bytes stored in the
+ * nsIStorageStream internal buffer, regardless of any consumption by input
+ * streams. Assigning to the length field can be used to truncate the
+ * buffer data, but can not be used when either the instance's output
+ * stream is in use.
+ *
+ * @See #writeInProgress */
+ attribute uint32_t length;
+
+ /**
+ * True, when output stream has not yet been Close'ed
+ */
+ readonly attribute boolean writeInProgress;
+};
+
+%{C++
+// Factory method
+nsresult
+NS_NewStorageStream(uint32_t segmentSize, uint32_t maxSize, nsIStorageStream **result);
+%}
diff --git a/xpcom/io/nsIStreamBufferAccess.idl b/xpcom/io/nsIStreamBufferAccess.idl
new file mode 100644
index 000000000..002fb2f1e
--- /dev/null
+++ b/xpcom/io/nsIStreamBufferAccess.idl
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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"
+#include "nsrootidl.idl"
+
+/**
+ * An interface for access to a buffering stream implementation's underlying
+ * memory buffer.
+ *
+ * Stream implementations that QueryInterface to nsIStreamBufferAccess must
+ * ensure that all buffers are aligned on the most restrictive type size for
+ * the current architecture (e.g., sizeof(double) for RISCy CPUs). malloc(3)
+ * satisfies this requirement.
+ */
+[scriptable, uuid(ac923b72-ac87-4892-ac7a-ca385d429435)]
+interface nsIStreamBufferAccess : nsISupports
+{
+ /**
+ * Get access to a contiguous, aligned run of bytes in the stream's buffer.
+ * Exactly one successful getBuffer call must occur before a putBuffer call
+ * taking the non-null pointer returned by the successful getBuffer.
+ *
+ * The run of bytes are the next bytes (modulo alignment padding) to read
+ * for an input stream, and the next bytes (modulo alignment padding) to
+ * store before (eventually) writing buffered data to an output stream.
+ * There can be space beyond this run of bytes in the buffer for further
+ * accesses before the fill or flush point is reached.
+ *
+ * @param aLength
+ * Count of contiguous bytes requested at the address A that satisfies
+ * (A & aAlignMask) == 0 in the buffer, starting from the current stream
+ * position, mapped to a buffer address B. The stream implementation
+ * must pad from B to A by skipping bytes (if input stream) or storing
+ * zero bytes (if output stream).
+ *
+ * @param aAlignMask
+ * Bit-mask computed by subtracting 1 from the power-of-two alignment
+ * modulus (e.g., 3 or sizeof(uint32_t)-1 for uint32_t alignment).
+ *
+ * @return
+ * The aligned pointer to aLength bytes in the buffer, or null if the
+ * buffer has no room for aLength bytes starting at the next address A
+ * after the current position that satisfies (A & aAlignMask) == 0.
+ */
+ [notxpcom,noscript] charPtr getBuffer(in uint32_t aLength, in uint32_t aAlignMask);
+
+ /**
+ * Relinquish access to the stream's buffer, filling if at end of an input
+ * buffer, flushing if completing an output buffer. After a getBuffer call
+ * that returns non-null, putBuffer must be called.
+ *
+ * @param aBuffer
+ * A non-null pointer returned by getBuffer on the same stream buffer
+ * access object.
+ *
+ * @param aLength
+ * The same count of contiguous bytes passed to the getBuffer call that
+ * returned aBuffer.
+ */
+ [notxpcom,noscript] void putBuffer(in charPtr aBuffer, in uint32_t aLength);
+
+ /**
+ * Disable and enable buffering on the stream implementing this interface.
+ * DisableBuffering flushes an output stream's buffer, and invalidates an
+ * input stream's buffer.
+ */
+ void disableBuffering();
+ void enableBuffering();
+
+ /**
+ * The underlying, unbuffered input or output stream.
+ */
+ readonly attribute nsISupports unbufferedStream;
+};
+
+%{C++
+
+/**
+ * These macros get and put a buffer given either an sba parameter that may
+ * point to an object implementing nsIStreamBufferAccess, nsIObjectInputStream,
+ * or nsIObjectOutputStream.
+ */
+#define NS_GET_BUFFER(sba,n,a) ((sba)->GetBuffer(n, a))
+#define NS_PUT_BUFFER(sba,p,n) ((sba)->PutBuffer(p, n))
+
+%}
diff --git a/xpcom/io/nsIStringStream.idl b/xpcom/io/nsIStringStream.idl
new file mode 100644
index 000000000..fec418a4f
--- /dev/null
+++ b/xpcom/io/nsIStringStream.idl
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsIInputStream.idl"
+
+/**
+ * nsIStringInputStream
+ *
+ * Provides scriptable and specialized C++-only methods for initializing a
+ * nsIInputStream implementation with a simple character array.
+ */
+[scriptable, uuid(450cd2d4-f0fd-424d-b365-b1251f80fd53)]
+interface nsIStringInputStream : nsIInputStream
+{
+ /**
+ * SetData - assign data to the input stream (copied on assignment).
+ *
+ * @param data - stream data
+ * @param dataLen - stream data length (-1 if length should be computed)
+ *
+ * NOTE: C++ code should consider using AdoptData or ShareData to avoid
+ * making an extra copy of the stream data.
+ *
+ * NOTE: For JS callers, the given data must not contain null characters
+ * (other than a null terminator) because a null character in the middle of
+ * the data string will be seen as a terminator when the data is converted
+ * from a JS string to a C++ character array.
+ */
+ void setData(in string data, in long dataLen);
+
+ /**
+ * NOTE: the following methods are designed to give C++ code added control
+ * over the ownership and lifetime of the stream data. Use with care :-)
+ */
+
+ /**
+ * AdoptData - assign data to the input stream. the input stream takes
+ * ownership of the given data buffer and will nsMemory::Free it when
+ * the input stream is destroyed.
+ *
+ * @param data - stream data
+ * @param dataLen - stream data length (-1 if length should be computed)
+ */
+ [noscript] void adoptData(in charPtr data, in long dataLen);
+
+ /**
+ * ShareData - assign data to the input stream. the input stream references
+ * the given data buffer until the input stream is destroyed. the given
+ * data buffer must outlive the input stream.
+ *
+ * @param data - stream data
+ * @param dataLen - stream data length (-1 if length should be computed)
+ */
+ [noscript] void shareData(in string data, in long dataLen);
+};
diff --git a/xpcom/io/nsIUnicharInputStream.idl b/xpcom/io/nsIUnicharInputStream.idl
new file mode 100644
index 000000000..f57557cc9
--- /dev/null
+++ b/xpcom/io/nsIUnicharInputStream.idl
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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"
+
+interface nsIUnicharInputStream;
+interface nsIInputStream;
+
+%{C++
+/**
+ * The signature of the writer function passed to ReadSegments. This
+ * is the "consumer" of data that gets read from the stream's buffer.
+ *
+ * @param aInStream stream being read
+ * @param aClosure opaque parameter passed to ReadSegments
+ * @param aFromSegment pointer to memory owned by the input stream
+ * @param aToOffset amount already read (since ReadSegments was called)
+ * @param aCount length of fromSegment
+ * @param aWriteCount number of bytes read
+ *
+ * Implementers should return the following:
+ *
+ * @throws <any-error> if not interested in consuming any data
+ *
+ * Errors are never passed to the caller of ReadSegments.
+ *
+ * NOTE: returning NS_OK and (*aWriteCount = 0) has undefined behavior.
+ */
+typedef NS_CALLBACK(nsWriteUnicharSegmentFun)(nsIUnicharInputStream *aInStream,
+ void *aClosure,
+ const PRUnichar *aFromSegment,
+ uint32_t aToOffset,
+ uint32_t aCount,
+ uint32_t *aWriteCount);
+%}
+native nsWriteUnicharSegmentFun(nsWriteUnicharSegmentFun);
+
+/**
+ * Abstract unicode character input stream
+ * @see nsIInputStream
+ */
+[scriptable, uuid(d5e3bd80-6723-4b92-b0c9-22f6162fd94f)]
+interface nsIUnicharInputStream : nsISupports {
+ /**
+ * Reads into a caller-provided character array.
+ *
+ * @return The number of characters that were successfully read. May be less
+ * than aCount, even if there is more data in the input stream.
+ * A return value of 0 means EOF.
+ *
+ * @note To read more than 2^32 characters, call this method multiple times.
+ */
+ [noscript] unsigned long read([array, size_is(aCount)] in PRUnichar aBuf,
+ in unsigned long aCount);
+
+ /**
+ * Low-level read method that has access to the stream's underlying buffer.
+ * The writer function may be called multiple times for segmented buffers.
+ * ReadSegments is expected to keep calling the writer until either there is
+ * nothing left to read or the writer returns an error. ReadSegments should
+ * not call the writer with zero characters to consume.
+ *
+ * @param aWriter the "consumer" of the data to be read
+ * @param aClosure opaque parameter passed to writer
+ * @param aCount the maximum number of characters to be read
+ *
+ * @return number of characters read (may be less than aCount)
+ * @return 0 if reached end of file (or if aWriter refused to consume data)
+ *
+ * @throws NS_BASE_STREAM_WOULD_BLOCK if reading from the input stream would
+ * block the calling thread (non-blocking mode only)
+ * @throws <other-error> on failure
+ *
+ * NOTE: this function may be unimplemented if a stream has no underlying
+ * buffer
+ */
+ [noscript] unsigned long readSegments(in nsWriteUnicharSegmentFun aWriter,
+ in voidPtr aClosure,
+ in unsigned long aCount);
+
+ /**
+ * Read into a string object.
+ * @param aCount The number of characters that should be read
+ * @return The number of characters that were read.
+ */
+ unsigned long readString(in unsigned long aCount, out AString aString);
+
+ /**
+ * Close the stream and free associated resources. This also closes the
+ * underlying stream, if any.
+ */
+ void close();
+};
diff --git a/xpcom/io/nsIUnicharLineInputStream.idl b/xpcom/io/nsIUnicharLineInputStream.idl
new file mode 100644
index 000000000..34a67f099
--- /dev/null
+++ b/xpcom/io/nsIUnicharLineInputStream.idl
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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"
+
+[scriptable, uuid(67f42475-ba80-40f8-ac0b-649c89230184)]
+interface nsIUnicharLineInputStream : nsISupports
+{
+ /**
+ * Read a single line from the stream, where a line is a
+ * possibly zero length sequence of characters terminated by a
+ * CR, LF, CRLF, LFCR, or eof.
+ * The line terminator is not returned.
+ * @retval false
+ * End of file. This line is the last line of the file
+ * (aLine is valid).
+ * @retval true
+ * The file contains further lines.
+ * @note Do not mix readLine with other read functions.
+ * Doing so can cause various problems and is not supported.
+ */
+ boolean readLine(out AString aLine);
+};
diff --git a/xpcom/io/nsIUnicharOutputStream.idl b/xpcom/io/nsIUnicharOutputStream.idl
new file mode 100644
index 000000000..fb5fd241b
--- /dev/null
+++ b/xpcom/io/nsIUnicharOutputStream.idl
@@ -0,0 +1,47 @@
+/* vim:set expandtab ts=4 sw=4 sts=4 cin: */
+/* 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"
+
+/**
+ * An interface that allows writing unicode data.
+ */
+[scriptable, uuid(2d00b1bb-8b21-4a63-bcc6-7213f513ac2e)]
+interface nsIUnicharOutputStream : nsISupports
+{
+ /**
+ * Write a single character to the stream. When writing many characters,
+ * prefer the string-taking write method.
+ *
+ * @retval true The character was written successfully
+ * @retval false Not all bytes of the character could be written.
+ */
+ boolean write(in unsigned long aCount,
+ [const, array, size_is(aCount)] in PRUnichar c);
+
+ /**
+ * Write a string to the stream.
+ *
+ * @retval true The string was written successfully
+ * @retval false Not all bytes of the string could be written.
+ */
+ boolean writeString(in AString str);
+
+ /**
+ * Flush the stream. This finishes the conversion and writes any bytes that
+ * finish the current byte sequence.
+ *
+ * It does NOT flush the underlying stream.
+ *
+ * @see nsIUnicodeEncoder::Finish
+ */
+ void flush();
+
+ /**
+ * Close the stream and free associated resources. This also closes the
+ * underlying stream.
+ */
+ void close();
+};
diff --git a/xpcom/io/nsInputStreamTee.cpp b/xpcom/io/nsInputStreamTee.cpp
new file mode 100644
index 000000000..dd68fc6f2
--- /dev/null
+++ b/xpcom/io/nsInputStreamTee.cpp
@@ -0,0 +1,347 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 <stdlib.h>
+#include "prlog.h"
+
+#include "mozilla/Mutex.h"
+#include "mozilla/Attributes.h"
+#include "nsIInputStreamTee.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsIEventTarget.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla;
+
+#ifdef PR_LOGGING
+static PRLogModuleInfo*
+GetTeeLog()
+{
+ static PRLogModuleInfo *sLog;
+ if (!sLog)
+ sLog = PR_NewLogModule("nsInputStreamTee");
+ return sLog;
+}
+#define LOG(args) PR_LOG(GetTeeLog(), PR_LOG_DEBUG, args)
+#else
+#define LOG(args)
+#endif
+
+class nsInputStreamTee MOZ_FINAL : public nsIInputStreamTee
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAM
+ NS_DECL_NSIINPUTSTREAMTEE
+
+ nsInputStreamTee();
+ bool SinkIsValid();
+ void InvalidateSink();
+
+private:
+ ~nsInputStreamTee() {}
+
+ nsresult TeeSegment(const char *buf, uint32_t count);
+
+ static NS_METHOD WriteSegmentFun(nsIInputStream *, void *, const char *,
+ uint32_t, uint32_t, uint32_t *);
+
+private:
+ nsCOMPtr<nsIInputStream> mSource;
+ nsCOMPtr<nsIOutputStream> mSink;
+ nsCOMPtr<nsIEventTarget> mEventTarget;
+ nsWriteSegmentFun mWriter; // for implementing ReadSegments
+ void *mClosure; // for implementing ReadSegments
+ nsAutoPtr<Mutex> mLock; // synchronize access to mSinkIsValid
+ bool mSinkIsValid; // False if TeeWriteEvent fails
+};
+
+class nsInputStreamTeeWriteEvent : public nsRunnable {
+public:
+ // aTee's lock is held across construction of this object
+ nsInputStreamTeeWriteEvent(const char *aBuf, uint32_t aCount,
+ nsIOutputStream *aSink,
+ nsInputStreamTee *aTee)
+ {
+ // copy the buffer - will be free'd by dtor
+ mBuf = (char *)malloc(aCount);
+ if (mBuf) memcpy(mBuf, (char *)aBuf, aCount);
+ mCount = aCount;
+ mSink = aSink;
+ bool isNonBlocking;
+ mSink->IsNonBlocking(&isNonBlocking);
+ NS_ASSERTION(isNonBlocking == false, "mSink is nonblocking");
+ mTee = aTee;
+ }
+
+ NS_IMETHOD Run()
+ {
+ if (!mBuf) {
+ NS_WARNING("nsInputStreamTeeWriteEvent::Run() "
+ "memory not allocated\n");
+ return NS_OK;
+ }
+ NS_ABORT_IF_FALSE(mSink, "mSink is null!");
+
+ // The output stream could have been invalidated between when
+ // this event was dispatched and now, so check before writing.
+ if (!mTee->SinkIsValid()) {
+ return NS_OK;
+ }
+
+ LOG(("nsInputStreamTeeWriteEvent::Run() [%p]"
+ "will write %u bytes to %p\n",
+ this, mCount, mSink.get()));
+
+ uint32_t totalBytesWritten = 0;
+ while (mCount) {
+ nsresult rv;
+ uint32_t bytesWritten = 0;
+ rv = mSink->Write(mBuf + totalBytesWritten, mCount, &bytesWritten);
+ if (NS_FAILED(rv)) {
+ LOG(("nsInputStreamTeeWriteEvent::Run[%p] error %x in writing",
+ this,rv));
+ mTee->InvalidateSink();
+ break;
+ }
+ totalBytesWritten += bytesWritten;
+ NS_ASSERTION(bytesWritten <= mCount, "wrote too much");
+ mCount -= bytesWritten;
+ }
+ return NS_OK;
+ }
+
+protected:
+ virtual ~nsInputStreamTeeWriteEvent()
+ {
+ if (mBuf) free(mBuf);
+ mBuf = nullptr;
+ }
+
+private:
+ char *mBuf;
+ uint32_t mCount;
+ nsCOMPtr<nsIOutputStream> mSink;
+ // back pointer to the tee that created this runnable
+ nsRefPtr<nsInputStreamTee> mTee;
+};
+
+nsInputStreamTee::nsInputStreamTee(): mLock(nullptr)
+ , mSinkIsValid(true)
+{
+}
+
+bool
+nsInputStreamTee::SinkIsValid()
+{
+ MutexAutoLock lock(*mLock);
+ return mSinkIsValid;
+}
+
+void
+nsInputStreamTee::InvalidateSink()
+{
+ MutexAutoLock lock(*mLock);
+ mSinkIsValid = false;
+}
+
+nsresult
+nsInputStreamTee::TeeSegment(const char *buf, uint32_t count)
+{
+ if (!mSink) return NS_OK; // nothing to do
+ if (mLock) { // asynchronous case
+ NS_ASSERTION(mEventTarget, "mEventTarget is null, mLock is not null.");
+ if (!SinkIsValid()) {
+ return NS_OK; // nothing to do
+ }
+ nsRefPtr<nsIRunnable> event =
+ new nsInputStreamTeeWriteEvent(buf, count, mSink, this);
+ NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
+ LOG(("nsInputStreamTee::TeeSegment [%p] dispatching write %u bytes\n",
+ this, count));
+ return mEventTarget->Dispatch(event, NS_DISPATCH_NORMAL);
+ } else { // synchronous case
+ NS_ASSERTION(!mEventTarget, "mEventTarget is not null, mLock is null.");
+ nsresult rv;
+ uint32_t totalBytesWritten = 0;
+ while (count) {
+ uint32_t bytesWritten = 0;
+ rv = mSink->Write(buf + totalBytesWritten, count, &bytesWritten);
+ if (NS_FAILED(rv)) {
+ // ok, this is not a fatal error... just drop our reference to mSink
+ // and continue on as if nothing happened.
+ NS_WARNING("Write failed (non-fatal)");
+ // catch possible misuse of the input stream tee
+ NS_ASSERTION(rv != NS_BASE_STREAM_WOULD_BLOCK, "sink must be a blocking stream");
+ mSink = 0;
+ break;
+ }
+ totalBytesWritten += bytesWritten;
+ NS_ASSERTION(bytesWritten <= count, "wrote too much");
+ count -= bytesWritten;
+ }
+ return NS_OK;
+ }
+}
+
+NS_METHOD
+nsInputStreamTee::WriteSegmentFun(nsIInputStream *in, void *closure, const char *fromSegment,
+ uint32_t offset, uint32_t count, uint32_t *writeCount)
+{
+ nsInputStreamTee *tee = reinterpret_cast<nsInputStreamTee *>(closure);
+
+ nsresult rv = tee->mWriter(in, tee->mClosure, fromSegment, offset, count, writeCount);
+ if (NS_FAILED(rv) || (*writeCount == 0)) {
+ NS_ASSERTION((NS_FAILED(rv) ? (*writeCount == 0) : true),
+ "writer returned an error with non-zero writeCount");
+ return rv;
+ }
+
+ return tee->TeeSegment(fromSegment, *writeCount);
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsInputStreamTee,
+ nsIInputStreamTee,
+ nsIInputStream)
+NS_IMETHODIMP
+nsInputStreamTee::Close()
+{
+ NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
+ nsresult rv = mSource->Close();
+ mSource = 0;
+ mSink = 0;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::Available(uint64_t *avail)
+{
+ NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
+ return mSource->Available(avail);
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::Read(char *buf, uint32_t count, uint32_t *bytesRead)
+{
+ NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
+
+ nsresult rv = mSource->Read(buf, count, bytesRead);
+ if (NS_FAILED(rv) || (*bytesRead == 0))
+ return rv;
+
+ return TeeSegment(buf, *bytesRead);
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::ReadSegments(nsWriteSegmentFun writer,
+ void *closure,
+ uint32_t count,
+ uint32_t *bytesRead)
+{
+ NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
+
+ mWriter = writer;
+ mClosure = closure;
+
+ return mSource->ReadSegments(WriteSegmentFun, this, count, bytesRead);
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::IsNonBlocking(bool *result)
+{
+ NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED);
+ return mSource->IsNonBlocking(result);
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::SetSource(nsIInputStream *source)
+{
+ mSource = source;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::GetSource(nsIInputStream **source)
+{
+ NS_IF_ADDREF(*source = mSource);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::SetSink(nsIOutputStream *sink)
+{
+#ifdef DEBUG
+ if (sink) {
+ bool nonBlocking;
+ nsresult rv = sink->IsNonBlocking(&nonBlocking);
+ if (NS_FAILED(rv) || nonBlocking)
+ NS_ERROR("sink should be a blocking stream");
+ }
+#endif
+ mSink = sink;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::GetSink(nsIOutputStream **sink)
+{
+ NS_IF_ADDREF(*sink = mSink);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::SetEventTarget(nsIEventTarget *anEventTarget)
+{
+ mEventTarget = anEventTarget;
+ if (mEventTarget) {
+ // Only need synchronization if this is an async tee
+ mLock = new Mutex("nsInputStreamTee.mLock");
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsInputStreamTee::GetEventTarget(nsIEventTarget **anEventTarget)
+{
+ NS_IF_ADDREF(*anEventTarget = mEventTarget);
+ return NS_OK;
+}
+
+
+nsresult
+NS_NewInputStreamTeeAsync(nsIInputStream **result,
+ nsIInputStream *source,
+ nsIOutputStream *sink,
+ nsIEventTarget *anEventTarget)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIInputStreamTee> tee = new nsInputStreamTee();
+ if (!tee)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = tee->SetSource(source);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = tee->SetSink(sink);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = tee->SetEventTarget(anEventTarget);
+ if (NS_FAILED(rv)) return rv;
+
+ NS_ADDREF(*result = tee);
+ return rv;
+}
+
+nsresult
+NS_NewInputStreamTee(nsIInputStream **result,
+ nsIInputStream *source,
+ nsIOutputStream *sink)
+{
+ return NS_NewInputStreamTeeAsync(result, source, sink, nullptr);
+}
diff --git a/xpcom/io/nsLinebreakConverter.cpp b/xpcom/io/nsLinebreakConverter.cpp
new file mode 100644
index 000000000..dd4609b00
--- /dev/null
+++ b/xpcom/io/nsLinebreakConverter.cpp
@@ -0,0 +1,465 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsLinebreakConverter.h"
+
+#include "nsMemory.h"
+#include "nsCRT.h"
+
+
+/*----------------------------------------------------------------------------
+ GetLinebreakString
+
+ Could make this inline
+----------------------------------------------------------------------------*/
+static const char* GetLinebreakString(nsLinebreakConverter::ELinebreakType aBreakType)
+{
+ static const char* const sLinebreaks[] = {
+ "", // any
+ NS_LINEBREAK, // platform
+ LFSTR, // content
+ CRLF, // net
+ CRSTR, // Mac
+ LFSTR, // Unix
+ CRLF, // Windows
+ " ", // space
+ nullptr
+ };
+
+ return sLinebreaks[aBreakType];
+}
+
+
+/*----------------------------------------------------------------------------
+ AppendLinebreak
+
+ Wee inline method to append a line break. Modifies ioDest.
+----------------------------------------------------------------------------*/
+template<class T>
+void AppendLinebreak(T*& ioDest, const char* lineBreakStr)
+{
+ *ioDest++ = *lineBreakStr;
+
+ if (lineBreakStr[1])
+ *ioDest++ = lineBreakStr[1];
+}
+
+/*----------------------------------------------------------------------------
+ CountChars
+
+ Counts occurrences of breakStr in aSrc
+----------------------------------------------------------------------------*/
+template<class T>
+int32_t CountLinebreaks(const T* aSrc, int32_t inLen, const char* breakStr)
+{
+ const T* src = aSrc;
+ const T* srcEnd = aSrc + inLen;
+ int32_t theCount = 0;
+
+ while (src < srcEnd)
+ {
+ if (*src == *breakStr)
+ {
+ src++;
+
+ if (breakStr[1])
+ {
+ if (src < srcEnd && *src == breakStr[1])
+ {
+ src++;
+ theCount++;
+ }
+ }
+ else
+ {
+ theCount++;
+ }
+ }
+ else
+ {
+ src++;
+ }
+ }
+
+ return theCount;
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertBreaks
+
+ ioLen *includes* a terminating null, if any
+----------------------------------------------------------------------------*/
+template<class T>
+static T* ConvertBreaks(const T* inSrc, int32_t& ioLen, const char* srcBreak, const char* destBreak)
+{
+ NS_ASSERTION(inSrc && srcBreak && destBreak, "Got a null string");
+
+ T* resultString = nullptr;
+
+ // handle the no conversion case
+ if (nsCRT::strcmp(srcBreak, destBreak) == 0)
+ {
+ resultString = (T *)nsMemory::Alloc(sizeof(T) * ioLen);
+ if (!resultString) return nullptr;
+ memcpy(resultString, inSrc, sizeof(T) * ioLen); // includes the null, if any
+ return resultString;
+ }
+
+ int32_t srcBreakLen = strlen(srcBreak);
+ int32_t destBreakLen = strlen(destBreak);
+
+ // handle the easy case, where the string length does not change, and the
+ // breaks are only 1 char long, i.e. CR <-> LF
+ if (srcBreakLen == destBreakLen && srcBreakLen == 1)
+ {
+ resultString = (T *)nsMemory::Alloc(sizeof(T) * ioLen);
+ if (!resultString) return nullptr;
+
+ const T* src = inSrc;
+ const T* srcEnd = inSrc + ioLen; // includes null, if any
+ T* dst = resultString;
+
+ char srcBreakChar = *srcBreak; // we know it's one char long already
+ char dstBreakChar = *destBreak;
+
+ while (src < srcEnd)
+ {
+ if (*src == srcBreakChar)
+ {
+ *dst++ = dstBreakChar;
+ src++;
+ }
+ else
+ {
+ *dst++ = *src++;
+ }
+ }
+
+ // ioLen does not change
+ }
+ else
+ {
+ // src and dest termination is different length. Do it a slower way.
+
+ // count linebreaks in src. Assumes that chars in 2-char linebreaks are unique.
+ int32_t numLinebreaks = CountLinebreaks(inSrc, ioLen, srcBreak);
+
+ int32_t newBufLen = ioLen - (numLinebreaks * srcBreakLen) + (numLinebreaks * destBreakLen);
+ resultString = (T *)nsMemory::Alloc(sizeof(T) * newBufLen);
+ if (!resultString) return nullptr;
+
+ const T* src = inSrc;
+ const T* srcEnd = inSrc + ioLen; // includes null, if any
+ T* dst = resultString;
+
+ while (src < srcEnd)
+ {
+ if (*src == *srcBreak)
+ {
+ *dst++ = *destBreak;
+ if (destBreak[1])
+ *dst++ = destBreak[1];
+
+ src++;
+ if (src < srcEnd && srcBreak[1] && *src == srcBreak[1])
+ src++;
+ }
+ else
+ {
+ *dst++ = *src++;
+ }
+ }
+
+ ioLen = newBufLen;
+ }
+
+ return resultString;
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertBreaksInSitu
+
+ Convert breaks in situ. Can only do this if the linebreak length
+ does not change.
+----------------------------------------------------------------------------*/
+template<class T>
+static void ConvertBreaksInSitu(T* inSrc, int32_t inLen, char srcBreak, char destBreak)
+{
+ T* src = inSrc;
+ T* srcEnd = inSrc + inLen;
+
+ while (src < srcEnd)
+ {
+ if (*src == srcBreak)
+ *src = destBreak;
+
+ src++;
+ }
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertUnknownBreaks
+
+ Convert unknown line breaks to the specified break.
+
+ This will convert CRLF pairs to one break, and single CR or LF to a break.
+----------------------------------------------------------------------------*/
+template<class T>
+static T* ConvertUnknownBreaks(const T* inSrc, int32_t& ioLen, const char* destBreak)
+{
+ const T* src = inSrc;
+ const T* srcEnd = inSrc + ioLen; // includes null, if any
+
+ int32_t destBreakLen = strlen(destBreak);
+ int32_t finalLen = 0;
+
+ while (src < srcEnd)
+ {
+ if (*src == nsCRT::CR)
+ {
+ if (src < srcEnd && src[1] == nsCRT::LF)
+ {
+ // CRLF
+ finalLen += destBreakLen;
+ src++;
+ }
+ else
+ {
+ // Lone CR
+ finalLen += destBreakLen;
+ }
+ }
+ else if (*src == nsCRT::LF)
+ {
+ // Lone LF
+ finalLen += destBreakLen;
+ }
+ else
+ {
+ finalLen++;
+ }
+ src++;
+ }
+
+ T* resultString = (T *)nsMemory::Alloc(sizeof(T) * finalLen);
+ if (!resultString) return nullptr;
+
+ src = inSrc;
+ srcEnd = inSrc + ioLen; // includes null, if any
+
+ T* dst = resultString;
+
+ while (src < srcEnd)
+ {
+ if (*src == nsCRT::CR)
+ {
+ if (src < srcEnd && src[1] == nsCRT::LF)
+ {
+ // CRLF
+ AppendLinebreak(dst, destBreak);
+ src++;
+ }
+ else
+ {
+ // Lone CR
+ AppendLinebreak(dst, destBreak);
+ }
+ }
+ else if (*src == nsCRT::LF)
+ {
+ // Lone LF
+ AppendLinebreak(dst, destBreak);
+ }
+ else
+ {
+ *dst++ = *src;
+ }
+ src++;
+ }
+
+ ioLen = finalLen;
+ return resultString;
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertLineBreaks
+
+----------------------------------------------------------------------------*/
+char* nsLinebreakConverter::ConvertLineBreaks(const char* aSrc,
+ ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks, int32_t aSrcLen, int32_t* outLen)
+{
+ NS_ASSERTION(aDestBreaks != eLinebreakAny &&
+ aSrcBreaks != eLinebreakSpace, "Invalid parameter");
+ if (!aSrc) return nullptr;
+
+ int32_t sourceLen = (aSrcLen == kIgnoreLen) ? strlen(aSrc) + 1 : aSrcLen;
+
+ char* resultString;
+ if (aSrcBreaks == eLinebreakAny)
+ resultString = ConvertUnknownBreaks(aSrc, sourceLen, GetLinebreakString(aDestBreaks));
+ else
+ resultString = ConvertBreaks(aSrc, sourceLen, GetLinebreakString(aSrcBreaks), GetLinebreakString(aDestBreaks));
+
+ if (outLen)
+ *outLen = sourceLen;
+ return resultString;
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertLineBreaksInSitu
+
+----------------------------------------------------------------------------*/
+nsresult nsLinebreakConverter::ConvertLineBreaksInSitu(char **ioBuffer, ELinebreakType aSrcBreaks,
+ ELinebreakType aDestBreaks, int32_t aSrcLen, int32_t* outLen)
+{
+ NS_ASSERTION(ioBuffer && *ioBuffer, "Null pointer passed");
+ if (!ioBuffer || !*ioBuffer) return NS_ERROR_NULL_POINTER;
+
+ NS_ASSERTION(aDestBreaks != eLinebreakAny &&
+ aSrcBreaks != eLinebreakSpace, "Invalid parameter");
+
+ int32_t sourceLen = (aSrcLen == kIgnoreLen) ? strlen(*ioBuffer) + 1 : aSrcLen;
+
+ // can we convert in-place?
+ const char* srcBreaks = GetLinebreakString(aSrcBreaks);
+ const char* dstBreaks = GetLinebreakString(aDestBreaks);
+
+ if ( (aSrcBreaks != eLinebreakAny) &&
+ (strlen(srcBreaks) == 1) &&
+ (strlen(dstBreaks) == 1) )
+ {
+ ConvertBreaksInSitu(*ioBuffer, sourceLen, *srcBreaks, *dstBreaks);
+ if (outLen)
+ *outLen = sourceLen;
+ }
+ else
+ {
+ char* destBuffer;
+
+ if (aSrcBreaks == eLinebreakAny)
+ destBuffer = ConvertUnknownBreaks(*ioBuffer, sourceLen, dstBreaks);
+ else
+ destBuffer = ConvertBreaks(*ioBuffer, sourceLen, srcBreaks, dstBreaks);
+
+ if (!destBuffer) return NS_ERROR_OUT_OF_MEMORY;
+ *ioBuffer = destBuffer;
+ if (outLen)
+ *outLen = sourceLen;
+ }
+
+ return NS_OK;
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertUnicharLineBreaks
+
+----------------------------------------------------------------------------*/
+PRUnichar* nsLinebreakConverter::ConvertUnicharLineBreaks(const PRUnichar* aSrc,
+ ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks, int32_t aSrcLen, int32_t* outLen)
+{
+ NS_ASSERTION(aDestBreaks != eLinebreakAny &&
+ aSrcBreaks != eLinebreakSpace, "Invalid parameter");
+ if (!aSrc) return nullptr;
+
+ int32_t bufLen = (aSrcLen == kIgnoreLen) ? NS_strlen(aSrc) + 1 : aSrcLen;
+
+ PRUnichar* resultString;
+ if (aSrcBreaks == eLinebreakAny)
+ resultString = ConvertUnknownBreaks(aSrc, bufLen, GetLinebreakString(aDestBreaks));
+ else
+ resultString = ConvertBreaks(aSrc, bufLen, GetLinebreakString(aSrcBreaks), GetLinebreakString(aDestBreaks));
+
+ if (outLen)
+ *outLen = bufLen;
+ return resultString;
+}
+
+
+/*----------------------------------------------------------------------------
+ ConvertStringLineBreaks
+
+----------------------------------------------------------------------------*/
+nsresult nsLinebreakConverter::ConvertUnicharLineBreaksInSitu(PRUnichar **ioBuffer,
+ ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks, int32_t aSrcLen, int32_t* outLen)
+{
+ NS_ASSERTION(ioBuffer && *ioBuffer, "Null pointer passed");
+ if (!ioBuffer || !*ioBuffer) return NS_ERROR_NULL_POINTER;
+ NS_ASSERTION(aDestBreaks != eLinebreakAny &&
+ aSrcBreaks != eLinebreakSpace, "Invalid parameter");
+
+ int32_t sourceLen = (aSrcLen == kIgnoreLen) ? NS_strlen(*ioBuffer) + 1 : aSrcLen;
+
+ // can we convert in-place?
+ const char* srcBreaks = GetLinebreakString(aSrcBreaks);
+ const char* dstBreaks = GetLinebreakString(aDestBreaks);
+
+ if ( (aSrcBreaks != eLinebreakAny) &&
+ (strlen(srcBreaks) == 1) &&
+ (strlen(dstBreaks) == 1) )
+ {
+ ConvertBreaksInSitu(*ioBuffer, sourceLen, *srcBreaks, *dstBreaks);
+ if (outLen)
+ *outLen = sourceLen;
+ }
+ else
+ {
+ PRUnichar* destBuffer;
+
+ if (aSrcBreaks == eLinebreakAny)
+ destBuffer = ConvertUnknownBreaks(*ioBuffer, sourceLen, dstBreaks);
+ else
+ destBuffer = ConvertBreaks(*ioBuffer, sourceLen, srcBreaks, dstBreaks);
+
+ if (!destBuffer) return NS_ERROR_OUT_OF_MEMORY;
+ *ioBuffer = destBuffer;
+ if (outLen)
+ *outLen = sourceLen;
+ }
+
+ return NS_OK;
+}
+
+/*----------------------------------------------------------------------------
+ ConvertStringLineBreaks
+
+----------------------------------------------------------------------------*/
+nsresult nsLinebreakConverter::ConvertStringLineBreaks(nsString& ioString,
+ ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks)
+{
+
+ NS_ASSERTION(aDestBreaks != eLinebreakAny &&
+ aSrcBreaks != eLinebreakSpace, "Invalid parameter");
+
+ // nothing to do
+ if (ioString.IsEmpty()) return NS_OK;
+
+ nsresult rv;
+
+ // remember the old buffer in case
+ // we blow it away later
+ nsString::char_iterator stringBuf;
+ ioString.BeginWriting(stringBuf);
+
+ int32_t newLen;
+
+ rv = ConvertUnicharLineBreaksInSitu(&stringBuf,
+ aSrcBreaks, aDestBreaks,
+ ioString.Length() + 1, &newLen);
+ if (NS_FAILED(rv)) return rv;
+
+ if (stringBuf != ioString.get())
+ ioString.Adopt(stringBuf);
+
+ return NS_OK;
+}
+
+
+
diff --git a/xpcom/io/nsLinebreakConverter.h b/xpcom/io/nsLinebreakConverter.h
new file mode 100644
index 000000000..ad445430a
--- /dev/null
+++ b/xpcom/io/nsLinebreakConverter.h
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 nsLinebreakConverter_h_
+#define nsLinebreakConverter_h_
+
+#include "nscore.h"
+#include "nsString.h"
+
+// utility class for converting between different line breaks.
+
+class nsLinebreakConverter
+{
+public:
+
+ // Note: enum must match char* array in GetLinebreakString
+ typedef enum {
+ eLinebreakAny, // any kind of linebreak (i.e. "don't care" source)
+
+ eLinebreakPlatform, // platform linebreak
+ eLinebreakContent, // Content model linebreak (LF)
+ eLinebreakNet, // Form submission linebreak (CRLF)
+
+ eLinebreakMac, // CR
+ eLinebreakUnix, // LF
+ eLinebreakWindows, // CRLF
+
+ eLinebreakSpace // space characters. Only valid as destination type
+
+ } ELinebreakType;
+
+ enum {
+ kIgnoreLen = -1
+ };
+
+ /* ConvertLineBreaks
+ * Convert line breaks in the supplied string, allocating and returning
+ * a new buffer. Returns nullptr on failure.
+ * @param aSrc: the source string. if aSrcLen == kIgnoreLen this string is assumed
+ * to be null terminated, otherwise it must be at least aSrcLen long.
+ * @param aSrcBreaks: the line breaks in the source. If unknown, pass eLinebreakAny.
+ * If known, pass the known value, as this may be more efficient.
+ * @param aDestBreaks: the line breaks you want in the output.
+ * @param aSrcLen: length of the source. If -1, the source is assumed to be a null-
+ * terminated string.
+ * @param aOutLen: used to return character length of returned buffer, if not null.
+ */
+ static char* ConvertLineBreaks(const char* aSrc,
+ ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks,
+ int32_t aSrcLen = kIgnoreLen, int32_t* aOutLen = nullptr);
+
+
+ /* ConvertUnicharLineBreaks
+ * Convert line breaks in the supplied string, allocating and returning
+ * a new buffer. Returns nullptr on failure.
+ * @param aSrc: the source string. if aSrcLen == kIgnoreLen this string is assumed
+ * to be null terminated, otherwise it must be at least aSrcLen long.
+ * @param aSrcBreaks: the line breaks in the source. If unknown, pass eLinebreakAny.
+ * If known, pass the known value, as this may be more efficient.
+ * @param aDestBreaks: the line breaks you want in the output.
+ * @param aSrcLen: length of the source, in characters. If -1, the source is assumed to be a null-
+ * terminated string.
+ * @param aOutLen: used to return character length of returned buffer, if not null.
+ */
+ static PRUnichar* ConvertUnicharLineBreaks(const PRUnichar* aSrc,
+ ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks,
+ int32_t aSrcLen = kIgnoreLen, int32_t* aOutLen = nullptr);
+
+
+ /* ConvertStringLineBreaks
+ * Convert line breaks in the supplied string, changing the string buffer (i.e. in-place conversion)
+ * @param ioString: the string to be converted.
+ * @param aSrcBreaks: the line breaks in the source. If unknown, pass eLinebreakAny.
+ * If known, pass the known value, as this may be more efficient.
+ * @param aDestBreaks: the line breaks you want in the output.
+ * @param aSrcLen: length of the source, in characters. If -1, the source is assumed to be a null-
+ * terminated string.
+ */
+ static nsresult ConvertStringLineBreaks(nsString& ioString, ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks);
+
+
+ /* ConvertLineBreaksInSitu
+ * Convert line breaks in place if possible. NOTE: THIS MAY REALLOCATE THE BUFFER,
+ * BUT IT WON'T FREE THE OLD BUFFER (because it doesn't know how). So be prepared
+ * to keep a copy of the old pointer, and free it if this passes back a new pointer.
+ * ALSO NOTE: DON'T PASS A STATIC STRING POINTER TO THIS FUNCTION.
+ *
+ * @param ioBuffer: the source buffer. if aSrcLen == kIgnoreLen this string is assumed
+ * to be null terminated, otherwise it must be at least aSrcLen long.
+ * @param aSrcBreaks: the line breaks in the source. If unknown, pass eLinebreakAny.
+ * If known, pass the known value, as this may be more efficient.
+ * @param aDestBreaks: the line breaks you want in the output.
+ * @param aSrcLen: length of the source. If -1, the source is assumed to be a null-
+ * terminated string.
+ * @param aOutLen: used to return character length of returned buffer, if not null.
+ */
+ static nsresult ConvertLineBreaksInSitu(char **ioBuffer, ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks,
+ int32_t aSrcLen = kIgnoreLen, int32_t* aOutLen = nullptr);
+
+
+ /* ConvertUnicharLineBreaksInSitu
+ * Convert line breaks in place if possible. NOTE: THIS MAY REALLOCATE THE BUFFER,
+ * BUT IT WON'T FREE THE OLD BUFFER (because it doesn't know how). So be prepared
+ * to keep a copy of the old pointer, and free it if this passes back a new pointer.
+ *
+ * @param ioBuffer: the source buffer. if aSrcLen == kIgnoreLen this string is assumed
+ * to be null terminated, otherwise it must be at least aSrcLen long.
+ * @param aSrcBreaks: the line breaks in the source. If unknown, pass eLinebreakAny.
+ * If known, pass the known value, as this may be more efficient.
+ * @param aDestBreaks: the line breaks you want in the output.
+ * @param aSrcLen: length of the source in characters. If -1, the source is assumed to be a null-
+ * terminated string.
+ * @param aOutLen: used to return character length of returned buffer, if not null.
+ */
+ static nsresult ConvertUnicharLineBreaksInSitu(PRUnichar **ioBuffer, ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks,
+ int32_t aSrcLen = kIgnoreLen, int32_t* aOutLen = nullptr);
+
+};
+
+#endif // nsLinebreakConverter_h_
diff --git a/xpcom/io/nsLocalFile.h b/xpcom/io/nsLocalFile.h
new file mode 100644
index 000000000..1972eb757
--- /dev/null
+++ b/xpcom/io/nsLocalFile.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * This Original Code has been modified by IBM Corporation. Modifications made by IBM
+ * described herein are Copyright (c) International Business Machines Corporation, 2000.
+ * Modifications to Mozilla code or documentation identified per MPL Section 3.3
+ *
+ * Date Modified by Description of modification
+ * 04/20/2000 IBM Corp. OS/2 build.
+ */
+
+#ifndef _NS_LOCAL_FILE_H_
+#define _NS_LOCAL_FILE_H_
+
+#include "nscore.h"
+
+#define NS_LOCAL_FILE_CID {0x2e23e220, 0x60be, 0x11d3, {0x8c, 0x4a, 0x00, 0x00, 0x64, 0x65, 0x73, 0x74}}
+
+#define NS_DECL_NSLOCALFILE_UNICODE_METHODS \
+ nsresult AppendUnicode(const PRUnichar *aNode); \
+ nsresult GetUnicodeLeafName(PRUnichar **aLeafName); \
+ nsresult SetUnicodeLeafName(const PRUnichar *aLeafName); \
+ nsresult CopyToUnicode(nsIFile *aNewParentDir, const PRUnichar *aNewLeafName); \
+ nsresult CopyToFollowingLinksUnicode(nsIFile *aNewParentDir, const PRUnichar *aNewLeafName); \
+ nsresult MoveToUnicode(nsIFile *aNewParentDir, const PRUnichar *aNewLeafName); \
+ nsresult GetUnicodeTarget(PRUnichar **aTarget); \
+ nsresult GetUnicodePath(PRUnichar **aPath); \
+ nsresult InitWithUnicodePath(const PRUnichar *aPath); \
+ nsresult AppendRelativeUnicodePath(const PRUnichar *aRelativePath);
+
+// nsXPComInit needs to know about how we are implemented,
+// so here we will export it. Other users should not depend
+// on this.
+
+#include <errno.h>
+#include "nsILocalFile.h"
+
+#ifdef XP_WIN
+#include "nsLocalFileWin.h"
+#elif defined(XP_UNIX)
+#include "nsLocalFileUnix.h"
+#elif defined(XP_OS2)
+#include "nsLocalFileOS2.h"
+#else
+#error NOT_IMPLEMENTED
+#endif
+
+#define NSRESULT_FOR_RETURN(ret) (((ret) < 0) ? NSRESULT_FOR_ERRNO() : NS_OK)
+
+inline nsresult
+nsresultForErrno(int err)
+{
+ switch (err) {
+ case 0:
+ return NS_OK;
+ case ENOENT:
+ return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
+ case ENOTDIR:
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+#ifdef ENOLINK
+ case ENOLINK:
+ return NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
+#endif /* ENOLINK */
+ case EEXIST:
+ return NS_ERROR_FILE_ALREADY_EXISTS;
+#ifdef EPERM
+ case EPERM:
+#endif /* EPERM */
+ case EACCES:
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ /*
+ * On AIX 4.3, ENOTEMPTY is defined as EEXIST,
+ * so there can't be cases for both without
+ * preprocessing.
+ */
+#if ENOTEMPTY != EEXIST
+ case ENOTEMPTY:
+ return NS_ERROR_FILE_DIR_NOT_EMPTY;
+#endif /* ENOTEMPTY != EEXIST */
+ default:
+ return NS_ERROR_FAILURE;
+ }
+}
+
+#define NSRESULT_FOR_ERRNO() nsresultForErrno(errno)
+
+void NS_StartupLocalFile();
+void NS_ShutdownLocalFile();
+
+#endif
diff --git a/xpcom/io/nsLocalFileCommon.cpp b/xpcom/io/nsLocalFileCommon.cpp
new file mode 100644
index 000000000..5ec4bae22
--- /dev/null
+++ b/xpcom/io/nsLocalFileCommon.cpp
@@ -0,0 +1,290 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIServiceManager.h"
+
+#include "nsLocalFile.h" // includes platform-specific headers
+
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsReadableUtils.h"
+#include "nsPrintfCString.h"
+#include "nsCRT.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsUTF8Utils.h"
+
+#ifdef XP_WIN
+#include <string.h>
+#endif
+
+
+void NS_StartupLocalFile()
+{
+ nsLocalFile::GlobalInit();
+}
+
+void NS_ShutdownLocalFile()
+{
+ nsLocalFile::GlobalShutdown();
+}
+
+#if !defined(MOZ_WIDGET_COCOA) && !defined(XP_WIN)
+NS_IMETHODIMP
+nsLocalFile::InitWithFile(nsIFile *aFile)
+{
+ NS_ENSURE_ARG(aFile);
+
+ nsAutoCString path;
+ aFile->GetNativePath(path);
+ if (path.IsEmpty())
+ return NS_ERROR_INVALID_ARG;
+ return InitWithNativePath(path);
+}
+#endif
+
+#define kMaxFilenameLength 255
+#define kMaxExtensionLength 100
+#define kMaxSequenceNumberLength 5 // "-9999"
+// requirement: kMaxExtensionLength < kMaxFilenameLength - kMaxSequenceNumberLength
+
+NS_IMETHODIMP
+nsLocalFile::CreateUnique(uint32_t type, uint32_t attributes)
+{
+ nsresult rv;
+ bool longName;
+
+#ifdef XP_WIN
+ nsAutoString pathName, leafName, rootName, suffix;
+ rv = GetPath(pathName);
+#else
+ nsAutoCString pathName, leafName, rootName, suffix;
+ rv = GetNativePath(pathName);
+#endif
+ if (NS_FAILED(rv))
+ return rv;
+
+ longName = (pathName.Length() + kMaxSequenceNumberLength >
+ kMaxFilenameLength);
+ if (!longName)
+ {
+ rv = Create(type, attributes);
+ if (rv != NS_ERROR_FILE_ALREADY_EXISTS)
+ return rv;
+ }
+
+#ifdef XP_WIN
+ rv = GetLeafName(leafName);
+ if (NS_FAILED(rv))
+ return rv;
+
+ const int32_t lastDot = leafName.RFindChar(PRUnichar('.'));
+#else
+ rv = GetNativeLeafName(leafName);
+ if (NS_FAILED(rv))
+ return rv;
+
+ const int32_t lastDot = leafName.RFindChar('.');
+#endif
+
+ if (lastDot == kNotFound)
+ {
+ rootName = leafName;
+ }
+ else
+ {
+ suffix = Substring(leafName, lastDot); // include '.'
+ rootName = Substring(leafName, 0, lastDot); // strip suffix and dot
+ }
+
+ if (longName)
+ {
+ int32_t maxRootLength = (kMaxFilenameLength -
+ (pathName.Length() - leafName.Length()) -
+ suffix.Length() - kMaxSequenceNumberLength);
+
+ // We cannot create an item inside a directory whose name is too long.
+ // Also, ensure that at least one character remains after we truncate
+ // the root name, as we don't want to end up with an empty leaf name.
+ if (maxRootLength < 2)
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+#ifdef XP_WIN
+ // ensure that we don't cut the name in mid-UTF16-character
+ rootName.SetLength(NS_IS_LOW_SURROGATE(rootName[maxRootLength]) ?
+ maxRootLength - 1 : maxRootLength);
+ SetLeafName(rootName + suffix);
+#else
+ if (NS_IsNativeUTF8())
+ {
+ // ensure that we don't cut the name in mid-UTF8-character
+ // (assume the name is valid UTF8 to begin with)
+ while (UTF8traits::isInSeq(rootName[maxRootLength]))
+ --maxRootLength;
+
+ // Another check to avoid ending up with an empty leaf name.
+ if (maxRootLength == 0 && suffix.IsEmpty())
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+
+ rootName.SetLength(maxRootLength);
+ SetNativeLeafName(rootName + suffix);
+#endif
+ nsresult rv = Create(type, attributes);
+ if (rv != NS_ERROR_FILE_ALREADY_EXISTS)
+ return rv;
+ }
+
+ for (int indx = 1; indx < 10000; indx++)
+ {
+ // start with "Picture-1.jpg" after "Picture.jpg" exists
+#ifdef XP_WIN
+ SetLeafName(rootName +
+ NS_ConvertASCIItoUTF16(nsPrintfCString("-%d", indx)) +
+ suffix);
+#else
+ SetNativeLeafName(rootName + nsPrintfCString("-%d", indx) + suffix);
+#endif
+ rv = Create(type, attributes);
+ if (NS_SUCCEEDED(rv) || rv != NS_ERROR_FILE_ALREADY_EXISTS)
+ return rv;
+ }
+
+ // The disk is full, sort of
+ return NS_ERROR_FILE_TOO_BIG;
+}
+
+#if defined(XP_WIN) || defined(XP_OS2)
+static const PRUnichar kPathSeparatorChar = '\\';
+#elif defined(XP_UNIX)
+static const PRUnichar kPathSeparatorChar = '/';
+#else
+#error Need to define file path separator for your platform
+#endif
+
+static int32_t SplitPath(PRUnichar *path, PRUnichar **nodeArray, int32_t arrayLen)
+{
+ if (*path == 0)
+ return 0;
+
+ PRUnichar **nodePtr = nodeArray;
+ if (*path == kPathSeparatorChar)
+ path++;
+ *nodePtr++ = path;
+
+ for (PRUnichar *cp = path; *cp != 0; cp++) {
+ if (*cp == kPathSeparatorChar) {
+ *cp++ = 0;
+ if (*cp == 0)
+ break;
+ if (nodePtr - nodeArray >= arrayLen)
+ return -1;
+ *nodePtr++ = cp;
+ }
+ }
+ return nodePtr - nodeArray;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetRelativeDescriptor(nsIFile *fromFile, nsACString& _retval)
+{
+ NS_ENSURE_ARG_POINTER(fromFile);
+ const int32_t kMaxNodesInPath = 32;
+
+ //
+ // _retval will be UTF-8 encoded
+ //
+
+ nsresult rv;
+ _retval.Truncate(0);
+
+ nsAutoString thisPath, fromPath;
+ PRUnichar *thisNodes[kMaxNodesInPath], *fromNodes[kMaxNodesInPath];
+ int32_t thisNodeCnt, fromNodeCnt, nodeIndex;
+
+ rv = GetPath(thisPath);
+ if (NS_FAILED(rv))
+ return rv;
+ rv = fromFile->GetPath(fromPath);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // get raw pointer to mutable string buffer
+ PRUnichar *thisPathPtr; thisPath.BeginWriting(thisPathPtr);
+ PRUnichar *fromPathPtr; fromPath.BeginWriting(fromPathPtr);
+
+ thisNodeCnt = SplitPath(thisPathPtr, thisNodes, kMaxNodesInPath);
+ fromNodeCnt = SplitPath(fromPathPtr, fromNodes, kMaxNodesInPath);
+ if (thisNodeCnt < 0 || fromNodeCnt < 0)
+ return NS_ERROR_FAILURE;
+
+ for (nodeIndex = 0; nodeIndex < thisNodeCnt && nodeIndex < fromNodeCnt; ++nodeIndex) {
+#ifdef XP_WIN
+ if (_wcsicmp(thisNodes[nodeIndex], fromNodes[nodeIndex]))
+ break;
+#else
+ if (nsCRT::strcmp(thisNodes[nodeIndex], fromNodes[nodeIndex]))
+ break;
+#endif
+ }
+
+ int32_t branchIndex = nodeIndex;
+ for (nodeIndex = branchIndex; nodeIndex < fromNodeCnt; nodeIndex++)
+ _retval.AppendLiteral("../");
+ for (nodeIndex = branchIndex; nodeIndex < thisNodeCnt; nodeIndex++) {
+ NS_ConvertUTF16toUTF8 nodeStr(thisNodes[nodeIndex]);
+ _retval.Append(nodeStr);
+ if (nodeIndex + 1 < thisNodeCnt)
+ _retval.Append('/');
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetRelativeDescriptor(nsIFile *fromFile, const nsACString& relativeDesc)
+{
+ NS_NAMED_LITERAL_CSTRING(kParentDirStr, "../");
+
+ nsCOMPtr<nsIFile> targetFile;
+ nsresult rv = fromFile->Clone(getter_AddRefs(targetFile));
+ if (NS_FAILED(rv))
+ return rv;
+
+ //
+ // relativeDesc is UTF-8 encoded
+ //
+
+ nsCString::const_iterator strBegin, strEnd;
+ relativeDesc.BeginReading(strBegin);
+ relativeDesc.EndReading(strEnd);
+
+ nsCString::const_iterator nodeBegin(strBegin), nodeEnd(strEnd);
+ nsCString::const_iterator pos(strBegin);
+
+ nsCOMPtr<nsIFile> parentDir;
+ while (FindInReadable(kParentDirStr, nodeBegin, nodeEnd)) {
+ rv = targetFile->GetParent(getter_AddRefs(parentDir));
+ if (NS_FAILED(rv))
+ return rv;
+ if (!parentDir)
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ targetFile = parentDir;
+
+ nodeBegin = nodeEnd;
+ pos = nodeEnd;
+ nodeEnd = strEnd;
+ }
+
+ nodeBegin = nodeEnd = pos;
+ while (nodeEnd != strEnd) {
+ FindCharInReadable('/', nodeEnd, strEnd);
+ targetFile->Append(NS_ConvertUTF8toUTF16(Substring(nodeBegin, nodeEnd)));
+ if (nodeEnd != strEnd) // If there's more left in the string, inc over the '/' nodeEnd is on.
+ ++nodeEnd;
+ nodeBegin = nodeEnd;
+ }
+
+ return InitWithFile(targetFile);
+}
diff --git a/xpcom/io/nsLocalFileOS2.cpp b/xpcom/io/nsLocalFileOS2.cpp
new file mode 100644
index 000000000..2fbccccb4
--- /dev/null
+++ b/xpcom/io/nsLocalFileOS2.cpp
@@ -0,0 +1,2572 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+// N.B. the ns* & pr* headers below will #include all
+// of the standard library headers this file requires
+
+#include "nsCOMPtr.h"
+#include "nsMemory.h"
+
+#include "nsLocalFile.h"
+#include "nsNativeCharsetUtils.h"
+
+#include "nsISimpleEnumerator.h"
+#include "nsIDirectoryEnumerator.h"
+#include "nsIComponentManager.h"
+#include "prio.h"
+
+#include "nsReadableUtils.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIMutableArray.h"
+#include "nsTraceRefcntImpl.h"
+
+using namespace mozilla;
+
+#define CHECK_mWorkingPath() \
+ PR_BEGIN_MACRO \
+ if (mWorkingPath.IsEmpty()) \
+ return NS_ERROR_NOT_INITIALIZED; \
+ PR_END_MACRO
+
+//-----------------------------------------------------------------------------
+// static helper functions
+//-----------------------------------------------------------------------------
+
+static nsresult ConvertOS2Error(int err)
+{
+ nsresult rv;
+
+ switch (err)
+ {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ case ERROR_INVALID_DRIVE:
+ rv = NS_ERROR_FILE_NOT_FOUND;
+ break;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_NOT_SAME_DEVICE:
+ rv = NS_ERROR_FILE_ACCESS_DENIED;
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_INVALID_BLOCK:
+ case ERROR_INVALID_HANDLE:
+ case ERROR_ARENA_TRASHED:
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ break;
+ case ERROR_CURRENT_DIRECTORY:
+ rv = NS_ERROR_FILE_DIR_NOT_EMPTY;
+ break;
+ case ERROR_WRITE_PROTECT:
+ rv = NS_ERROR_FILE_READ_ONLY;
+ break;
+ case ERROR_HANDLE_DISK_FULL:
+ rv = NS_ERROR_FILE_TOO_BIG;
+ break;
+ case ERROR_FILE_EXISTS:
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_CANNOT_MAKE:
+ rv = NS_ERROR_FILE_ALREADY_EXISTS;
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ rv = NS_ERROR_FILE_NAME_TOO_LONG;
+ break;
+ case 0:
+ rv = NS_OK;
+ default:
+ rv = NS_ERROR_FAILURE;
+ }
+ return rv;
+}
+
+static void
+myLL_L2II(int64_t result, int32_t *hi, int32_t *lo )
+{
+ int64_t a64, b64; // probably could have been done with
+ // only one int64_t, but these are macros,
+ // and I am a wimp.
+
+ // shift the hi word to the low word, then push it into a long.
+ a64 = result >> 32;
+ *hi = int32_t(a64);
+
+ // shift the low word to the hi word first, then shift it back.
+ b64 = result << 32;
+ a64 = b64 >> 32;
+ *lo = int32_t(a64);
+}
+
+// Locates the first occurrence of charToSearchFor in the stringToSearch
+static unsigned char*
+_mbschr(const unsigned char* stringToSearch, int charToSearchFor)
+{
+ const unsigned char* p = stringToSearch;
+
+ do {
+ if (*p == charToSearchFor)
+ break;
+ p = (const unsigned char*)WinNextChar(0,0,0,(char*)p);
+ } while (*p);
+
+ // Result is p or NULL
+ return *p ? (unsigned char*)p : NULL;
+}
+
+// Locates the first occurrence of subString in the stringToSearch
+static unsigned char*
+_mbsstr(const unsigned char* stringToSearch, const unsigned char* subString)
+{
+ const unsigned char* pStr = stringToSearch;
+ const unsigned char* pSub = subString;
+
+ do {
+ while (*pStr && *pStr != *pSub)
+ pStr = (const unsigned char*)WinNextChar(0,0,0,(char*)pStr);
+
+ if (!*pStr)
+ break;
+
+ const unsigned char* pNxt = pStr;
+ do {
+ pSub = (const unsigned char*)WinNextChar(0,0,0,(char*)pSub);
+ pNxt = (const unsigned char*)WinNextChar(0,0,0,(char*)pNxt);
+ } while (*pSub && *pSub == *pNxt);
+
+ if (!*pSub)
+ break;
+
+ pSub = subString;
+ pStr = (const unsigned char*)WinNextChar(0,0,0,(char*)pStr);
+
+ } while (*pStr);
+
+ // if we got to the end of pSub, we've found it
+ return *pSub ? NULL : (unsigned char*)pStr;
+}
+
+// Locates last occurence of charToSearchFor in the stringToSearch
+NS_EXPORT unsigned char*
+_mbsrchr(const unsigned char* stringToSearch, int charToSearchFor)
+{
+ int length = strlen((const char*)stringToSearch);
+ const unsigned char* p = stringToSearch+length;
+
+ do {
+ if (*p == charToSearchFor)
+ break;
+ p = (const unsigned char*)WinPrevChar(0,0,0,(char*)stringToSearch,(char*)p);
+ } while (p > stringToSearch);
+
+ // Result is p or NULL
+ return (*p == charToSearchFor) ? (unsigned char*)p : NULL;
+}
+
+// Implement equivalent of Win32 CreateDirectoryA
+static nsresult
+CreateDirectoryA(PSZ path, PEAOP2 ppEABuf)
+{
+ APIRET rc;
+ nsresult rv;
+ FILESTATUS3 pathInfo;
+
+ rc = DosCreateDir(path, ppEABuf);
+ if (rc != NO_ERROR)
+ {
+ rv = ConvertOS2Error(rc);
+
+ // Check if directory already exists and if so,
+ // reflect that in the return value
+ rc = DosQueryPathInfo(path, FIL_STANDARD,
+ &pathInfo, sizeof(pathInfo));
+ if (rc == NO_ERROR)
+ rv = ERROR_FILE_EXISTS;
+ }
+ else
+ rv = rc;
+
+ return rv;
+}
+
+static int isleadbyte(int c)
+{
+ static BOOL bDBCSFilled = FALSE;
+ // According to the Control Program Guide&Ref, 12 bytes is sufficient
+ static BYTE DBCSInfo[12] = { 0 };
+ BYTE *curr;
+ BOOL retval = FALSE;
+
+ if(!bDBCSFilled)
+ {
+ COUNTRYCODE ctrycodeInfo = { 0 };
+ APIRET rc = NO_ERROR;
+ ctrycodeInfo.country = 0; // Current Country
+ ctrycodeInfo.codepage = 0; // Current Codepage
+
+ rc = DosQueryDBCSEnv(sizeof(DBCSInfo), &ctrycodeInfo, DBCSInfo);
+ // we had an error, do something?
+ if (rc != NO_ERROR)
+ return FALSE;
+
+ bDBCSFilled=TRUE;
+ }
+
+ // DBCSInfo returned by DosQueryDBCSEnv is terminated
+ // with two '0' bytes in a row
+ curr = DBCSInfo;
+ while(*curr != 0 && *(curr+1) != 0)
+ {
+ if(c >= *curr && c <= *(curr+1))
+ {
+ retval=TRUE;
+ break;
+ }
+ curr+=2;
+ }
+
+ return retval;
+}
+
+//-----------------------------------------------------------------------------
+// nsDirEnumerator
+//-----------------------------------------------------------------------------
+
+class nsDirEnumerator : public nsISimpleEnumerator,
+ public nsIDirectoryEnumerator
+{
+ public:
+
+ NS_DECL_ISUPPORTS
+
+ nsDirEnumerator() : mDir(nullptr)
+ {
+ }
+
+ nsresult Init(nsIFile* parent)
+ {
+ nsAutoCString filepath;
+ parent->GetNativeTarget(filepath);
+
+ if (filepath.IsEmpty())
+ {
+ parent->GetNativePath(filepath);
+ }
+
+ if (filepath.IsEmpty())
+ {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ mDir = PR_OpenDir(filepath.get());
+ if (mDir == nullptr) // not a directory?
+ return NS_ERROR_FAILURE;
+
+ mParent = parent;
+ return NS_OK;
+ }
+
+ NS_IMETHOD HasMoreElements(bool *result)
+ {
+ nsresult rv;
+ if (mNext == nullptr && mDir)
+ {
+ PRDirEntry* entry = PR_ReadDir(mDir, PR_SKIP_BOTH);
+ if (entry == nullptr)
+ {
+ // end of dir entries
+
+ PRStatus status = PR_CloseDir(mDir);
+ if (status != PR_SUCCESS)
+ return NS_ERROR_FAILURE;
+ mDir = nullptr;
+
+ *result = false;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIFile> file;
+ rv = mParent->Clone(getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = file->AppendNative(nsDependentCString(entry->name));
+ if (NS_FAILED(rv))
+ return rv;
+
+ // make sure the thing exists. If it does, try the next one.
+ bool exists;
+ rv = file->Exists(&exists);
+ if (NS_FAILED(rv) || !exists)
+ {
+ return HasMoreElements(result);
+ }
+
+ mNext = do_QueryInterface(file);
+ }
+ *result = mNext != nullptr;
+ if (!*result)
+ Close();
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetNext(nsISupports **result)
+ {
+ nsresult rv;
+ bool hasMore;
+ rv = HasMoreElements(&hasMore);
+ if (NS_FAILED(rv)) return rv;
+
+ *result = mNext; // might return nullptr
+ NS_IF_ADDREF(*result);
+
+ mNext = nullptr;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetNextFile(nsIFile **result)
+ {
+ *result = nullptr;
+ bool hasMore = false;
+ nsresult rv = HasMoreElements(&hasMore);
+ if (NS_FAILED(rv) || !hasMore)
+ return rv;
+ *result = mNext;
+ NS_IF_ADDREF(*result);
+ mNext = nullptr;
+ return NS_OK;
+ }
+
+ NS_IMETHOD Close()
+ {
+ if (mDir)
+ {
+ PRStatus status = PR_CloseDir(mDir);
+ NS_ASSERTION(status == PR_SUCCESS, "close failed");
+ if (status != PR_SUCCESS)
+ return NS_ERROR_FAILURE;
+ mDir = nullptr;
+ }
+ return NS_OK;
+ }
+
+ // dtor can be non-virtual since there are no subclasses, but must be
+ // public to use the class on the stack.
+ ~nsDirEnumerator()
+ {
+ Close();
+ }
+
+ protected:
+ PRDir* mDir;
+ nsCOMPtr<nsIFile> mParent;
+ nsCOMPtr<nsIFile> mNext;
+};
+
+NS_IMPL_ISUPPORTS2(nsDirEnumerator, nsISimpleEnumerator, nsIDirectoryEnumerator)
+
+//-----------------------------------------------------------------------------
+// nsDriveEnumerator
+//-----------------------------------------------------------------------------
+
+class nsDriveEnumerator : public nsISimpleEnumerator
+{
+public:
+ nsDriveEnumerator();
+ virtual ~nsDriveEnumerator();
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISIMPLEENUMERATOR
+ nsresult Init();
+
+private:
+ // mDrives is a bitmap representing the available drives
+ // mLetter is incremented each time mDrives is shifted rightward
+ uint32_t mDrives;
+ uint8_t mLetter;
+};
+
+NS_IMPL_ISUPPORTS1(nsDriveEnumerator, nsISimpleEnumerator)
+
+nsDriveEnumerator::nsDriveEnumerator()
+ : mDrives(0), mLetter(0)
+{
+}
+
+nsDriveEnumerator::~nsDriveEnumerator()
+{
+}
+
+nsresult nsDriveEnumerator::Init()
+{
+ ULONG ulCurrent;
+
+ // bits 0-25 in mDrives represent each possible drive, A-Z
+ DosError(FERR_DISABLEHARDERR);
+ APIRET rc = DosQueryCurrentDisk(&ulCurrent, (PULONG)&mDrives);
+ DosError(FERR_ENABLEHARDERR);
+ if (rc)
+ return NS_ERROR_FAILURE;
+
+ mLetter = 'A';
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsDriveEnumerator::HasMoreElements(bool *aHasMore)
+{
+ // no more bits means no more drives
+ *aHasMore = (mDrives != 0);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsDriveEnumerator::GetNext(nsISupports **aNext)
+{
+ if (!mDrives)
+ {
+ *aNext = nullptr;
+ return NS_OK;
+ }
+
+ // if bit 0 is off, advance to the next bit that's on
+ while ((mDrives & 1) == 0)
+ {
+ mDrives >>= 1;
+ mLetter++;
+ }
+
+ // format a drive string, then advance to the next possible drive
+ char drive[4] = "x:\\";
+ drive[0] = mLetter;
+ mDrives >>= 1;
+ mLetter++;
+
+ nsIFile *file;
+ nsresult rv = NS_NewNativeLocalFile(nsDependentCString(drive),
+ false, &file);
+ *aNext = file;
+
+ return rv;
+}
+
+//-----------------------------------------------------------------------------
+// class TypeEaEnumerator - a convenience for accessing
+// a file's .TYPE extended attribute
+//-----------------------------------------------------------------------------
+
+// this struct describes the first entry for an MVMT or MVST EA;
+// .TYPE is supposed to be MVMT but is sometimes malformed as MVST
+
+typedef struct _MVHDR {
+ USHORT usEAType;
+ USHORT usCodePage;
+ USHORT usNumEntries;
+ USHORT usDataType;
+ USHORT usDataLth;
+ char data[1];
+} MVHDR;
+
+typedef MVHDR *PMVHDR;
+
+
+class TypeEaEnumerator
+{
+public:
+ TypeEaEnumerator() : mEaBuf(nullptr) { }
+ ~TypeEaEnumerator() { if (mEaBuf) NS_Free(mEaBuf); }
+
+ nsresult Init(nsLocalFile * aFile);
+ char * GetNext(uint32_t *lth);
+
+private:
+ char * mEaBuf;
+ char * mpCur;
+ PMVHDR mpMvh;
+ USHORT mLth;
+ USHORT mCtr;
+};
+
+
+nsresult TypeEaEnumerator::Init(nsLocalFile * aFile)
+{
+#define EABUFSIZE 512
+
+ // provide a buffer for the results
+ mEaBuf = (char*)NS_Alloc(EABUFSIZE);
+ if (!mEaBuf)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ PFEA2LIST pfea2list = (PFEA2LIST)mEaBuf;
+ pfea2list->cbList = EABUFSIZE;
+
+ // ask for the .TYPE extended attribute
+ nsresult rv = aFile->GetEA(".TYPE", pfea2list);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // point at the data - it starts immediately after the EA's name;
+ // then confirm the EA is MVMT (correct) or MVST (acceptable)
+ mpMvh = (PMVHDR)&(pfea2list->list[0].szName[pfea2list->list[0].cbName+1]);
+ if (mpMvh->usEAType != EAT_MVMT)
+ if (mpMvh->usEAType != EAT_MVST || mpMvh->usDataType != EAT_ASCII)
+ return NS_ERROR_FAILURE;
+
+ // init the variables that tell us where we are in the lsit
+ mLth = 0;
+ mCtr = 0;
+ mpCur = (char*)(mpMvh->usEAType == EAT_MVMT ?
+ &mpMvh->usDataType : &mpMvh->usDataLth);
+
+ return NS_OK;
+}
+
+
+char * TypeEaEnumerator::GetNext(uint32_t *lth)
+{
+ char * result = nullptr;
+
+ // this is a loop so we can skip invalid entries if needed;
+ // normally, it will break out on the first iteration
+ while (mCtr++ < mpMvh->usNumEntries) {
+
+ // advance to the next entry
+ mpCur += mLth;
+
+ // if MVMT, ensure the datatype is OK, then advance
+ // to the length field present in both formats
+ if (mpMvh->usEAType == EAT_MVMT) {
+ if (*((PUSHORT)mpCur) != EAT_ASCII)
+ continue;
+ mpCur += sizeof(USHORT);
+ }
+
+ // get the data's length, point at the data itself, then exit
+ mLth = *lth = *((PUSHORT)mpCur);
+ mpCur += sizeof(USHORT);
+ result = mpCur;
+ break;
+ }
+
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+// nsLocalFile <public>
+//-----------------------------------------------------------------------------
+
+nsLocalFile::nsLocalFile()
+ : mDirty(true)
+{
+}
+
+nsresult
+nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
+{
+ NS_ENSURE_ARG_POINTER(aInstancePtr);
+ NS_ENSURE_NO_AGGREGATION(outer);
+
+ nsLocalFile* inst = new nsLocalFile();
+ if (inst == NULL)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
+ if (NS_FAILED(rv))
+ {
+ delete inst;
+ return rv;
+ }
+ return NS_OK;
+}
+
+
+//-----------------------------------------------------------------------------
+// nsLocalFile::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_THREADSAFE_ISUPPORTS4(nsLocalFile,
+ nsILocalFile,
+ nsIFile,
+ nsILocalFileOS2,
+ nsIHashable)
+
+
+//-----------------------------------------------------------------------------
+// nsLocalFile <private>
+//-----------------------------------------------------------------------------
+
+nsLocalFile::nsLocalFile(const nsLocalFile& other)
+ : mDirty(true)
+ , mWorkingPath(other.mWorkingPath)
+{
+}
+
+
+// Stat the path. After a successful return the path is
+// guaranteed valid and the members of mFileInfo64 can be used.
+nsresult
+nsLocalFile::Stat()
+{
+ // if we aren't dirty then we are already done
+ if (!mDirty)
+ return NS_OK;
+
+ // we can't stat anything that isn't a valid NSPR addressable path
+ if (mWorkingPath.IsEmpty())
+ return NS_ERROR_FILE_INVALID_PATH;
+
+ // hack designed to work around bug 134796 until it is fixed
+ char temp[4];
+ const char *nsprPath = mWorkingPath.get();
+ if (mWorkingPath.Length() == 2 && mWorkingPath.CharAt(1) == ':')
+ {
+ temp[0] = mWorkingPath[0];
+ temp[1] = mWorkingPath[1];
+ temp[2] = '\\';
+ temp[3] = '\0';
+ nsprPath = temp;
+ }
+
+ // see if the working path exists
+ DosError(FERR_DISABLEHARDERR);
+ PRStatus status = PR_GetFileInfo64(nsprPath, &mFileInfo64);
+ DosError(FERR_ENABLEHARDERR);
+ if (status != PR_SUCCESS)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ mDirty = false;
+ return NS_OK;
+}
+
+
+//-----------------------------------------------------------------------------
+// nsLocalFile::nsIFile,nsILocalFile
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsLocalFile::Clone(nsIFile **file)
+{
+ // Just copy-construct ourselves
+ *file = new nsLocalFile(*this);
+ if (!*file)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*file);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::InitWithNativePath(const nsACString &filePath)
+{
+ MakeDirty();
+
+ nsACString::const_iterator begin, end;
+ filePath.BeginReading(begin);
+ filePath.EndReading(end);
+
+ // input string must not be empty
+ if (begin == end)
+ return NS_ERROR_FAILURE;
+
+ char firstChar = *begin;
+ char secondChar = *(++begin);
+
+ // just do a sanity check. if it has any forward slashes, it is not
+ // a Native path. Also, it must have a colon at after the first char.
+ if (FindCharInReadable('/', begin, end))
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ if (secondChar != ':' && (secondChar != '\\' || firstChar != '\\'))
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ mWorkingPath = filePath;
+ // kill any trailing '\' provided it isn't the second char of DBCS
+ int32_t len = mWorkingPath.Length() - 1;
+ if (mWorkingPath[len] == '\\' && !::isleadbyte(mWorkingPath[len - 1]))
+ mWorkingPath.Truncate(len);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::OpenNSPRFileDesc(int32_t flags, int32_t mode, PRFileDesc **_retval)
+{
+ nsresult rv = Stat();
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
+ return rv;
+
+ *_retval = PR_Open(mWorkingPath.get(), flags, mode);
+ if (*_retval)
+ return NS_OK;
+
+ if (flags & DELETE_ON_CLOSE) {
+ PR_Delete(mWorkingPath.get());
+ }
+
+ return NS_ErrorAccordingToNSPR();
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::OpenANSIFileDesc(const char *mode, FILE * *_retval)
+{
+ nsresult rv = Stat();
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
+ return rv;
+
+ *_retval = fopen(mWorkingPath.get(), mode);
+ if (*_retval)
+ return NS_OK;
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Create(uint32_t type, uint32_t attributes)
+{
+ if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
+ return NS_ERROR_FILE_UNKNOWN_TYPE;
+
+ nsresult rv = Stat();
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
+ return rv;
+
+ // create directories to target
+ //
+ // A given local file can be either one of these forms:
+ //
+ // - normal: X:\some\path\on\this\drive
+ // ^--- start here
+ //
+ // - UNC path: \\machine\volume\some\path\on\this\drive
+ // ^--- start here
+ //
+ // Skip the first 'X:\' for the first form, and skip the first full
+ // '\\machine\volume\' segment for the second form.
+
+ unsigned char* path = (unsigned char*) mWorkingPath.BeginWriting();
+
+ if (path[0] == '\\' && path[1] == '\\')
+ {
+ // dealing with a UNC path here; skip past '\\machine\'
+ path = _mbschr(path + 2, '\\');
+ if (!path)
+ return NS_ERROR_FILE_INVALID_PATH;
+ ++path;
+ }
+
+ // search for first slash after the drive (or volume) name
+ unsigned char* slash = _mbschr(path, '\\');
+
+ if (slash)
+ {
+ // skip the first '\\'
+ ++slash;
+ slash = _mbschr(slash, '\\');
+
+ while (slash)
+ {
+ *slash = '\0';
+
+ rv = CreateDirectoryA(const_cast<char*>(mWorkingPath.get()), NULL);
+ if (rv) {
+ rv = ConvertOS2Error(rv);
+ if (rv != NS_ERROR_FILE_ALREADY_EXISTS)
+ return rv;
+ }
+ *slash = '\\';
+ ++slash;
+ slash = _mbschr(slash, '\\');
+ }
+ }
+
+ if (type == NORMAL_FILE_TYPE)
+ {
+ PRFileDesc* file = PR_Open(mWorkingPath.get(), PR_RDONLY | PR_CREATE_FILE | PR_APPEND | PR_EXCL, attributes);
+ if (!file)
+ return NS_ERROR_FILE_ALREADY_EXISTS;
+
+ PR_Close(file);
+ return NS_OK;
+ }
+
+ if (type == DIRECTORY_TYPE)
+ {
+ rv = CreateDirectoryA(const_cast<char*>(mWorkingPath.get()), NULL);
+ if (rv)
+ return ConvertOS2Error(rv);
+ else
+ return NS_OK;
+ }
+
+ return NS_ERROR_FILE_UNKNOWN_TYPE;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::AppendNative(const nsACString &node)
+{
+ // append this path, multiple components are not permitted
+ return AppendNativeInternal(PromiseFlatCString(node), false);
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendRelativeNativePath(const nsACString &node)
+{
+ // append this path, multiple components are permitted
+ return AppendNativeInternal(PromiseFlatCString(node), true);
+}
+
+nsresult
+nsLocalFile::AppendNativeInternal(const nsAFlatCString &node, bool multipleComponents)
+{
+ if (node.IsEmpty())
+ return NS_OK;
+
+ // check the relative path for validity
+ const unsigned char * nodePath = (const unsigned char *) node.get();
+ if (*nodePath == '\\' // can't start with an '\'
+ || _mbschr(nodePath, '/') // can't contain /
+ || node.Equals("..")) // can't be ..
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ if (multipleComponents)
+ {
+ // can't contain .. as a path component. Ensure that the valid components
+ // "foo..foo", "..foo", and "foo.." are not falsely detected, but the invalid
+ // paths "..\", "foo\..", "foo\..\foo", "..\foo", etc are.
+ const unsigned char * doubleDot = _mbsstr(nodePath, (const unsigned char *)"\\..");
+ while (doubleDot)
+ {
+ doubleDot += 3;
+ if (*doubleDot == '\0' || *doubleDot == '\\')
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ doubleDot = _mbsstr(doubleDot, (unsigned char *)"\\..");
+ }
+ // catches the remaining cases of prefixes (i.e. '..\')
+ // note: this is a substitute for Win32's _mbsncmp(nodePath, "..\\", 3)
+ if (*nodePath == '.') {
+ nodePath = (const unsigned char*)WinNextChar(0,0,0,(char*)nodePath);
+ if (*nodePath == '.' &&
+ *WinNextChar(0,0,0,(char*)nodePath) == '\\')
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+ }
+ else if (_mbschr(nodePath, '\\')) // single components can't contain '\'
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ MakeDirty();
+
+ mWorkingPath.Append(NS_LITERAL_CSTRING("\\") + node);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Normalize()
+{
+ // XXX See bug 187957 comment 18 for possible problems with this implementation.
+
+ if (mWorkingPath.IsEmpty())
+ return NS_OK;
+
+ // work in unicode for ease
+ nsAutoString path;
+ NS_CopyNativeToUnicode(mWorkingPath, path);
+
+ // find the index of the root backslash for the path. Everything before
+ // this is considered fully normalized and cannot be ascended beyond
+ // using ".." For a local drive this is the first slash (e.g. "c:\").
+ // For a UNC path it is the slash following the share name
+ // (e.g. "\\server\share\").
+ int32_t rootIdx = 2; // default to local drive
+ if (path.First() == '\\') // if a share then calculate the rootIdx
+ {
+ rootIdx = path.FindChar('\\', 2); // skip \\ in front of the server
+ if (rootIdx == kNotFound)
+ return NS_OK; // already normalized
+ rootIdx = path.FindChar('\\', rootIdx+1);
+ if (rootIdx == kNotFound)
+ return NS_OK; // already normalized
+ }
+ else if (path.CharAt(rootIdx) != '\\')
+ {
+ // The path has been specified relative to the current working directory
+ // for that drive. To normalize it, the current working directory for
+ // that drive needs to be inserted before the supplied relative path
+ // which will provide an absolute path (and the rootIdx will still be 2).
+ char drv[4] = "A:.";
+ char cwd[CCHMAXPATH];
+
+ drv[0] = mWorkingPath.First();
+ if (DosQueryPathInfo(drv, FIL_QUERYFULLNAME, cwd, sizeof(cwd)))
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ nsAutoString currentDir;
+ NS_CopyNativeToUnicode(nsDependentCString(cwd), currentDir);
+
+ if (currentDir.Last() == '\\')
+ path.Replace(0, 2, currentDir);
+ else
+ path.Replace(0, 2, currentDir + NS_LITERAL_STRING("\\"));
+ }
+ NS_POSTCONDITION(0 < rootIdx && rootIdx < (int32_t)path.Length(), "rootIdx is invalid");
+ NS_POSTCONDITION(path.CharAt(rootIdx) == '\\', "rootIdx is invalid");
+
+ // if there is nothing following the root path then it is already normalized
+ if (rootIdx + 1 == (int32_t)path.Length())
+ return NS_OK;
+
+ // assign the root
+ nsAutoString normal;
+ const PRUnichar * pathBuffer = path.get(); // simplify access to the buffer
+ normal.SetCapacity(path.Length()); // it won't ever grow longer
+ normal.Assign(pathBuffer, rootIdx);
+
+ // Normalize the path components. The actions taken are:
+ //
+ // "\\" condense to single backslash
+ // "." remove from path
+ // ".." up a directory
+ // "..." remove from path (any number of dots > 2)
+ //
+ // The last form is something that Windows 95 and 98 supported and
+ // is a shortcut for changing up multiple directories. Windows XP
+ // and ilk ignore it in a path, as is done here.
+ int32_t len, begin, end = rootIdx;
+ while (end < (int32_t)path.Length())
+ {
+ // find the current segment (text between the backslashes) to
+ // be examined, this will set the following variables:
+ // begin == index of first char in segment
+ // end == index 1 char after last char in segment
+ // len == length of segment
+ begin = end + 1;
+ end = path.FindChar('\\', begin);
+ if (end == kNotFound)
+ end = path.Length();
+ len = end - begin;
+
+ // ignore double backslashes
+ if (len == 0)
+ continue;
+
+ // len != 0, and interesting paths always begin with a dot
+ if (pathBuffer[begin] == '.')
+ {
+ // ignore single dots
+ if (len == 1)
+ continue;
+
+ // handle multiple dots
+ if (len >= 2 && pathBuffer[begin+1] == '.')
+ {
+ // back up a path component on double dot
+ if (len == 2)
+ {
+ int32_t prev = normal.RFindChar('\\');
+ if (prev >= rootIdx)
+ normal.Truncate(prev);
+ continue;
+ }
+
+ // length is > 2 and the first two characters are dots.
+ // if the rest of the string is dots, then ignore it.
+ int idx = len - 1;
+ for (; idx >= 2; --idx)
+ {
+ if (pathBuffer[begin+idx] != '.')
+ break;
+ }
+
+ // this is true if the loop above didn't break
+ // and all characters in this segment are dots.
+ if (idx < 2)
+ continue;
+ }
+ }
+
+ // add the current component to the path, including the preceding backslash
+ normal.Append(pathBuffer + begin - 1, len + 1);
+ }
+
+ // kill trailing dots and spaces.
+ int32_t filePathLen = normal.Length() - 1;
+ while(filePathLen > 0 && (normal[filePathLen] == ' ' || normal[filePathLen] == '.'))
+ {
+ normal.Truncate(filePathLen--);
+ }
+
+ NS_CopyUnicodeToNative(normal, mWorkingPath);
+ MakeDirty();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
+{
+ aLeafName.Truncate();
+
+ const char* temp = mWorkingPath.get();
+ if(temp == nullptr)
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ const char* leaf = (const char*) _mbsrchr((const unsigned char*) temp, '\\');
+
+ // if the working path is just a node without any lashes.
+ if (leaf == nullptr)
+ leaf = temp;
+ else
+ leaf++;
+
+ aLeafName.Assign(leaf);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
+{
+ MakeDirty();
+
+ const unsigned char* temp = (const unsigned char*) mWorkingPath.get();
+ if(temp == nullptr)
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ // cannot use nsCString::RFindChar() due to 0x5c problem
+ int32_t offset = (int32_t) (_mbsrchr(temp, '\\') - temp);
+ if (offset)
+ {
+ mWorkingPath.Truncate(offset+1);
+ }
+ mWorkingPath.Append(aLeafName);
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetNativePath(nsACString &_retval)
+{
+ _retval = mWorkingPath;
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+// get any single extended attribute for the current file or directory
+
+nsresult
+nsLocalFile::GetEA(const char *eaName, PFEA2LIST pfea2list)
+{
+ // ensure we have an out-buffer whose length is specified
+ if (!pfea2list || !pfea2list->cbList)
+ return NS_ERROR_FAILURE;
+
+ // the gea2list's name field is only 1 byte long;
+ // this expands its allocation to hold a 33 byte name
+ union {
+ GEA2LIST gea2list;
+ char dummy[sizeof(GEA2LIST)+32];
+ };
+ EAOP2 eaop2;
+
+ eaop2.fpFEA2List = pfea2list;
+ eaop2.fpGEA2List = &gea2list;
+
+ // fill in the request structure
+ dummy[sizeof(GEA2LIST)+31] = 0;
+ gea2list.list[0].oNextEntryOffset = 0;
+ strcpy(gea2list.list[0].szName, eaName);
+ gea2list.list[0].cbName = strlen(gea2list.list[0].szName);
+ gea2list.cbList = sizeof(GEA2LIST) + gea2list.list[0].cbName;
+
+ // see what we get - this will succeed even if the EA doesn't exist
+ APIRET rc = DosQueryPathInfo(mWorkingPath.get(), FIL_QUERYEASFROMLIST,
+ &eaop2, sizeof(eaop2));
+ if (rc)
+ return ConvertOS2Error(rc);
+
+ // if the data length is zero, requested EA doesn't exist
+ if (!pfea2list->list[0].cbValue)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+
+// return an array of file types or null if there are none
+
+NS_IMETHODIMP
+nsLocalFile::GetFileTypes(nsIArray **_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = 0;
+
+ // fetch the .TYPE ea & prepare for enumeration
+ TypeEaEnumerator typeEnum;
+ nsresult rv = typeEnum.Init(this);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // create an array that's scriptable
+ nsCOMPtr<nsIMutableArray> mutArray =
+ do_CreateInstance(NS_ARRAY_CONTRACTID);
+ NS_ENSURE_STATE(mutArray);
+
+ int32_t cnt;
+ uint32_t lth;
+ char * ptr;
+
+ // get each file type, convert to a CString, then add to the array
+ for (cnt=0, ptr=typeEnum.GetNext(&lth); ptr; ptr=typeEnum.GetNext(&lth)) {
+ nsCOMPtr<nsISupportsCString> typeString(
+ do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString temp;
+ temp.Assign(ptr, lth);
+ typeString->SetData(temp);
+ mutArray->AppendElement(typeString, false);
+ cnt++;
+ }
+ }
+
+ // if the array has any contents, addref & return it
+ if (cnt) {
+ *_retval = mutArray;
+ NS_ADDREF(*_retval);
+ rv = NS_OK;
+ }
+ else
+ rv = NS_ERROR_FAILURE;
+
+ return rv;
+}
+
+
+// see if the file is of the requested type
+
+NS_IMETHODIMP
+nsLocalFile::IsFileType(const nsACString& fileType, bool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = false;
+
+ // fetch the .TYPE ea & prepare for enumeration
+ TypeEaEnumerator typeEnum;
+ nsresult rv = typeEnum.Init(this);
+ if (NS_FAILED(rv))
+ return rv;
+
+ uint32_t lth;
+ char * ptr;
+
+ // compare each type to the request; if there's a match, exit
+ for (ptr = typeEnum.GetNext(&lth); ptr; ptr = typeEnum.GetNext(&lth))
+ if (fileType.EqualsASCII(ptr, lth)) {
+ *_retval = true;
+ break;
+ }
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+// this struct combines an FEA2LIST, an FEA2, plus additional fields
+// needed to write a .TYPE EA in the correct EAT_MVMT format
+#pragma pack(1)
+ typedef struct _TYPEEA {
+ struct {
+ ULONG ulcbList;
+ ULONG uloNextEntryOffset;
+ BYTE bfEA;
+ BYTE bcbName;
+ USHORT uscbValue;
+ char chszName[sizeof(".TYPE")];
+ } hdr;
+ struct {
+ USHORT usEAType;
+ USHORT usCodePage;
+ USHORT usNumEntries;
+ USHORT usDataType;
+ USHORT usDataLth;
+ } info;
+ char data[1];
+ } TYPEEA;
+
+ typedef struct _TYPEEA2 {
+ USHORT usDataType;
+ USHORT usDataLth;
+ } TYPEEA2;
+#pragma pack()
+
+// writes one or more .TYPE extended attributes taken
+// from a comma-delimited string
+NS_IMETHODIMP
+nsLocalFile::SetFileTypes(const nsACString& fileTypes)
+{
+ if (fileTypes.IsEmpty())
+ return NS_ERROR_FAILURE;
+
+ uint32_t cnt = CountCharInReadable(fileTypes, ',');
+ uint32_t lth = fileTypes.Length() - cnt + (cnt * sizeof(TYPEEA2));
+ uint32_t size = sizeof(TYPEEA) + lth;
+
+ char *pBuf = (char*)NS_Alloc(size);
+ if (!pBuf)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ TYPEEA *pEA = (TYPEEA *)pBuf;
+
+ // the buffer has an extra byte due to TYPEEA.data[1]
+ pEA->hdr.ulcbList = size - 1;
+ pEA->hdr.uloNextEntryOffset = 0;
+ pEA->hdr.bfEA = 0;
+ pEA->hdr.bcbName = sizeof(".TYPE") - 1;
+ pEA->hdr.uscbValue = sizeof(pEA->info) + lth;
+ strcpy(pEA->hdr.chszName, ".TYPE");
+
+ pEA->info.usEAType = EAT_MVMT;
+ pEA->info.usCodePage = 0;
+ pEA->info.usNumEntries = ++cnt;
+
+ nsACString::const_iterator begin, end, delim;
+ fileTypes.BeginReading(begin);
+ fileTypes.EndReading(end);
+ delim = begin;
+
+ // fill in type & length, copy the current type name (which
+ // is not zero-terminated), then advance the ptr so the next
+ // iteration can reuse the trailing members of the structure
+ do {
+ FindCharInReadable( ',', delim, end);
+ lth = delim.get() - begin.get();
+ pEA->info.usDataType = EAT_ASCII;
+ pEA->info.usDataLth = lth;
+ memcpy(pEA->data, begin.get(), lth);
+ pEA = (TYPEEA *)((char*)pEA + lth + sizeof(TYPEEA2));
+ begin = ++delim;
+ } while (--cnt);
+
+ // write the EA, then free the buffer
+ EAOP2 eaop2;
+ eaop2.fpGEA2List = 0;
+ eaop2.fpFEA2List = (PFEA2LIST)pBuf;
+
+ int rc = DosSetPathInfo(mWorkingPath.get(), FIL_QUERYEASIZE,
+ &eaop2, sizeof(eaop2), 0);
+ NS_Free(pBuf);
+
+ if (rc)
+ return ConvertOS2Error(rc);
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+// this struct combines an FEA2LIST, an FEA2, plus additional fields
+// needed to write a .SUBJECT EA in the correct EAT_ASCII format;
+#pragma pack(1)
+ typedef struct _SUBJEA {
+ struct {
+ ULONG ulcbList;
+ ULONG uloNextEntryOffset;
+ BYTE bfEA;
+ BYTE bcbName;
+ USHORT uscbValue;
+ char chszName[sizeof(".SUBJECT")];
+ } hdr;
+ struct {
+ USHORT usEAType;
+ USHORT usDataLth;
+ } info;
+ char data[1];
+ } SUBJEA;
+#pragma pack()
+
+// saves the file's source URI in its .SUBJECT extended attribute
+NS_IMETHODIMP
+nsLocalFile::SetFileSource(const nsACString& aURI)
+{
+ if (aURI.IsEmpty())
+ return NS_ERROR_FAILURE;
+
+ // this includes an extra character for the spec's trailing null
+ uint32_t lth = sizeof(SUBJEA) + aURI.Length();
+ char * pBuf = (char*)NS_Alloc(lth);
+ if (!pBuf)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ SUBJEA *pEA = (SUBJEA *)pBuf;
+
+ // the trailing null doesn't get saved, so don't include it in the size
+ pEA->hdr.ulcbList = lth - 1;
+ pEA->hdr.uloNextEntryOffset = 0;
+ pEA->hdr.bfEA = 0;
+ pEA->hdr.bcbName = sizeof(".SUBJECT") - 1;
+ pEA->hdr.uscbValue = sizeof(pEA->info) + aURI.Length();
+ strcpy(pEA->hdr.chszName, ".SUBJECT");
+ pEA->info.usEAType = EAT_ASCII;
+ pEA->info.usDataLth = aURI.Length();
+ strcpy(pEA->data, PromiseFlatCString(aURI).get());
+
+ // write the EA, then free the buffer
+ EAOP2 eaop2;
+ eaop2.fpGEA2List = 0;
+ eaop2.fpFEA2List = (PFEA2LIST)pEA;
+
+ int rc = DosSetPathInfo(mWorkingPath.get(), FIL_QUERYEASIZE,
+ &eaop2, sizeof(eaop2), 0);
+ NS_Free(pBuf);
+
+ if (rc)
+ return ConvertOS2Error(rc);
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+nsresult
+nsLocalFile::CopySingleFile(nsIFile *sourceFile, nsIFile *destParent,
+ const nsACString &newName, bool move)
+{
+ nsresult rv;
+ nsAutoCString filePath;
+
+ nsAutoCString destPath;
+ destParent->GetNativeTarget(destPath);
+
+ destPath.Append("\\");
+
+ if (newName.IsEmpty())
+ {
+ nsAutoCString aFileName;
+ sourceFile->GetNativeLeafName(aFileName);
+ destPath.Append(aFileName);
+ }
+ else
+ {
+ destPath.Append(newName);
+ }
+
+ rv = sourceFile->GetNativePath(filePath);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ APIRET rc = NO_ERROR;
+
+ if (move)
+ rc = DosMove(filePath.get(), (PSZ)const_cast<char*>(destPath.get()));
+
+ if (!move || rc == ERROR_NOT_SAME_DEVICE || rc == ERROR_ACCESS_DENIED)
+ {
+ // will get an error if the destination and source files aren't on
+ // the same drive. "MoveFile()" on Windows will go ahead and move
+ // the file without error, so we need to do the same IBM-AKR
+
+ do {
+ rc = DosCopy(filePath.get(), (PSZ)const_cast<char*>(destPath.get()), DCPY_EXISTING);
+ if (rc == ERROR_TOO_MANY_OPEN_FILES) {
+ ULONG CurMaxFH = 0;
+ LONG ReqCount = 20;
+ APIRET rc2;
+ rc2 = DosSetRelMaxFH(&ReqCount, &CurMaxFH);
+ if (rc2 != NO_ERROR)
+ break;
+ }
+ } while (rc == ERROR_TOO_MANY_OPEN_FILES);
+
+ // WSOD2 HACK - NETWORK_ACCESS_DENIED
+ if (rc == 65)
+ {
+ CHAR achProgram[CCHMAXPATH]; // buffer for program name, parameters
+ RESULTCODES rescResults; // buffer for results of dosexecpgm
+
+ strcpy(achProgram, "CMD.EXE /C ");
+ strcat(achProgram, """COPY ");
+ strcat(achProgram, filePath.get());
+ strcat(achProgram, " ");
+ strcat(achProgram, (PSZ)const_cast<char*>(destPath.get()));
+ strcat(achProgram, """");
+ achProgram[strlen(achProgram) + 1] = '\0';
+ achProgram[7] = '\0';
+ DosExecPgm(NULL, 0,
+ EXEC_SYNC, achProgram, (PSZ)NULL,
+ &rescResults, achProgram);
+ rc = 0; // Assume it worked
+
+ } // rc == 65
+
+ // moving the file is supposed to act like a rename, so delete the
+ // original file if we got this far without error
+ if(move && (rc == NO_ERROR))
+ DosDelete( filePath.get());
+
+ } // !move or ERROR
+
+ if (rc)
+ rv = ConvertOS2Error(rc);
+
+ return rv;
+}
+
+
+nsresult
+nsLocalFile::CopyMove(nsIFile *aParentDir, const nsACString &newName, bool move)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ nsCOMPtr<nsIFile> newParentDir = aParentDir;
+
+ nsresult rv = Stat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!newParentDir)
+ {
+ // no parent was specified. We must rename.
+
+ if (newName.IsEmpty())
+ return NS_ERROR_INVALID_ARG;
+
+ rv = GetParent(getter_AddRefs(newParentDir));
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ if (!newParentDir)
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+
+ // make sure it exists and is a directory. Create it if not there.
+ bool exists;
+ newParentDir->Exists(&exists);
+ if (!exists)
+ {
+ rv = newParentDir->Create(DIRECTORY_TYPE, 0644); // TODO, what permissions should we use
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ else
+ {
+ bool isDir;
+ newParentDir->IsDirectory(&isDir);
+ if (!isDir)
+ {
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+ }
+ }
+
+ // Try different ways to move/copy files/directories
+ bool done = false;
+ bool isDir;
+ IsDirectory(&isDir);
+
+ // Try to move the file or directory, or try to copy a single file
+ if (move || !isDir)
+ {
+ // when moving things, first try to just MoveFile it,
+ // even if it is a directory
+ rv = CopySingleFile(this, newParentDir, newName, move);
+ done = NS_SUCCEEDED(rv);
+ // If we are moving a directory and that fails, fallback on directory
+ // enumeration. See bug 231300 for details.
+ if (!done && !(move && isDir))
+ return rv;
+ }
+
+ // Not able to copy or move directly, so enumerate it
+ if (!done)
+ {
+ // create a new target destination in the new parentDir;
+ nsCOMPtr<nsIFile> target;
+ rv = newParentDir->Clone(getter_AddRefs(target));
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoCString allocatedNewName;
+ if (newName.IsEmpty())
+ {
+ GetNativeLeafName(allocatedNewName);
+ }
+ else
+ {
+ allocatedNewName = newName;
+ }
+
+ rv = target->AppendNative(allocatedNewName);
+ if (NS_FAILED(rv))
+ return rv;
+
+ allocatedNewName.Truncate();
+
+ // check if the destination directory already exists
+ target->Exists(&exists);
+ if (!exists)
+ {
+ // if the destination directory cannot be created, return an error
+ rv = target->Create(DIRECTORY_TYPE, 0644); // TODO, what permissions should we use
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ else
+ {
+ // check if the destination directory is writable and empty
+ bool isWritable;
+
+ target->IsWritable(&isWritable);
+ if (!isWritable)
+ return NS_ERROR_FILE_ACCESS_DENIED;
+
+ nsCOMPtr<nsISimpleEnumerator> targetIterator;
+ rv = target->GetDirectoryEntries(getter_AddRefs(targetIterator));
+ if (NS_FAILED(rv))
+ return rv;
+
+ bool more;
+ targetIterator->HasMoreElements(&more);
+ // return error if target directory is not empty
+ if (more)
+ return NS_ERROR_FILE_DIR_NOT_EMPTY;
+ }
+
+ nsDirEnumerator dirEnum;
+
+ rv = dirEnum.Init(this);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("dirEnum initialization failed");
+ return rv;
+ }
+
+ bool more;
+ while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
+ {
+ nsCOMPtr<nsISupports> item;
+ nsCOMPtr<nsIFile> file;
+ dirEnum.GetNext(getter_AddRefs(item));
+ file = do_QueryInterface(item);
+ if (file)
+ {
+ if (move)
+ {
+ rv = file->MoveToNative(target, EmptyCString());
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ else
+ {
+ rv = file->CopyToNative(target, EmptyCString());
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ }
+ }
+ // we've finished moving all the children of this directory
+ // in the new directory. so now delete the directory
+ // note, we don't need to do a recursive delete.
+ // MoveTo() is recursive. At this point,
+ // we've already moved the children of the current folder
+ // to the new location. nothing should be left in the folder.
+ if (move)
+ {
+ rv = Remove(false); // recursive
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ }
+
+
+ // If we moved, we want to adjust this.
+ if (move)
+ {
+ MakeDirty();
+
+ nsAutoCString newParentPath;
+ newParentDir->GetNativePath(newParentPath);
+
+ if (newParentPath.IsEmpty())
+ return NS_ERROR_FAILURE;
+
+ if (newName.IsEmpty())
+ {
+ nsAutoCString aFileName;
+ GetNativeLeafName(aFileName);
+
+ InitWithNativePath(newParentPath);
+ AppendNative(aFileName);
+ }
+ else
+ {
+ InitWithNativePath(newParentPath);
+ AppendNative(newName);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString &newName)
+{
+ return CopyMove(newParentDir, newName, false);
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString &newName)
+{
+ return CopyMove(newParentDir, newName, false);
+}
+
+NS_IMETHODIMP
+nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString &newName)
+{
+ return CopyMove(newParentDir, newName, true);
+}
+
+NS_IMETHODIMP
+nsLocalFile::Load(PRLibrary * *_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ bool isFile;
+ nsresult rv = IsFile(&isFile);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!isFile)
+ return NS_ERROR_FILE_IS_DIRECTORY;
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ nsTraceRefcntImpl::SetActivityIsLegal(false);
+#endif
+
+ *_retval = PR_LoadLibrary(mWorkingPath.get());
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ nsTraceRefcntImpl::SetActivityIsLegal(true);
+#endif
+
+ if (*_retval)
+ return NS_OK;
+
+ return NS_ERROR_NULL_POINTER;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Remove(bool recursive)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ bool isDir = false;
+
+ nsresult rv = IsDirectory(&isDir);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (isDir)
+ {
+ if (recursive)
+ {
+ nsDirEnumerator dirEnum;
+
+ rv = dirEnum.Init(this);
+ if (NS_FAILED(rv))
+ return rv;
+
+ bool more;
+ while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
+ {
+ nsCOMPtr<nsISupports> item;
+ dirEnum.GetNext(getter_AddRefs(item));
+ nsCOMPtr<nsIFile> file = do_QueryInterface(item);
+ if (file)
+ file->Remove(recursive);
+ }
+ }
+ rv = rmdir(mWorkingPath.get());
+ }
+ else
+ {
+ rv = remove(mWorkingPath.get());
+ }
+
+ // fixup error code if necessary...
+ if (rv == (nsresult)-1)
+ rv = NSRESULT_FOR_ERRNO();
+
+ MakeDirty();
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetLastModifiedTime(PRTime *aLastModifiedTime)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_ENSURE_ARG(aLastModifiedTime);
+
+ *aLastModifiedTime = 0;
+ nsresult rv = Stat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ // microseconds -> milliseconds
+ *aLastModifiedTime = mFileInfo64.modifyTime / PR_USEC_PER_MSEC;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetLastModifiedTimeOfLink(PRTime *aLastModifiedTime)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::SetLastModifiedTime(PRTime aLastModifiedTime)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ return nsLocalFile::SetModDate(aLastModifiedTime);
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::SetLastModifiedTimeOfLink(PRTime aLastModifiedTime)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+nsLocalFile::SetModDate(PRTime aLastModifiedTime)
+{
+ nsresult rv = Stat();
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRExplodedTime pret;
+ FILESTATUS3 pathInfo;
+
+ // Level 1 info
+ rv = DosQueryPathInfo(mWorkingPath.get(), FIL_STANDARD,
+ &pathInfo, sizeof(pathInfo));
+
+ if (NS_FAILED(rv))
+ {
+ rv = ConvertOS2Error(rv);
+ return rv;
+ }
+
+ // PR_ExplodeTime expects usecs...
+ PR_ExplodeTime(aLastModifiedTime * PR_USEC_PER_MSEC, PR_LocalTimeParameters, &pret);
+
+ // fdateLastWrite.year is based off of 1980
+ if (pret.tm_year >= 1980)
+ pathInfo.fdateLastWrite.year = pret.tm_year-1980;
+ else
+ pathInfo.fdateLastWrite.year = pret.tm_year;
+
+ // Convert start offset -- OS/2: Jan=1; NSPR: Jan=0
+ pathInfo.fdateLastWrite.month = pret.tm_month + 1;
+ pathInfo.fdateLastWrite.day = pret.tm_mday;
+ pathInfo.ftimeLastWrite.hours = pret.tm_hour;
+ pathInfo.ftimeLastWrite.minutes = pret.tm_min;
+ pathInfo.ftimeLastWrite.twosecs = pret.tm_sec / 2;
+
+ rv = DosSetPathInfo(mWorkingPath.get(), FIL_STANDARD,
+ &pathInfo, sizeof(pathInfo), 0UL);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ MakeDirty();
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPermissions(uint32_t *aPermissions)
+{
+ NS_ENSURE_ARG(aPermissions);
+
+ nsresult rv = Stat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ bool isWritable, isExecutable;
+ IsWritable(&isWritable);
+ IsExecutable(&isExecutable);
+
+ *aPermissions = PR_IRUSR|PR_IRGRP|PR_IROTH; // all read
+ if (isWritable)
+ *aPermissions |= PR_IWUSR|PR_IWGRP|PR_IWOTH; // all write
+ if (isExecutable)
+ *aPermissions |= PR_IXUSR|PR_IXGRP|PR_IXOTH; // all execute
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPermissionsOfLink(uint32_t *aPermissionsOfLink)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// the only Unix-style permission OS/2 knows is whether a file is
+// writable; as a matter of policy, a browser shouldn't be able
+// to change any of the other DOS-style attributes; to enforce
+// this, we use DosSetPathInfo() rather than chmod()
+NS_IMETHODIMP
+nsLocalFile::SetPermissions(uint32_t aPermissions)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ nsresult rv = Stat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ APIRET rc;
+ FILESTATUS3 pathInfo;
+
+ // Level 1 info
+ rc = DosQueryPathInfo(mWorkingPath.get(), FIL_STANDARD,
+ &pathInfo, sizeof(pathInfo));
+
+ if (rc != NO_ERROR)
+ return ConvertOS2Error(rc);
+
+ ULONG attr = 0;
+ if (!(aPermissions & (PR_IWUSR|PR_IWGRP|PR_IWOTH)))
+ attr = FILE_READONLY;
+
+ if (attr == (pathInfo.attrFile & FILE_READONLY))
+ return NS_OK;
+
+ pathInfo.attrFile ^= FILE_READONLY;
+
+ rc = DosSetPathInfo(mWorkingPath.get(), FIL_STANDARD,
+ &pathInfo, sizeof(pathInfo), 0UL);
+
+ if (rc != NO_ERROR)
+ return ConvertOS2Error(rc);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPermissionsOfLink(uint32_t aPermissions)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSize(int64_t *aFileSize)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_ENSURE_ARG(aFileSize);
+ *aFileSize = 0;
+
+ nsresult rv = Stat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aFileSize = mFileInfo64.size;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSizeOfLink(int64_t *aFileSize)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::SetFileSize(int64_t aFileSize)
+{
+ nsresult rv = Stat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ APIRET rc;
+ HFILE hFile;
+ ULONG actionTaken;
+
+ rc = DosOpen(mWorkingPath.get(),
+ &hFile,
+ &actionTaken,
+ 0,
+ FILE_NORMAL,
+ OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
+ OPEN_SHARE_DENYREADWRITE | OPEN_ACCESS_READWRITE,
+ NULL);
+
+ if (rc != NO_ERROR)
+ {
+ MakeDirty();
+ return NS_ERROR_FAILURE;
+ }
+
+ // Seek to new, desired end of file
+ int32_t hi, lo;
+ myLL_L2II(aFileSize, &hi, &lo );
+
+ rc = DosSetFileSize(hFile, lo);
+ DosClose(hFile);
+ MakeDirty();
+
+ if (rc != NO_ERROR)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetDiskSpaceAvailable(int64_t *aDiskSpaceAvailable)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_ENSURE_ARG(aDiskSpaceAvailable);
+ *aDiskSpaceAvailable = 0;
+
+ nsresult rv = Stat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ ULONG ulDriveNo = toupper(mWorkingPath.CharAt(0)) + 1 - 'A';
+ FSALLOCATE fsAllocate;
+ APIRET rc = DosQueryFSInfo(ulDriveNo,
+ FSIL_ALLOC,
+ &fsAllocate,
+ sizeof(fsAllocate));
+
+ if (rc != NO_ERROR)
+ return NS_ERROR_FAILURE;
+
+ *aDiskSpaceAvailable = fsAllocate.cUnitAvail;
+ *aDiskSpaceAvailable *= fsAllocate.cSectorUnit;
+ *aDiskSpaceAvailable *= fsAllocate.cbSector;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetParent(nsIFile * *aParent)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_ENSURE_ARG_POINTER(aParent);
+
+ nsAutoCString parentPath(mWorkingPath);
+
+ // cannot use nsCString::RFindChar() due to 0x5c problem
+ int32_t offset = (int32_t) (_mbsrchr((const unsigned char *) parentPath.get(), '\\')
+ - (const unsigned char *) parentPath.get());
+ // adding this offset check that was removed in bug 241708 fixes mail
+ // directories that aren't relative to/underneath the profile dir.
+ // e.g., on a different drive. Before you remove them, please make
+ // sure local mail directories that aren't underneath the profile dir work.
+ if (offset < 0)
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ if (offset == 1 && parentPath[0] == '\\') {
+ aParent = nullptr;
+ return NS_OK;
+ }
+ if (offset > 0)
+ parentPath.Truncate(offset);
+ else
+ parentPath.AssignLiteral("\\\\.");
+
+ nsCOMPtr<nsIFile> localFile;
+ nsresult rv = NS_NewNativeLocalFile(parentPath, false, getter_AddRefs(localFile));
+
+ if(NS_SUCCEEDED(rv) && localFile)
+ {
+ return CallQueryInterface(localFile, aParent);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Exists(bool *_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_ENSURE_ARG(_retval);
+ *_retval = false;
+
+ MakeDirty();
+ nsresult rv = Stat();
+
+ *_retval = NS_SUCCEEDED(rv);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsWritable(bool *_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_ENSURE_ARG(_retval);
+ *_retval = false;
+
+ nsresult rv = Stat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ APIRET rc;
+ FILESTATUS3 pathInfo;
+
+ // Level 1 info
+ rc = DosQueryPathInfo(mWorkingPath.get(), FIL_STANDARD,
+ &pathInfo, sizeof(pathInfo));
+
+ if (rc != NO_ERROR)
+ {
+ rc = ConvertOS2Error(rc);
+ return rc;
+ }
+
+ // on OS/2, unlike Windows, directories on writable media
+ // can not be assigned a readonly attribute
+ *_retval = !((pathInfo.attrFile & FILE_READONLY) != 0);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsReadable(bool *_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_ENSURE_ARG(_retval);
+ *_retval = false;
+
+ nsresult rv = Stat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ *_retval = true;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::IsExecutable(bool *_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_ENSURE_ARG(_retval);
+ *_retval = false;
+
+ nsresult rv = Stat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ // no need to bother if this isn't a file
+ bool isFile;
+ rv = IsFile(&isFile);
+ if (NS_FAILED(rv) || !isFile)
+ return rv;
+
+ nsAutoCString path;
+ GetNativeTarget(path);
+
+ // get the filename, including the leading backslash
+ const char* leaf = (const char*) _mbsrchr((const unsigned char*)path.get(), '\\');
+ if (!leaf)
+ return NS_OK;
+
+ // eliminate trailing dots & spaces in a DBCS-aware manner
+ // XXX shouldn't this have been done when the path was set?
+
+ char* pathEnd = WinPrevChar(0, 0, 0, leaf, strchr(leaf, 0));
+ while (pathEnd > leaf)
+ {
+ if (*pathEnd != ' ' && *pathEnd != '.')
+ break;
+ *pathEnd = 0;
+ pathEnd = WinPrevChar(0, 0, 0, leaf, pathEnd);
+ }
+
+ if (pathEnd == leaf)
+ return NS_OK;
+
+ // get the extension, including the dot
+ char* ext = (char*) _mbsrchr((const unsigned char*)leaf, '.');
+ if (!ext)
+ return NS_OK;
+
+ if (stricmp(ext, ".exe") == 0 ||
+ stricmp(ext, ".cmd") == 0 ||
+ stricmp(ext, ".com") == 0 ||
+ stricmp(ext, ".bat") == 0)
+ *_retval = true;
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::IsDirectory(bool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = false;
+
+ nsresult rv = Stat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ *_retval = (mFileInfo64.type == PR_FILE_DIRECTORY);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsFile(bool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = false;
+
+ nsresult rv = Stat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ *_retval = (mFileInfo64.type == PR_FILE_FILE);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsHidden(bool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = false;
+
+ nsresult rv = Stat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ APIRET rc;
+ FILESTATUS3 pathInfo;
+
+ // Level 1 info
+ rc = DosQueryPathInfo(mWorkingPath.get(), FIL_STANDARD,
+ &pathInfo, sizeof(pathInfo));
+
+ if (rc != NO_ERROR)
+ {
+ rc = ConvertOS2Error(rc);
+ return rc;
+ }
+
+ *_retval = ((pathInfo.attrFile & FILE_HIDDEN) != 0);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::IsSymlink(bool *_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ // No Symlinks on OS/2
+ *_retval = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsSpecial(bool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+
+ // when implemented, IsSpecial will be used for WPS objects
+ *_retval = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Equals(nsIFile *inFile, bool *_retval)
+{
+ NS_ENSURE_ARG(inFile);
+ NS_ENSURE_ARG(_retval);
+
+ nsAutoCString inFilePath;
+ inFile->GetNativePath(inFilePath);
+
+ *_retval = inFilePath.Equals(mWorkingPath);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Contains(nsIFile *inFile, bool recur, bool *_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ *_retval = false;
+
+ nsAutoCString myFilePath;
+ if ( NS_FAILED(GetNativeTarget(myFilePath)))
+ GetNativePath(myFilePath);
+
+ int32_t myFilePathLen = myFilePath.Length();
+
+ nsAutoCString inFilePath;
+ if ( NS_FAILED(inFile->GetNativeTarget(inFilePath)))
+ inFile->GetNativePath(inFilePath);
+
+ if ( strnicmp( myFilePath.get(), inFilePath.get(), myFilePathLen) == 0)
+ {
+ // now make sure that the |inFile|'s path has a trailing
+ // separator.
+
+ if (inFilePath[myFilePathLen] == '\\')
+ {
+ *_retval = true;
+ }
+
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeTarget(nsACString &_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ _retval = mWorkingPath;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFollowLinks(bool *aFollowLinks)
+{
+ NS_ENSURE_ARG(aFollowLinks);
+ *aFollowLinks = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetFollowLinks(bool aFollowLinks)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator * *entries)
+{
+ NS_ENSURE_ARG(entries);
+ nsresult rv;
+ *entries = nullptr;
+
+ if (mWorkingPath.EqualsLiteral("\\\\.")) {
+ nsDriveEnumerator *drives = new nsDriveEnumerator;
+ if (!drives)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(drives);
+ rv = drives->Init();
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(drives);
+ return rv;
+ }
+ *entries = drives;
+ return NS_OK;
+ }
+
+ bool isDir;
+ rv = IsDirectory(&isDir);
+ if (NS_FAILED(rv))
+ return rv;
+ if (!isDir)
+ return NS_ERROR_FILE_NOT_DIRECTORY;
+
+ nsDirEnumerator* dirEnum = new nsDirEnumerator();
+ if (dirEnum == nullptr)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(dirEnum);
+ rv = dirEnum->Init(this);
+ if (NS_FAILED(rv))
+ {
+ NS_RELEASE(dirEnum);
+ return rv;
+ }
+
+ *entries = dirEnum;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
+{
+ return GetNativePath(aPersistentDescriptor);
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
+{
+ return InitWithNativePath(aPersistentDescriptor);
+}
+
+#ifndef OPEN_DEFAULT
+#define OPEN_DEFAULT 0
+#define OPEN_CONTENTS 1
+#endif
+
+NS_IMETHODIMP
+nsLocalFile::Reveal()
+{
+ bool isDirectory = false;
+ nsAutoCString path;
+
+ IsDirectory(&isDirectory);
+ if (isDirectory)
+ {
+ GetNativePath(path);
+ }
+ else
+ {
+ nsCOMPtr<nsIFile> parent;
+ GetParent(getter_AddRefs(parent));
+ if (parent)
+ parent->GetNativePath(path);
+ }
+
+ HOBJECT hobject = WinQueryObject(path.get());
+ WinSetFocus(HWND_DESKTOP, HWND_DESKTOP);
+ WinOpenObject(hobject, OPEN_DEFAULT, TRUE);
+
+ // we don't care if it succeeded or failed.
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::Launch()
+{
+ HOBJECT hobject = WinQueryObject(mWorkingPath.get());
+ WinSetFocus(HWND_DESKTOP, HWND_DESKTOP);
+ WinOpenObject(hobject, OPEN_DEFAULT, TRUE);
+
+ // we don't care if it succeeded or failed.
+ return NS_OK;
+}
+
+nsresult
+NS_NewNativeLocalFile(const nsACString &path, bool followLinks, nsIFile* *result)
+{
+ nsLocalFile* file = new nsLocalFile();
+ if (file == nullptr)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(file);
+
+ if (!path.IsEmpty()) {
+ nsresult rv = file->InitWithNativePath(path);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(file);
+ return rv;
+ }
+ }
+
+ *result = file;
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// UCS2 interface
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsLocalFile::InitWithPath(const nsAString &filePath)
+{
+ if (filePath.IsEmpty())
+ return InitWithNativePath(EmptyCString());
+
+ nsAutoCString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(filePath, tmp);
+ if (NS_SUCCEEDED(rv))
+ return InitWithNativePath(tmp);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Append(const nsAString &node)
+{
+ if (node.IsEmpty())
+ return NS_OK;
+
+ nsAutoCString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(node, tmp);
+ if (NS_SUCCEEDED(rv))
+ return AppendNative(tmp);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendRelativePath(const nsAString &node)
+{
+ if (node.IsEmpty())
+ return NS_OK;
+
+ nsAutoCString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(node, tmp);
+ if (NS_SUCCEEDED(rv))
+ return AppendRelativeNativePath(tmp);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetLeafName(nsAString &aLeafName)
+{
+ nsAutoCString tmp;
+ nsresult rv = GetNativeLeafName(tmp);
+ if (NS_SUCCEEDED(rv))
+ rv = NS_CopyNativeToUnicode(tmp, aLeafName);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetLeafName(const nsAString &aLeafName)
+{
+ if (aLeafName.IsEmpty())
+ return SetNativeLeafName(EmptyCString());
+
+ nsAutoCString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(aLeafName, tmp);
+ if (NS_SUCCEEDED(rv))
+ return SetNativeLeafName(tmp);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPath(nsAString &_retval)
+{
+ return NS_CopyNativeToUnicode(mWorkingPath, _retval);
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
+{
+ if (newName.IsEmpty())
+ return CopyToNative(newParentDir, EmptyCString());
+
+ nsAutoCString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
+ if (NS_SUCCEEDED(rv))
+ return CopyToNative(newParentDir, tmp);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
+{
+ if (newName.IsEmpty())
+ return CopyToFollowingLinksNative(newParentDir, EmptyCString());
+
+ nsAutoCString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
+ if (NS_SUCCEEDED(rv))
+ return CopyToFollowingLinksNative(newParentDir, tmp);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
+{
+ if (newName.IsEmpty())
+ return MoveToNative(newParentDir, EmptyCString());
+
+ nsAutoCString tmp;
+ nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
+ if (NS_SUCCEEDED(rv))
+ return MoveToNative(newParentDir, tmp);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetTarget(nsAString &_retval)
+{
+ nsAutoCString tmp;
+ nsresult rv = GetNativeTarget(tmp);
+ if (NS_SUCCEEDED(rv))
+ rv = NS_CopyNativeToUnicode(tmp, _retval);
+
+ return rv;
+}
+
+// nsIHashable
+
+NS_IMETHODIMP
+nsLocalFile::Equals(nsIHashable* aOther, bool *aResult)
+{
+ nsCOMPtr<nsIFile> otherfile(do_QueryInterface(aOther));
+ if (!otherfile) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ return Equals(otherfile, aResult);
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetHashCode(uint32_t *aResult)
+{
+ *aResult = HashString(mWorkingPath);
+ return NS_OK;
+}
+
+nsresult
+NS_NewLocalFile(const nsAString &path, bool followLinks, nsIFile* *result)
+{
+ nsAutoCString buf;
+ nsresult rv = NS_CopyUnicodeToNative(path, buf);
+ if (NS_FAILED(rv)) {
+ *result = nullptr;
+ return rv;
+ }
+ return NS_NewNativeLocalFile(buf, followLinks, result);
+}
+
+//-----------------------------------------------------------------------------
+// nsLocalFile <static members>
+//-----------------------------------------------------------------------------
+
+void
+nsLocalFile::GlobalInit()
+{
+}
+
+void
+nsLocalFile::GlobalShutdown()
+{
+}
+
+//-----------------------------------------------------------------------------
+
diff --git a/xpcom/io/nsLocalFileOS2.h b/xpcom/io/nsLocalFileOS2.h
new file mode 100644
index 000000000..d23fb8d9a
--- /dev/null
+++ b/xpcom/io/nsLocalFileOS2.h
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * This Original Code has been modified by IBM Corporation. Modifications made by IBM
+ * described herein are Copyright (c) International Business Machines Corporation, 2000.
+ * Modifications to Mozilla code or documentation identified per MPL Section 3.3
+ *
+ * Date Modified by Description of modification
+ * 05/26/2000 IBM Corp. Make more like Windows.
+ */
+
+#ifndef _nsLocalFileOS2_H_
+#define _nsLocalFileOS2_H_
+
+#include "nscore.h"
+#include "nsError.h"
+#include "nsString.h"
+#include "nsCRT.h"
+#include "nsIFile.h"
+#include "nsIFactory.h"
+#include "nsILocalFileOS2.h"
+#include "nsIHashable.h"
+#include "nsIClassInfoImpl.h"
+
+#define INCL_DOS
+#define INCL_DOSERRORS
+#define INCL_WINCOUNTRY
+#define INCL_WINWORKPLACE
+
+#include <os2.h>
+
+class TypeEaEnumerator;
+
+class nsLocalFile : public nsILocalFileOS2,
+ public nsIHashable
+{
+public:
+ NS_DEFINE_STATIC_CID_ACCESSOR(NS_LOCAL_FILE_CID)
+
+ nsLocalFile();
+
+ static nsresult nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr);
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsIFile interface
+ NS_DECL_NSIFILE
+
+ // nsILocalFile interface
+ NS_DECL_NSILOCALFILE
+
+ // nsILocalFileOS2 interface
+ NS_DECL_NSILOCALFILEOS2
+
+ // nsIHashable interface
+ NS_DECL_NSIHASHABLE
+
+public:
+ static void GlobalInit();
+ static void GlobalShutdown();
+
+private:
+ nsLocalFile(const nsLocalFile& other);
+ ~nsLocalFile() {}
+
+ // cached information can only be used when this is false
+ bool mDirty;
+
+ // this string will always be in native format!
+ nsCString mWorkingPath;
+
+ PRFileInfo64 mFileInfo64;
+
+ void MakeDirty() { mDirty = true; }
+
+ nsresult Stat();
+
+ nsresult CopyMove(nsIFile *newParentDir, const nsACString &newName, bool move);
+ nsresult CopySingleFile(nsIFile *source, nsIFile* dest, const nsACString &newName, bool move);
+
+ nsresult SetModDate(int64_t aLastModifiedTime);
+ nsresult AppendNativeInternal(const nsAFlatCString &node, bool multipleComponents);
+
+ nsresult GetEA(const char *eaName, PFEA2LIST pfea2list);
+ friend class TypeEaEnumerator;
+};
+
+#endif
diff --git a/xpcom/io/nsLocalFileUnix.cpp b/xpcom/io/nsLocalFileUnix.cpp
new file mode 100644
index 000000000..a38f7942f
--- /dev/null
+++ b/xpcom/io/nsLocalFileUnix.cpp
@@ -0,0 +1,2478 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/**
+ * Implementation of nsIFile for "unixy" systems.
+ */
+
+#include "mozilla/Util.h"
+#include "mozilla/Attributes.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <utime.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <locale.h>
+#if defined(VMS)
+ #include <fabdef.h>
+#endif
+
+#if defined(HAVE_SYS_QUOTA_H) && defined(HAVE_LINUX_QUOTA_H)
+#define USE_LINUX_QUOTACTL
+#include <sys/quota.h>
+#endif
+
+#if (MOZ_PLATFORM_MAEMO == 6)
+#include <QUrl>
+#include <QString>
+#if (MOZ_ENABLE_CONTENTACTION)
+#include <contentaction/contentaction.h>
+#endif
+#endif
+
+#include "xpcom-private.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsCRT.h"
+#include "nsCOMPtr.h"
+#include "nsMemory.h"
+#include "nsIFile.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsLocalFile.h"
+#include "nsIComponentManager.h"
+#include "nsXPIDLString.h"
+#include "prproces.h"
+#include "nsIDirectoryEnumerator.h"
+#include "nsISimpleEnumerator.h"
+#include "private/pprio.h"
+
+#ifdef MOZ_WIDGET_GTK
+#include "nsIGIOService.h"
+#include "nsIGnomeVFSService.h"
+#endif
+
+#ifdef MOZ_WIDGET_COCOA
+#include <Carbon/Carbon.h>
+#include "CocoaFileUtils.h"
+#include "prmem.h"
+#include "plbase64.h"
+
+static nsresult MacErrorMapper(OSErr inErr);
+#endif
+
+#if (MOZ_PLATFORM_MAEMO == 5)
+#include <glib.h>
+#include <hildon-uri.h>
+#include <hildon-mime.h>
+#include <libosso.h>
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#include "nsIMIMEService.h"
+#include <linux/magic.h>
+#endif
+
+#include "nsNativeCharsetUtils.h"
+#include "nsTraceRefcntImpl.h"
+#include "nsHashKeys.h"
+
+using namespace mozilla;
+
+#define ENSURE_STAT_CACHE() \
+ PR_BEGIN_MACRO \
+ if (!FillStatCache()) \
+ return NSRESULT_FOR_ERRNO(); \
+ PR_END_MACRO
+
+#define CHECK_mPath() \
+ PR_BEGIN_MACRO \
+ if (mPath.IsEmpty()) \
+ return NS_ERROR_NOT_INITIALIZED; \
+ PR_END_MACRO
+
+/* directory enumerator */
+class
+nsDirEnumeratorUnix MOZ_FINAL : public nsISimpleEnumerator,
+ public nsIDirectoryEnumerator
+{
+ public:
+ nsDirEnumeratorUnix();
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator interface
+ NS_DECL_NSISIMPLEENUMERATOR
+
+ // nsIDirectoryEnumerator interface
+ NS_DECL_NSIDIRECTORYENUMERATOR
+
+ NS_IMETHOD Init(nsLocalFile *parent, bool ignored);
+
+ private:
+ ~nsDirEnumeratorUnix();
+
+ protected:
+ NS_IMETHOD GetNextEntry();
+
+ DIR *mDir;
+ struct dirent *mEntry;
+ nsCString mParentPath;
+};
+
+nsDirEnumeratorUnix::nsDirEnumeratorUnix() :
+ mDir(nullptr),
+ mEntry(nullptr)
+{
+}
+
+nsDirEnumeratorUnix::~nsDirEnumeratorUnix()
+{
+ Close();
+}
+
+NS_IMPL_ISUPPORTS2(nsDirEnumeratorUnix, nsISimpleEnumerator, nsIDirectoryEnumerator)
+
+NS_IMETHODIMP
+nsDirEnumeratorUnix::Init(nsLocalFile *parent, bool resolveSymlinks /*ignored*/)
+{
+ nsAutoCString dirPath;
+ if (NS_FAILED(parent->GetNativePath(dirPath)) ||
+ dirPath.IsEmpty()) {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+
+ if (NS_FAILED(parent->GetNativePath(mParentPath)))
+ return NS_ERROR_FAILURE;
+
+ mDir = opendir(dirPath.get());
+ if (!mDir)
+ return NSRESULT_FOR_ERRNO();
+ return GetNextEntry();
+}
+
+NS_IMETHODIMP
+nsDirEnumeratorUnix::HasMoreElements(bool *result)
+{
+ *result = mDir && mEntry;
+ if (!*result)
+ Close();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDirEnumeratorUnix::GetNext(nsISupports **_retval)
+{
+ nsCOMPtr<nsIFile> file;
+ nsresult rv = GetNextFile(getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return rv;
+ NS_IF_ADDREF(*_retval = file);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDirEnumeratorUnix::GetNextEntry()
+{
+ do {
+ errno = 0;
+ mEntry = readdir(mDir);
+
+ // end of dir or error
+ if (!mEntry)
+ return NSRESULT_FOR_ERRNO();
+
+ // keep going past "." and ".."
+ } while (mEntry->d_name[0] == '.' &&
+ (mEntry->d_name[1] == '\0' || // .\0
+ (mEntry->d_name[1] == '.' &&
+ mEntry->d_name[2] == '\0'))); // ..\0
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDirEnumeratorUnix::GetNextFile(nsIFile **_retval)
+{
+ nsresult rv;
+ if (!mDir || !mEntry) {
+ *_retval = nullptr;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIFile> file = new nsLocalFile();
+ if (!file)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (NS_FAILED(rv = file->InitWithNativePath(mParentPath)) ||
+ NS_FAILED(rv = file->AppendNative(nsDependentCString(mEntry->d_name))))
+ return rv;
+
+ *_retval = file;
+ NS_ADDREF(*_retval);
+ return GetNextEntry();
+}
+
+NS_IMETHODIMP
+nsDirEnumeratorUnix::Close()
+{
+ if (mDir) {
+ closedir(mDir);
+ mDir = nullptr;
+ }
+ return NS_OK;
+}
+
+nsLocalFile::nsLocalFile()
+{
+}
+
+nsLocalFile::nsLocalFile(const nsLocalFile& other)
+ : mPath(other.mPath)
+{
+}
+
+#ifdef MOZ_WIDGET_COCOA
+NS_IMPL_THREADSAFE_ISUPPORTS4(nsLocalFile,
+ nsILocalFileMac,
+ nsILocalFile,
+ nsIFile,
+ nsIHashable)
+#else
+NS_IMPL_THREADSAFE_ISUPPORTS3(nsLocalFile,
+ nsILocalFile,
+ nsIFile,
+ nsIHashable)
+#endif
+
+nsresult
+nsLocalFile::nsLocalFileConstructor(nsISupports *outer,
+ const nsIID &aIID,
+ void **aInstancePtr)
+{
+ NS_ENSURE_ARG_POINTER(aInstancePtr);
+ NS_ENSURE_NO_AGGREGATION(outer);
+
+ *aInstancePtr = nullptr;
+
+ nsCOMPtr<nsIFile> inst = new nsLocalFile();
+ if (!inst)
+ return NS_ERROR_OUT_OF_MEMORY;
+ return inst->QueryInterface(aIID, aInstancePtr);
+}
+
+bool
+nsLocalFile::FillStatCache() {
+ if (STAT(mPath.get(), &mCachedStat) == -1) {
+ // try lstat it may be a symlink
+ if (LSTAT(mPath.get(), &mCachedStat) == -1) {
+ return false;
+ }
+ }
+ return true;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Clone(nsIFile **file)
+{
+ // Just copy-construct ourselves
+ *file = new nsLocalFile(*this);
+ if (!*file)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*file);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::InitWithNativePath(const nsACString &filePath)
+{
+ if (filePath.Equals("~") || Substring(filePath, 0, 2).EqualsLiteral("~/")) {
+ nsCOMPtr<nsIFile> homeDir;
+ nsAutoCString homePath;
+ if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_HOME_DIR,
+ getter_AddRefs(homeDir))) ||
+ NS_FAILED(homeDir->GetNativePath(homePath))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mPath = homePath;
+ if (filePath.Length() > 2)
+ mPath.Append(Substring(filePath, 1, filePath.Length() - 1));
+ } else {
+ if (filePath.IsEmpty() || filePath.First() != '/')
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ mPath = filePath;
+ }
+
+ // trim off trailing slashes
+ ssize_t len = mPath.Length();
+ while ((len > 1) && (mPath[len - 1] == '/'))
+ --len;
+ mPath.SetLength(len);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::CreateAllAncestors(uint32_t permissions)
+{
+ // <jband> I promise to play nice
+ char *buffer = mPath.BeginWriting(),
+ *slashp = buffer;
+
+#ifdef DEBUG_NSIFILE
+ fprintf(stderr, "nsIFile: before: %s\n", buffer);
+#endif
+
+ while ((slashp = strchr(slashp + 1, '/'))) {
+ /*
+ * Sequences of '/' are equivalent to a single '/'.
+ */
+ if (slashp[1] == '/')
+ continue;
+
+ /*
+ * If the path has a trailing slash, don't make the last component,
+ * because we'll get EEXIST in Create when we try to build the final
+ * component again, and it's easier to condition the logic here than
+ * there.
+ */
+ if (slashp[1] == '\0')
+ break;
+
+ /* Temporarily NUL-terminate here */
+ *slashp = '\0';
+#ifdef DEBUG_NSIFILE
+ fprintf(stderr, "nsIFile: mkdir(\"%s\")\n", buffer);
+#endif
+ int mkdir_result = mkdir(buffer, permissions);
+ int mkdir_errno = errno;
+ if (mkdir_result == -1) {
+ /*
+ * Always set |errno| to EEXIST if the dir already exists
+ * (we have to do this here since the errno value is not consistent
+ * in all cases - various reasons like different platform,
+ * automounter-controlled dir, etc. can affect it (see bug 125489
+ * for details)).
+ */
+ if (access(buffer, F_OK) == 0) {
+ mkdir_errno = EEXIST;
+ }
+ }
+
+ /* Put the / back before we (maybe) return */
+ *slashp = '/';
+
+ /*
+ * We could get EEXIST for an existing file -- not directory --
+ * with the name of one of our ancestors, but that's OK: we'll get
+ * ENOTDIR when we try to make the next component in the path,
+ * either here on back in Create, and error out appropriately.
+ */
+ if (mkdir_result == -1 && mkdir_errno != EEXIST)
+ return nsresultForErrno(mkdir_errno);
+ }
+
+#ifdef DEBUG_NSIFILE
+ fprintf(stderr, "nsIFile: after: %s\n", buffer);
+#endif
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::OpenNSPRFileDesc(int32_t flags, int32_t mode, PRFileDesc **_retval)
+{
+ *_retval = PR_Open(mPath.get(), flags, mode);
+ if (! *_retval)
+ return NS_ErrorAccordingToNSPR();
+
+ if (flags & DELETE_ON_CLOSE) {
+ PR_Delete(mPath.get());
+ }
+
+#if defined(LINUX) && !defined(ANDROID)
+ if (flags & OS_READAHEAD) {
+ posix_fadvise(PR_FileDesc2NativeHandle(*_retval), 0, 0,
+ POSIX_FADV_SEQUENTIAL);
+ }
+#endif
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval)
+{
+ *_retval = fopen(mPath.get(), mode);
+ if (! *_retval)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+static int
+do_create(const char *path, int flags, mode_t mode, PRFileDesc **_retval)
+{
+ *_retval = PR_Open(path, flags, mode);
+ return *_retval ? 0 : -1;
+}
+
+static int
+do_mkdir(const char *path, int flags, mode_t mode, PRFileDesc **_retval)
+{
+ *_retval = nullptr;
+ return mkdir(path, mode);
+}
+
+nsresult
+nsLocalFile::CreateAndKeepOpen(uint32_t type, int flags,
+ uint32_t permissions, PRFileDesc **_retval)
+{
+ if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
+ return NS_ERROR_FILE_UNKNOWN_TYPE;
+
+ int result;
+ int (*createFunc)(const char *, int, mode_t, PRFileDesc **) =
+ (type == NORMAL_FILE_TYPE) ? do_create : do_mkdir;
+
+ result = createFunc(mPath.get(), flags, permissions, _retval);
+ if (result == -1 && errno == ENOENT) {
+ /*
+ * If we failed because of missing ancestor components, try to create
+ * them and then retry the original creation.
+ *
+ * Ancestor directories get the same permissions as the file we're
+ * creating, with the X bit set for each of (user,group,other) with
+ * an R bit in the original permissions. If you want to do anything
+ * fancy like setgid or sticky bits, do it by hand.
+ */
+ int dirperm = permissions;
+ if (permissions & S_IRUSR)
+ dirperm |= S_IXUSR;
+ if (permissions & S_IRGRP)
+ dirperm |= S_IXGRP;
+ if (permissions & S_IROTH)
+ dirperm |= S_IXOTH;
+
+#ifdef DEBUG_NSIFILE
+ fprintf(stderr, "nsIFile: perm = %o, dirperm = %o\n", permissions,
+ dirperm);
+#endif
+
+ if (NS_FAILED(CreateAllAncestors(dirperm)))
+ return NS_ERROR_FAILURE;
+
+#ifdef DEBUG_NSIFILE
+ fprintf(stderr, "nsIFile: Create(\"%s\") again\n", mPath.get());
+#endif
+ result = createFunc(mPath.get(), flags, permissions, _retval);
+ }
+ return NSRESULT_FOR_RETURN(result);
+}
+
+NS_IMETHODIMP
+nsLocalFile::Create(uint32_t type, uint32_t permissions)
+{
+ PRFileDesc *junk = nullptr;
+ nsresult rv = CreateAndKeepOpen(type,
+ PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE |
+ PR_EXCL,
+ permissions,
+ &junk);
+ if (junk)
+ PR_Close(junk);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendNative(const nsACString &fragment)
+{
+ if (fragment.IsEmpty())
+ return NS_OK;
+
+ // only one component of path can be appended
+ nsACString::const_iterator begin, end;
+ if (FindCharInReadable('/', fragment.BeginReading(begin),
+ fragment.EndReading(end)))
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ return AppendRelativeNativePath(fragment);
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendRelativeNativePath(const nsACString &fragment)
+{
+ if (fragment.IsEmpty())
+ return NS_OK;
+
+ // No leading '/'
+ if (fragment.First() == '/')
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ if (mPath.EqualsLiteral("/"))
+ mPath.Append(fragment);
+ else
+ mPath.Append(NS_LITERAL_CSTRING("/") + fragment);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Normalize()
+{
+ char resolved_path[PATH_MAX] = "";
+ char *resolved_path_ptr = nullptr;
+
+ resolved_path_ptr = realpath(mPath.get(), resolved_path);
+
+ // if there is an error, the return is null.
+ if (!resolved_path_ptr)
+ return NSRESULT_FOR_ERRNO();
+
+ mPath = resolved_path;
+ return NS_OK;
+}
+
+void
+nsLocalFile::LocateNativeLeafName(nsACString::const_iterator &begin,
+ nsACString::const_iterator &end)
+{
+ // XXX perhaps we should cache this??
+
+ mPath.BeginReading(begin);
+ mPath.EndReading(end);
+
+ nsACString::const_iterator it = end;
+ nsACString::const_iterator stop = begin;
+ --stop;
+ while (--it != stop) {
+ if (*it == '/') {
+ begin = ++it;
+ return;
+ }
+ }
+ // else, the entire path is the leaf name (which means this
+ // isn't an absolute path... unexpected??)
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
+{
+ nsACString::const_iterator begin, end;
+ LocateNativeLeafName(begin, end);
+ aLeafName = Substring(begin, end);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
+{
+ nsACString::const_iterator begin, end;
+ LocateNativeLeafName(begin, end);
+ mPath.Replace(begin.get() - mPath.get(), Distance(begin, end), aLeafName);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetNativePath(nsACString &_retval)
+{
+ _retval = mPath;
+ return NS_OK;
+}
+
+nsresult
+nsLocalFile::GetNativeTargetPathName(nsIFile *newParent,
+ const nsACString &newName,
+ nsACString &_retval)
+{
+ nsresult rv;
+ nsCOMPtr<nsIFile> oldParent;
+
+ if (!newParent) {
+ if (NS_FAILED(rv = GetParent(getter_AddRefs(oldParent))))
+ return rv;
+ newParent = oldParent.get();
+ } else {
+ // check to see if our target directory exists
+ bool targetExists;
+ if (NS_FAILED(rv = newParent->Exists(&targetExists)))
+ return rv;
+
+ if (!targetExists) {
+ // XXX create the new directory with some permissions
+ rv = newParent->Create(DIRECTORY_TYPE, 0755);
+ if (NS_FAILED(rv))
+ return rv;
+ } else {
+ // make sure that the target is actually a directory
+ bool targetIsDirectory;
+ if (NS_FAILED(rv = newParent->IsDirectory(&targetIsDirectory)))
+ return rv;
+ if (!targetIsDirectory)
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+ }
+ }
+
+ nsACString::const_iterator nameBegin, nameEnd;
+ if (!newName.IsEmpty()) {
+ newName.BeginReading(nameBegin);
+ newName.EndReading(nameEnd);
+ }
+ else
+ LocateNativeLeafName(nameBegin, nameEnd);
+
+ nsAutoCString dirName;
+ if (NS_FAILED(rv = newParent->GetNativePath(dirName)))
+ return rv;
+
+ _retval = dirName
+ + NS_LITERAL_CSTRING("/")
+ + Substring(nameBegin, nameEnd);
+ return NS_OK;
+}
+
+nsresult
+nsLocalFile::CopyDirectoryTo(nsIFile *newParent)
+{
+ nsresult rv;
+ /*
+ * dirCheck is used for various boolean test results such as from Equals,
+ * Exists, isDir, etc.
+ */
+ bool dirCheck, isSymlink;
+ uint32_t oldPerms;
+
+ if (NS_FAILED(rv = IsDirectory(&dirCheck)))
+ return rv;
+ if (!dirCheck)
+ return CopyToNative(newParent, EmptyCString());
+
+ if (NS_FAILED(rv = Equals(newParent, &dirCheck)))
+ return rv;
+ if (dirCheck) {
+ // can't copy dir to itself
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (NS_FAILED(rv = newParent->Exists(&dirCheck)))
+ return rv;
+ // get the dirs old permissions
+ if (NS_FAILED(rv = GetPermissions(&oldPerms)))
+ return rv;
+ if (!dirCheck) {
+ if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
+ return rv;
+ } else { // dir exists lets try to use leaf
+ nsAutoCString leafName;
+ if (NS_FAILED(rv = GetNativeLeafName(leafName)))
+ return rv;
+ if (NS_FAILED(rv = newParent->AppendNative(leafName)))
+ return rv;
+ if (NS_FAILED(rv = newParent->Exists(&dirCheck)))
+ return rv;
+ if (dirCheck)
+ return NS_ERROR_FILE_ALREADY_EXISTS; // dest exists
+ if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
+ return rv;
+ }
+
+ nsCOMPtr<nsISimpleEnumerator> dirIterator;
+ if (NS_FAILED(rv = GetDirectoryEntries(getter_AddRefs(dirIterator))))
+ return rv;
+
+ bool hasMore = false;
+ while (dirIterator->HasMoreElements(&hasMore), hasMore) {
+ nsCOMPtr<nsIFile> entry;
+ rv = dirIterator->GetNext((nsISupports**)getter_AddRefs(entry));
+ if (NS_FAILED(rv))
+ continue;
+ if (NS_FAILED(rv = entry->IsSymlink(&isSymlink)))
+ return rv;
+ if (NS_FAILED(rv = entry->IsDirectory(&dirCheck)))
+ return rv;
+ if (dirCheck && !isSymlink) {
+ nsCOMPtr<nsIFile> destClone;
+ rv = newParent->Clone(getter_AddRefs(destClone));
+ if (NS_SUCCEEDED(rv)) {
+ if (NS_FAILED(rv = entry->CopyToNative(destClone, EmptyCString()))) {
+#ifdef DEBUG
+ nsresult rv2;
+ nsAutoCString pathName;
+ if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
+ return rv2;
+ printf("Operation not supported: %s\n", pathName.get());
+#endif
+ if (rv == NS_ERROR_OUT_OF_MEMORY)
+ return rv;
+ continue;
+ }
+ }
+ } else {
+ if (NS_FAILED(rv = entry->CopyToNative(newParent, EmptyCString()))) {
+#ifdef DEBUG
+ nsresult rv2;
+ nsAutoCString pathName;
+ if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
+ return rv2;
+ printf("Operation not supported: %s\n", pathName.get());
+#endif
+ if (rv == NS_ERROR_OUT_OF_MEMORY)
+ return rv;
+ continue;
+ }
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToNative(nsIFile *newParent, const nsACString &newName)
+{
+ nsresult rv;
+ // check to make sure that this has been initialized properly
+ CHECK_mPath();
+
+ // we copy the parent here so 'newParent' remains immutable
+ nsCOMPtr <nsIFile> workParent;
+ if (newParent) {
+ if (NS_FAILED(rv = newParent->Clone(getter_AddRefs(workParent))))
+ return rv;
+ } else {
+ if (NS_FAILED(rv = GetParent(getter_AddRefs(workParent))))
+ return rv;
+ }
+
+ // check to see if we are a directory or if we are a file
+ bool isDirectory;
+ if (NS_FAILED(rv = IsDirectory(&isDirectory)))
+ return rv;
+
+ nsAutoCString newPathName;
+ if (isDirectory) {
+ if (!newName.IsEmpty()) {
+ if (NS_FAILED(rv = workParent->AppendNative(newName)))
+ return rv;
+ } else {
+ if (NS_FAILED(rv = GetNativeLeafName(newPathName)))
+ return rv;
+ if (NS_FAILED(rv = workParent->AppendNative(newPathName)))
+ return rv;
+ }
+ if (NS_FAILED(rv = CopyDirectoryTo(workParent)))
+ return rv;
+ } else {
+ rv = GetNativeTargetPathName(workParent, newName, newPathName);
+ if (NS_FAILED(rv))
+ return rv;
+
+#ifdef DEBUG_blizzard
+ printf("nsLocalFile::CopyTo() %s -> %s\n", mPath.get(), newPathName.get());
+#endif
+
+ // actually create the file.
+ nsLocalFile *newFile = new nsLocalFile();
+ if (!newFile)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsCOMPtr<nsIFile> fileRef(newFile); // release on exit
+
+ rv = newFile->InitWithNativePath(newPathName);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // get the old permissions
+ uint32_t myPerms;
+ GetPermissions(&myPerms);
+
+ // Create the new file with the old file's permissions, even if write
+ // permission is missing. We can't create with write permission and
+ // then change back to myPerm on all filesystems (FAT on Linux, e.g.).
+ // But we can write to a read-only file on all Unix filesystems if we
+ // open it successfully for writing.
+
+ PRFileDesc *newFD;
+ rv = newFile->CreateAndKeepOpen(NORMAL_FILE_TYPE,
+ PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE,
+ myPerms,
+ &newFD);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // open the old file, too
+ bool specialFile;
+ if (NS_FAILED(rv = IsSpecial(&specialFile))) {
+ PR_Close(newFD);
+ return rv;
+ }
+ if (specialFile) {
+#ifdef DEBUG
+ printf("Operation not supported: %s\n", mPath.get());
+#endif
+ // make sure to clean up properly
+ PR_Close(newFD);
+ return NS_OK;
+ }
+
+ PRFileDesc *oldFD;
+ rv = OpenNSPRFileDesc(PR_RDONLY, myPerms, &oldFD);
+ if (NS_FAILED(rv)) {
+ // make sure to clean up properly
+ PR_Close(newFD);
+ return rv;
+ }
+
+#ifdef DEBUG_blizzard
+ int32_t totalRead = 0;
+ int32_t totalWritten = 0;
+#endif
+ char buf[BUFSIZ];
+ int32_t bytesRead;
+
+ while ((bytesRead = PR_Read(oldFD, buf, BUFSIZ)) > 0) {
+#ifdef DEBUG_blizzard
+ totalRead += bytesRead;
+#endif
+
+ // PR_Write promises never to do a short write
+ int32_t bytesWritten = PR_Write(newFD, buf, bytesRead);
+ if (bytesWritten < 0) {
+ bytesRead = -1;
+ break;
+ }
+ NS_ASSERTION(bytesWritten == bytesRead, "short PR_Write?");
+
+#ifdef DEBUG_blizzard
+ totalWritten += bytesWritten;
+#endif
+ }
+
+#ifdef DEBUG_blizzard
+ printf("read %d bytes, wrote %d bytes\n",
+ totalRead, totalWritten);
+#endif
+
+ // close the files
+ PR_Close(newFD);
+ PR_Close(oldFD);
+
+ // check for read (or write) error after cleaning up
+ if (bytesRead < 0)
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParent, const nsACString &newName)
+{
+ return CopyToNative(newParent, newName);
+}
+
+NS_IMETHODIMP
+nsLocalFile::MoveToNative(nsIFile *newParent, const nsACString &newName)
+{
+ nsresult rv;
+
+ // check to make sure that this has been initialized properly
+ CHECK_mPath();
+
+ // check to make sure that we have a new parent
+ nsAutoCString newPathName;
+ rv = GetNativeTargetPathName(newParent, newName, newPathName);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // try for atomic rename, falling back to copy/delete
+ if (rename(mPath.get(), newPathName.get()) < 0) {
+#ifdef VMS
+ if (errno == EXDEV || errno == ENXIO) {
+#else
+ if (errno == EXDEV) {
+#endif
+ rv = CopyToNative(newParent, newName);
+ if (NS_SUCCEEDED(rv))
+ rv = Remove(true);
+ } else {
+ rv = NSRESULT_FOR_ERRNO();
+ }
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ // Adjust this
+ mPath = newPathName;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Remove(bool recursive)
+{
+ CHECK_mPath();
+ ENSURE_STAT_CACHE();
+
+ bool isSymLink;
+
+ nsresult rv = IsSymlink(&isSymLink);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (isSymLink || !S_ISDIR(mCachedStat.st_mode))
+ return NSRESULT_FOR_RETURN(unlink(mPath.get()));
+
+ if (recursive) {
+ nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
+ if (!dir)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsCOMPtr<nsISimpleEnumerator> dirRef(dir); // release on exit
+
+ rv = dir->Init(this, false);
+ if (NS_FAILED(rv))
+ return rv;
+
+ bool more;
+ while (dir->HasMoreElements(&more), more) {
+ nsCOMPtr<nsISupports> item;
+ rv = dir->GetNext(getter_AddRefs(item));
+ if (NS_FAILED(rv))
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIFile> file = do_QueryInterface(item, &rv);
+ if (NS_FAILED(rv))
+ return NS_ERROR_FAILURE;
+ rv = file->Remove(recursive);
+
+#ifdef ANDROID
+ // See bug 580434 - Bionic gives us just deleted files
+ if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
+ continue;
+#endif
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ }
+
+ return NSRESULT_FOR_RETURN(rmdir(mPath.get()));
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetLastModifiedTime(PRTime *aLastModTime)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG(aLastModTime);
+
+ PRFileInfo64 info;
+ if (PR_GetFileInfo64(mPath.get(), &info) != PR_SUCCESS)
+ return NSRESULT_FOR_ERRNO();
+ PRTime modTime = info.modifyTime;
+ if (modTime == 0)
+ *aLastModTime = 0;
+ else
+ *aLastModTime = modTime / PR_USEC_PER_MSEC;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetLastModifiedTime(PRTime aLastModTime)
+{
+ CHECK_mPath();
+
+ int result;
+ if (aLastModTime != 0) {
+ ENSURE_STAT_CACHE();
+ struct utimbuf ut;
+ ut.actime = mCachedStat.st_atime;
+
+ // convert milliseconds to seconds since the unix epoch
+ ut.modtime = (time_t)(aLastModTime / PR_MSEC_PER_SEC);
+ result = utime(mPath.get(), &ut);
+ } else {
+ result = utime(mPath.get(), nullptr);
+ }
+ return NSRESULT_FOR_RETURN(result);
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetLastModifiedTimeOfLink(PRTime *aLastModTimeOfLink)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG(aLastModTimeOfLink);
+
+ struct STAT sbuf;
+ if (LSTAT(mPath.get(), &sbuf) == -1)
+ return NSRESULT_FOR_ERRNO();
+ *aLastModTimeOfLink = PRTime(sbuf.st_mtime) * PR_MSEC_PER_SEC;
+
+ return NS_OK;
+}
+
+/*
+ * utime(2) may or may not dereference symlinks, joy.
+ */
+NS_IMETHODIMP
+nsLocalFile::SetLastModifiedTimeOfLink(PRTime aLastModTimeOfLink)
+{
+ return SetLastModifiedTime(aLastModTimeOfLink);
+}
+
+/*
+ * Only send back permissions bits: maybe we want to send back the whole
+ * mode_t to permit checks against other file types?
+ */
+
+#define NORMALIZE_PERMS(mode) ((mode)& (S_IRWXU | S_IRWXG | S_IRWXO))
+
+NS_IMETHODIMP
+nsLocalFile::GetPermissions(uint32_t *aPermissions)
+{
+ NS_ENSURE_ARG(aPermissions);
+ ENSURE_STAT_CACHE();
+ *aPermissions = NORMALIZE_PERMS(mCachedStat.st_mode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPermissionsOfLink(uint32_t *aPermissionsOfLink)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG(aPermissionsOfLink);
+
+ struct STAT sbuf;
+ if (LSTAT(mPath.get(), &sbuf) == -1)
+ return NSRESULT_FOR_ERRNO();
+ *aPermissionsOfLink = NORMALIZE_PERMS(sbuf.st_mode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPermissions(uint32_t aPermissions)
+{
+ CHECK_mPath();
+
+ /*
+ * Race condition here: we should use fchmod instead, there's no way to
+ * guarantee the name still refers to the same file.
+ */
+ if (chmod(mPath.get(), aPermissions) >= 0)
+ return NS_OK;
+#if defined(ANDROID) && defined(STATFS)
+ // For the time being, this is restricted for use by Android, but we
+ // will figure out what to do for all platforms in bug 638503
+ struct STATFS sfs;
+ if (STATFS(mPath.get(), &sfs) < 0)
+ return NSRESULT_FOR_ERRNO();
+
+ // if this is a FAT file system we can't set file permissions
+ if (sfs.f_type == MSDOS_SUPER_MAGIC )
+ return NS_OK;
+#endif
+ return NSRESULT_FOR_ERRNO();
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPermissionsOfLink(uint32_t aPermissions)
+{
+ // There isn't a consistent mechanism for doing this on UNIX platforms. We
+ // might want to carefully implement this in the future though.
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSize(int64_t *aFileSize)
+{
+ NS_ENSURE_ARG_POINTER(aFileSize);
+ *aFileSize = 0;
+ ENSURE_STAT_CACHE();
+
+#if defined(VMS)
+ /* Only two record formats can report correct file content size */
+ if ((mCachedStat.st_fab_rfm != FAB$C_STMLF) &&
+ (mCachedStat.st_fab_rfm != FAB$C_STMCR)) {
+ return NS_ERROR_FAILURE;
+ }
+#endif
+
+ if (!S_ISDIR(mCachedStat.st_mode)) {
+ *aFileSize = (int64_t)mCachedStat.st_size;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetFileSize(int64_t aFileSize)
+{
+ CHECK_mPath();
+
+#if defined(ANDROID)
+ /* no truncate on bionic */
+ int fd = open(mPath.get(), O_WRONLY);
+ if (fd == -1)
+ return NSRESULT_FOR_ERRNO();
+
+ int ret = ftruncate(fd, (off_t)aFileSize);
+ close(fd);
+
+ if (ret == -1)
+ return NSRESULT_FOR_ERRNO();
+#elif defined(HAVE_TRUNCATE64)
+ if (truncate64(mPath.get(), (off64_t)aFileSize) == -1)
+ return NSRESULT_FOR_ERRNO();
+#else
+ off_t size = (off_t)aFileSize;
+ if (truncate(mPath.get(), size) == -1)
+ return NSRESULT_FOR_ERRNO();
+#endif
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSizeOfLink(int64_t *aFileSize)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG(aFileSize);
+
+ struct STAT sbuf;
+ if (LSTAT(mPath.get(), &sbuf) == -1)
+ return NSRESULT_FOR_ERRNO();
+
+ *aFileSize = (int64_t)sbuf.st_size;
+ return NS_OK;
+}
+
+#if defined(USE_LINUX_QUOTACTL)
+/*
+ * Searches /proc/self/mountinfo for given device (Major:Minor),
+ * returns exported name from /dev
+ *
+ * Fails when /proc/self/mountinfo or diven device don't exist.
+ */
+static bool
+GetDeviceName(int deviceMajor, int deviceMinor, nsACString &deviceName)
+{
+ bool ret = false;
+
+ const int kMountInfoLineLength = 200;
+ const int kMountInfoDevPosition = 6;
+
+ char mountinfo_line[kMountInfoLineLength];
+ char device_num[kMountInfoLineLength];
+
+ snprintf(device_num,kMountInfoLineLength,"%d:%d", deviceMajor, deviceMinor);
+
+ FILE *f = fopen("/proc/self/mountinfo","rt");
+ if(!f)
+ return ret;
+
+ // Expects /proc/self/mountinfo in format:
+ // 'ID ID major:minor root mountpoint flags - type devicename flags'
+ while(fgets(mountinfo_line,kMountInfoLineLength,f)) {
+ char *p_dev = strstr(mountinfo_line,device_num);
+
+ int i;
+ for(i = 0; i < kMountInfoDevPosition && p_dev != NULL; i++) {
+ p_dev = strchr(p_dev,' ');
+ if(p_dev)
+ p_dev++;
+ }
+
+ if(p_dev) {
+ char *p_dev_end = strchr(p_dev,' ');
+ if(p_dev_end) {
+ *p_dev_end = '\0';
+ deviceName.Assign(p_dev);
+ ret = true;
+ break;
+ }
+ }
+ }
+
+ fclose(f);
+ return ret;
+}
+#endif
+
+NS_IMETHODIMP
+nsLocalFile::GetDiskSpaceAvailable(int64_t *aDiskSpaceAvailable)
+{
+ NS_ENSURE_ARG_POINTER(aDiskSpaceAvailable);
+
+ // These systems have the operations necessary to check disk space.
+
+#ifdef STATFS
+
+ // check to make sure that mPath is properly initialized
+ CHECK_mPath();
+
+ struct STATFS fs_buf;
+
+ /*
+ * Members of the STATFS struct that you should know about:
+ * F_BSIZE = block size on disk.
+ * f_bavail = number of free blocks available to a non-superuser.
+ * f_bfree = number of total free blocks in file system.
+ */
+
+ if (STATFS(mPath.get(), &fs_buf) < 0) {
+ // The call to STATFS failed.
+#ifdef DEBUG
+ printf("ERROR: GetDiskSpaceAvailable: STATFS call FAILED. \n");
+#endif
+ return NS_ERROR_FAILURE;
+ }
+
+ *aDiskSpaceAvailable = (int64_t) fs_buf.F_BSIZE * fs_buf.f_bavail;
+
+#ifdef DEBUG_DISK_SPACE
+ printf("DiskSpaceAvailable: %lu bytes\n",
+ *aDiskSpaceAvailable);
+#endif
+
+#if defined(USE_LINUX_QUOTACTL)
+
+ if(!FillStatCache()) {
+ // Return available size from statfs
+ return NS_OK;
+ }
+
+ nsCString deviceName;
+ if(!GetDeviceName(major(mCachedStat.st_dev), minor(mCachedStat.st_dev), deviceName)) {
+ return NS_OK;
+ }
+
+ struct dqblk dq;
+ if(!quotactl(QCMD(Q_GETQUOTA, USRQUOTA), deviceName.get(), getuid(), (caddr_t)&dq)
+#ifdef QIF_BLIMITS
+ && dq.dqb_valid & QIF_BLIMITS
+#endif
+ && dq.dqb_bhardlimit)
+ {
+ int64_t QuotaSpaceAvailable = 0;
+ if (dq.dqb_bhardlimit > dq.dqb_curspace)
+ QuotaSpaceAvailable = int64_t(fs_buf.F_BSIZE * (dq.dqb_bhardlimit - dq.dqb_curspace));
+ if(QuotaSpaceAvailable < *aDiskSpaceAvailable) {
+ *aDiskSpaceAvailable = QuotaSpaceAvailable;
+ }
+ }
+#endif
+
+ return NS_OK;
+
+#else
+ /*
+ * This platform doesn't have statfs or statvfs. I'm sure that there's
+ * a way to check for free disk space on platforms that don't have statfs
+ * (I'm SURE they have df, for example).
+ *
+ * Until we figure out how to do that, lets be honest and say that this
+ * command isn't implemented properly for these platforms yet.
+ */
+#ifdef DEBUG
+ printf("ERROR: GetDiskSpaceAvailable: Not implemented for plaforms without statfs.\n");
+#endif
+ return NS_ERROR_NOT_IMPLEMENTED;
+
+#endif /* STATFS */
+
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetParent(nsIFile **aParent)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG_POINTER(aParent);
+ *aParent = nullptr;
+
+ // if '/' we are at the top of the volume, return null
+ if (mPath.Equals("/"))
+ return NS_OK;
+
+ // <brendan, after jband> I promise to play nice
+ char *buffer = mPath.BeginWriting(),
+ *slashp = buffer;
+
+ // find the last significant slash in buffer
+ slashp = strrchr(buffer, '/');
+ NS_ASSERTION(slashp, "non-canonical path?");
+ if (!slashp)
+ return NS_ERROR_FILE_INVALID_PATH;
+
+ // for the case where we are at '/'
+ if (slashp == buffer)
+ slashp++;
+
+ // temporarily terminate buffer at the last significant slash
+ char c = *slashp;
+ *slashp = '\0';
+
+ nsCOMPtr<nsIFile> localFile;
+ nsresult rv = NS_NewNativeLocalFile(nsDependentCString(buffer), true,
+ getter_AddRefs(localFile));
+
+ // make buffer whole again
+ *slashp = c;
+
+ if (NS_SUCCEEDED(rv) && localFile)
+ rv = CallQueryInterface(localFile, aParent);
+ return rv;
+}
+
+/*
+ * The results of Exists, isWritable and isReadable are not cached.
+ */
+
+
+NS_IMETHODIMP
+nsLocalFile::Exists(bool *_retval)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ *_retval = (access(mPath.get(), F_OK) == 0);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::IsWritable(bool *_retval)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ *_retval = (access(mPath.get(), W_OK) == 0);
+ if (*_retval || errno == EACCES)
+ return NS_OK;
+ return NSRESULT_FOR_ERRNO();
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsReadable(bool *_retval)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ *_retval = (access(mPath.get(), R_OK) == 0);
+ if (*_retval || errno == EACCES)
+ return NS_OK;
+ return NSRESULT_FOR_ERRNO();
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsExecutable(bool *_retval)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ // Check extension (bug 663899). On certain platforms, the file
+ // extension may cause the OS to treat it as executable regardless of
+ // the execute bit, such as .jar on Mac OS X. We borrow the code from
+ // nsLocalFileWin, slightly modified.
+
+ // Don't be fooled by symlinks.
+ bool symLink;
+ nsresult rv = IsSymlink(&symLink);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoString path;
+ if (symLink)
+ GetTarget(path);
+ else
+ GetPath(path);
+
+ int32_t dotIdx = path.RFindChar(PRUnichar('.'));
+ if (dotIdx != kNotFound) {
+ // Convert extension to lower case.
+ PRUnichar *p = path.BeginWriting();
+ for(p += dotIdx + 1; *p; p++)
+ *p += (*p >= L'A' && *p <= L'Z') ? 'a' - 'A' : 0;
+
+ // Search for any of the set of executable extensions.
+ static const char * const executableExts[] = {
+ "air", // Adobe AIR installer
+ "jar"}; // java application bundle
+ nsDependentSubstring ext = Substring(path, dotIdx + 1);
+ for (size_t i = 0; i < ArrayLength(executableExts); i++) {
+ if (ext.EqualsASCII(executableExts[i])) {
+ // Found a match. Set result and quit.
+ *_retval = true;
+ return NS_OK;
+ }
+ }
+ }
+
+ // On OS X, then query Launch Services.
+#ifdef MOZ_WIDGET_COCOA
+ // Certain Mac applications, such as Classic applications, which
+ // run under Rosetta, might not have the +x mode bit but are still
+ // considered to be executable by Launch Services (bug 646748).
+ CFURLRef url;
+ if (NS_FAILED(GetCFURL(&url))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ LSRequestedInfo theInfoRequest = kLSRequestAllInfo;
+ LSItemInfoRecord theInfo;
+ OSStatus result = ::LSCopyItemInfoForURL(url, theInfoRequest, &theInfo);
+ ::CFRelease(url);
+ if (result == noErr) {
+ if ((theInfo.flags & kLSItemInfoIsApplication) != 0) {
+ *_retval = true;
+ return NS_OK;
+ }
+ }
+#endif
+
+ // Then check the execute bit.
+ *_retval = (access(mPath.get(), X_OK) == 0);
+#ifdef SOLARIS
+ // On Solaris, access will always return 0 for root user, however
+ // the file is only executable if S_IXUSR | S_IXGRP | S_IXOTH is set.
+ // See bug 351950, https://bugzilla.mozilla.org/show_bug.cgi?id=351950
+ if (*_retval) {
+ struct STAT buf;
+
+ *_retval = (STAT(mPath.get(), &buf) == 0);
+ if (*_retval || errno == EACCES) {
+ *_retval = *_retval &&
+ (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH ));
+ return NS_OK;
+ }
+
+ return NSRESULT_FOR_ERRNO();
+ }
+#endif
+ if (*_retval || errno == EACCES)
+ return NS_OK;
+ return NSRESULT_FOR_ERRNO();
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsDirectory(bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = false;
+ ENSURE_STAT_CACHE();
+ *_retval = S_ISDIR(mCachedStat.st_mode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsFile(bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = false;
+ ENSURE_STAT_CACHE();
+ *_retval = S_ISREG(mCachedStat.st_mode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsHidden(bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ nsACString::const_iterator begin, end;
+ LocateNativeLeafName(begin, end);
+ *_retval = (*begin == '.');
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsSymlink(bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ CHECK_mPath();
+
+ struct STAT symStat;
+ if (LSTAT(mPath.get(), &symStat) == -1)
+ return NSRESULT_FOR_ERRNO();
+ *_retval=S_ISLNK(symStat.st_mode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsSpecial(bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ ENSURE_STAT_CACHE();
+ *_retval = S_ISCHR(mCachedStat.st_mode) ||
+ S_ISBLK(mCachedStat.st_mode) ||
+#ifdef S_ISSOCK
+ S_ISSOCK(mCachedStat.st_mode) ||
+#endif
+ S_ISFIFO(mCachedStat.st_mode);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Equals(nsIFile *inFile, bool *_retval)
+{
+ NS_ENSURE_ARG(inFile);
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = false;
+
+ nsAutoCString inPath;
+ nsresult rv = inFile->GetNativePath(inPath);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // We don't need to worry about "/foo/" vs. "/foo" here
+ // because trailing slashes are stripped on init.
+ *_retval = !strcmp(inPath.get(), mPath.get());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Contains(nsIFile *inFile, bool recur, bool *_retval)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG(inFile);
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsAutoCString inPath;
+ nsresult rv;
+
+ if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
+ return rv;
+
+ *_retval = false;
+
+ ssize_t len = mPath.Length();
+ if (strncmp(mPath.get(), inPath.get(), len) == 0) {
+ // Now make sure that the |inFile|'s path has a separator at len,
+ // which implies that it has more components after len.
+ if (inPath[len] == '/')
+ *_retval = true;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeTarget(nsACString &_retval)
+{
+ CHECK_mPath();
+ _retval.Truncate();
+
+ struct STAT symStat;
+ if (LSTAT(mPath.get(), &symStat) == -1)
+ return NSRESULT_FOR_ERRNO();
+
+ if (!S_ISLNK(symStat.st_mode))
+ return NS_ERROR_FILE_INVALID_PATH;
+
+ int32_t size = (int32_t)symStat.st_size;
+ char *target = (char *)nsMemory::Alloc(size + 1);
+ if (!target)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (readlink(mPath.get(), target, (size_t)size) < 0) {
+ nsMemory::Free(target);
+ return NSRESULT_FOR_ERRNO();
+ }
+ target[size] = '\0';
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIFile> self(this);
+ int32_t maxLinks = 40;
+ while (true) {
+ if (maxLinks-- == 0) {
+ rv = NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
+ break;
+ }
+
+ if (target[0] != '/') {
+ nsCOMPtr<nsIFile> parent;
+ if (NS_FAILED(rv = self->GetParent(getter_AddRefs(parent))))
+ break;
+ if (NS_FAILED(rv = parent->AppendRelativeNativePath(nsDependentCString(target))))
+ break;
+ if (NS_FAILED(rv = parent->GetNativePath(_retval)))
+ break;
+ self = parent;
+ } else {
+ _retval = target;
+ }
+
+ const nsPromiseFlatCString &flatRetval = PromiseFlatCString(_retval);
+
+ // Any failure in testing the current target we'll just interpret
+ // as having reached our destiny.
+ if (LSTAT(flatRetval.get(), &symStat) == -1)
+ break;
+
+ // And of course we're done if it isn't a symlink.
+ if (!S_ISLNK(symStat.st_mode))
+ break;
+
+ int32_t newSize = (int32_t)symStat.st_size;
+ if (newSize > size) {
+ char *newTarget = (char *)nsMemory::Realloc(target, newSize + 1);
+ if (!newTarget) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ break;
+ }
+ target = newTarget;
+ size = newSize;
+ }
+
+ int32_t linkLen = readlink(flatRetval.get(), target, size);
+ if (linkLen == -1) {
+ rv = NSRESULT_FOR_ERRNO();
+ break;
+ }
+ target[linkLen] = '\0';
+ }
+
+ nsMemory::Free(target);
+
+ if (NS_FAILED(rv))
+ _retval.Truncate();
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFollowLinks(bool *aFollowLinks)
+{
+ *aFollowLinks = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetFollowLinks(bool aFollowLinks)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **entries)
+{
+ nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
+ if (!dir)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(dir);
+ nsresult rv = dir->Init(this, false);
+ if (NS_FAILED(rv)) {
+ *entries = nullptr;
+ NS_RELEASE(dir);
+ } else {
+ *entries = dir; // transfer reference
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Load(PRLibrary **_retval)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG_POINTER(_retval);
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ nsTraceRefcntImpl::SetActivityIsLegal(false);
+#endif
+
+ *_retval = PR_LoadLibrary(mPath.get());
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ nsTraceRefcntImpl::SetActivityIsLegal(true);
+#endif
+
+ if (!*_retval)
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
+{
+ return GetNativePath(aPersistentDescriptor);
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
+{
+#ifdef MOZ_WIDGET_COCOA
+ if (aPersistentDescriptor.IsEmpty())
+ return NS_ERROR_INVALID_ARG;
+
+ // Support pathnames as user-supplied descriptors if they begin with '/'
+ // or '~'. These characters do not collide with the base64 set used for
+ // encoding alias records.
+ char first = aPersistentDescriptor.First();
+ if (first == '/' || first == '~')
+ return InitWithNativePath(aPersistentDescriptor);
+
+ uint32_t dataSize = aPersistentDescriptor.Length();
+ char* decodedData = PL_Base64Decode(PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nullptr);
+ if (!decodedData) {
+ NS_ERROR("SetPersistentDescriptor was given bad data");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Cast to an alias record and resolve.
+ AliasRecord aliasHeader = *(AliasPtr)decodedData;
+ int32_t aliasSize = ::GetAliasSizeFromPtr(&aliasHeader);
+ if (aliasSize > ((int32_t)dataSize * 3) / 4) { // be paranoid about having too few data
+ PR_Free(decodedData);
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv = NS_OK;
+
+ // Move the now-decoded data into the Handle.
+ // The size of the decoded data is 3/4 the size of the encoded data. See plbase64.h
+ Handle newHandle = nullptr;
+ if (::PtrToHand(decodedData, &newHandle, aliasSize) != noErr)
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ PR_Free(decodedData);
+ if (NS_FAILED(rv))
+ return rv;
+
+ Boolean changed;
+ FSRef resolvedFSRef;
+ OSErr err = ::FSResolveAlias(nullptr, (AliasHandle)newHandle, &resolvedFSRef, &changed);
+
+ rv = MacErrorMapper(err);
+ DisposeHandle(newHandle);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return InitWithFSRef(&resolvedFSRef);
+#else
+ return InitWithNativePath(aPersistentDescriptor);
+#endif
+}
+
+NS_IMETHODIMP
+nsLocalFile::Reveal()
+{
+#ifdef MOZ_WIDGET_GTK
+ nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
+ nsCOMPtr<nsIGnomeVFSService> gnomevfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
+ if (!giovfs && !gnomevfs)
+ return NS_ERROR_FAILURE;
+
+ bool isDirectory;
+ if (NS_FAILED(IsDirectory(&isDirectory)))
+ return NS_ERROR_FAILURE;
+
+ if (isDirectory) {
+ if (giovfs)
+ return giovfs->ShowURIForInput(mPath);
+ else
+ /* Fallback to GnomeVFS */
+ return gnomevfs->ShowURIForInput(mPath);
+ } else {
+ nsCOMPtr<nsIFile> parentDir;
+ nsAutoCString dirPath;
+ if (NS_FAILED(GetParent(getter_AddRefs(parentDir))))
+ return NS_ERROR_FAILURE;
+ if (NS_FAILED(parentDir->GetNativePath(dirPath)))
+ return NS_ERROR_FAILURE;
+
+ if (giovfs)
+ return giovfs->ShowURIForInput(dirPath);
+ else
+ return gnomevfs->ShowURIForInput(dirPath);
+ }
+#elif defined(MOZ_WIDGET_COCOA)
+ CFURLRef url;
+ if (NS_SUCCEEDED(GetCFURL(&url))) {
+ nsresult rv = CocoaFileUtils::RevealFileInFinder(url);
+ ::CFRelease(url);
+ return rv;
+ }
+ return NS_ERROR_FAILURE;
+#else
+ return NS_ERROR_FAILURE;
+#endif
+}
+
+NS_IMETHODIMP
+nsLocalFile::Launch()
+{
+#ifdef MOZ_WIDGET_GTK
+#if (MOZ_PLATFORM_MAEMO==5)
+ const int32_t kHILDON_SUCCESS = 1;
+ DBusError err;
+ dbus_error_init(&err);
+
+ DBusConnection *connection = dbus_bus_get(DBUS_BUS_SESSION, &err);
+ if (dbus_error_is_set(&err)) {
+ dbus_error_free(&err);
+ return NS_ERROR_FAILURE;
+ }
+
+ if (nullptr == connection)
+ return NS_ERROR_FAILURE;
+
+ if (hildon_mime_open_file(connection, mPath.get()) != kHILDON_SUCCESS)
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+#else
+ nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
+ nsCOMPtr<nsIGnomeVFSService> gnomevfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
+ if (giovfs) {
+ return giovfs->ShowURIForInput(mPath);
+ } else if (gnomevfs) {
+ /* GnomeVFS fallback */
+ return gnomevfs->ShowURIForInput(mPath);
+ }
+
+ return NS_ERROR_FAILURE;
+#endif
+#elif defined(MOZ_ENABLE_CONTENTACTION)
+ QUrl uri = QUrl::fromLocalFile(QString::fromUtf8(mPath.get()));
+ ContentAction::Action action =
+ ContentAction::Action::defaultActionForFile(uri);
+
+ if (action.isValid()) {
+ action.trigger();
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+#elif defined(MOZ_WIDGET_ANDROID)
+ // Try to get a mimetype, if this fails just use the file uri alone
+ nsresult rv;
+ nsAutoCString type;
+ nsCOMPtr<nsIMIMEService> mimeService(do_GetService("@mozilla.org/mime;1", &rv));
+ if (NS_SUCCEEDED(rv))
+ rv = mimeService->GetTypeFromFile(this, type);
+
+ nsDependentCString fileUri = NS_LITERAL_CSTRING("file://");
+ fileUri.Append(mPath);
+ mozilla::AndroidBridge* bridge = mozilla::AndroidBridge::Bridge();
+ return bridge->OpenUriExternal(fileUri, type) ? NS_OK : NS_ERROR_FAILURE;
+#elif defined(MOZ_WIDGET_COCOA)
+ CFURLRef url;
+ if (NS_SUCCEEDED(GetCFURL(&url))) {
+ nsresult rv = CocoaFileUtils::OpenURL(url);
+ ::CFRelease(url);
+ return rv;
+ }
+ return NS_ERROR_FAILURE;
+#else
+ return NS_ERROR_FAILURE;
+#endif
+}
+
+nsresult
+NS_NewNativeLocalFile(const nsACString &path, bool followSymlinks, nsIFile **result)
+{
+ nsLocalFile *file = new nsLocalFile();
+ if (!file)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(file);
+
+ file->SetFollowLinks(followSymlinks);
+
+ if (!path.IsEmpty()) {
+ nsresult rv = file->InitWithNativePath(path);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(file);
+ return rv;
+ }
+ }
+ *result = file;
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// unicode support
+//-----------------------------------------------------------------------------
+
+#define SET_UCS(func, ucsArg) \
+ { \
+ nsAutoCString buf; \
+ nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
+ if (NS_FAILED(rv)) \
+ return rv; \
+ return (func)(buf); \
+ }
+
+#define GET_UCS(func, ucsArg) \
+ { \
+ nsAutoCString buf; \
+ nsresult rv = (func)(buf); \
+ if (NS_FAILED(rv)) return rv; \
+ return NS_CopyNativeToUnicode(buf, ucsArg); \
+ }
+
+#define SET_UCS_2ARGS_2(func, opaqueArg, ucsArg) \
+ { \
+ nsAutoCString buf; \
+ nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
+ if (NS_FAILED(rv)) \
+ return rv; \
+ return (func)(opaqueArg, buf); \
+ }
+
+// Unicode interface Wrapper
+nsresult
+nsLocalFile::InitWithPath(const nsAString &filePath)
+{
+ SET_UCS(InitWithNativePath, filePath);
+}
+nsresult
+nsLocalFile::Append(const nsAString &node)
+{
+ SET_UCS(AppendNative, node);
+}
+nsresult
+nsLocalFile::AppendRelativePath(const nsAString &node)
+{
+ SET_UCS(AppendRelativeNativePath, node);
+}
+nsresult
+nsLocalFile::GetLeafName(nsAString &aLeafName)
+{
+ GET_UCS(GetNativeLeafName, aLeafName);
+}
+nsresult
+nsLocalFile::SetLeafName(const nsAString &aLeafName)
+{
+ SET_UCS(SetNativeLeafName, aLeafName);
+}
+nsresult
+nsLocalFile::GetPath(nsAString &_retval)
+{
+ return NS_CopyNativeToUnicode(mPath, _retval);
+}
+nsresult
+nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
+{
+ SET_UCS_2ARGS_2(CopyToNative , newParentDir, newName);
+}
+nsresult
+nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
+{
+ SET_UCS_2ARGS_2(CopyToFollowingLinksNative , newParentDir, newName);
+}
+nsresult
+nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
+{
+ SET_UCS_2ARGS_2(MoveToNative, newParentDir, newName);
+}
+nsresult
+nsLocalFile::GetTarget(nsAString &_retval)
+{
+ GET_UCS(GetNativeTarget, _retval);
+}
+
+// nsIHashable
+
+NS_IMETHODIMP
+nsLocalFile::Equals(nsIHashable* aOther, bool *aResult)
+{
+ nsCOMPtr<nsIFile> otherFile(do_QueryInterface(aOther));
+ if (!otherFile) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ return Equals(otherFile, aResult);
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetHashCode(uint32_t *aResult)
+{
+ *aResult = HashString(mPath);
+ return NS_OK;
+}
+
+nsresult
+NS_NewLocalFile(const nsAString &path, bool followLinks, nsIFile* *result)
+{
+ nsAutoCString buf;
+ nsresult rv = NS_CopyUnicodeToNative(path, buf);
+ if (NS_FAILED(rv))
+ return rv;
+ return NS_NewNativeLocalFile(buf, followLinks, result);
+}
+
+//-----------------------------------------------------------------------------
+// global init/shutdown
+//-----------------------------------------------------------------------------
+
+void
+nsLocalFile::GlobalInit()
+{
+}
+
+void
+nsLocalFile::GlobalShutdown()
+{
+}
+
+// nsILocalFileMac
+
+#ifdef MOZ_WIDGET_COCOA
+
+static nsresult MacErrorMapper(OSErr inErr)
+{
+ nsresult outErr;
+
+ switch (inErr)
+ {
+ case noErr:
+ outErr = NS_OK;
+ break;
+
+ case fnfErr:
+ case afpObjectNotFound:
+ case afpDirNotFound:
+ outErr = NS_ERROR_FILE_NOT_FOUND;
+ break;
+
+ case dupFNErr:
+ case afpObjectExists:
+ outErr = NS_ERROR_FILE_ALREADY_EXISTS;
+ break;
+
+ case dskFulErr:
+ case afpDiskFull:
+ outErr = NS_ERROR_FILE_DISK_FULL;
+ break;
+
+ case fLckdErr:
+ case afpVolLocked:
+ outErr = NS_ERROR_FILE_IS_LOCKED;
+ break;
+
+ case afpAccessDenied:
+ outErr = NS_ERROR_FILE_ACCESS_DENIED;
+ break;
+
+ case afpDirNotEmpty:
+ outErr = NS_ERROR_FILE_DIR_NOT_EMPTY;
+ break;
+
+ // Can't find good map for some
+ case bdNamErr:
+ outErr = NS_ERROR_FAILURE;
+ break;
+
+ default:
+ outErr = NS_ERROR_FAILURE;
+ break;
+ }
+
+ return outErr;
+}
+
+static nsresult CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr)
+{
+ // first see if the conversion would succeed and find the length of the result
+ CFIndex usedBufLen, inStrLen = ::CFStringGetLength(aInStrRef);
+ CFIndex charsConverted = ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen),
+ kCFStringEncodingUTF8, 0, false,
+ NULL, 0, &usedBufLen);
+ if (charsConverted == inStrLen) {
+ // all characters converted, do the actual conversion
+ aOutStr.SetLength(usedBufLen);
+ if (aOutStr.Length() != (unsigned int)usedBufLen)
+ return NS_ERROR_OUT_OF_MEMORY;
+ UInt8 *buffer = (UInt8*)aOutStr.BeginWriting();
+ ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen), kCFStringEncodingUTF8,
+ 0, false, buffer, usedBufLen, &usedBufLen);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::InitWithCFURL(CFURLRef aCFURL)
+{
+ UInt8 path[PATH_MAX];
+ if (::CFURLGetFileSystemRepresentation(aCFURL, false, path, PATH_MAX)) {
+ nsDependentCString nativePath((char*)path);
+ return InitWithNativePath(nativePath);
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::InitWithFSRef(const FSRef *aFSRef)
+{
+ NS_ENSURE_ARG(aFSRef);
+
+ CFURLRef newURLRef = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aFSRef);
+ if (newURLRef) {
+ nsresult rv = InitWithCFURL(newURLRef);
+ ::CFRelease(newURLRef);
+ return rv;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetCFURL(CFURLRef *_retval)
+{
+ CHECK_mPath();
+
+ bool isDir;
+ IsDirectory(&isDir);
+ *_retval = ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
+ (UInt8*)mPath.get(),
+ mPath.Length(),
+ isDir);
+
+ return (*_retval ? NS_OK : NS_ERROR_FAILURE);
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFSRef(FSRef *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsresult rv = NS_ERROR_FAILURE;
+
+ CFURLRef url = NULL;
+ if (NS_SUCCEEDED(GetCFURL(&url))) {
+ if (::CFURLGetFSRef(url, _retval)) {
+ rv = NS_OK;
+ }
+ ::CFRelease(url);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFSSpec(FSSpec *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ FSRef fsRef;
+ nsresult rv = GetFSRef(&fsRef);
+ if (NS_SUCCEEDED(rv)) {
+ OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNone, nullptr, nullptr, _retval, nullptr);
+ return MacErrorMapper(err);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSizeWithResFork(int64_t *aFileSizeWithResFork)
+{
+ NS_ENSURE_ARG_POINTER(aFileSizeWithResFork);
+
+ FSRef fsRef;
+ nsresult rv = GetFSRef(&fsRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ FSCatalogInfo catalogInfo;
+ OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoDataSizes + kFSCatInfoRsrcSizes,
+ &catalogInfo, nullptr, nullptr, nullptr);
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ *aFileSizeWithResFork = catalogInfo.dataLogicalSize + catalogInfo.rsrcLogicalSize;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFileType(OSType *aFileType)
+{
+ CFURLRef url;
+ if (NS_SUCCEEDED(GetCFURL(&url))) {
+ nsresult rv = CocoaFileUtils::GetFileTypeCode(url, aFileType);
+ ::CFRelease(url);
+ return rv;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetFileType(OSType aFileType)
+{
+ CFURLRef url;
+ if (NS_SUCCEEDED(GetCFURL(&url))) {
+ nsresult rv = CocoaFileUtils::SetFileTypeCode(url, aFileType);
+ ::CFRelease(url);
+ return rv;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetFileCreator(OSType *aFileCreator)
+{
+ CFURLRef url;
+ if (NS_SUCCEEDED(GetCFURL(&url))) {
+ nsresult rv = CocoaFileUtils::GetFileCreatorCode(url, aFileCreator);
+ ::CFRelease(url);
+ return rv;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetFileCreator(OSType aFileCreator)
+{
+ CFURLRef url;
+ if (NS_SUCCEEDED(GetCFURL(&url))) {
+ nsresult rv = CocoaFileUtils::SetFileCreatorCode(url, aFileCreator);
+ ::CFRelease(url);
+ return rv;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsLocalFile::LaunchWithDoc(nsIFile *aDocToLoad, bool aLaunchInBackground)
+{
+ bool isExecutable;
+ nsresult rv = IsExecutable(&isExecutable);
+ if (NS_FAILED(rv))
+ return rv;
+ if (!isExecutable)
+ return NS_ERROR_FILE_EXECUTION_FAILED;
+
+ FSRef appFSRef, docFSRef;
+ rv = GetFSRef(&appFSRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (aDocToLoad) {
+ nsCOMPtr<nsILocalFileMac> macDoc = do_QueryInterface(aDocToLoad);
+ rv = macDoc->GetFSRef(&docFSRef);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
+ LSLaunchFSRefSpec thelaunchSpec;
+
+ if (aLaunchInBackground)
+ theLaunchFlags |= kLSLaunchDontSwitch;
+ memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
+
+ thelaunchSpec.appRef = &appFSRef;
+ if (aDocToLoad) {
+ thelaunchSpec.numDocs = 1;
+ thelaunchSpec.itemRefs = &docFSRef;
+ }
+ thelaunchSpec.launchFlags = theLaunchFlags;
+
+ OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL);
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::OpenDocWithApp(nsIFile *aAppToOpenWith, bool aLaunchInBackground)
+{
+ FSRef docFSRef;
+ nsresult rv = GetFSRef(&docFSRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!aAppToOpenWith) {
+ OSErr err = ::LSOpenFSRef(&docFSRef, NULL);
+ return MacErrorMapper(err);
+ }
+
+ nsCOMPtr<nsILocalFileMac> appFileMac = do_QueryInterface(aAppToOpenWith, &rv);
+ if (!appFileMac)
+ return rv;
+
+ bool isExecutable;
+ rv = appFileMac->IsExecutable(&isExecutable);
+ if (NS_FAILED(rv))
+ return rv;
+ if (!isExecutable)
+ return NS_ERROR_FILE_EXECUTION_FAILED;
+
+ FSRef appFSRef;
+ rv = appFileMac->GetFSRef(&appFSRef);
+ if (NS_FAILED(rv))
+ return rv;
+
+ LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
+ LSLaunchFSRefSpec thelaunchSpec;
+
+ if (aLaunchInBackground)
+ theLaunchFlags |= kLSLaunchDontSwitch;
+ memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
+
+ thelaunchSpec.appRef = &appFSRef;
+ thelaunchSpec.numDocs = 1;
+ thelaunchSpec.itemRefs = &docFSRef;
+ thelaunchSpec.launchFlags = theLaunchFlags;
+
+ OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL);
+ if (err != noErr)
+ return MacErrorMapper(err);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsPackage(bool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = false;
+
+ CFURLRef url;
+ nsresult rv = GetCFURL(&url);
+ if (NS_FAILED(rv))
+ return rv;
+
+ LSItemInfoRecord info;
+ OSStatus status = ::LSCopyItemInfoForURL(url, kLSRequestBasicFlagsOnly, &info);
+
+ ::CFRelease(url);
+
+ if (status != noErr) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *_retval = !!(info.flags & kLSItemInfoIsPackage);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetBundleDisplayName(nsAString& outBundleName)
+{
+ bool isPackage = false;
+ nsresult rv = IsPackage(&isPackage);
+ if (NS_FAILED(rv) || !isPackage)
+ return NS_ERROR_FAILURE;
+
+ nsAutoString name;
+ rv = GetLeafName(name);
+ if (NS_FAILED(rv))
+ return rv;
+
+ int32_t length = name.Length();
+ if (Substring(name, length - 4, length).EqualsLiteral(".app")) {
+ // 4 characters in ".app"
+ outBundleName = Substring(name, 0, length - 4);
+ }
+ else {
+ outBundleName = name;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetBundleIdentifier(nsACString& outBundleIdentifier)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+
+ CFURLRef urlRef;
+ if (NS_SUCCEEDED(GetCFURL(&urlRef))) {
+ CFBundleRef bundle = ::CFBundleCreate(NULL, urlRef);
+ if (bundle) {
+ CFStringRef bundleIdentifier = ::CFBundleGetIdentifier(bundle);
+ if (bundleIdentifier)
+ rv = CFStringReftoUTF8(bundleIdentifier, outBundleIdentifier);
+ ::CFRelease(bundle);
+ }
+ ::CFRelease(urlRef);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetBundleContentsLastModifiedTime(int64_t *aLastModTime)
+{
+ CHECK_mPath();
+ NS_ENSURE_ARG_POINTER(aLastModTime);
+
+ bool isPackage = false;
+ nsresult rv = IsPackage(&isPackage);
+ if (NS_FAILED(rv) || !isPackage) {
+ return GetLastModifiedTime(aLastModTime);
+ }
+
+ nsAutoCString infoPlistPath(mPath);
+ infoPlistPath.AppendLiteral("/Contents/Info.plist");
+ PRFileInfo64 info;
+ if (PR_GetFileInfo64(infoPlistPath.get(), &info) != PR_SUCCESS) {
+ return GetLastModifiedTime(aLastModTime);
+ }
+ int64_t modTime = int64_t(info.modifyTime);
+ if (modTime == 0) {
+ *aLastModTime = 0;
+ } else {
+ *aLastModTime = modTime / int64_t(PR_USEC_PER_MSEC);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalFile::InitWithFile(nsIFile *aFile)
+{
+ NS_ENSURE_ARG(aFile);
+
+ nsAutoCString nativePath;
+ nsresult rv = aFile->GetNativePath(nativePath);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return InitWithNativePath(nativePath);
+}
+
+nsresult
+NS_NewLocalFileWithFSRef(const FSRef* aFSRef, bool aFollowLinks, nsILocalFileMac** result)
+{
+ nsLocalFile* file = new nsLocalFile();
+ if (file == nullptr)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(file);
+
+ file->SetFollowLinks(aFollowLinks);
+
+ nsresult rv = file->InitWithFSRef(aFSRef);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(file);
+ return rv;
+ }
+ *result = file;
+ return NS_OK;
+}
+
+nsresult
+NS_NewLocalFileWithCFURL(const CFURLRef aURL, bool aFollowLinks, nsILocalFileMac** result)
+{
+ nsLocalFile* file = new nsLocalFile();
+ if (!file)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(file);
+
+ file->SetFollowLinks(aFollowLinks);
+
+ nsresult rv = file->InitWithCFURL(aURL);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(file);
+ return rv;
+ }
+ *result = file;
+ return NS_OK;
+}
+
+#endif
diff --git a/xpcom/io/nsLocalFileUnix.h b/xpcom/io/nsLocalFileUnix.h
new file mode 100644
index 000000000..7a99b8d28
--- /dev/null
+++ b/xpcom/io/nsLocalFileUnix.h
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ * Implementation of nsIFile for ``Unixy'' systems.
+ */
+
+#ifndef _nsLocalFileUNIX_H_
+#define _nsLocalFileUNIX_H_
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "nscore.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsIHashable.h"
+#include "nsIClassInfoImpl.h"
+#include "mozilla/Attributes.h"
+#ifdef MOZ_WIDGET_COCOA
+#include "nsILocalFileMac.h"
+#endif
+
+/**
+ * we need these for statfs()
+ */
+#ifdef HAVE_SYS_STATVFS_H
+ #if defined(__osf__) && defined(__DECCXX)
+ extern "C" int statvfs(const char *, struct statvfs *);
+ #endif
+ #include <sys/statvfs.h>
+#endif
+
+#ifdef HAVE_SYS_STATFS_H
+ #include <sys/statfs.h>
+#endif
+
+#ifdef HAVE_SYS_VFS_H
+ #include <sys/vfs.h>
+#endif
+
+#ifdef HAVE_SYS_MOUNT_H
+ #include <sys/param.h>
+ #include <sys/mount.h>
+#endif
+
+#if defined(HAVE_STATVFS64) && (!defined(LINUX) && !defined(__osf__))
+ #define STATFS statvfs64
+ #define F_BSIZE f_frsize
+#elif defined(HAVE_STATVFS) && (!defined(LINUX) && !defined(__osf__))
+ #define STATFS statvfs
+ #define F_BSIZE f_frsize
+#elif defined(HAVE_STATFS64)
+ #define STATFS statfs64
+ #define F_BSIZE f_bsize
+#elif defined(HAVE_STATFS)
+ #define STATFS statfs
+ #define F_BSIZE f_bsize
+#endif
+
+#if defined(HAVE_STAT64) && defined(HAVE_LSTAT64)
+ #if defined (AIX)
+ #if defined STAT
+ #undef STAT
+ #endif
+ #endif
+ #define STAT stat64
+ #define LSTAT lstat64
+ #define HAVE_STATS64 1
+#else
+ #define STAT stat
+ #define LSTAT lstat
+#endif
+
+
+class nsLocalFile MOZ_FINAL :
+#ifdef MOZ_WIDGET_COCOA
+ public nsILocalFileMac,
+#else
+ public nsILocalFile,
+#endif
+ public nsIHashable
+{
+public:
+ NS_DEFINE_STATIC_CID_ACCESSOR(NS_LOCAL_FILE_CID)
+
+ nsLocalFile();
+
+ static nsresult nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIFILE
+ NS_DECL_NSILOCALFILE
+#ifdef MOZ_WIDGET_COCOA
+ NS_DECL_NSILOCALFILEMAC
+#endif
+ NS_DECL_NSIHASHABLE
+
+public:
+ static void GlobalInit();
+ static void GlobalShutdown();
+
+private:
+ nsLocalFile(const nsLocalFile& other);
+ ~nsLocalFile() {}
+
+protected:
+ // This stat cache holds the *last stat* - it does not invalidate.
+ // Call "FillStatCache" whenever you want to stat our file.
+ struct STAT mCachedStat;
+ nsCString mPath;
+
+ void LocateNativeLeafName(nsACString::const_iterator &,
+ nsACString::const_iterator &);
+
+ nsresult CopyDirectoryTo(nsIFile *newParent);
+ nsresult CreateAllAncestors(uint32_t permissions);
+ nsresult GetNativeTargetPathName(nsIFile *newParent,
+ const nsACString &newName,
+ nsACString &_retval);
+
+ bool FillStatCache();
+
+ nsresult CreateAndKeepOpen(uint32_t type, int flags,
+ uint32_t permissions, PRFileDesc **_retval);
+};
+
+#endif /* _nsLocalFileUNIX_H_ */
diff --git a/xpcom/io/nsLocalFileWin.cpp b/xpcom/io/nsLocalFileWin.cpp
new file mode 100644
index 000000000..460106174
--- /dev/null
+++ b/xpcom/io/nsLocalFileWin.cpp
@@ -0,0 +1,3455 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "mozilla/DebugOnly.h"
+#include "mozilla/Util.h"
+
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsMemory.h"
+
+#include "nsLocalFile.h"
+#include "nsIDirectoryEnumerator.h"
+#include "nsNativeCharsetUtils.h"
+
+#include "nsISimpleEnumerator.h"
+#include "nsIComponentManager.h"
+#include "prio.h"
+#include "private/pprio.h" // To get PR_ImportFile
+#include "prprf.h"
+#include "prmem.h"
+#include "nsHashKeys.h"
+
+#include "nsXPIDLString.h"
+#include "nsReadableUtils.h"
+
+#include <direct.h>
+#include <windows.h>
+#include <shlwapi.h>
+#include <aclapi.h>
+
+#include "shellapi.h"
+#include "shlguid.h"
+
+#include <io.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <mbstring.h>
+
+#include "nsXPIDLString.h"
+#include "prproces.h"
+
+#include "mozilla/Mutex.h"
+#include "SpecialSystemDirectory.h"
+
+#include "nsTraceRefcntImpl.h"
+#include "nsXPCOMCIDInternal.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla;
+
+#define CHECK_mWorkingPath() \
+ PR_BEGIN_MACRO \
+ if (mWorkingPath.IsEmpty()) \
+ return NS_ERROR_NOT_INITIALIZED; \
+ PR_END_MACRO
+
+// CopyFileEx only supports unbuffered I/O in Windows Vista and above
+#ifndef COPY_FILE_NO_BUFFERING
+#define COPY_FILE_NO_BUFFERING 0x00001000
+#endif
+
+#ifndef FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
+#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000
+#endif
+
+#ifndef DRIVE_REMOTE
+#define DRIVE_REMOTE 4
+#endif
+
+/**
+ * A runnable to dispatch back to the main thread when
+ * AsyncLocalFileWinOperation completes.
+*/
+class AsyncLocalFileWinDone : public nsRunnable
+{
+public:
+ AsyncLocalFileWinDone() :
+ mWorkerThread(do_GetCurrentThread())
+ {
+ // Objects of this type must only be created on worker threads
+ MOZ_ASSERT(!NS_IsMainThread());
+ }
+
+ NS_IMETHOD Run() {
+ // This event shuts down the worker thread and so must be main thread.
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // If we don't destroy the thread when we're done with it, it will hang
+ // around forever... and that is bad!
+ mWorkerThread->Shutdown();
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIThread> mWorkerThread;
+};
+
+/**
+ * A runnable to dispatch from the main thread when an async operation should
+ * be performed.
+*/
+class AsyncLocalFileWinOperation : public nsRunnable
+{
+public:
+ enum FileOp { RevealOp, LaunchOp };
+
+ AsyncLocalFileWinOperation(AsyncLocalFileWinOperation::FileOp aOperation,
+ const nsAString &aResolvedPath) :
+ mOperation(aOperation),
+ mResolvedPath(aResolvedPath)
+ {
+ }
+
+ NS_IMETHOD Run() {
+ MOZ_ASSERT(!NS_IsMainThread(),
+ "AsyncLocalFileWinOperation should not be run on the main thread!");
+
+ CoInitialize(NULL);
+ switch(mOperation) {
+ case RevealOp: {
+ Reveal();
+ }
+ break;
+ case LaunchOp: {
+ Launch();
+ }
+ break;
+ }
+ CoUninitialize();
+
+ // Send the result back to the main thread so that it can shutdown
+ nsCOMPtr<nsIRunnable> resultrunnable = new AsyncLocalFileWinDone();
+ NS_DispatchToMainThread(resultrunnable);
+ return NS_OK;
+ }
+
+private:
+ // Reveals the path in explorer.
+ nsresult Reveal()
+ {
+ DWORD attributes = GetFileAttributesW(mResolvedPath.get());
+ if (INVALID_FILE_ATTRIBUTES == attributes) {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+
+ HRESULT hr;
+ if (attributes & FILE_ATTRIBUTE_DIRECTORY) {
+ // We have a directory so we should open the directory itself.
+ ITEMIDLIST *dir = ILCreateFromPathW(mResolvedPath.get());
+ if (!dir) {
+ return NS_ERROR_FAILURE;
+ }
+
+ const ITEMIDLIST* selection[] = { dir };
+ UINT count = ArrayLength(selection);
+
+ //Perform the open of the directory.
+ hr = SHOpenFolderAndSelectItems(dir, count, selection, 0);
+ CoTaskMemFree(dir);
+ } else {
+ int32_t len = mResolvedPath.Length();
+ // We don't currently handle UNC long paths of the form \\?\ anywhere so
+ // this should be fine.
+ if (len > MAX_PATH) {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+ WCHAR parentDirectoryPath[MAX_PATH + 1] = { 0 };
+ wcsncpy(parentDirectoryPath, mResolvedPath.get(), MAX_PATH);
+ PathRemoveFileSpecW(parentDirectoryPath);
+
+ // We have a file so we should open the parent directory.
+ ITEMIDLIST *dir = ILCreateFromPathW(parentDirectoryPath);
+ if (!dir) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Set the item in the directory to select to the file we want to reveal.
+ ITEMIDLIST *item = ILCreateFromPathW(mResolvedPath.get());
+ if (!item) {
+ CoTaskMemFree(dir);
+ return NS_ERROR_FAILURE;
+ }
+
+ const ITEMIDLIST* selection[] = { item };
+ UINT count = ArrayLength(selection);
+
+ //Perform the selection of the file.
+ hr = SHOpenFolderAndSelectItems(dir, count, selection, 0);
+
+ CoTaskMemFree(dir);
+ CoTaskMemFree(item);
+ }
+
+ return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
+ }
+
+ // Launches the default shell operation for the file path
+ nsresult Launch()
+ {
+ // use the app registry name to launch a shell execute....
+ SHELLEXECUTEINFOW seinfo;
+ memset(&seinfo, 0, sizeof(seinfo));
+ seinfo.cbSize = sizeof(SHELLEXECUTEINFOW);
+ seinfo.fMask = 0;
+ seinfo.hwnd = NULL;
+ seinfo.lpVerb = NULL;
+ seinfo.lpFile = mResolvedPath.get();
+ seinfo.lpParameters = NULL;
+ seinfo.lpDirectory = NULL;
+ seinfo.nShow = SW_SHOWNORMAL;
+
+ // Use the directory of the file we're launching as the working
+ // directory. That way if we have a self extracting EXE it won't
+ // suggest to extract to the install directory.
+ WCHAR workingDirectory[MAX_PATH + 1] = { L'\0' };
+ wcsncpy(workingDirectory, mResolvedPath.get(), MAX_PATH);
+ if (PathRemoveFileSpecW(workingDirectory)) {
+ seinfo.lpDirectory = workingDirectory;
+ } else {
+ NS_WARNING("Could not set working directory for launched file.");
+ }
+
+ if (ShellExecuteExW(&seinfo)) {
+ return NS_OK;
+ }
+ DWORD r = GetLastError();
+ // if the file has no association, we launch windows'
+ // "what do you want to do" dialog
+ if (r == SE_ERR_NOASSOC) {
+ nsAutoString shellArg;
+ shellArg.Assign(NS_LITERAL_STRING("shell32.dll,OpenAs_RunDLL ") +
+ mResolvedPath);
+ seinfo.lpFile = L"RUNDLL32.EXE";
+ seinfo.lpParameters = shellArg.get();
+ if (ShellExecuteExW(&seinfo))
+ return NS_OK;
+ r = GetLastError();
+ }
+ if (r < 32) {
+ switch (r) {
+ case 0:
+ case SE_ERR_OOM:
+ return NS_ERROR_OUT_OF_MEMORY;
+ case ERROR_FILE_NOT_FOUND:
+ return NS_ERROR_FILE_NOT_FOUND;
+ case ERROR_PATH_NOT_FOUND:
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ case ERROR_BAD_FORMAT:
+ return NS_ERROR_FILE_CORRUPTED;
+ case SE_ERR_ACCESSDENIED:
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ case SE_ERR_ASSOCINCOMPLETE:
+ case SE_ERR_NOASSOC:
+ return NS_ERROR_UNEXPECTED;
+ case SE_ERR_DDEBUSY:
+ case SE_ERR_DDEFAIL:
+ case SE_ERR_DDETIMEOUT:
+ return NS_ERROR_NOT_AVAILABLE;
+ case SE_ERR_DLLNOTFOUND:
+ return NS_ERROR_FAILURE;
+ case SE_ERR_SHARE:
+ return NS_ERROR_FILE_IS_LOCKED;
+ default:
+ return NS_ERROR_FILE_EXECUTION_FAILED;
+ }
+ }
+ return NS_OK;
+ }
+
+ // Stores the operation that will be performed on the thread
+ AsyncLocalFileWinOperation::FileOp mOperation;
+
+ // Stores the path to perform the operation on
+ nsString mResolvedPath;
+};
+
+class nsDriveEnumerator : public nsISimpleEnumerator
+{
+public:
+ nsDriveEnumerator();
+ virtual ~nsDriveEnumerator();
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISIMPLEENUMERATOR
+ nsresult Init();
+private:
+ /* mDrives stores the null-separated drive names.
+ * Init sets them.
+ * HasMoreElements checks mStartOfCurrentDrive.
+ * GetNext advances mStartOfCurrentDrive.
+ */
+ nsString mDrives;
+ nsAString::const_iterator mStartOfCurrentDrive;
+ nsAString::const_iterator mEndOfDrivesString;
+};
+
+//----------------------------------------------------------------------------
+// short cut resolver
+//----------------------------------------------------------------------------
+class ShortcutResolver
+{
+public:
+ ShortcutResolver();
+ // nonvirtual since we're not subclassed
+ ~ShortcutResolver();
+
+ nsresult Init();
+ nsresult Resolve(const WCHAR* in, WCHAR* out);
+ nsresult SetShortcut(bool updateExisting,
+ const WCHAR* shortcutPath,
+ const WCHAR* targetPath,
+ const WCHAR* workingDir,
+ const WCHAR* args,
+ const WCHAR* description,
+ const WCHAR* iconFile,
+ int32_t iconIndex);
+
+private:
+ Mutex mLock;
+ nsRefPtr<IPersistFile> mPersistFile;
+ nsRefPtr<IShellLinkW> mShellLink;
+};
+
+ShortcutResolver::ShortcutResolver() :
+ mLock("ShortcutResolver.mLock")
+{
+ CoInitialize(NULL);
+}
+
+ShortcutResolver::~ShortcutResolver()
+{
+ CoUninitialize();
+}
+
+nsresult
+ShortcutResolver::Init()
+{
+ // Get a pointer to the IPersistFile interface.
+ if (FAILED(CoCreateInstance(CLSID_ShellLink,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IShellLinkW,
+ getter_AddRefs(mShellLink))) ||
+ FAILED(mShellLink->QueryInterface(IID_IPersistFile,
+ getter_AddRefs(mPersistFile)))) {
+ mShellLink = nullptr;
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+// |out| must be an allocated buffer of size MAX_PATH
+nsresult
+ShortcutResolver::Resolve(const WCHAR* in, WCHAR* out)
+{
+ if (!mShellLink)
+ return NS_ERROR_FAILURE;
+
+ MutexAutoLock lock(mLock);
+
+ if (FAILED(mPersistFile->Load(in, STGM_READ)) ||
+ FAILED(mShellLink->Resolve(nullptr, SLR_NO_UI)) ||
+ FAILED(mShellLink->GetPath(out, MAX_PATH, NULL, SLGP_UNCPRIORITY)))
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+}
+
+nsresult
+ShortcutResolver::SetShortcut(bool updateExisting,
+ const WCHAR* shortcutPath,
+ const WCHAR* targetPath,
+ const WCHAR* workingDir,
+ const WCHAR* args,
+ const WCHAR* description,
+ const WCHAR* iconPath,
+ int32_t iconIndex)
+{
+ if (!mShellLink) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!shortcutPath) {
+ return NS_ERROR_FAILURE;
+ }
+
+ MutexAutoLock lock(mLock);
+
+ if (updateExisting) {
+ if (FAILED(mPersistFile->Load(shortcutPath, STGM_READWRITE))) {
+ return NS_ERROR_FAILURE;
+ }
+ } else {
+ if (!targetPath) {
+ return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
+ }
+
+ // Since we reuse our IPersistFile, we have to clear out any values that
+ // may be left over from previous calls to SetShortcut.
+ if (FAILED(mShellLink->SetWorkingDirectory(L""))
+ || FAILED(mShellLink->SetArguments(L""))
+ || FAILED(mShellLink->SetDescription(L""))
+ || FAILED(mShellLink->SetIconLocation(L"", 0))) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ if (targetPath && FAILED(mShellLink->SetPath(targetPath))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (workingDir && FAILED(mShellLink->SetWorkingDirectory(workingDir))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (args && FAILED(mShellLink->SetArguments(args))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (description && FAILED(mShellLink->SetDescription(description))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (iconPath && FAILED(mShellLink->SetIconLocation(iconPath, iconIndex))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (FAILED(mPersistFile->Save(shortcutPath,
+ TRUE))) {
+ // Second argument indicates whether the file path specified in the
+ // first argument should become the "current working file" for this
+ // IPersistFile
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+static ShortcutResolver * gResolver = nullptr;
+
+static nsresult NS_CreateShortcutResolver()
+{
+ gResolver = new ShortcutResolver();
+ if (!gResolver)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ return gResolver->Init();
+}
+
+static void NS_DestroyShortcutResolver()
+{
+ delete gResolver;
+ gResolver = nullptr;
+}
+
+
+//-----------------------------------------------------------------------------
+// static helper functions
+//-----------------------------------------------------------------------------
+
+// certainly not all the error that can be
+// encountered, but many of them common ones
+static nsresult ConvertWinError(DWORD winErr)
+{
+ nsresult rv;
+
+ switch (winErr)
+ {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ case ERROR_INVALID_DRIVE:
+ rv = NS_ERROR_FILE_NOT_FOUND;
+ break;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_NOT_SAME_DEVICE:
+ rv = NS_ERROR_FILE_ACCESS_DENIED;
+ break;
+ case ERROR_SHARING_VIOLATION: // CreateFile without sharing flags
+ case ERROR_LOCK_VIOLATION: // LockFile, LockFileEx
+ rv = NS_ERROR_FILE_IS_LOCKED;
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_INVALID_BLOCK:
+ case ERROR_INVALID_HANDLE:
+ case ERROR_ARENA_TRASHED:
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ break;
+ case ERROR_CURRENT_DIRECTORY:
+ rv = NS_ERROR_FILE_DIR_NOT_EMPTY;
+ break;
+ case ERROR_WRITE_PROTECT:
+ rv = NS_ERROR_FILE_READ_ONLY;
+ break;
+ case ERROR_HANDLE_DISK_FULL:
+ rv = NS_ERROR_FILE_TOO_BIG;
+ break;
+ case ERROR_FILE_EXISTS:
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_CANNOT_MAKE:
+ rv = NS_ERROR_FILE_ALREADY_EXISTS;
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ rv = NS_ERROR_FILE_NAME_TOO_LONG;
+ break;
+ case ERROR_DIRECTORY:
+ rv = NS_ERROR_FILE_NOT_DIRECTORY;
+ break;
+ case 0:
+ rv = NS_OK;
+ break;
+ default:
+ rv = NS_ERROR_FAILURE;
+ break;
+ }
+ return rv;
+}
+
+// as suggested in the MSDN documentation on SetFilePointer
+static __int64
+MyFileSeek64(HANDLE aHandle, __int64 aDistance, DWORD aMoveMethod)
+{
+ LARGE_INTEGER li;
+
+ li.QuadPart = aDistance;
+ li.LowPart = SetFilePointer(aHandle, li.LowPart, &li.HighPart, aMoveMethod);
+ if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
+ {
+ li.QuadPart = -1;
+ }
+
+ return li.QuadPart;
+}
+
+static bool
+IsShortcutPath(const nsAString &path)
+{
+ // Under Windows, the shortcuts are just files with a ".lnk" extension.
+ // Note also that we don't resolve links in the middle of paths.
+ // i.e. "c:\foo.lnk\bar.txt" is invalid.
+ NS_ABORT_IF_FALSE(!path.IsEmpty(), "don't pass an empty string");
+ int32_t len = path.Length();
+ return len >= 4 && (StringTail(path, 4).LowerCaseEqualsASCII(".lnk"));
+}
+
+//-----------------------------------------------------------------------------
+// We need the following three definitions to make |OpenFile| convert a file
+// handle to an NSPR file descriptor correctly when |O_APPEND| flag is
+// specified. It is defined in a private header of NSPR (primpl.h) we can't
+// include. As a temporary workaround until we decide how to extend
+// |PR_ImportFile|, we define it here. Currently, |_PR_HAVE_PEEK_BUFFER|
+// and |PR_STRICT_ADDR_LEN| are not defined for the 'w95'-dependent portion
+// of NSPR so that fields of |PRFilePrivate| #ifdef'd by them are not copied.
+// Similarly, |_MDFileDesc| is taken from nsprpub/pr/include/md/_win95.h.
+// In an unlikely case we switch to 'NT'-dependent NSPR AND this temporary
+// workaround last beyond the switch, |PRFilePrivate| and |_MDFileDesc|
+// need to be changed to match the definitions for WinNT.
+//-----------------------------------------------------------------------------
+typedef enum {
+ _PR_TRI_TRUE = 1,
+ _PR_TRI_FALSE = 0,
+ _PR_TRI_UNKNOWN = -1
+} _PRTriStateBool;
+
+struct _MDFileDesc {
+ PROsfd osfd;
+};
+
+struct PRFilePrivate {
+ int32_t state;
+ bool nonblocking;
+ _PRTriStateBool inheritable;
+ PRFileDesc *next;
+ int lockCount; /* 0: not locked
+ * -1: a native lockfile call is in progress
+ * > 0: # times the file is locked */
+ bool appendMode;
+ _MDFileDesc md;
+};
+
+//-----------------------------------------------------------------------------
+// Six static methods defined below (OpenFile, FileTimeToPRTime, GetFileInfo,
+// OpenDir, CloseDir, ReadDir) should go away once the corresponding
+// UTF-16 APIs are implemented on all the supported platforms (or at least
+// Windows 9x/ME) in NSPR. Currently, they're only implemented on
+// Windows NT4 or later. (bug 330665)
+//-----------------------------------------------------------------------------
+
+// copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} :
+// PR_Open and _PR_MD_OPEN
+static nsresult
+OpenFile(const nsAFlatString &name, int osflags, int mode,
+ PRFileDesc **fd)
+{
+ int32_t access = 0;
+
+ int32_t disposition = 0;
+ int32_t attributes = 0;
+
+ if (osflags & PR_SYNC)
+ attributes = FILE_FLAG_WRITE_THROUGH;
+ if (osflags & PR_RDONLY || osflags & PR_RDWR)
+ access |= GENERIC_READ;
+ if (osflags & PR_WRONLY || osflags & PR_RDWR)
+ access |= GENERIC_WRITE;
+
+ if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL )
+ disposition = CREATE_NEW;
+ else if (osflags & PR_CREATE_FILE) {
+ if (osflags & PR_TRUNCATE)
+ disposition = CREATE_ALWAYS;
+ else
+ disposition = OPEN_ALWAYS;
+ } else {
+ if (osflags & PR_TRUNCATE)
+ disposition = TRUNCATE_EXISTING;
+ else
+ disposition = OPEN_EXISTING;
+ }
+
+ if (osflags & nsIFile::DELETE_ON_CLOSE) {
+ attributes |= FILE_FLAG_DELETE_ON_CLOSE;
+ }
+
+ if (osflags & nsIFile::OS_READAHEAD) {
+ attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
+ }
+
+ // If no write permissions are requested, and if we are possibly creating
+ // the file, then set the new file as read only.
+ // The flag has no effect if we happen to open the file.
+ if (!(mode & (PR_IWUSR | PR_IWGRP | PR_IWOTH)) &&
+ disposition != OPEN_EXISTING) {
+ attributes |= FILE_ATTRIBUTE_READONLY;
+ }
+
+ HANDLE file = ::CreateFileW(name.get(), access,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL, disposition, attributes, NULL);
+
+ if (file == INVALID_HANDLE_VALUE) {
+ *fd = nullptr;
+ return ConvertWinError(GetLastError());
+ }
+
+ *fd = PR_ImportFile((PROsfd) file);
+ if (*fd) {
+ // On Windows, _PR_HAVE_O_APPEND is not defined so that we have to
+ // add it manually. (see |PR_Open| in nsprpub/pr/src/io/prfile.c)
+ (*fd)->secret->appendMode = (PR_APPEND & osflags) ? true : false;
+ return NS_OK;
+ }
+
+ nsresult rv = NS_ErrorAccordingToNSPR();
+
+ CloseHandle(file);
+
+ return rv;
+}
+
+// copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} :
+// PR_FileTimeToPRTime and _PR_FileTimeToPRTime
+static
+void FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm)
+{
+#ifdef __GNUC__
+ const PRTime _pr_filetime_offset = 116444736000000000LL;
+#else
+ const PRTime _pr_filetime_offset = 116444736000000000i64;
+#endif
+
+ PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
+ ::CopyMemory(prtm, filetime, sizeof(PRTime));
+#ifdef __GNUC__
+ *prtm = (*prtm - _pr_filetime_offset) / 10LL;
+#else
+ *prtm = (*prtm - _pr_filetime_offset) / 10i64;
+#endif
+}
+
+// copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} with some
+// changes : PR_GetFileInfo64, _PR_MD_GETFILEINFO64
+static nsresult
+GetFileInfo(const nsAFlatString &name, PRFileInfo64 *info)
+{
+ WIN32_FILE_ATTRIBUTE_DATA fileData;
+
+ if (name.IsEmpty() || name.FindCharInSet(L"?*") != kNotFound)
+ return NS_ERROR_INVALID_ARG;
+
+ if (!::GetFileAttributesExW(name.get(), GetFileExInfoStandard, &fileData))
+ return ConvertWinError(GetLastError());
+
+ if (fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ info->type = PR_FILE_DIRECTORY;
+ } else {
+ info->type = PR_FILE_FILE;
+ }
+
+ info->size = fileData.nFileSizeHigh;
+ info->size = (info->size << 32) + fileData.nFileSizeLow;
+
+ FileTimeToPRTime(&fileData.ftLastWriteTime, &info->modifyTime);
+
+ if (0 == fileData.ftCreationTime.dwLowDateTime &&
+ 0 == fileData.ftCreationTime.dwHighDateTime) {
+ info->creationTime = info->modifyTime;
+ } else {
+ FileTimeToPRTime(&fileData.ftCreationTime, &info->creationTime);
+ }
+
+ return NS_OK;
+}
+
+struct nsDir
+{
+ HANDLE handle;
+ WIN32_FIND_DATAW data;
+ bool firstEntry;
+};
+
+static nsresult
+OpenDir(const nsAFlatString &name, nsDir * *dir)
+{
+ NS_ENSURE_ARG_POINTER(dir);
+
+ *dir = nullptr;
+ if (name.Length() + 3 >= MAX_PATH)
+ return NS_ERROR_FILE_NAME_TOO_LONG;
+
+ nsDir *d = PR_NEW(nsDir);
+ if (!d)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsAutoString filename(name);
+
+ //If 'name' ends in a slash or backslash, do not append
+ //another backslash.
+ if (filename.Last() == L'/' || filename.Last() == L'\\')
+ filename.Append('*');
+ else
+ filename.AppendLiteral("\\*");
+
+ filename.ReplaceChar(L'/', L'\\');
+
+ // FindFirstFileW Will have a last error of ERROR_DIRECTORY if
+ // <file_path>\* is passed in. If <unknown_path>\* is passed in then
+ // ERROR_PATH_NOT_FOUND will be the last error.
+ d->handle = ::FindFirstFileW(filename.get(), &(d->data) );
+
+ if (d->handle == INVALID_HANDLE_VALUE) {
+ PR_Free(d);
+ return ConvertWinError(GetLastError());
+ }
+ d->firstEntry = true;
+
+ *dir = d;
+ return NS_OK;
+}
+
+static nsresult
+ReadDir(nsDir *dir, PRDirFlags flags, nsString& name)
+{
+ name.Truncate();
+ NS_ENSURE_ARG(dir);
+
+ while (1) {
+ BOOL rv;
+ if (dir->firstEntry)
+ {
+ dir->firstEntry = false;
+ rv = 1;
+ } else
+ rv = ::FindNextFileW(dir->handle, &(dir->data));
+
+ if (rv == 0)
+ break;
+
+ const PRUnichar *fileName;
+ nsString tmp;
+ fileName = (dir)->data.cFileName;
+
+ if ((flags & PR_SKIP_DOT) &&
+ (fileName[0] == L'.') && (fileName[1] == L'\0'))
+ continue;
+ if ((flags & PR_SKIP_DOT_DOT) &&
+ (fileName[0] == L'.') && (fileName[1] == L'.') &&
+ (fileName[2] == L'\0'))
+ continue;
+
+ DWORD attrib = dir->data.dwFileAttributes;
+ if ((flags & PR_SKIP_HIDDEN) && (attrib & FILE_ATTRIBUTE_HIDDEN))
+ continue;
+
+ if (fileName == tmp.get())
+ name = tmp;
+ else
+ name = fileName;
+ return NS_OK;
+ }
+
+ DWORD err = GetLastError();
+ return err == ERROR_NO_MORE_FILES ? NS_OK : ConvertWinError(err);
+}
+
+static nsresult
+CloseDir(nsDir *&d)
+{
+ NS_ENSURE_ARG(d);
+
+ BOOL isOk = FindClose(d->handle);
+ // PR_DELETE also nulls out the passed in pointer.
+ PR_DELETE(d);
+ return isOk ? NS_OK : ConvertWinError(GetLastError());
+}
+
+//-----------------------------------------------------------------------------
+// nsDirEnumerator
+//-----------------------------------------------------------------------------
+
+class nsDirEnumerator MOZ_FINAL : public nsISimpleEnumerator,
+ public nsIDirectoryEnumerator
+{
+ public:
+
+ NS_DECL_ISUPPORTS
+
+ nsDirEnumerator() : mDir(nullptr)
+ {
+ }
+
+ nsresult Init(nsIFile* parent)
+ {
+ nsAutoString filepath;
+ parent->GetTarget(filepath);
+
+ if (filepath.IsEmpty())
+ {
+ parent->GetPath(filepath);
+ }
+
+ if (filepath.IsEmpty())
+ {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // IsDirectory is not needed here because OpenDir will return
+ // NS_ERROR_FILE_NOT_DIRECTORY if the passed in path is a file.
+ nsresult rv = OpenDir(filepath, &mDir);
+ if (NS_FAILED(rv))
+ return rv;
+
+ mParent = parent;
+ return NS_OK;
+ }
+
+ NS_IMETHOD HasMoreElements(bool *result)
+ {
+ nsresult rv;
+ if (mNext == nullptr && mDir)
+ {
+ nsString name;
+ rv = ReadDir(mDir, PR_SKIP_BOTH, name);
+ if (NS_FAILED(rv))
+ return rv;
+ if (name.IsEmpty())
+ {
+ // end of dir entries
+ if (NS_FAILED(CloseDir(mDir)))
+ return NS_ERROR_FAILURE;
+
+ *result = false;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIFile> file;
+ rv = mParent->Clone(getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = file->Append(name);
+ if (NS_FAILED(rv))
+ return rv;
+
+ mNext = do_QueryInterface(file);
+ }
+ *result = mNext != nullptr;
+ if (!*result)
+ Close();
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetNext(nsISupports **result)
+ {
+ nsresult rv;
+ bool hasMore;
+ rv = HasMoreElements(&hasMore);
+ if (NS_FAILED(rv)) return rv;
+
+ *result = mNext; // might return nullptr
+ NS_IF_ADDREF(*result);
+
+ mNext = nullptr;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetNextFile(nsIFile **result)
+ {
+ *result = nullptr;
+ bool hasMore = false;
+ nsresult rv = HasMoreElements(&hasMore);
+ if (NS_FAILED(rv) || !hasMore)
+ return rv;
+ *result = mNext;
+ NS_IF_ADDREF(*result);
+ mNext = nullptr;
+ return NS_OK;
+ }
+
+ NS_IMETHOD Close()
+ {
+ if (mDir)
+ {
+ nsresult rv = CloseDir(mDir);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "close failed");
+ if (NS_FAILED(rv))
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+ }
+
+ // dtor can be non-virtual since there are no subclasses, but must be
+ // public to use the class on the stack.
+ ~nsDirEnumerator()
+ {
+ Close();
+ }
+
+ protected:
+ nsDir* mDir;
+ nsCOMPtr<nsIFile> mParent;
+ nsCOMPtr<nsIFile> mNext;
+};
+
+NS_IMPL_ISUPPORTS2(nsDirEnumerator, nsISimpleEnumerator, nsIDirectoryEnumerator)
+
+
+//-----------------------------------------------------------------------------
+// nsLocalFile <public>
+//-----------------------------------------------------------------------------
+
+nsLocalFile::nsLocalFile()
+ : mDirty(true)
+ , mResolveDirty(true)
+ , mFollowSymlinks(false)
+{
+}
+
+nsresult
+nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
+{
+ NS_ENSURE_ARG_POINTER(aInstancePtr);
+ NS_ENSURE_NO_AGGREGATION(outer);
+
+ nsLocalFile* inst = new nsLocalFile();
+ if (inst == NULL)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
+ if (NS_FAILED(rv))
+ {
+ delete inst;
+ return rv;
+ }
+ return NS_OK;
+}
+
+
+//-----------------------------------------------------------------------------
+// nsLocalFile::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_THREADSAFE_ISUPPORTS4(nsLocalFile,
+ nsILocalFile,
+ nsIFile,
+ nsILocalFileWin,
+ nsIHashable)
+
+
+//-----------------------------------------------------------------------------
+// nsLocalFile <private>
+//-----------------------------------------------------------------------------
+
+nsLocalFile::nsLocalFile(const nsLocalFile& other)
+ : mDirty(true)
+ , mResolveDirty(true)
+ , mFollowSymlinks(other.mFollowSymlinks)
+ , mWorkingPath(other.mWorkingPath)
+{
+}
+
+// Resolve the shortcut file from mWorkingPath and write the path
+// it points to into mResolvedPath.
+nsresult
+nsLocalFile::ResolveShortcut()
+{
+ // we can't do anything without the resolver
+ if (!gResolver)
+ return NS_ERROR_FAILURE;
+
+ mResolvedPath.SetLength(MAX_PATH);
+ if (mResolvedPath.Length() != MAX_PATH)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ PRUnichar *resolvedPath = mResolvedPath.BeginWriting();
+
+ // resolve this shortcut
+ nsresult rv = gResolver->Resolve(mWorkingPath.get(), resolvedPath);
+
+ size_t len = NS_FAILED(rv) ? 0 : wcslen(resolvedPath);
+ mResolvedPath.SetLength(len);
+
+ return rv;
+}
+
+// Resolve any shortcuts and stat the resolved path. After a successful return
+// the path is guaranteed valid and the members of mFileInfo64 can be used.
+nsresult
+nsLocalFile::ResolveAndStat()
+{
+ // if we aren't dirty then we are already done
+ if (!mDirty)
+ return NS_OK;
+
+ // we can't resolve/stat anything that isn't a valid NSPR addressable path
+ if (mWorkingPath.IsEmpty())
+ return NS_ERROR_FILE_INVALID_PATH;
+
+ // this is usually correct
+ mResolvedPath.Assign(mWorkingPath);
+
+ // slutty hack designed to work around bug 134796 until it is fixed
+ nsAutoString nsprPath(mWorkingPath.get());
+ if (mWorkingPath.Length() == 2 && mWorkingPath.CharAt(1) == L':')
+ nsprPath.Append('\\');
+
+ // first we will see if the working path exists. If it doesn't then
+ // there is nothing more that can be done
+ nsresult rv = GetFileInfo(nsprPath, &mFileInfo64);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // if this isn't a shortcut file or we aren't following symlinks then we're done
+ if (!mFollowSymlinks
+ || mFileInfo64.type != PR_FILE_FILE
+ || !IsShortcutPath(mWorkingPath))
+ {
+ mDirty = false;
+ mResolveDirty = false;
+ return NS_OK;
+ }
+
+ // we need to resolve this shortcut to what it points to, this will
+ // set mResolvedPath. Even if it fails we need to have the resolved
+ // path equal to working path for those functions that always use
+ // the resolved path.
+ rv = ResolveShortcut();
+ if (NS_FAILED(rv))
+ {
+ mResolvedPath.Assign(mWorkingPath);
+ return rv;
+ }
+ mResolveDirty = false;
+
+ // get the details of the resolved path
+ rv = GetFileInfo(mResolvedPath, &mFileInfo64);
+ if (NS_FAILED(rv))
+ return rv;
+
+ mDirty = false;
+ return NS_OK;
+}
+
+/**
+ * Fills the mResolvedPath member variable with the file or symlink target
+ * if follow symlinks is on. This is a copy of the Resolve parts from
+ * ResolveAndStat. ResolveAndStat is much slower though because of the stat.
+ *
+ * @return NS_OK on success.
+*/
+nsresult
+nsLocalFile::Resolve()
+{
+ // if we aren't dirty then we are already done
+ if (!mResolveDirty) {
+ return NS_OK;
+ }
+
+ // we can't resolve/stat anything that isn't a valid NSPR addressable path
+ if (mWorkingPath.IsEmpty()) {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+
+ // this is usually correct
+ mResolvedPath.Assign(mWorkingPath);
+
+ // if this isn't a shortcut file or we aren't following symlinks then
+ // we're done.
+ if (!mFollowSymlinks ||
+ !IsShortcutPath(mWorkingPath)) {
+ mResolveDirty = false;
+ return NS_OK;
+ }
+
+ // we need to resolve this shortcut to what it points to, this will
+ // set mResolvedPath. Even if it fails we need to have the resolved
+ // path equal to working path for those functions that always use
+ // the resolved path.
+ nsresult rv = ResolveShortcut();
+ if (NS_FAILED(rv)) {
+ mResolvedPath.Assign(mWorkingPath);
+ return rv;
+ }
+
+ mResolveDirty = false;
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsLocalFile::nsIFile,nsILocalFile
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsLocalFile::Clone(nsIFile **file)
+{
+ // Just copy-construct ourselves
+ *file = new nsLocalFile(*this);
+ if (!*file)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*file);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::InitWithFile(nsIFile *aFile)
+{
+ NS_ENSURE_ARG(aFile);
+
+ nsAutoString path;
+ aFile->GetPath(path);
+ if (path.IsEmpty())
+ return NS_ERROR_INVALID_ARG;
+ return InitWithPath(path);
+}
+
+NS_IMETHODIMP
+nsLocalFile::InitWithPath(const nsAString &filePath)
+{
+ MakeDirty();
+
+ nsAString::const_iterator begin, end;
+ filePath.BeginReading(begin);
+ filePath.EndReading(end);
+
+ // input string must not be empty
+ if (begin == end)
+ return NS_ERROR_FAILURE;
+
+ PRUnichar firstChar = *begin;
+ PRUnichar secondChar = *(++begin);
+
+ // just do a sanity check. if it has any forward slashes, it is not a Native path
+ // on windows. Also, it must have a colon at after the first char.
+ if (FindCharInReadable(L'/', begin, end))
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ if (secondChar != L':' && (secondChar != L'\\' || firstChar != L'\\'))
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ if (secondChar == L':') {
+ // Make sure we have a valid drive, later code assumes the drive letter
+ // is a single char a-z or A-Z.
+ if (PathGetDriveNumberW(filePath.Data()) == -1) {
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+ }
+
+ mWorkingPath = filePath;
+ // kill any trailing '\'
+ if (mWorkingPath.Last() == L'\\')
+ mWorkingPath.Truncate(mWorkingPath.Length() - 1);
+
+ return NS_OK;
+
+}
+
+NS_IMETHODIMP
+nsLocalFile::OpenNSPRFileDesc(int32_t flags, int32_t mode, PRFileDesc **_retval)
+{
+ nsresult rv = Resolve();
+ if (NS_FAILED(rv))
+ return rv;
+
+ return OpenFile(mResolvedPath, flags, mode, _retval);
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::OpenANSIFileDesc(const char *mode, FILE * *_retval)
+{
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
+ return rv;
+
+ *_retval = _wfopen(mResolvedPath.get(), NS_ConvertASCIItoUTF16(mode).get());
+ if (*_retval)
+ return NS_OK;
+
+ return NS_ERROR_FAILURE;
+}
+
+
+
+NS_IMETHODIMP
+nsLocalFile::Create(uint32_t type, uint32_t attributes)
+{
+ if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
+ return NS_ERROR_FILE_UNKNOWN_TYPE;
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
+ return rv;
+
+ // create directories to target
+ //
+ // A given local file can be either one of these forms:
+ //
+ // - normal: X:\some\path\on\this\drive
+ // ^--- start here
+ //
+ // - UNC path: \\machine\volume\some\path\on\this\drive
+ // ^--- start here
+ //
+ // Skip the first 'X:\' for the first form, and skip the first full
+ // '\\machine\volume\' segment for the second form.
+
+ PRUnichar* path = mResolvedPath.BeginWriting();
+
+ if (path[0] == L'\\' && path[1] == L'\\')
+ {
+ // dealing with a UNC path here; skip past '\\machine\'
+ path = wcschr(path + 2, L'\\');
+ if (!path)
+ return NS_ERROR_FILE_INVALID_PATH;
+ ++path;
+ }
+
+ // search for first slash after the drive (or volume) name
+ PRUnichar* slash = wcschr(path, L'\\');
+
+ nsresult directoryCreateError = NS_OK;
+ if (slash)
+ {
+ // skip the first '\\'
+ ++slash;
+ slash = wcschr(slash, L'\\');
+
+ while (slash)
+ {
+ *slash = L'\0';
+
+ if (!::CreateDirectoryW(mResolvedPath.get(), NULL)) {
+ rv = ConvertWinError(GetLastError());
+ if (NS_ERROR_FILE_NOT_FOUND == rv &&
+ NS_ERROR_FILE_ACCESS_DENIED == directoryCreateError) {
+ // If a previous CreateDirectory failed due to access, return that.
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ }
+ // perhaps the base path already exists, or perhaps we don't have
+ // permissions to create the directory. NOTE: access denied could
+ // occur on a parent directory even though it exists.
+ else if (NS_ERROR_FILE_ALREADY_EXISTS != rv &&
+ NS_ERROR_FILE_ACCESS_DENIED != rv) {
+ return rv;
+ }
+
+ directoryCreateError = rv;
+ }
+ *slash = L'\\';
+ ++slash;
+ slash = wcschr(slash, L'\\');
+ }
+ }
+
+ if (type == NORMAL_FILE_TYPE)
+ {
+ PRFileDesc* file;
+ rv = OpenFile(mResolvedPath,
+ PR_RDONLY | PR_CREATE_FILE | PR_APPEND | PR_EXCL, attributes,
+ &file);
+ if (file)
+ PR_Close(file);
+
+ if (rv == NS_ERROR_FILE_ACCESS_DENIED)
+ {
+ // need to return already-exists for directories (bug 452217)
+ bool isdir;
+ if (NS_SUCCEEDED(IsDirectory(&isdir)) && isdir)
+ rv = NS_ERROR_FILE_ALREADY_EXISTS;
+ } else if (NS_ERROR_FILE_NOT_FOUND == rv &&
+ NS_ERROR_FILE_ACCESS_DENIED == directoryCreateError) {
+ // If a previous CreateDirectory failed due to access, return that.
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ }
+ return rv;
+ }
+
+ if (type == DIRECTORY_TYPE)
+ {
+ if (!::CreateDirectoryW(mResolvedPath.get(), NULL)) {
+ rv = ConvertWinError(GetLastError());
+ if (NS_ERROR_FILE_NOT_FOUND == rv &&
+ NS_ERROR_FILE_ACCESS_DENIED == directoryCreateError) {
+ // If a previous CreateDirectory failed due to access, return that.
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ } else {
+ return rv;
+ }
+ }
+ else
+ return NS_OK;
+ }
+
+ return NS_ERROR_FILE_UNKNOWN_TYPE;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::Append(const nsAString &node)
+{
+ // append this path, multiple components are not permitted
+ return AppendInternal(PromiseFlatString(node), false);
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendRelativePath(const nsAString &node)
+{
+ // append this path, multiple components are permitted
+ return AppendInternal(PromiseFlatString(node), true);
+}
+
+
+nsresult
+nsLocalFile::AppendInternal(const nsAFlatString &node, bool multipleComponents)
+{
+ if (node.IsEmpty())
+ return NS_OK;
+
+ // check the relative path for validity
+ if (node.First() == L'\\' // can't start with an '\'
+ || node.FindChar(L'/') != kNotFound // can't contain /
+ || node.EqualsASCII("..")) // can't be ..
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ if (multipleComponents)
+ {
+ // can't contain .. as a path component. Ensure that the valid components
+ // "foo..foo", "..foo", and "foo.." are not falsely detected,
+ // but the invalid paths "..\", "foo\..", "foo\..\foo",
+ // "..\foo", etc are.
+ NS_NAMED_LITERAL_STRING(doubleDot, "\\..");
+ nsAString::const_iterator start, end, offset;
+ node.BeginReading(start);
+ node.EndReading(end);
+ offset = end;
+ while (FindInReadable(doubleDot, start, offset))
+ {
+ if (offset == end || *offset == L'\\')
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ start = offset;
+ offset = end;
+ }
+
+ // catches the remaining cases of prefixes
+ if (StringBeginsWith(node, NS_LITERAL_STRING("..\\")))
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+ // single components can't contain '\'
+ else if (node.FindChar(L'\\') != kNotFound)
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ MakeDirty();
+
+ mWorkingPath.Append(NS_LITERAL_STRING("\\") + node);
+
+ return NS_OK;
+}
+
+#define TOUPPER(u) (((u) >= L'a' && (u) <= L'z') ? \
+ (u) - (L'a' - L'A') : (u))
+
+NS_IMETHODIMP
+nsLocalFile::Normalize()
+{
+ // XXX See bug 187957 comment 18 for possible problems with this implementation.
+
+ if (mWorkingPath.IsEmpty())
+ return NS_OK;
+
+ nsAutoString path(mWorkingPath);
+
+ // find the index of the root backslash for the path. Everything before
+ // this is considered fully normalized and cannot be ascended beyond
+ // using ".." For a local drive this is the first slash (e.g. "c:\").
+ // For a UNC path it is the slash following the share name
+ // (e.g. "\\server\share\").
+ int32_t rootIdx = 2; // default to local drive
+ if (path.First() == L'\\') // if a share then calculate the rootIdx
+ {
+ rootIdx = path.FindChar(L'\\', 2); // skip \\ in front of the server
+ if (rootIdx == kNotFound)
+ return NS_OK; // already normalized
+ rootIdx = path.FindChar(L'\\', rootIdx+1);
+ if (rootIdx == kNotFound)
+ return NS_OK; // already normalized
+ }
+ else if (path.CharAt(rootIdx) != L'\\')
+ {
+ // The path has been specified relative to the current working directory
+ // for that drive. To normalize it, the current working directory for
+ // that drive needs to be inserted before the supplied relative path
+ // which will provide an absolute path (and the rootIdx will still be 2).
+ WCHAR cwd[MAX_PATH];
+ WCHAR * pcwd = cwd;
+ int drive = TOUPPER(path.First()) - 'A' + 1;
+ /* We need to worry about IPH, for details read bug 419326.
+ * _getdrives - http://msdn2.microsoft.com/en-us/library/xdhk0xd2.aspx
+ * uses a bitmask, bit 0 is 'a:'
+ * _chdrive - http://msdn2.microsoft.com/en-us/library/0d1409hb.aspx
+ * _getdcwd - http://msdn2.microsoft.com/en-us/library/7t2zk3s4.aspx
+ * take an int, 1 is 'a:'.
+ *
+ * Because of this, we need to do some math. Subtract 1 to convert from
+ * _chdrive/_getdcwd format to _getdrives drive numbering.
+ * Shift left x bits to convert from integer indexing to bitfield indexing.
+ * And of course, we need to find out if the drive is in the bitmask.
+ *
+ * If we're really unlucky, we can still lose, but only if the user
+ * manages to eject the drive between our call to _getdrives() and
+ * our *calls* to _wgetdcwd.
+ */
+ if (!((1 << (drive - 1)) & _getdrives()))
+ return NS_ERROR_FILE_INVALID_PATH;
+ if (!_wgetdcwd(drive, pcwd, MAX_PATH))
+ pcwd = _wgetdcwd(drive, 0, 0);
+ if (!pcwd)
+ return NS_ERROR_OUT_OF_MEMORY;
+ nsAutoString currentDir(pcwd);
+ if (pcwd != cwd)
+ free(pcwd);
+
+ if (currentDir.Last() == '\\')
+ path.Replace(0, 2, currentDir);
+ else
+ path.Replace(0, 2, currentDir + NS_LITERAL_STRING("\\"));
+ }
+ NS_POSTCONDITION(0 < rootIdx && rootIdx < (int32_t)path.Length(), "rootIdx is invalid");
+ NS_POSTCONDITION(path.CharAt(rootIdx) == '\\', "rootIdx is invalid");
+
+ // if there is nothing following the root path then it is already normalized
+ if (rootIdx + 1 == (int32_t)path.Length())
+ return NS_OK;
+
+ // assign the root
+ const PRUnichar * pathBuffer = path.get(); // simplify access to the buffer
+ mWorkingPath.SetCapacity(path.Length()); // it won't ever grow longer
+ mWorkingPath.Assign(pathBuffer, rootIdx);
+
+ // Normalize the path components. The actions taken are:
+ //
+ // "\\" condense to single backslash
+ // "." remove from path
+ // ".." up a directory
+ // "..." remove from path (any number of dots > 2)
+ //
+ // The last form is something that Windows 95 and 98 supported and
+ // is a shortcut for changing up multiple directories. Windows XP
+ // and ilk ignore it in a path, as is done here.
+ int32_t len, begin, end = rootIdx;
+ while (end < (int32_t)path.Length())
+ {
+ // find the current segment (text between the backslashes) to
+ // be examined, this will set the following variables:
+ // begin == index of first char in segment
+ // end == index 1 char after last char in segment
+ // len == length of segment
+ begin = end + 1;
+ end = path.FindChar('\\', begin);
+ if (end == kNotFound)
+ end = path.Length();
+ len = end - begin;
+
+ // ignore double backslashes
+ if (len == 0)
+ continue;
+
+ // len != 0, and interesting paths always begin with a dot
+ if (pathBuffer[begin] == '.')
+ {
+ // ignore single dots
+ if (len == 1)
+ continue;
+
+ // handle multiple dots
+ if (len >= 2 && pathBuffer[begin+1] == L'.')
+ {
+ // back up a path component on double dot
+ if (len == 2)
+ {
+ int32_t prev = mWorkingPath.RFindChar('\\');
+ if (prev >= rootIdx)
+ mWorkingPath.Truncate(prev);
+ continue;
+ }
+
+ // length is > 2 and the first two characters are dots.
+ // if the rest of the string is dots, then ignore it.
+ int idx = len - 1;
+ for (; idx >= 2; --idx)
+ {
+ if (pathBuffer[begin+idx] != L'.')
+ break;
+ }
+
+ // this is true if the loop above didn't break
+ // and all characters in this segment are dots.
+ if (idx < 2)
+ continue;
+ }
+ }
+
+ // add the current component to the path, including the preceding backslash
+ mWorkingPath.Append(pathBuffer + begin - 1, len + 1);
+ }
+
+ // kill trailing dots and spaces.
+ int32_t filePathLen = mWorkingPath.Length() - 1;
+ while(filePathLen > 0 && (mWorkingPath[filePathLen] == L' ' ||
+ mWorkingPath[filePathLen] == L'.'))
+ {
+ mWorkingPath.Truncate(filePathLen--);
+ }
+
+ MakeDirty();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetLeafName(nsAString &aLeafName)
+{
+ aLeafName.Truncate();
+
+ if (mWorkingPath.IsEmpty())
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ int32_t offset = mWorkingPath.RFindChar(L'\\');
+
+ // if the working path is just a node without any lashes.
+ if (offset == kNotFound)
+ aLeafName = mWorkingPath;
+ else
+ aLeafName = Substring(mWorkingPath, offset + 1);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetLeafName(const nsAString &aLeafName)
+{
+ MakeDirty();
+
+ if (mWorkingPath.IsEmpty())
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ // cannot use nsCString::RFindChar() due to 0x5c problem
+ int32_t offset = mWorkingPath.RFindChar(L'\\');
+ if (offset)
+ {
+ mWorkingPath.Truncate(offset+1);
+ }
+ mWorkingPath.Append(aLeafName);
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetPath(nsAString &_retval)
+{
+ _retval = mWorkingPath;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetCanonicalPath(nsAString &aResult)
+{
+ EnsureShortPath();
+ aResult.Assign(mShortWorkingPath);
+ return NS_OK;
+}
+
+typedef struct {
+ WORD wLanguage;
+ WORD wCodePage;
+} LANGANDCODEPAGE;
+
+NS_IMETHODIMP
+nsLocalFile::GetVersionInfoField(const char* aField, nsAString& _retval)
+{
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = NS_ERROR_FAILURE;
+
+ // Cast away const-ness here because WinAPI functions don't understand it,
+ // the path is used for [in] parameters only however so it's safe.
+ WCHAR *path = const_cast<WCHAR*>(mFollowSymlinks ? mResolvedPath.get()
+ : mWorkingPath.get());
+
+ DWORD dummy;
+ DWORD size = ::GetFileVersionInfoSizeW(path, &dummy);
+ if (!size)
+ return rv;
+
+ void* ver = calloc(size, 1);
+ if (!ver)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (::GetFileVersionInfoW(path, 0, size, ver))
+ {
+ LANGANDCODEPAGE* translate = nullptr;
+ UINT pageCount;
+ BOOL queryResult = ::VerQueryValueW(ver, L"\\VarFileInfo\\Translation",
+ (void**)&translate, &pageCount);
+ if (queryResult && translate)
+ {
+ for (int32_t i = 0; i < 2; ++i)
+ {
+ PRUnichar subBlock[MAX_PATH];
+ _snwprintf(subBlock, MAX_PATH,
+ L"\\StringFileInfo\\%04x%04x\\%s",
+ (i == 0 ? translate[0].wLanguage
+ : ::GetUserDefaultLangID()),
+ translate[0].wCodePage,
+ NS_ConvertASCIItoUTF16(
+ nsDependentCString(aField)).get());
+ subBlock[MAX_PATH - 1] = 0;
+ LPVOID value = nullptr;
+ UINT size;
+ queryResult = ::VerQueryValueW(ver, subBlock, &value, &size);
+ if (queryResult && value)
+ {
+ _retval.Assign(static_cast<PRUnichar*>(value));
+ if (!_retval.IsEmpty())
+ {
+ rv = NS_OK;
+ break;
+ }
+ }
+ }
+ }
+ }
+ free(ver);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetShortcut(nsIFile* targetFile,
+ nsIFile* workingDir,
+ const PRUnichar* args,
+ const PRUnichar* description,
+ nsIFile* iconFile,
+ int32_t iconIndex)
+{
+ bool exists;
+ nsresult rv = this->Exists(&exists);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ const WCHAR* targetFilePath = NULL;
+ const WCHAR* workingDirPath = NULL;
+ const WCHAR* iconFilePath = NULL;
+
+ nsAutoString targetFilePathAuto;
+ if (targetFile) {
+ rv = targetFile->GetPath(targetFilePathAuto);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ targetFilePath = targetFilePathAuto.get();
+ }
+
+ nsAutoString workingDirPathAuto;
+ if (workingDir) {
+ rv = workingDir->GetPath(workingDirPathAuto);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ workingDirPath = workingDirPathAuto.get();
+ }
+
+ nsAutoString iconPathAuto;
+ if (iconFile) {
+ rv = iconFile->GetPath(iconPathAuto);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ iconFilePath = iconPathAuto.get();
+ }
+
+ rv = gResolver->SetShortcut(exists,
+ mWorkingPath.get(),
+ targetFilePath,
+ workingDirPath,
+ args,
+ description,
+ iconFilePath,
+ iconFilePath? iconIndex : 0);
+ if (targetFilePath && NS_SUCCEEDED(rv)) {
+ MakeDirty();
+ }
+
+ return rv;
+}
+
+/**
+ * Determines if the drive type for the specified file is rmeote or local.
+ *
+ * @param path The path of the file to check
+ * @param remote Out parameter, on function success holds true if the specified
+ * file path is remote, or false if the file path is local.
+ * @return true on success. The return value implies absolutely nothing about
+ * wether the file is local or remote.
+*/
+static bool
+IsRemoteFilePath(LPCWSTR path, bool &remote)
+{
+ // Obtain the parent directory path and make sure it ends with
+ // a trailing backslash.
+ WCHAR dirPath[MAX_PATH + 1] = { 0 };
+ wcsncpy(dirPath, path, MAX_PATH);
+ if (!PathRemoveFileSpecW(dirPath)) {
+ return false;
+ }
+ size_t len = wcslen(dirPath);
+ // In case the dirPath holds exaclty MAX_PATH and remains unchanged, we
+ // recheck the required length here since we need to terminate it with
+ // a backslash.
+ if (len >= MAX_PATH) {
+ return false;
+ }
+
+ dirPath[len] = L'\\';
+ dirPath[len + 1] = L'\0';
+ UINT driveType = GetDriveTypeW(dirPath);
+ remote = driveType == DRIVE_REMOTE;
+ return true;
+}
+
+nsresult
+nsLocalFile::CopySingleFile(nsIFile *sourceFile, nsIFile *destParent,
+ const nsAString &newName,
+ bool followSymlinks, bool move,
+ bool skipNtfsAclReset)
+{
+ nsresult rv;
+ nsAutoString filePath;
+
+ // get the path that we are going to copy to.
+ // Since windows does not know how to auto
+ // resolve shortcuts, we must work with the
+ // target.
+ nsAutoString destPath;
+ destParent->GetTarget(destPath);
+
+ destPath.Append('\\');
+
+ if (newName.IsEmpty())
+ {
+ nsAutoString aFileName;
+ sourceFile->GetLeafName(aFileName);
+ destPath.Append(aFileName);
+ }
+ else
+ {
+ destPath.Append(newName);
+ }
+
+
+ if (followSymlinks)
+ {
+ rv = sourceFile->GetTarget(filePath);
+ if (filePath.IsEmpty())
+ rv = sourceFile->GetPath(filePath);
+ }
+ else
+ {
+ rv = sourceFile->GetPath(filePath);
+ }
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Pass the flag COPY_FILE_NO_BUFFERING to CopyFileEx as we may be copying
+ // to a SMBV2 remote drive. Without this parameter subsequent append mode
+ // file writes can cause the resultant file to become corrupt. We only need to do
+ // this if the major version of Windows is > 5(Only Windows Vista and above
+ // can support SMBV2). With a 7200RPM hard drive:
+ // Copying a 1KB file with COPY_FILE_NO_BUFFERING takes about 30-60ms.
+ // Copying a 1KB file without COPY_FILE_NO_BUFFERING takes < 1ms.
+ // So we only use COPY_FILE_NO_BUFFERING when we have a remote drive.
+ int copyOK;
+ DWORD dwVersion = GetVersion();
+ DWORD dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
+ DWORD dwCopyFlags = COPY_FILE_ALLOW_DECRYPTED_DESTINATION;
+ if (dwMajorVersion > 5) {
+ bool path1Remote, path2Remote;
+ if (!IsRemoteFilePath(filePath.get(), path1Remote) ||
+ !IsRemoteFilePath(destPath.get(), path2Remote) ||
+ path1Remote || path2Remote) {
+ dwCopyFlags |= COPY_FILE_NO_BUFFERING;
+ }
+ }
+
+ if (!move)
+ {
+ copyOK = ::CopyFileExW(filePath.get(), destPath.get(), NULL, NULL, NULL, dwCopyFlags);
+ }
+ else
+ {
+ copyOK = ::MoveFileExW(filePath.get(), destPath.get(), MOVEFILE_REPLACE_EXISTING);
+
+ // Check if copying the source file to a different volume,
+ // as this could be an SMBV2 mapped drive.
+ if (!copyOK && GetLastError() == ERROR_NOT_SAME_DEVICE)
+ {
+ copyOK = CopyFileExW(filePath.get(), destPath.get(), NULL, NULL, NULL, dwCopyFlags);
+
+ if (copyOK)
+ DeleteFileW(filePath.get());
+ }
+ }
+
+ if (!copyOK) // CopyFileEx and MoveFileEx return zero at failure.
+ rv = ConvertWinError(GetLastError());
+ else if (move && !skipNtfsAclReset)
+ {
+ // Set security permissions to inherit from parent.
+ // Note: propagates to all children: slow for big file trees
+ PACL pOldDACL = NULL;
+ PSECURITY_DESCRIPTOR pSD = NULL;
+ ::GetNamedSecurityInfoW((LPWSTR)destPath.get(), SE_FILE_OBJECT,
+ DACL_SECURITY_INFORMATION,
+ NULL, NULL, &pOldDACL, NULL, &pSD);
+ if (pOldDACL)
+ ::SetNamedSecurityInfoW((LPWSTR)destPath.get(), SE_FILE_OBJECT,
+ DACL_SECURITY_INFORMATION |
+ UNPROTECTED_DACL_SECURITY_INFORMATION,
+ NULL, NULL, pOldDACL, NULL);
+ if (pSD)
+ LocalFree((HLOCAL)pSD);
+ }
+
+ return rv;
+}
+
+nsresult
+nsLocalFile::CopyMove(nsIFile *aParentDir, const nsAString &newName, bool followSymlinks, bool move)
+{
+ nsCOMPtr<nsIFile> newParentDir = aParentDir;
+ // check to see if this exists, otherwise return an error.
+ // we will check this by resolving. If the user wants us
+ // to follow links, then we are talking about the target,
+ // hence we can use the |followSymlinks| parameter.
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!newParentDir)
+ {
+ // no parent was specified. We must rename.
+ if (newName.IsEmpty())
+ return NS_ERROR_INVALID_ARG;
+
+ rv = GetParent(getter_AddRefs(newParentDir));
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ if (!newParentDir)
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+
+ // make sure it exists and is a directory. Create it if not there.
+ bool exists;
+ newParentDir->Exists(&exists);
+ if (!exists)
+ {
+ rv = newParentDir->Create(DIRECTORY_TYPE, 0644); // TODO, what permissions should we use
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ else
+ {
+ bool isDir;
+ newParentDir->IsDirectory(&isDir);
+ if (!isDir)
+ {
+ if (followSymlinks)
+ {
+ bool isLink;
+ newParentDir->IsSymlink(&isLink);
+ if (isLink)
+ {
+ nsAutoString target;
+ newParentDir->GetTarget(target);
+
+ nsCOMPtr<nsIFile> realDest = new nsLocalFile();
+ if (realDest == nullptr)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = realDest->InitWithPath(target);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ return CopyMove(realDest, newName, followSymlinks, move);
+ }
+ }
+ else
+ {
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+ }
+ }
+ }
+
+ // Try different ways to move/copy files/directories
+ bool done = false;
+ bool isDir;
+ IsDirectory(&isDir);
+ bool isSymlink;
+ IsSymlink(&isSymlink);
+
+ // Try to move the file or directory, or try to copy a single file (or non-followed symlink)
+ if (move || !isDir || (isSymlink && !followSymlinks))
+ {
+ // Copy/Move single file, or move a directory
+ rv = CopySingleFile(this, newParentDir, newName, followSymlinks, move,
+ !aParentDir);
+ done = NS_SUCCEEDED(rv);
+ // If we are moving a directory and that fails, fallback on directory
+ // enumeration. See bug 231300 for details.
+ if (!done && !(move && isDir))
+ return rv;
+ }
+
+ // Not able to copy or move directly, so enumerate it
+ if (!done)
+ {
+ // create a new target destination in the new parentDir;
+ nsCOMPtr<nsIFile> target;
+ rv = newParentDir->Clone(getter_AddRefs(target));
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoString allocatedNewName;
+ if (newName.IsEmpty())
+ {
+ bool isLink;
+ IsSymlink(&isLink);
+ if (isLink)
+ {
+ nsAutoString temp;
+ GetTarget(temp);
+ int32_t offset = temp.RFindChar(L'\\');
+ if (offset == kNotFound)
+ allocatedNewName = temp;
+ else
+ allocatedNewName = Substring(temp, offset + 1);
+ }
+ else
+ {
+ GetLeafName(allocatedNewName);// this should be the leaf name of the
+ }
+ }
+ else
+ {
+ allocatedNewName = newName;
+ }
+
+ rv = target->Append(allocatedNewName);
+ if (NS_FAILED(rv))
+ return rv;
+
+ allocatedNewName.Truncate();
+
+ // check if the destination directory already exists
+ target->Exists(&exists);
+ if (!exists)
+ {
+ // if the destination directory cannot be created, return an error
+ rv = target->Create(DIRECTORY_TYPE, 0644); // TODO, what permissions should we use
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ else
+ {
+ // check if the destination directory is writable and empty
+ bool isWritable;
+
+ target->IsWritable(&isWritable);
+ if (!isWritable)
+ return NS_ERROR_FILE_ACCESS_DENIED;
+
+ nsCOMPtr<nsISimpleEnumerator> targetIterator;
+ rv = target->GetDirectoryEntries(getter_AddRefs(targetIterator));
+ if (NS_FAILED(rv))
+ return rv;
+
+ bool more;
+ targetIterator->HasMoreElements(&more);
+ // return error if target directory is not empty
+ if (more)
+ return NS_ERROR_FILE_DIR_NOT_EMPTY;
+ }
+
+ nsDirEnumerator dirEnum;
+
+ rv = dirEnum.Init(this);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("dirEnum initialization failed");
+ return rv;
+ }
+
+ bool more = false;
+ while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
+ {
+ nsCOMPtr<nsISupports> item;
+ nsCOMPtr<nsIFile> file;
+ dirEnum.GetNext(getter_AddRefs(item));
+ file = do_QueryInterface(item);
+ if (file)
+ {
+ bool isDir, isLink;
+
+ file->IsDirectory(&isDir);
+ file->IsSymlink(&isLink);
+
+ if (move)
+ {
+ if (followSymlinks)
+ return NS_ERROR_FAILURE;
+
+ rv = file->MoveTo(target, EmptyString());
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ else
+ {
+ if (followSymlinks)
+ rv = file->CopyToFollowingLinks(target, EmptyString());
+ else
+ rv = file->CopyTo(target, EmptyString());
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ }
+ }
+ // we've finished moving all the children of this directory
+ // in the new directory. so now delete the directory
+ // note, we don't need to do a recursive delete.
+ // MoveTo() is recursive. At this point,
+ // we've already moved the children of the current folder
+ // to the new location. nothing should be left in the folder.
+ if (move)
+ {
+ rv = Remove(false /* recursive */);
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ }
+
+
+ // If we moved, we want to adjust this.
+ if (move)
+ {
+ MakeDirty();
+
+ nsAutoString newParentPath;
+ newParentDir->GetPath(newParentPath);
+
+ if (newParentPath.IsEmpty())
+ return NS_ERROR_FAILURE;
+
+ if (newName.IsEmpty())
+ {
+ nsAutoString aFileName;
+ GetLeafName(aFileName);
+
+ InitWithPath(newParentPath);
+ Append(aFileName);
+ }
+ else
+ {
+ InitWithPath(newParentPath);
+ Append(newName);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
+{
+ return CopyMove(newParentDir, newName, false, false);
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
+{
+ return CopyMove(newParentDir, newName, true, false);
+}
+
+NS_IMETHODIMP
+nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
+{
+ return CopyMove(newParentDir, newName, false, true);
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::Load(PRLibrary * *_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ bool isFile;
+ nsresult rv = IsFile(&isFile);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (! isFile)
+ return NS_ERROR_FILE_IS_DIRECTORY;
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ nsTraceRefcntImpl::SetActivityIsLegal(false);
+#endif
+
+ PRLibSpec libSpec;
+ libSpec.value.pathname_u = mResolvedPath.get();
+ libSpec.type = PR_LibSpec_PathnameU;
+ *_retval = PR_LoadLibraryWithFlags(libSpec, 0);
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ nsTraceRefcntImpl::SetActivityIsLegal(true);
+#endif
+
+ if (*_retval)
+ return NS_OK;
+ return NS_ERROR_NULL_POINTER;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Remove(bool recursive)
+{
+ // NOTE:
+ //
+ // if the working path points to a shortcut, then we will only
+ // delete the shortcut itself. even if the shortcut points to
+ // a directory, we will not recurse into that directory or
+ // delete that directory itself. likewise, if the shortcut
+ // points to a normal file, we will not delete the real file.
+ // this is done to be consistent with the other platforms that
+ // behave this way. we do this even if the followLinks attribute
+ // is set to true. this helps protect against misuse that could
+ // lead to security bugs (e.g., bug 210588).
+ //
+ // Since shortcut files are no longer permitted to be used as unix-like
+ // symlinks interspersed in the path (e.g. "c:/file.lnk/foo/bar.txt")
+ // this processing is a lot simpler. Even if the shortcut file is
+ // pointing to a directory, only the mWorkingPath value is used and so
+ // only the shortcut file will be deleted.
+
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ bool isDir, isLink;
+ nsresult rv;
+
+ isDir = false;
+ rv = IsSymlink(&isLink);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // only check to see if we have a directory if it isn't a link
+ if (!isLink)
+ {
+ rv = IsDirectory(&isDir);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ if (isDir)
+ {
+ if (recursive)
+ {
+ nsDirEnumerator dirEnum;
+
+ rv = dirEnum.Init(this);
+ if (NS_FAILED(rv))
+ return rv;
+
+ bool more = false;
+ while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
+ {
+ nsCOMPtr<nsISupports> item;
+ dirEnum.GetNext(getter_AddRefs(item));
+ nsCOMPtr<nsIFile> file = do_QueryInterface(item);
+ if (file)
+ file->Remove(recursive);
+ }
+ }
+ if (RemoveDirectoryW(mWorkingPath.get()) == 0)
+ return ConvertWinError(GetLastError());
+ }
+ else
+ {
+ if (DeleteFileW(mWorkingPath.get()) == 0)
+ return ConvertWinError(GetLastError());
+ }
+
+ MakeDirty();
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetLastModifiedTime(PRTime *aLastModifiedTime)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_ENSURE_ARG(aLastModifiedTime);
+
+ // get the modified time of the target as determined by mFollowSymlinks
+ // If true, then this will be for the target of the shortcut file,
+ // otherwise it will be for the shortcut file itself (i.e. the same
+ // results as GetLastModifiedTimeOfLink)
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ // microseconds -> milliseconds
+ *aLastModifiedTime = mFileInfo64.modifyTime / PR_USEC_PER_MSEC;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetLastModifiedTimeOfLink(PRTime *aLastModifiedTime)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_ENSURE_ARG(aLastModifiedTime);
+
+ // The caller is assumed to have already called IsSymlink
+ // and to have found that this file is a link.
+
+ PRFileInfo64 info;
+ nsresult rv = GetFileInfo(mWorkingPath, &info);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // microseconds -> milliseconds
+ *aLastModifiedTime = info.modifyTime / PR_USEC_PER_MSEC;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::SetLastModifiedTime(PRTime aLastModifiedTime)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ // set the modified time of the target as determined by mFollowSymlinks
+ // If true, then this will be for the target of the shortcut file,
+ // otherwise it will be for the shortcut file itself (i.e. the same
+ // results as SetLastModifiedTimeOfLink)
+
+ rv = SetModDate(aLastModifiedTime, mResolvedPath.get());
+ if (NS_SUCCEEDED(rv))
+ MakeDirty();
+
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::SetLastModifiedTimeOfLink(PRTime aLastModifiedTime)
+{
+ // The caller is assumed to have already called IsSymlink
+ // and to have found that this file is a link.
+
+ nsresult rv = SetModDate(aLastModifiedTime, mWorkingPath.get());
+ if (NS_SUCCEEDED(rv))
+ MakeDirty();
+
+ return rv;
+}
+
+nsresult
+nsLocalFile::SetModDate(PRTime aLastModifiedTime, const PRUnichar *filePath)
+{
+ // The FILE_FLAG_BACKUP_SEMANTICS is required in order to change the
+ // modification time for directories.
+ HANDLE file = ::CreateFileW(filePath, // pointer to name of the file
+ GENERIC_WRITE, // access (write) mode
+ 0, // share mode
+ NULL, // pointer to security attributes
+ OPEN_EXISTING, // how to create
+ FILE_FLAG_BACKUP_SEMANTICS, // file attributes
+ NULL);
+
+ if (file == INVALID_HANDLE_VALUE)
+ {
+ return ConvertWinError(GetLastError());
+ }
+
+ FILETIME ft;
+ SYSTEMTIME st;
+ PRExplodedTime pret;
+
+ // PR_ExplodeTime expects usecs...
+ PR_ExplodeTime(aLastModifiedTime * PR_USEC_PER_MSEC, PR_GMTParameters, &pret);
+ st.wYear = pret.tm_year;
+ st.wMonth = pret.tm_month + 1; // Convert start offset -- Win32: Jan=1; NSPR: Jan=0
+ st.wDayOfWeek = pret.tm_wday;
+ st.wDay = pret.tm_mday;
+ st.wHour = pret.tm_hour;
+ st.wMinute = pret.tm_min;
+ st.wSecond = pret.tm_sec;
+ st.wMilliseconds = pret.tm_usec/1000;
+
+ nsresult rv = NS_OK;
+ // if at least one of these fails...
+ if (!(SystemTimeToFileTime(&st, &ft) != 0 &&
+ SetFileTime(file, NULL, &ft, &ft) != 0))
+ {
+ rv = ConvertWinError(GetLastError());
+ }
+
+ CloseHandle(file);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPermissions(uint32_t *aPermissions)
+{
+ NS_ENSURE_ARG(aPermissions);
+
+ // get the permissions of the target as determined by mFollowSymlinks
+ // If true, then this will be for the target of the shortcut file,
+ // otherwise it will be for the shortcut file itself (i.e. the same
+ // results as GetPermissionsOfLink)
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ bool isWritable, isExecutable;
+ IsWritable(&isWritable);
+ IsExecutable(&isExecutable);
+
+ *aPermissions = PR_IRUSR|PR_IRGRP|PR_IROTH; // all read
+ if (isWritable)
+ *aPermissions |= PR_IWUSR|PR_IWGRP|PR_IWOTH; // all write
+ if (isExecutable)
+ *aPermissions |= PR_IXUSR|PR_IXGRP|PR_IXOTH; // all execute
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPermissionsOfLink(uint32_t *aPermissions)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_ENSURE_ARG(aPermissions);
+
+ // The caller is assumed to have already called IsSymlink
+ // and to have found that this file is a link. It is not
+ // possible for a link file to be executable.
+
+ DWORD word = ::GetFileAttributesW(mWorkingPath.get());
+ if (word == INVALID_FILE_ATTRIBUTES)
+ return NS_ERROR_FILE_INVALID_PATH;
+
+ bool isWritable = !(word & FILE_ATTRIBUTE_READONLY);
+ *aPermissions = PR_IRUSR|PR_IRGRP|PR_IROTH; // all read
+ if (isWritable)
+ *aPermissions |= PR_IWUSR|PR_IWGRP|PR_IWOTH; // all write
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::SetPermissions(uint32_t aPermissions)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ // set the permissions of the target as determined by mFollowSymlinks
+ // If true, then this will be for the target of the shortcut file,
+ // otherwise it will be for the shortcut file itself (i.e. the same
+ // results as SetPermissionsOfLink)
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ // windows only knows about the following permissions
+ int mode = 0;
+ if (aPermissions & (PR_IRUSR|PR_IRGRP|PR_IROTH)) // any read
+ mode |= _S_IREAD;
+ if (aPermissions & (PR_IWUSR|PR_IWGRP|PR_IWOTH)) // any write
+ mode |= _S_IWRITE;
+
+ if (_wchmod(mResolvedPath.get(), mode) == -1)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPermissionsOfLink(uint32_t aPermissions)
+{
+ // The caller is assumed to have already called IsSymlink
+ // and to have found that this file is a link.
+
+ // windows only knows about the following permissions
+ int mode = 0;
+ if (aPermissions & (PR_IRUSR|PR_IRGRP|PR_IROTH)) // any read
+ mode |= _S_IREAD;
+ if (aPermissions & (PR_IWUSR|PR_IWGRP|PR_IWOTH)) // any write
+ mode |= _S_IWRITE;
+
+ if (_wchmod(mWorkingPath.get(), mode) == -1)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSize(int64_t *aFileSize)
+{
+ NS_ENSURE_ARG(aFileSize);
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aFileSize = mFileInfo64.size;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetFileSizeOfLink(int64_t *aFileSize)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_ENSURE_ARG(aFileSize);
+
+ // The caller is assumed to have already called IsSymlink
+ // and to have found that this file is a link.
+
+ PRFileInfo64 info;
+ if (NS_FAILED(GetFileInfo(mWorkingPath, &info)))
+ return NS_ERROR_FILE_INVALID_PATH;
+
+ *aFileSize = info.size;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetFileSize(int64_t aFileSize)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ HANDLE hFile = ::CreateFileW(mResolvedPath.get(),// pointer to name of the file
+ GENERIC_WRITE, // access (write) mode
+ FILE_SHARE_READ, // share mode
+ NULL, // pointer to security attributes
+ OPEN_EXISTING, // how to create
+ FILE_ATTRIBUTE_NORMAL, // file attributes
+ NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ return ConvertWinError(GetLastError());
+ }
+
+ // seek the file pointer to the new, desired end of file
+ // and then truncate the file at that position
+ rv = NS_ERROR_FAILURE;
+ aFileSize = MyFileSeek64(hFile, aFileSize, FILE_BEGIN);
+ if (aFileSize != -1 && SetEndOfFile(hFile))
+ {
+ MakeDirty();
+ rv = NS_OK;
+ }
+
+ CloseHandle(hFile);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetDiskSpaceAvailable(int64_t *aDiskSpaceAvailable)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_ENSURE_ARG(aDiskSpaceAvailable);
+
+ ResolveAndStat();
+
+ if (mFileInfo64.type == PR_FILE_FILE) {
+ // Since GetDiskFreeSpaceExW works only on directories, use the parent.
+ nsCOMPtr<nsIFile> parent;
+ if (NS_SUCCEEDED(GetParent(getter_AddRefs(parent))) && parent) {
+ return parent->GetDiskSpaceAvailable(aDiskSpaceAvailable);
+ }
+ }
+
+ ULARGE_INTEGER liFreeBytesAvailableToCaller, liTotalNumberOfBytes;
+ if (::GetDiskFreeSpaceExW(mResolvedPath.get(), &liFreeBytesAvailableToCaller,
+ &liTotalNumberOfBytes, NULL))
+ {
+ *aDiskSpaceAvailable = liFreeBytesAvailableToCaller.QuadPart;
+ return NS_OK;
+ }
+ *aDiskSpaceAvailable = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetParent(nsIFile * *aParent)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_ENSURE_ARG_POINTER(aParent);
+
+ // A two-character path must be a drive such as C:, so it has no parent
+ if (mWorkingPath.Length() == 2) {
+ *aParent = nullptr;
+ return NS_OK;
+ }
+
+ int32_t offset = mWorkingPath.RFindChar(PRUnichar('\\'));
+ // adding this offset check that was removed in bug 241708 fixes mail
+ // directories that aren't relative to/underneath the profile dir.
+ // e.g., on a different drive. Before you remove them, please make
+ // sure local mail directories that aren't underneath the profile dir work.
+ if (offset == kNotFound)
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+
+ // A path of the form \\NAME is a top-level path and has no parent
+ if (offset == 1 && mWorkingPath[0] == L'\\') {
+ *aParent = nullptr;
+ return NS_OK;
+ }
+
+ nsAutoString parentPath(mWorkingPath);
+
+ if (offset > 0)
+ parentPath.Truncate(offset);
+ else
+ parentPath.AssignLiteral("\\\\.");
+
+ nsCOMPtr<nsIFile> localFile;
+ nsresult rv = NS_NewLocalFile(parentPath, mFollowSymlinks, getter_AddRefs(localFile));
+
+ if (NS_SUCCEEDED(rv) && localFile) {
+ return CallQueryInterface(localFile, aParent);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Exists(bool *_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_ENSURE_ARG(_retval);
+ *_retval = false;
+
+ MakeDirty();
+ nsresult rv = ResolveAndStat();
+ *_retval = NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_IS_LOCKED;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsWritable(bool *aIsWritable)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ // The read-only attribute on a FAT directory only means that it can't
+ // be deleted. It is still possible to modify the contents of the directory.
+ nsresult rv = IsDirectory(aIsWritable);
+ if (rv == NS_ERROR_FILE_ACCESS_DENIED) {
+ *aIsWritable = true;
+ return NS_OK;
+ } else if (rv == NS_ERROR_FILE_IS_LOCKED) {
+ // If the file is normally allowed write access
+ // we should still return that the file is writable.
+ } else if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (*aIsWritable)
+ return NS_OK;
+
+ // writable if the file doesn't have the readonly attribute
+ rv = HasFileAttribute(FILE_ATTRIBUTE_READONLY, aIsWritable);
+ if (rv == NS_ERROR_FILE_ACCESS_DENIED) {
+ *aIsWritable = false;
+ return NS_OK;
+ } else if (rv == NS_ERROR_FILE_IS_LOCKED) {
+ // If the file is normally allowed write access
+ // we should still return that the file is writable.
+ } else if (NS_FAILED(rv)) {
+ return rv;
+ }
+ *aIsWritable = !*aIsWritable;
+
+ // If the read only attribute is not set, check to make sure
+ // we can open the file with write access.
+ if (*aIsWritable) {
+ PRFileDesc* file;
+ rv = OpenFile(mResolvedPath, PR_WRONLY, 0, &file);
+ if (NS_SUCCEEDED(rv)) {
+ PR_Close(file);
+ } else if (rv == NS_ERROR_FILE_ACCESS_DENIED) {
+ *aIsWritable = false;
+ } else if (rv == NS_ERROR_FILE_IS_LOCKED) {
+ // If it is locked and read only we would have
+ // gotten access denied
+ *aIsWritable = true;
+ } else {
+ return rv;
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsReadable(bool *_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_ENSURE_ARG(_retval);
+ *_retval = false;
+
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv))
+ return rv;
+
+ *_retval = true;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::IsExecutable(bool *_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_ENSURE_ARG(_retval);
+ *_retval = false;
+
+ nsresult rv;
+
+ // only files can be executables
+ bool isFile;
+ rv = IsFile(&isFile);
+ if (NS_FAILED(rv))
+ return rv;
+ if (!isFile)
+ return NS_OK;
+
+ //TODO: shouldn't we be checking mFollowSymlinks here?
+ bool symLink;
+ rv = IsSymlink(&symLink);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoString path;
+ if (symLink)
+ GetTarget(path);
+ else
+ GetPath(path);
+
+ // kill trailing dots and spaces.
+ int32_t filePathLen = path.Length() - 1;
+ while(filePathLen > 0 && (path[filePathLen] == L' ' || path[filePathLen] == L'.'))
+ {
+ path.Truncate(filePathLen--);
+ }
+
+ // Get extension.
+ int32_t dotIdx = path.RFindChar(PRUnichar('.'));
+ if ( dotIdx != kNotFound ) {
+ // Convert extension to lower case.
+ PRUnichar *p = path.BeginWriting();
+ for( p+= dotIdx + 1; *p; p++ )
+ *p += (*p >= L'A' && *p <= L'Z') ? 'a' - 'A' : 0;
+
+ // Search for any of the set of executable extensions.
+ static const char * const executableExts[] = {
+ "ad",
+ "ade", // access project extension
+ "adp",
+ "air", // Adobe AIR installer
+ "app", // executable application
+ "application", // from bug 348763
+ "asp",
+ "bas",
+ "bat",
+ "chm",
+ "cmd",
+ "com",
+ "cpl",
+ "crt",
+ "exe",
+ "fxp", // FoxPro compiled app
+ "hlp",
+ "hta",
+ "inf",
+ "ins",
+ "isp",
+ "jar", // java application bundle
+ "js",
+ "jse",
+ "lnk",
+ "mad", // Access Module Shortcut
+ "maf", // Access
+ "mag", // Access Diagram Shortcut
+ "mam", // Access Macro Shortcut
+ "maq", // Access Query Shortcut
+ "mar", // Access Report Shortcut
+ "mas", // Access Stored Procedure
+ "mat", // Access Table Shortcut
+ "mau", // Media Attachment Unit
+ "mav", // Access View Shortcut
+ "maw", // Access Data Access Page
+ "mda", // Access Add-in, MDA Access 2 Workgroup
+ "mdb",
+ "mde",
+ "mdt", // Access Add-in Data
+ "mdw", // Access Workgroup Information
+ "mdz", // Access Wizard Template
+ "msc",
+ "msh", // Microsoft Shell
+ "mshxml", // Microsoft Shell
+ "msi",
+ "msp",
+ "mst",
+ "ops", // Office Profile Settings
+ "pcd",
+ "pif",
+ "plg", // Developer Studio Build Log
+ "prf", // windows system file
+ "prg",
+ "pst",
+ "reg",
+ "scf", // Windows explorer command
+ "scr",
+ "sct",
+ "shb",
+ "shs",
+ "url",
+ "vb",
+ "vbe",
+ "vbs",
+ "vsd",
+ "vsmacros", // Visual Studio .NET Binary-based Macro Project
+ "vss",
+ "vst",
+ "vsw",
+ "ws",
+ "wsc",
+ "wsf",
+ "wsh"};
+ nsDependentSubstring ext = Substring(path, dotIdx + 1);
+ for ( size_t i = 0; i < ArrayLength(executableExts); i++ ) {
+ if ( ext.EqualsASCII(executableExts[i])) {
+ // Found a match. Set result and quit.
+ *_retval = true;
+ break;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::IsDirectory(bool *_retval)
+{
+ return HasFileAttribute(FILE_ATTRIBUTE_DIRECTORY, _retval);
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsFile(bool *_retval)
+{
+ nsresult rv = HasFileAttribute(FILE_ATTRIBUTE_DIRECTORY, _retval);
+ if (NS_SUCCEEDED(rv)) {
+ *_retval = !*_retval;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsHidden(bool *_retval)
+{
+ return HasFileAttribute(FILE_ATTRIBUTE_HIDDEN, _retval);
+}
+
+nsresult
+nsLocalFile::HasFileAttribute(DWORD fileAttrib, bool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+
+ nsresult rv = Resolve();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ DWORD attributes = GetFileAttributesW(mResolvedPath.get());
+ if (INVALID_FILE_ATTRIBUTES == attributes) {
+ return ConvertWinError(GetLastError());
+ }
+
+ *_retval = ((attributes & fileAttrib) != 0);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsSymlink(bool *_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_ENSURE_ARG(_retval);
+
+ // unless it is a valid shortcut path it's not a symlink
+ if (!IsShortcutPath(mWorkingPath)) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ // we need to know if this is a file or directory
+ nsresult rv = ResolveAndStat();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // We should not check mFileInfo64.type here for PR_FILE_FILE because lnk
+ // files can point to directories or files. Important security checks
+ // depend on correctly identifying lnk files. mFileInfo64 now holds info
+ // about the target of the lnk file, not the actual lnk file!
+ *_retval = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::IsSpecial(bool *_retval)
+{
+ return HasFileAttribute(FILE_ATTRIBUTE_SYSTEM, _retval);
+}
+
+NS_IMETHODIMP
+nsLocalFile::Equals(nsIFile *inFile, bool *_retval)
+{
+ NS_ENSURE_ARG(inFile);
+ NS_ENSURE_ARG(_retval);
+
+ EnsureShortPath();
+
+ nsCOMPtr<nsILocalFileWin> lf(do_QueryInterface(inFile));
+ if (!lf) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ nsAutoString inFilePath;
+ lf->GetCanonicalPath(inFilePath);
+
+ // Ok : Win9x
+ *_retval = _wcsicmp(mShortWorkingPath.get(), inFilePath.get()) == 0;
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::Contains(nsIFile *inFile, bool recur, bool *_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ *_retval = false;
+
+ nsAutoString myFilePath;
+ if (NS_FAILED(GetTarget(myFilePath)))
+ GetPath(myFilePath);
+
+ uint32_t myFilePathLen = myFilePath.Length();
+
+ nsAutoString inFilePath;
+ if (NS_FAILED(inFile->GetTarget(inFilePath)))
+ inFile->GetPath(inFilePath);
+
+ // make sure that the |inFile|'s path has a trailing separator.
+ if (inFilePath.Length() >= myFilePathLen && inFilePath[myFilePathLen] == L'\\')
+ {
+ if (_wcsnicmp(myFilePath.get(), inFilePath.get(), myFilePathLen) == 0)
+ {
+ *_retval = true;
+ }
+
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetTarget(nsAString &_retval)
+{
+ _retval.Truncate();
+#if STRICT_FAKE_SYMLINKS
+ bool symLink;
+
+ nsresult rv = IsSymlink(&symLink);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!symLink)
+ {
+ return NS_ERROR_FILE_INVALID_PATH;
+ }
+#endif
+ ResolveAndStat();
+
+ _retval = mResolvedPath;
+ return NS_OK;
+}
+
+
+/* attribute bool followLinks; */
+NS_IMETHODIMP
+nsLocalFile::GetFollowLinks(bool *aFollowLinks)
+{
+ *aFollowLinks = mFollowSymlinks;
+ return NS_OK;
+}
+NS_IMETHODIMP
+nsLocalFile::SetFollowLinks(bool aFollowLinks)
+{
+ MakeDirty();
+ mFollowSymlinks = aFollowLinks;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator * *entries)
+{
+ nsresult rv;
+
+ *entries = nullptr;
+ if (mWorkingPath.EqualsLiteral("\\\\.")) {
+ nsDriveEnumerator *drives = new nsDriveEnumerator;
+ if (!drives)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(drives);
+ rv = drives->Init();
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(drives);
+ return rv;
+ }
+ *entries = drives;
+ return NS_OK;
+ }
+
+ nsDirEnumerator* dirEnum = new nsDirEnumerator();
+ if (dirEnum == nullptr)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(dirEnum);
+ rv = dirEnum->Init(this);
+ if (NS_FAILED(rv))
+ {
+ NS_RELEASE(dirEnum);
+ return rv;
+ }
+
+ *entries = dirEnum;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
+{
+ CopyUTF16toUTF8(mWorkingPath, aPersistentDescriptor);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
+{
+ if (IsUTF8(aPersistentDescriptor))
+ return InitWithPath(NS_ConvertUTF8toUTF16(aPersistentDescriptor));
+ else
+ return InitWithNativePath(aPersistentDescriptor);
+}
+
+/* attrib unsigned long fileAttributesWin; */
+NS_IMETHODIMP
+nsLocalFile::GetFileAttributesWin(uint32_t *aAttribs)
+{
+ *aAttribs = 0;
+ DWORD dwAttrs = GetFileAttributesW(mWorkingPath.get());
+ if (dwAttrs == INVALID_FILE_ATTRIBUTES)
+ return NS_ERROR_FILE_INVALID_PATH;
+
+ if (!(dwAttrs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED))
+ *aAttribs |= WFA_SEARCH_INDEXED;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetFileAttributesWin(uint32_t aAttribs)
+{
+ DWORD dwAttrs = GetFileAttributesW(mWorkingPath.get());
+ if (dwAttrs == INVALID_FILE_ATTRIBUTES)
+ return NS_ERROR_FILE_INVALID_PATH;
+
+ if (aAttribs & WFA_SEARCH_INDEXED) {
+ dwAttrs &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+ } else {
+ dwAttrs |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+ }
+
+ if (aAttribs & WFA_READONLY) {
+ dwAttrs |= FILE_ATTRIBUTE_READONLY;
+ } else if ((aAttribs & WFA_READWRITE) &&
+ (dwAttrs & FILE_ATTRIBUTE_READONLY)) {
+ dwAttrs &= ~FILE_ATTRIBUTE_READONLY;
+ }
+
+ if (SetFileAttributesW(mWorkingPath.get(), dwAttrs) == 0)
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::Reveal()
+{
+ // This API should be main thread only
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // make sure mResolvedPath is set
+ nsresult rv = Resolve();
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
+ return rv;
+ }
+
+ // To create a new thread, get the thread manager
+ nsCOMPtr<nsIThreadManager> tm = do_GetService(NS_THREADMANAGER_CONTRACTID);
+ nsCOMPtr<nsIThread> mythread;
+ rv = tm->NewThread(0, 0, getter_AddRefs(mythread));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIRunnable> runnable =
+ new AsyncLocalFileWinOperation(AsyncLocalFileWinOperation::RevealOp,
+ mResolvedPath);
+
+ // After the dispatch, the result runnable will shut down the worker
+ // thread, so we can let it go.
+ mythread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLocalFile::Launch()
+{
+ // This API should be main thread only
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // make sure mResolvedPath is set
+ nsresult rv = Resolve();
+ if (NS_FAILED(rv))
+ return rv;
+
+ // To create a new thread, get the thread manager
+ nsCOMPtr<nsIThreadManager> tm = do_GetService(NS_THREADMANAGER_CONTRACTID);
+ nsCOMPtr<nsIThread> mythread;
+ rv = tm->NewThread(0, 0, getter_AddRefs(mythread));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIRunnable> runnable =
+ new AsyncLocalFileWinOperation(AsyncLocalFileWinOperation::LaunchOp,
+ mResolvedPath);
+
+ // After the dispatch, the result runnable will shut down the worker
+ // thread, so we can let it go.
+ mythread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+ return NS_OK;
+}
+
+nsresult
+NS_NewLocalFile(const nsAString &path, bool followLinks, nsIFile* *result)
+{
+ nsLocalFile* file = new nsLocalFile();
+ if (file == nullptr)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(file);
+
+ file->SetFollowLinks(followLinks);
+
+ if (!path.IsEmpty()) {
+ nsresult rv = file->InitWithPath(path);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(file);
+ return rv;
+ }
+ }
+
+ *result = file;
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Native (lossy) interface
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsLocalFile::InitWithNativePath(const nsACString &filePath)
+{
+ nsAutoString tmp;
+ nsresult rv = NS_CopyNativeToUnicode(filePath, tmp);
+ if (NS_SUCCEEDED(rv))
+ return InitWithPath(tmp);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendNative(const nsACString &node)
+{
+ nsAutoString tmp;
+ nsresult rv = NS_CopyNativeToUnicode(node, tmp);
+ if (NS_SUCCEEDED(rv))
+ return Append(tmp);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::AppendRelativeNativePath(const nsACString &node)
+{
+ nsAutoString tmp;
+ nsresult rv = NS_CopyNativeToUnicode(node, tmp);
+ if (NS_SUCCEEDED(rv))
+ return AppendRelativePath(tmp);
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
+{
+ //NS_WARNING("This API is lossy. Use GetLeafName !");
+ nsAutoString tmp;
+ nsresult rv = GetLeafName(tmp);
+ if (NS_SUCCEEDED(rv))
+ rv = NS_CopyUnicodeToNative(tmp, aLeafName);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
+{
+ nsAutoString tmp;
+ nsresult rv = NS_CopyNativeToUnicode(aLeafName, tmp);
+ if (NS_SUCCEEDED(rv))
+ return SetLeafName(tmp);
+
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetNativePath(nsACString &_retval)
+{
+ //NS_WARNING("This API is lossy. Use GetPath !");
+ nsAutoString tmp;
+ nsresult rv = GetPath(tmp);
+ if (NS_SUCCEEDED(rv))
+ rv = NS_CopyUnicodeToNative(tmp, _retval);
+
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeCanonicalPath(nsACString &aResult)
+{
+ NS_WARNING("This method is lossy. Use GetCanonicalPath !");
+ EnsureShortPath();
+ NS_CopyUnicodeToNative(mShortWorkingPath, aResult);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString &newName)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ if (newName.IsEmpty())
+ return CopyTo(newParentDir, EmptyString());
+
+ nsAutoString tmp;
+ nsresult rv = NS_CopyNativeToUnicode(newName, tmp);
+ if (NS_SUCCEEDED(rv))
+ return CopyTo(newParentDir, tmp);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString &newName)
+{
+ if (newName.IsEmpty())
+ return CopyToFollowingLinks(newParentDir, EmptyString());
+
+ nsAutoString tmp;
+ nsresult rv = NS_CopyNativeToUnicode(newName, tmp);
+ if (NS_SUCCEEDED(rv))
+ return CopyToFollowingLinks(newParentDir, tmp);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString &newName)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ if (newName.IsEmpty())
+ return MoveTo(newParentDir, EmptyString());
+
+ nsAutoString tmp;
+ nsresult rv = NS_CopyNativeToUnicode(newName, tmp);
+ if (NS_SUCCEEDED(rv))
+ return MoveTo(newParentDir, tmp);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetNativeTarget(nsACString &_retval)
+{
+ // Check we are correctly initialized.
+ CHECK_mWorkingPath();
+
+ NS_WARNING("This API is lossy. Use GetTarget !");
+ nsAutoString tmp;
+ nsresult rv = GetTarget(tmp);
+ if (NS_SUCCEEDED(rv))
+ rv = NS_CopyUnicodeToNative(tmp, _retval);
+
+ return rv;
+}
+
+nsresult
+NS_NewNativeLocalFile(const nsACString &path, bool followLinks, nsIFile* *result)
+{
+ nsAutoString buf;
+ nsresult rv = NS_CopyNativeToUnicode(path, buf);
+ if (NS_FAILED(rv)) {
+ *result = nullptr;
+ return rv;
+ }
+ return NS_NewLocalFile(buf, followLinks, result);
+}
+
+void
+nsLocalFile::EnsureShortPath()
+{
+ if (!mShortWorkingPath.IsEmpty())
+ return;
+
+ WCHAR shortPath[MAX_PATH + 1];
+ DWORD lengthNeeded = ::GetShortPathNameW(mWorkingPath.get(), shortPath,
+ ArrayLength(shortPath));
+ // If an error occurred then lengthNeeded is set to 0 or the length of the
+ // needed buffer including NULL termination. If it succeeds the number of
+ // wide characters not including NULL termination is returned.
+ if (lengthNeeded != 0 && lengthNeeded < ArrayLength(shortPath))
+ mShortWorkingPath.Assign(shortPath);
+ else
+ mShortWorkingPath.Assign(mWorkingPath);
+}
+
+// nsIHashable
+
+NS_IMETHODIMP
+nsLocalFile::Equals(nsIHashable* aOther, bool *aResult)
+{
+ nsCOMPtr<nsIFile> otherfile(do_QueryInterface(aOther));
+ if (!otherfile) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ return Equals(otherfile, aResult);
+}
+
+NS_IMETHODIMP
+nsLocalFile::GetHashCode(uint32_t *aResult)
+{
+ // In order for short and long path names to hash to the same value we
+ // always hash on the short pathname.
+ EnsureShortPath();
+
+ *aResult = HashString(mShortWorkingPath);
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsLocalFile <static members>
+//-----------------------------------------------------------------------------
+
+void
+nsLocalFile::GlobalInit()
+{
+ DebugOnly<nsresult> rv = NS_CreateShortcutResolver();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Shortcut resolver could not be created");
+}
+
+void
+nsLocalFile::GlobalShutdown()
+{
+ NS_DestroyShortcutResolver();
+}
+
+NS_IMPL_ISUPPORTS1(nsDriveEnumerator, nsISimpleEnumerator)
+
+nsDriveEnumerator::nsDriveEnumerator()
+{
+}
+
+nsDriveEnumerator::~nsDriveEnumerator()
+{
+}
+
+nsresult nsDriveEnumerator::Init()
+{
+ /* If the length passed to GetLogicalDriveStrings is smaller
+ * than the length of the string it would return, it returns
+ * the length required for the string. */
+ DWORD length = GetLogicalDriveStringsW(0, 0);
+ /* The string is null terminated */
+ if (!mDrives.SetLength(length+1, fallible_t()))
+ return NS_ERROR_OUT_OF_MEMORY;
+ if (!GetLogicalDriveStringsW(length, mDrives.BeginWriting()))
+ return NS_ERROR_FAILURE;
+ mDrives.BeginReading(mStartOfCurrentDrive);
+ mDrives.EndReading(mEndOfDrivesString);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsDriveEnumerator::HasMoreElements(bool *aHasMore)
+{
+ *aHasMore = *mStartOfCurrentDrive != L'\0';
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsDriveEnumerator::GetNext(nsISupports **aNext)
+{
+ /* GetLogicalDrives stored in mDrives is a concatenation
+ * of null terminated strings, followed by a null terminator.
+ * mStartOfCurrentDrive is an iterator pointing at the first
+ * character of the current drive. */
+ if (*mStartOfCurrentDrive == L'\0') {
+ *aNext = nullptr;
+ return NS_OK;
+ }
+
+ nsAString::const_iterator driveEnd = mStartOfCurrentDrive;
+ FindCharInReadable(L'\0', driveEnd, mEndOfDrivesString);
+ nsString drive(Substring(mStartOfCurrentDrive, driveEnd));
+ mStartOfCurrentDrive = ++driveEnd;
+
+ nsIFile *file;
+ nsresult rv = NS_NewLocalFile(drive, false, &file);
+
+ *aNext = file;
+ return rv;
+}
diff --git a/xpcom/io/nsLocalFileWin.h b/xpcom/io/nsLocalFileWin.h
new file mode 100644
index 000000000..e60020882
--- /dev/null
+++ b/xpcom/io/nsLocalFileWin.h
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 _nsLocalFileWIN_H_
+#define _nsLocalFileWIN_H_
+
+#include "nscore.h"
+#include "nsError.h"
+#include "nsString.h"
+#include "nsCRT.h"
+#include "nsIFile.h"
+#include "nsIFactory.h"
+#include "nsILocalFileWin.h"
+#include "nsIHashable.h"
+#include "nsIClassInfoImpl.h"
+
+#include "mozilla/Attributes.h"
+
+#include "windows.h"
+#include "shlobj.h"
+
+#include <sys/stat.h>
+
+class nsLocalFile MOZ_FINAL : public nsILocalFileWin,
+ public nsIHashable
+{
+public:
+ NS_DEFINE_STATIC_CID_ACCESSOR(NS_LOCAL_FILE_CID)
+
+ nsLocalFile();
+
+ static nsresult nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr);
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsIFile interface
+ NS_DECL_NSIFILE
+
+ // nsILocalFile interface
+ NS_DECL_NSILOCALFILE
+
+ // nsILocalFileWin interface
+ NS_DECL_NSILOCALFILEWIN
+
+ // nsIHashable interface
+ NS_DECL_NSIHASHABLE
+
+public:
+ static void GlobalInit();
+ static void GlobalShutdown();
+
+private:
+ nsLocalFile(const nsLocalFile& other);
+ ~nsLocalFile() {}
+
+ bool mDirty; // cached information can only be used when this is false
+ bool mResolveDirty;
+ bool mFollowSymlinks; // should we follow symlinks when working on this file
+
+ // this string will always be in native format!
+ nsString mWorkingPath;
+
+ // this will be the resolved path of shortcuts, it will *NEVER*
+ // be returned to the user
+ nsString mResolvedPath;
+
+ // this string, if not empty, is the *short* pathname that represents
+ // mWorkingPath
+ nsString mShortWorkingPath;
+
+ PRFileInfo64 mFileInfo64;
+
+ void MakeDirty()
+ {
+ mDirty = true;
+ mResolveDirty = true;
+ mShortWorkingPath.Truncate();
+ }
+
+ nsresult ResolveAndStat();
+ nsresult Resolve();
+ nsresult ResolveShortcut();
+
+ void EnsureShortPath();
+
+ nsresult CopyMove(nsIFile *newParentDir, const nsAString &newName,
+ bool followSymlinks, bool move);
+ nsresult CopySingleFile(nsIFile *source, nsIFile* dest,
+ const nsAString &newName,
+ bool followSymlinks, bool move,
+ bool skipNtfsAclReset = false);
+
+ nsresult SetModDate(int64_t aLastModifiedTime, const PRUnichar *filePath);
+ nsresult HasFileAttribute(DWORD fileAttrib, bool *_retval);
+ nsresult AppendInternal(const nsAFlatString &node,
+ bool multipleComponents);
+};
+
+#endif
diff --git a/xpcom/io/nsMultiplexInputStream.cpp b/xpcom/io/nsMultiplexInputStream.cpp
new file mode 100644
index 000000000..553e56e20
--- /dev/null
+++ b/xpcom/io/nsMultiplexInputStream.cpp
@@ -0,0 +1,683 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/**
+ * The multiplex stream concatenates a list of input streams into a single
+ * stream.
+ */
+
+#include "mozilla/Attributes.h"
+#include "mozilla/MathAlgorithms.h"
+
+#include "base/basictypes.h"
+
+#include "nsMultiplexInputStream.h"
+#include "nsIMultiplexInputStream.h"
+#include "nsISeekableStream.h"
+#include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+#include "nsIClassInfoImpl.h"
+#include "nsIIPCSerializableInputStream.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+
+using namespace mozilla::ipc;
+
+using mozilla::DeprecatedAbs;
+
+class nsMultiplexInputStream MOZ_FINAL : public nsIMultiplexInputStream,
+ public nsISeekableStream,
+ public nsIIPCSerializableInputStream
+{
+public:
+ nsMultiplexInputStream();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAM
+ NS_DECL_NSIMULTIPLEXINPUTSTREAM
+ NS_DECL_NSISEEKABLESTREAM
+ NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
+
+private:
+ ~nsMultiplexInputStream() {}
+
+ struct ReadSegmentsState {
+ nsIInputStream* mThisStream;
+ uint32_t mOffset;
+ nsWriteSegmentFun mWriter;
+ void* mClosure;
+ bool mDone;
+ };
+
+ static NS_METHOD ReadSegCb(nsIInputStream* aIn, void* aClosure,
+ const char* aFromRawSegment, uint32_t aToOffset,
+ uint32_t aCount, uint32_t *aWriteCount);
+
+ nsTArray<nsCOMPtr<nsIInputStream> > mStreams;
+ uint32_t mCurrentStream;
+ bool mStartedReadingCurrent;
+ nsresult mStatus;
+};
+
+NS_IMPL_THREADSAFE_ADDREF(nsMultiplexInputStream)
+NS_IMPL_THREADSAFE_RELEASE(nsMultiplexInputStream)
+
+NS_IMPL_CLASSINFO(nsMultiplexInputStream, NULL, nsIClassInfo::THREADSAFE,
+ NS_MULTIPLEXINPUTSTREAM_CID)
+
+NS_IMPL_QUERY_INTERFACE4_CI(nsMultiplexInputStream,
+ nsIMultiplexInputStream,
+ nsIInputStream,
+ nsISeekableStream,
+ nsIIPCSerializableInputStream)
+NS_IMPL_CI_INTERFACE_GETTER3(nsMultiplexInputStream,
+ nsIMultiplexInputStream,
+ nsIInputStream,
+ nsISeekableStream)
+
+nsMultiplexInputStream::nsMultiplexInputStream()
+ : mCurrentStream(0),
+ mStartedReadingCurrent(false),
+ mStatus(NS_OK)
+{
+}
+
+/* readonly attribute unsigned long count; */
+NS_IMETHODIMP
+nsMultiplexInputStream::GetCount(uint32_t *aCount)
+{
+ *aCount = mStreams.Length();
+ return NS_OK;
+}
+
+#ifdef DEBUG
+static bool
+SeekableStreamAtBeginning(nsIInputStream *aStream)
+{
+ int64_t streamPos;
+ nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(aStream);
+ if (stream && NS_SUCCEEDED(stream->Tell(&streamPos)) && streamPos != 0) {
+ return false;
+ }
+ return true;
+}
+#endif
+
+/* void appendStream (in nsIInputStream stream); */
+NS_IMETHODIMP
+nsMultiplexInputStream::AppendStream(nsIInputStream *aStream)
+{
+ NS_ASSERTION(SeekableStreamAtBeginning(aStream), "Appended stream not at beginning.");
+ return mStreams.AppendElement(aStream) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+/* void insertStream (in nsIInputStream stream, in unsigned long index); */
+NS_IMETHODIMP
+nsMultiplexInputStream::InsertStream(nsIInputStream *aStream, uint32_t aIndex)
+{
+ NS_ASSERTION(SeekableStreamAtBeginning(aStream), "Inserted stream not at beginning.");
+ bool result = mStreams.InsertElementAt(aIndex, aStream);
+ NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
+ if (mCurrentStream > aIndex ||
+ (mCurrentStream == aIndex && mStartedReadingCurrent))
+ ++mCurrentStream;
+ return NS_OK;
+}
+
+/* void removeStream (in unsigned long index); */
+NS_IMETHODIMP
+nsMultiplexInputStream::RemoveStream(uint32_t aIndex)
+{
+ mStreams.RemoveElementAt(aIndex);
+ if (mCurrentStream > aIndex)
+ --mCurrentStream;
+ else if (mCurrentStream == aIndex)
+ mStartedReadingCurrent = false;
+
+ return NS_OK;
+}
+
+/* nsIInputStream getStream (in unsigned long index); */
+NS_IMETHODIMP
+nsMultiplexInputStream::GetStream(uint32_t aIndex, nsIInputStream **_retval)
+{
+ *_retval = mStreams.SafeElementAt(aIndex, nullptr);
+ NS_ENSURE_TRUE(*_retval, NS_ERROR_NOT_AVAILABLE);
+
+ NS_ADDREF(*_retval);
+ return NS_OK;
+}
+
+/* void close (); */
+NS_IMETHODIMP
+nsMultiplexInputStream::Close()
+{
+ mStatus = NS_BASE_STREAM_CLOSED;
+
+ nsresult rv = NS_OK;
+
+ uint32_t len = mStreams.Length();
+ for (uint32_t i = 0; i < len; ++i) {
+ nsresult rv2 = mStreams[i]->Close();
+ // We still want to close all streams, but we should return an error
+ if (NS_FAILED(rv2))
+ rv = rv2;
+ }
+ return rv;
+}
+
+/* unsigned long long available (); */
+NS_IMETHODIMP
+nsMultiplexInputStream::Available(uint64_t *_retval)
+{
+ if (NS_FAILED(mStatus))
+ return mStatus;
+
+ nsresult rv;
+ uint64_t avail = 0;
+
+ uint32_t len = mStreams.Length();
+ for (uint32_t i = mCurrentStream; i < len; i++) {
+ uint64_t streamAvail;
+ rv = mStreams[i]->Available(&streamAvail);
+ NS_ENSURE_SUCCESS(rv, rv);
+ avail += streamAvail;
+ }
+ *_retval = avail;
+ return NS_OK;
+}
+
+/* [noscript] unsigned long read (in charPtr buf, in unsigned long count); */
+NS_IMETHODIMP
+nsMultiplexInputStream::Read(char * aBuf, uint32_t aCount, uint32_t *_retval)
+{
+ // It is tempting to implement this method in terms of ReadSegments, but
+ // that would prevent this class from being used with streams that only
+ // implement Read (e.g., file streams).
+
+ *_retval = 0;
+
+ if (mStatus == NS_BASE_STREAM_CLOSED)
+ return NS_OK;
+ if (NS_FAILED(mStatus))
+ return mStatus;
+
+ nsresult rv = NS_OK;
+
+ uint32_t len = mStreams.Length();
+ while (mCurrentStream < len && aCount) {
+ uint32_t read;
+ rv = mStreams[mCurrentStream]->Read(aBuf, aCount, &read);
+
+ // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
+ // (This is a bug in those stream implementations)
+ if (rv == NS_BASE_STREAM_CLOSED) {
+ NS_NOTREACHED("Input stream's Read method returned NS_BASE_STREAM_CLOSED");
+ rv = NS_OK;
+ read = 0;
+ }
+ else if (NS_FAILED(rv))
+ break;
+
+ if (read == 0) {
+ ++mCurrentStream;
+ mStartedReadingCurrent = false;
+ }
+ else {
+ NS_ASSERTION(aCount >= read, "Read more than requested");
+ *_retval += read;
+ aCount -= read;
+ aBuf += read;
+ mStartedReadingCurrent = true;
+ }
+ }
+ return *_retval ? NS_OK : rv;
+}
+
+/* [noscript] unsigned long readSegments (in nsWriteSegmentFun writer,
+ * in voidPtr closure,
+ * in unsigned long count); */
+NS_IMETHODIMP
+nsMultiplexInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure,
+ uint32_t aCount, uint32_t *_retval)
+{
+ if (mStatus == NS_BASE_STREAM_CLOSED) {
+ *_retval = 0;
+ return NS_OK;
+ }
+ if (NS_FAILED(mStatus))
+ return mStatus;
+
+ NS_ASSERTION(aWriter, "missing aWriter");
+
+ nsresult rv = NS_OK;
+ ReadSegmentsState state;
+ state.mThisStream = this;
+ state.mOffset = 0;
+ state.mWriter = aWriter;
+ state.mClosure = aClosure;
+ state.mDone = false;
+
+ uint32_t len = mStreams.Length();
+ while (mCurrentStream < len && aCount) {
+ uint32_t read;
+ rv = mStreams[mCurrentStream]->ReadSegments(ReadSegCb, &state, aCount, &read);
+
+ // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
+ // (This is a bug in those stream implementations)
+ if (rv == NS_BASE_STREAM_CLOSED) {
+ NS_NOTREACHED("Input stream's Read method returned NS_BASE_STREAM_CLOSED");
+ rv = NS_OK;
+ read = 0;
+ }
+
+ // if |aWriter| decided to stop reading segments...
+ if (state.mDone || NS_FAILED(rv))
+ break;
+
+ // if stream is empty, then advance to the next stream.
+ if (read == 0) {
+ ++mCurrentStream;
+ mStartedReadingCurrent = false;
+ }
+ else {
+ NS_ASSERTION(aCount >= read, "Read more than requested");
+ state.mOffset += read;
+ aCount -= read;
+ mStartedReadingCurrent = true;
+ }
+ }
+
+ // if we successfully read some data, then this call succeeded.
+ *_retval = state.mOffset;
+ return state.mOffset ? NS_OK : rv;
+}
+
+NS_METHOD
+nsMultiplexInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure,
+ const char* aFromRawSegment,
+ uint32_t aToOffset, uint32_t aCount,
+ uint32_t *aWriteCount)
+{
+ nsresult rv;
+ ReadSegmentsState* state = (ReadSegmentsState*)aClosure;
+ rv = (state->mWriter)(state->mThisStream,
+ state->mClosure,
+ aFromRawSegment,
+ aToOffset + state->mOffset,
+ aCount,
+ aWriteCount);
+ if (NS_FAILED(rv))
+ state->mDone = true;
+ return rv;
+}
+
+/* readonly attribute boolean nonBlocking; */
+NS_IMETHODIMP
+nsMultiplexInputStream::IsNonBlocking(bool *aNonBlocking)
+{
+ uint32_t len = mStreams.Length();
+ if (len == 0) {
+ // Claim to be non-blocking, since we won't block the caller.
+ // On the other hand we'll never return NS_BASE_STREAM_WOULD_BLOCK,
+ // so maybe we should claim to be blocking? It probably doesn't
+ // matter in practice.
+ *aNonBlocking = true;
+ return NS_OK;
+ }
+ for (uint32_t i = 0; i < len; ++i) {
+ nsresult rv = mStreams[i]->IsNonBlocking(aNonBlocking);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // If one is non-blocking the entire stream becomes non-blocking
+ // (except that we don't implement nsIAsyncInputStream, so there's
+ // not much for the caller to do if Read returns "would block")
+ if (*aNonBlocking)
+ return NS_OK;
+ }
+ return NS_OK;
+}
+
+/* void seek (in int32_t whence, in int32_t offset); */
+NS_IMETHODIMP
+nsMultiplexInputStream::Seek(int32_t aWhence, int64_t aOffset)
+{
+ if (NS_FAILED(mStatus))
+ return mStatus;
+
+ nsresult rv;
+
+ uint32_t oldCurrentStream = mCurrentStream;
+ bool oldStartedReadingCurrent = mStartedReadingCurrent;
+
+ if (aWhence == NS_SEEK_SET) {
+ int64_t remaining = aOffset;
+ if (aOffset == 0) {
+ mCurrentStream = 0;
+ }
+ for (uint32_t i = 0; i < mStreams.Length(); ++i) {
+ nsCOMPtr<nsISeekableStream> stream =
+ do_QueryInterface(mStreams[i]);
+ if (!stream) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // See if all remaining streams should be rewound
+ if (remaining == 0) {
+ if (i < oldCurrentStream ||
+ (i == oldCurrentStream && oldStartedReadingCurrent)) {
+ rv = stream->Seek(NS_SEEK_SET, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+ continue;
+ }
+ else {
+ break;
+ }
+ }
+
+ // Get position in current stream
+ int64_t streamPos;
+ if (i > oldCurrentStream ||
+ (i == oldCurrentStream && !oldStartedReadingCurrent)) {
+ streamPos = 0;
+ }
+ else {
+ rv = stream->Tell(&streamPos);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // See if we need to seek current stream forward or backward
+ if (remaining < streamPos) {
+ rv = stream->Seek(NS_SEEK_SET, remaining);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mCurrentStream = i;
+ mStartedReadingCurrent = remaining != 0;
+
+ remaining = 0;
+ }
+ else if (remaining > streamPos) {
+ if (i < oldCurrentStream) {
+ // We're already at end so no need to seek this stream
+ remaining -= streamPos;
+ NS_ASSERTION(remaining >= 0, "Remaining invalid");
+ }
+ else {
+ uint64_t avail;
+ rv = mStreams[i]->Available(&avail);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int64_t newPos = XPCOM_MIN(remaining, streamPos + (int64_t)avail);
+
+ rv = stream->Seek(NS_SEEK_SET, newPos);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mCurrentStream = i;
+ mStartedReadingCurrent = true;
+
+ remaining -= newPos;
+ NS_ASSERTION(remaining >= 0, "Remaining invalid");
+ }
+ }
+ else {
+ NS_ASSERTION(remaining == streamPos, "Huh?");
+ remaining = 0;
+ }
+ }
+
+ return NS_OK;
+ }
+
+ if (aWhence == NS_SEEK_CUR && aOffset > 0) {
+ int64_t remaining = aOffset;
+ for (uint32_t i = mCurrentStream; remaining && i < mStreams.Length(); ++i) {
+ nsCOMPtr<nsISeekableStream> stream =
+ do_QueryInterface(mStreams[i]);
+
+ uint64_t avail;
+ rv = mStreams[i]->Available(&avail);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int64_t seek = XPCOM_MIN((int64_t)avail, remaining);
+
+ rv = stream->Seek(NS_SEEK_CUR, seek);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mCurrentStream = i;
+ mStartedReadingCurrent = true;
+
+ remaining -= seek;
+ }
+
+ return NS_OK;
+ }
+
+ if (aWhence == NS_SEEK_CUR && aOffset < 0) {
+ int64_t remaining = -aOffset;
+ for (uint32_t i = mCurrentStream; remaining && i != (uint32_t)-1; --i) {
+ nsCOMPtr<nsISeekableStream> stream =
+ do_QueryInterface(mStreams[i]);
+
+ int64_t pos;
+ rv = stream->Tell(&pos);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int64_t seek = XPCOM_MIN(pos, remaining);
+
+ rv = stream->Seek(NS_SEEK_CUR, -seek);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mCurrentStream = i;
+ mStartedReadingCurrent = seek != -pos;
+
+ remaining -= seek;
+ }
+
+ return NS_OK;
+ }
+
+ if (aWhence == NS_SEEK_CUR) {
+ NS_ASSERTION(aOffset == 0, "Should have handled all non-zero values");
+
+ return NS_OK;
+ }
+
+ if (aWhence == NS_SEEK_END) {
+ if (aOffset > 0) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ int64_t remaining = aOffset;
+ for (uint32_t i = mStreams.Length() - 1; i != (uint32_t)-1; --i) {
+ nsCOMPtr<nsISeekableStream> stream =
+ do_QueryInterface(mStreams[i]);
+
+ // See if all remaining streams should be seeked to end
+ if (remaining == 0) {
+ if (i >= oldCurrentStream) {
+ rv = stream->Seek(NS_SEEK_END, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ break;
+ }
+ }
+
+ // Get position in current stream
+ int64_t streamPos;
+ if (i < oldCurrentStream) {
+ streamPos = 0;
+ } else {
+ uint64_t avail;
+ rv = mStreams[i]->Available(&avail);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ streamPos = avail;
+ }
+
+ // See if we have enough data in the current stream.
+ if (DeprecatedAbs(remaining) < streamPos) {
+ rv = stream->Seek(NS_SEEK_END, remaining);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mCurrentStream = i;
+ mStartedReadingCurrent = true;
+
+ remaining = 0;
+ } else if (DeprecatedAbs(remaining) > streamPos) {
+ if (i > oldCurrentStream ||
+ (i == oldCurrentStream && !oldStartedReadingCurrent)) {
+ // We're already at start so no need to seek this stream
+ remaining += streamPos;
+ } else {
+ int64_t avail;
+ rv = stream->Tell(&avail);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int64_t newPos = streamPos + XPCOM_MIN(avail, DeprecatedAbs(remaining));
+
+ rv = stream->Seek(NS_SEEK_END, -newPos);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mCurrentStream = i;
+ mStartedReadingCurrent = true;
+
+ remaining += newPos;
+ }
+ }
+ else {
+ NS_ASSERTION(remaining == streamPos, "Huh?");
+ remaining = 0;
+ }
+ }
+
+ return NS_OK;
+ }
+
+ // other Seeks not implemented yet
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* uint32_t tell (); */
+NS_IMETHODIMP
+nsMultiplexInputStream::Tell(int64_t *_retval)
+{
+ if (NS_FAILED(mStatus))
+ return mStatus;
+
+ nsresult rv;
+ int64_t ret64 = 0;
+ uint32_t i, last;
+ last = mStartedReadingCurrent ? mCurrentStream+1 : mCurrentStream;
+ for (i = 0; i < last; ++i) {
+ nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStreams[i]);
+ NS_ENSURE_TRUE(stream, NS_ERROR_NO_INTERFACE);
+
+ int64_t pos;
+ rv = stream->Tell(&pos);
+ NS_ENSURE_SUCCESS(rv, rv);
+ ret64 += pos;
+ }
+ *_retval = ret64;
+
+ return NS_OK;
+}
+
+/* void setEOF (); */
+NS_IMETHODIMP
+nsMultiplexInputStream::SetEOF()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+nsMultiplexInputStreamConstructor(nsISupports *outer,
+ REFNSIID iid,
+ void **result)
+{
+ *result = nullptr;
+
+ if (outer)
+ return NS_ERROR_NO_AGGREGATION;
+
+ nsMultiplexInputStream *inst = new nsMultiplexInputStream();
+ if (!inst)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(inst);
+ nsresult rv = inst->QueryInterface(iid, result);
+ NS_RELEASE(inst);
+
+ return rv;
+}
+
+void
+nsMultiplexInputStream::Serialize(InputStreamParams& aParams)
+{
+ MultiplexInputStreamParams params;
+
+ uint32_t streamCount = mStreams.Length();
+
+ if (streamCount) {
+ InfallibleTArray<InputStreamParams>& streams = params.streams();
+
+ streams.SetCapacity(streamCount);
+ for (uint32_t index = 0; index < streamCount; index++) {
+ nsCOMPtr<nsIIPCSerializableInputStream> serializable =
+ do_QueryInterface(mStreams[index]);
+ NS_ASSERTION(serializable, "Child stream isn't serializable!");
+
+ if (serializable) {
+ InputStreamParams childStreamParams;
+ serializable->Serialize(childStreamParams);
+
+ NS_ASSERTION(childStreamParams.type() !=
+ InputStreamParams::T__None,
+ "Serialize failed!");
+
+ streams.AppendElement(childStreamParams);
+ }
+ }
+ }
+
+ params.currentStream() = mCurrentStream;
+ params.status() = mStatus;
+ params.startedReadingCurrent() = mStartedReadingCurrent;
+
+ aParams = params;
+}
+
+bool
+nsMultiplexInputStream::Deserialize(const InputStreamParams& aParams)
+{
+ if (aParams.type() !=
+ InputStreamParams::TMultiplexInputStreamParams) {
+ NS_ERROR("Received unknown parameters from the other process!");
+ return false;
+ }
+
+ const MultiplexInputStreamParams& params =
+ aParams.get_MultiplexInputStreamParams();
+
+ const InfallibleTArray<InputStreamParams>& streams = params.streams();
+
+ uint32_t streamCount = streams.Length();
+ for (uint32_t index = 0; index < streamCount; index++) {
+ nsCOMPtr<nsIInputStream> stream =
+ DeserializeInputStream(streams[index]);
+ if (!stream) {
+ NS_WARNING("Deserialize failed!");
+ return false;
+ }
+
+ if (NS_FAILED(AppendStream(stream))) {
+ NS_WARNING("AppendStream failed!");
+ return false;
+ }
+ }
+
+ mCurrentStream = params.currentStream();
+ mStatus = params.status();
+ mStartedReadingCurrent = params.startedReadingCurrent();
+
+ return true;
+}
diff --git a/xpcom/io/nsMultiplexInputStream.h b/xpcom/io/nsMultiplexInputStream.h
new file mode 100644
index 000000000..2d472d1f7
--- /dev/null
+++ b/xpcom/io/nsMultiplexInputStream.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/**
+ * The multiplex stream concatenates a list of input streams into a single
+ * stream.
+ */
+
+#ifndef _nsMultiplexInputStream_h_
+#define _nsMultiplexInputStream_h_
+
+#include "nsIMultiplexInputStream.h"
+
+#define NS_MULTIPLEXINPUTSTREAM_CONTRACTID "@mozilla.org/io/multiplex-input-stream;1"
+#define NS_MULTIPLEXINPUTSTREAM_CID \
+{ /* 565e3a2c-1dd2-11b2-8da1-b4cef17e568d */ \
+ 0x565e3a2c, \
+ 0x1dd2, \
+ 0x11b2, \
+ {0x8d, 0xa1, 0xb4, 0xce, 0xf1, 0x7e, 0x56, 0x8d} \
+}
+
+extern nsresult nsMultiplexInputStreamConstructor(nsISupports *outer,
+ REFNSIID iid,
+ void **result);
+
+#endif // _nsMultiplexInputStream_h_
diff --git a/xpcom/io/nsNativeCharsetUtils.cpp b/xpcom/io/nsNativeCharsetUtils.cpp
new file mode 100644
index 000000000..39cd21bcb
--- /dev/null
+++ b/xpcom/io/nsNativeCharsetUtils.cpp
@@ -0,0 +1,1136 @@
+/* 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 "xpcom-private.h"
+
+//-----------------------------------------------------------------------------
+// XP_MACOSX or ANDROID
+//-----------------------------------------------------------------------------
+#if defined(XP_MACOSX) || defined(ANDROID)
+
+#include "nsAString.h"
+#include "nsReadableUtils.h"
+#include "nsString.h"
+
+nsresult
+NS_CopyNativeToUnicode(const nsACString &input, nsAString &output)
+{
+ CopyUTF8toUTF16(input, output);
+ return NS_OK;
+}
+
+nsresult
+NS_CopyUnicodeToNative(const nsAString &input, nsACString &output)
+{
+ CopyUTF16toUTF8(input, output);
+ return NS_OK;
+}
+
+void
+NS_StartupNativeCharsetUtils()
+{
+}
+
+void
+NS_ShutdownNativeCharsetUtils()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// XP_UNIX
+//-----------------------------------------------------------------------------
+#elif defined(XP_UNIX)
+
+#include <stdlib.h> // mbtowc, wctomb
+#include <locale.h> // setlocale
+#include "mozilla/Mutex.h"
+#include "nscore.h"
+#include "nsAString.h"
+#include "nsReadableUtils.h"
+
+using namespace mozilla;
+
+//
+// choose a conversion library. we used to use mbrtowc/wcrtomb under Linux,
+// but that doesn't work for non-BMP characters whether we use '-fshort-wchar'
+// or not (see bug 206811 and
+// news://news.mozilla.org:119/bajml3$fvr1@ripley.netscape.com). we now use
+// iconv for all platforms where nltypes.h and nllanginfo.h are present
+// along with iconv.
+//
+#if defined(HAVE_ICONV) && defined(HAVE_NL_TYPES_H) && defined(HAVE_LANGINFO_CODESET)
+#define USE_ICONV 1
+#else
+#define USE_STDCONV 1
+#endif
+
+static void
+isolatin1_to_utf16(const char **input, uint32_t *inputLeft, PRUnichar **output, uint32_t *outputLeft)
+{
+ while (*inputLeft && *outputLeft) {
+ **output = (unsigned char) **input;
+ (*input)++;
+ (*inputLeft)--;
+ (*output)++;
+ (*outputLeft)--;
+ }
+}
+
+static void
+utf16_to_isolatin1(const PRUnichar **input, uint32_t *inputLeft, char **output, uint32_t *outputLeft)
+{
+ while (*inputLeft && *outputLeft) {
+ **output = (unsigned char) **input;
+ (*input)++;
+ (*inputLeft)--;
+ (*output)++;
+ (*outputLeft)--;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// conversion using iconv
+//-----------------------------------------------------------------------------
+#if defined(USE_ICONV)
+#include <nl_types.h> // CODESET
+#include <langinfo.h> // nl_langinfo
+#include <iconv.h> // iconv_open, iconv, iconv_close
+#include <errno.h>
+#include "plstr.h"
+
+#if defined(HAVE_ICONV_WITH_CONST_INPUT)
+#define ICONV_INPUT(x) (x)
+#else
+#define ICONV_INPUT(x) ((char **)x)
+#endif
+
+// solaris definitely needs this, but we'll enable it by default
+// just in case... but we know for sure that iconv(3) in glibc
+// doesn't need this.
+#if !defined(__GLIBC__)
+#define ENABLE_UTF8_FALLBACK_SUPPORT
+#endif
+
+#define INVALID_ICONV_T ((iconv_t) -1)
+
+static inline size_t
+xp_iconv(iconv_t converter,
+ const char **input,
+ size_t *inputLeft,
+ char **output,
+ size_t *outputLeft)
+{
+ size_t res, outputAvail = outputLeft ? *outputLeft : 0;
+ res = iconv(converter, ICONV_INPUT(input), inputLeft, output, outputLeft);
+ if (res == (size_t) -1) {
+ // on some platforms (e.g., linux) iconv will fail with
+ // E2BIG if it cannot convert _all_ of its input. it'll
+ // still adjust all of the in/out params correctly, so we
+ // can ignore this error. the assumption is that we will
+ // be called again to complete the conversion.
+ if ((errno == E2BIG) && (*outputLeft < outputAvail))
+ res = 0;
+ }
+ return res;
+}
+
+static inline void
+xp_iconv_reset(iconv_t converter)
+{
+ // NOTE: the man pages on Solaris claim that you can pass NULL
+ // for all parameter to reset the converter, but beware the
+ // evil Solaris crash if you go down this route >:-)
+
+ const char *zero_char_in_ptr = NULL;
+ char *zero_char_out_ptr = NULL;
+ size_t zero_size_in = 0,
+ zero_size_out = 0;
+
+ xp_iconv(converter, &zero_char_in_ptr,
+ &zero_size_in,
+ &zero_char_out_ptr,
+ &zero_size_out);
+}
+
+static inline iconv_t
+xp_iconv_open(const char **to_list, const char **from_list)
+{
+ iconv_t res;
+ const char **from_name;
+ const char **to_name;
+
+ // try all possible combinations to locate a converter.
+ to_name = to_list;
+ while (*to_name) {
+ if (**to_name) {
+ from_name = from_list;
+ while (*from_name) {
+ if (**from_name) {
+ res = iconv_open(*to_name, *from_name);
+ if (res != INVALID_ICONV_T)
+ return res;
+ }
+ from_name++;
+ }
+ }
+ to_name++;
+ }
+
+ return INVALID_ICONV_T;
+}
+
+/*
+ * PRUnichar[] is NOT a UCS-2 array BUT a UTF-16 string. Therefore, we
+ * have to use UTF-16 with iconv(3) on platforms where it's supported.
+ * However, the way UTF-16 and UCS-2 are interpreted varies across platforms
+ * and implementations of iconv(3). On Tru64, it also depends on the environment
+ * variable. To avoid the trouble arising from byte-swapping
+ * (bug 208809), we have to try UTF-16LE/BE and UCS-2LE/BE before falling
+ * back to UTF-16 and UCS-2 and variants. We assume that UTF-16 and UCS-2
+ * on systems without UTF-16LE/BE and UCS-2LE/BE have the native endianness,
+ * which isn't the case of glibc 2.1.x, for which we use 'UNICODELITTLE'
+ * and 'UNICODEBIG'. It's also not true of Tru64 V4 when the environment
+ * variable ICONV_BYTEORDER is set to 'big-endian', about which not much
+ * can be done other than adding a note in the release notes. (bug 206811)
+ */
+static const char *UTF_16_NAMES[] = {
+#if defined(IS_LITTLE_ENDIAN)
+ "UTF-16LE",
+#if defined(__GLIBC__)
+ "UNICODELITTLE",
+#endif
+ "UCS-2LE",
+#else
+ "UTF-16BE",
+#if defined(__GLIBC__)
+ "UNICODEBIG",
+#endif
+ "UCS-2BE",
+#endif
+ "UTF-16",
+ "UCS-2",
+ "UCS2",
+ "UCS_2",
+ "ucs-2",
+ "ucs2",
+ "ucs_2",
+ NULL
+};
+
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+static const char *UTF_8_NAMES[] = {
+ "UTF-8",
+ "UTF8",
+ "UTF_8",
+ "utf-8",
+ "utf8",
+ "utf_8",
+ NULL
+};
+#endif
+
+static const char *ISO_8859_1_NAMES[] = {
+ "ISO-8859-1",
+#if !defined(__GLIBC__)
+ "ISO8859-1",
+ "ISO88591",
+ "ISO_8859_1",
+ "ISO8859_1",
+ "iso-8859-1",
+ "iso8859-1",
+ "iso88591",
+ "iso_8859_1",
+ "iso8859_1",
+#endif
+ NULL
+};
+
+class nsNativeCharsetConverter
+{
+public:
+ nsNativeCharsetConverter();
+ ~nsNativeCharsetConverter();
+
+ nsresult NativeToUnicode(const char **input , uint32_t *inputLeft,
+ PRUnichar **output, uint32_t *outputLeft);
+ nsresult UnicodeToNative(const PRUnichar **input , uint32_t *inputLeft,
+ char **output, uint32_t *outputLeft);
+
+ static void GlobalInit();
+ static void GlobalShutdown();
+ static bool IsNativeUTF8();
+
+private:
+ static iconv_t gNativeToUnicode;
+ static iconv_t gUnicodeToNative;
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ static iconv_t gNativeToUTF8;
+ static iconv_t gUTF8ToNative;
+ static iconv_t gUnicodeToUTF8;
+ static iconv_t gUTF8ToUnicode;
+#endif
+ static Mutex *gLock;
+ static bool gInitialized;
+ static bool gIsNativeUTF8;
+
+ static void LazyInit();
+
+ static void Lock() { if (gLock) gLock->Lock(); }
+ static void Unlock() { if (gLock) gLock->Unlock(); }
+};
+
+iconv_t nsNativeCharsetConverter::gNativeToUnicode = INVALID_ICONV_T;
+iconv_t nsNativeCharsetConverter::gUnicodeToNative = INVALID_ICONV_T;
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+iconv_t nsNativeCharsetConverter::gNativeToUTF8 = INVALID_ICONV_T;
+iconv_t nsNativeCharsetConverter::gUTF8ToNative = INVALID_ICONV_T;
+iconv_t nsNativeCharsetConverter::gUnicodeToUTF8 = INVALID_ICONV_T;
+iconv_t nsNativeCharsetConverter::gUTF8ToUnicode = INVALID_ICONV_T;
+#endif
+Mutex *nsNativeCharsetConverter::gLock = nullptr;
+bool nsNativeCharsetConverter::gInitialized = false;
+bool nsNativeCharsetConverter::gIsNativeUTF8 = false;
+
+void
+nsNativeCharsetConverter::LazyInit()
+{
+ // LazyInit may be called before NS_StartupNativeCharsetUtils, but
+ // the setlocale it does has to be called before nl_langinfo. Like in
+ // NS_StartupNativeCharsetUtils, assume we are called early enough that
+ // we are the first to care about the locale's charset.
+ if (!gLock)
+ setlocale(LC_CTYPE, "");
+ const char *blank_list[] = { "", NULL };
+ const char **native_charset_list = blank_list;
+ const char *native_charset = nl_langinfo(CODESET);
+ if (native_charset == nullptr) {
+ NS_ERROR("native charset is unknown");
+ // fallback to ISO-8859-1
+ native_charset_list = ISO_8859_1_NAMES;
+ }
+ else
+ native_charset_list[0] = native_charset;
+
+ // Most, if not all, Unixen supporting UTF-8 and nl_langinfo(CODESET)
+ // return 'UTF-8' (or 'utf-8')
+ if (!PL_strcasecmp(native_charset, "UTF-8"))
+ gIsNativeUTF8 = true;
+
+ gNativeToUnicode = xp_iconv_open(UTF_16_NAMES, native_charset_list);
+ gUnicodeToNative = xp_iconv_open(native_charset_list, UTF_16_NAMES);
+
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ if (gNativeToUnicode == INVALID_ICONV_T) {
+ gNativeToUTF8 = xp_iconv_open(UTF_8_NAMES, native_charset_list);
+ gUTF8ToUnicode = xp_iconv_open(UTF_16_NAMES, UTF_8_NAMES);
+ NS_ASSERTION(gNativeToUTF8 != INVALID_ICONV_T, "no native to utf-8 converter");
+ NS_ASSERTION(gUTF8ToUnicode != INVALID_ICONV_T, "no utf-8 to utf-16 converter");
+ }
+ if (gUnicodeToNative == INVALID_ICONV_T) {
+ gUnicodeToUTF8 = xp_iconv_open(UTF_8_NAMES, UTF_16_NAMES);
+ gUTF8ToNative = xp_iconv_open(native_charset_list, UTF_8_NAMES);
+ NS_ASSERTION(gUnicodeToUTF8 != INVALID_ICONV_T, "no utf-16 to utf-8 converter");
+ NS_ASSERTION(gUTF8ToNative != INVALID_ICONV_T, "no utf-8 to native converter");
+ }
+#else
+ NS_ASSERTION(gNativeToUnicode != INVALID_ICONV_T, "no native to utf-16 converter");
+ NS_ASSERTION(gUnicodeToNative != INVALID_ICONV_T, "no utf-16 to native converter");
+#endif
+
+ /*
+ * On Solaris 8 (and newer?), the iconv modules converting to UCS-2
+ * prepend a byte order mark unicode character (BOM, u+FEFF) during
+ * the first use of the iconv converter. The same is the case of
+ * glibc 2.2.9x and Tru64 V5 (see bug 208809) when 'UTF-16' is used.
+ * However, we use 'UTF-16LE/BE' in both cases, instead so that we
+ * should be safe. But just in case...
+ *
+ * This dummy conversion gets rid of the BOMs and fixes bug 153562.
+ */
+ char dummy_input[1] = { ' ' };
+ char dummy_output[4];
+
+ if (gNativeToUnicode != INVALID_ICONV_T) {
+ const char *input = dummy_input;
+ size_t input_left = sizeof(dummy_input);
+ char *output = dummy_output;
+ size_t output_left = sizeof(dummy_output);
+
+ xp_iconv(gNativeToUnicode, &input, &input_left, &output, &output_left);
+ }
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ if (gUTF8ToUnicode != INVALID_ICONV_T) {
+ const char *input = dummy_input;
+ size_t input_left = sizeof(dummy_input);
+ char *output = dummy_output;
+ size_t output_left = sizeof(dummy_output);
+
+ xp_iconv(gUTF8ToUnicode, &input, &input_left, &output, &output_left);
+ }
+#endif
+
+ gInitialized = true;
+}
+
+void
+nsNativeCharsetConverter::GlobalInit()
+{
+ gLock = new Mutex("nsNativeCharsetConverter.gLock");
+}
+
+void
+nsNativeCharsetConverter::GlobalShutdown()
+{
+ if (gLock) {
+ delete gLock;
+ gLock = nullptr;
+ }
+
+ if (gNativeToUnicode != INVALID_ICONV_T) {
+ iconv_close(gNativeToUnicode);
+ gNativeToUnicode = INVALID_ICONV_T;
+ }
+
+ if (gUnicodeToNative != INVALID_ICONV_T) {
+ iconv_close(gUnicodeToNative);
+ gUnicodeToNative = INVALID_ICONV_T;
+ }
+
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ if (gNativeToUTF8 != INVALID_ICONV_T) {
+ iconv_close(gNativeToUTF8);
+ gNativeToUTF8 = INVALID_ICONV_T;
+ }
+ if (gUTF8ToNative != INVALID_ICONV_T) {
+ iconv_close(gUTF8ToNative);
+ gUTF8ToNative = INVALID_ICONV_T;
+ }
+ if (gUnicodeToUTF8 != INVALID_ICONV_T) {
+ iconv_close(gUnicodeToUTF8);
+ gUnicodeToUTF8 = INVALID_ICONV_T;
+ }
+ if (gUTF8ToUnicode != INVALID_ICONV_T) {
+ iconv_close(gUTF8ToUnicode);
+ gUTF8ToUnicode = INVALID_ICONV_T;
+ }
+#endif
+
+ gInitialized = false;
+}
+
+nsNativeCharsetConverter::nsNativeCharsetConverter()
+{
+ Lock();
+ if (!gInitialized)
+ LazyInit();
+}
+
+nsNativeCharsetConverter::~nsNativeCharsetConverter()
+{
+ // reset converters for next time
+ if (gNativeToUnicode != INVALID_ICONV_T)
+ xp_iconv_reset(gNativeToUnicode);
+ if (gUnicodeToNative != INVALID_ICONV_T)
+ xp_iconv_reset(gUnicodeToNative);
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ if (gNativeToUTF8 != INVALID_ICONV_T)
+ xp_iconv_reset(gNativeToUTF8);
+ if (gUTF8ToNative != INVALID_ICONV_T)
+ xp_iconv_reset(gUTF8ToNative);
+ if (gUnicodeToUTF8 != INVALID_ICONV_T)
+ xp_iconv_reset(gUnicodeToUTF8);
+ if (gUTF8ToUnicode != INVALID_ICONV_T)
+ xp_iconv_reset(gUTF8ToUnicode);
+#endif
+ Unlock();
+}
+
+nsresult
+nsNativeCharsetConverter::NativeToUnicode(const char **input,
+ uint32_t *inputLeft,
+ PRUnichar **output,
+ uint32_t *outputLeft)
+{
+ size_t res = 0;
+ size_t inLeft = (size_t) *inputLeft;
+ size_t outLeft = (size_t) *outputLeft * 2;
+
+ if (gNativeToUnicode != INVALID_ICONV_T) {
+
+ res = xp_iconv(gNativeToUnicode, input, &inLeft, (char **) output, &outLeft);
+
+ *inputLeft = inLeft;
+ *outputLeft = outLeft / 2;
+ if (res != (size_t) -1)
+ return NS_OK;
+
+ NS_WARNING("conversion from native to utf-16 failed");
+
+ // reset converter
+ xp_iconv_reset(gNativeToUnicode);
+ }
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ else if ((gNativeToUTF8 != INVALID_ICONV_T) &&
+ (gUTF8ToUnicode != INVALID_ICONV_T)) {
+ // convert first to UTF8, then from UTF8 to UCS2
+ const char *in = *input;
+
+ char ubuf[1024];
+
+ // we assume we're always called with enough space in |output|,
+ // so convert many chars at a time...
+ while (inLeft) {
+ char *p = ubuf;
+ size_t n = sizeof(ubuf);
+ res = xp_iconv(gNativeToUTF8, &in, &inLeft, &p, &n);
+ if (res == (size_t) -1) {
+ NS_ERROR("conversion from native to utf-8 failed");
+ break;
+ }
+ NS_ASSERTION(outLeft > 0, "bad assumption");
+ p = ubuf;
+ n = sizeof(ubuf) - n;
+ res = xp_iconv(gUTF8ToUnicode, (const char **) &p, &n, (char **) output, &outLeft);
+ if (res == (size_t) -1) {
+ NS_ERROR("conversion from utf-8 to utf-16 failed");
+ break;
+ }
+ }
+
+ (*input) += (*inputLeft - inLeft);
+ *inputLeft = inLeft;
+ *outputLeft = outLeft / 2;
+
+ if (res != (size_t) -1)
+ return NS_OK;
+
+ // reset converters
+ xp_iconv_reset(gNativeToUTF8);
+ xp_iconv_reset(gUTF8ToUnicode);
+ }
+#endif
+
+ // fallback: zero-pad and hope for the best
+ // XXX This is lame and we have to do better.
+ isolatin1_to_utf16(input, inputLeft, output, outputLeft);
+
+ return NS_OK;
+}
+
+nsresult
+nsNativeCharsetConverter::UnicodeToNative(const PRUnichar **input,
+ uint32_t *inputLeft,
+ char **output,
+ uint32_t *outputLeft)
+{
+ size_t res = 0;
+ size_t inLeft = (size_t) *inputLeft * 2;
+ size_t outLeft = (size_t) *outputLeft;
+
+ if (gUnicodeToNative != INVALID_ICONV_T) {
+ res = xp_iconv(gUnicodeToNative, (const char **) input, &inLeft, output, &outLeft);
+
+ *inputLeft = inLeft / 2;
+ *outputLeft = outLeft;
+ if (res != (size_t) -1) {
+ return NS_OK;
+ }
+
+ NS_ERROR("iconv failed");
+
+ // reset converter
+ xp_iconv_reset(gUnicodeToNative);
+ }
+#if defined(ENABLE_UTF8_FALLBACK_SUPPORT)
+ else if ((gUnicodeToUTF8 != INVALID_ICONV_T) &&
+ (gUTF8ToNative != INVALID_ICONV_T)) {
+ const char *in = (const char *) *input;
+
+ char ubuf[6]; // max utf-8 char length (really only needs to be 4 bytes)
+
+ // convert one uchar at a time...
+ while (inLeft && outLeft) {
+ char *p = ubuf;
+ size_t n = sizeof(ubuf), one_uchar = sizeof(PRUnichar);
+ res = xp_iconv(gUnicodeToUTF8, &in, &one_uchar, &p, &n);
+ if (res == (size_t) -1) {
+ NS_ERROR("conversion from utf-16 to utf-8 failed");
+ break;
+ }
+ p = ubuf;
+ n = sizeof(ubuf) - n;
+ res = xp_iconv(gUTF8ToNative, (const char **) &p, &n, output, &outLeft);
+ if (res == (size_t) -1) {
+ if (errno == E2BIG) {
+ // not enough room for last uchar... back up and return.
+ in -= sizeof(PRUnichar);
+ res = 0;
+ }
+ else
+ NS_ERROR("conversion from utf-8 to native failed");
+ break;
+ }
+ inLeft -= sizeof(PRUnichar);
+ }
+
+ (*input) += (*inputLeft - inLeft / 2);
+ *inputLeft = inLeft / 2;
+ *outputLeft = outLeft;
+ if (res != (size_t) -1) {
+ return NS_OK;
+ }
+
+ // reset converters
+ xp_iconv_reset(gUnicodeToUTF8);
+ xp_iconv_reset(gUTF8ToNative);
+ }
+#endif
+
+ // fallback: truncate and hope for the best
+ // XXX This is lame and we have to do better.
+ utf16_to_isolatin1(input, inputLeft, output, outputLeft);
+
+ return NS_OK;
+}
+
+bool
+nsNativeCharsetConverter::IsNativeUTF8()
+{
+ if (!gInitialized) {
+ Lock();
+ if (!gInitialized)
+ LazyInit();
+ Unlock();
+ }
+ return gIsNativeUTF8;
+}
+
+#endif // USE_ICONV
+
+//-----------------------------------------------------------------------------
+// conversion using mb[r]towc/wc[r]tomb
+//-----------------------------------------------------------------------------
+#if defined(USE_STDCONV)
+#if defined(HAVE_WCRTOMB) || defined(HAVE_MBRTOWC)
+#include <wchar.h> // mbrtowc, wcrtomb
+#endif
+
+class nsNativeCharsetConverter
+{
+public:
+ nsNativeCharsetConverter();
+
+ nsresult NativeToUnicode(const char **input , uint32_t *inputLeft,
+ PRUnichar **output, uint32_t *outputLeft);
+ nsresult UnicodeToNative(const PRUnichar **input , uint32_t *inputLeft,
+ char **output, uint32_t *outputLeft);
+
+ static void GlobalInit();
+ static void GlobalShutdown() { }
+ static bool IsNativeUTF8();
+
+private:
+ static bool gWCharIsUnicode;
+
+#if defined(HAVE_WCRTOMB) || defined(HAVE_MBRTOWC)
+ mbstate_t ps;
+#endif
+};
+
+bool nsNativeCharsetConverter::gWCharIsUnicode = false;
+
+nsNativeCharsetConverter::nsNativeCharsetConverter()
+{
+#if defined(HAVE_WCRTOMB) || defined(HAVE_MBRTOWC)
+ memset(&ps, 0, sizeof(ps));
+#endif
+}
+
+void
+nsNativeCharsetConverter::GlobalInit()
+{
+ // verify that wchar_t for the current locale is actually unicode.
+ // if it is not, then we should avoid calling mbtowc/wctomb and
+ // just fallback on zero-pad/truncation conversion.
+ //
+ // this test cannot be done at build time because the encoding of
+ // wchar_t may depend on the runtime locale. sad, but true!!
+ //
+ // so, if wchar_t is unicode then converting an ASCII character
+ // to wchar_t should not change its numeric value. we'll just
+ // check what happens with the ASCII 'a' character.
+ //
+ // this test is not perfect... obviously, it could yield false
+ // positives, but then at least ASCII text would be converted
+ // properly (or maybe just the 'a' character) -- oh well :(
+
+ char a = 'a';
+ unsigned int w = 0;
+
+ int res = mbtowc((wchar_t *) &w, &a, 1);
+
+ gWCharIsUnicode = (res != -1 && w == 'a');
+
+#ifdef DEBUG
+ if (!gWCharIsUnicode)
+ NS_WARNING("wchar_t is not unicode (unicode conversion will be lossy)");
+#endif
+}
+
+nsresult
+nsNativeCharsetConverter::NativeToUnicode(const char **input,
+ uint32_t *inputLeft,
+ PRUnichar **output,
+ uint32_t *outputLeft)
+{
+ if (gWCharIsUnicode) {
+ int incr;
+
+ // cannot use wchar_t here since it may have been redefined (e.g.,
+ // via -fshort-wchar). hopefully, sizeof(tmp) is sufficient XP.
+ unsigned int tmp = 0;
+ while (*inputLeft && *outputLeft) {
+#ifdef HAVE_MBRTOWC
+ incr = (int) mbrtowc((wchar_t *) &tmp, *input, *inputLeft, &ps);
+#else
+ // XXX is this thread-safe?
+ incr = (int) mbtowc((wchar_t *) &tmp, *input, *inputLeft);
+#endif
+ if (incr < 0) {
+ NS_WARNING("mbtowc failed: possible charset mismatch");
+ // zero-pad and hope for the best
+ tmp = (unsigned char) **input;
+ incr = 1;
+ }
+ **output = (PRUnichar) tmp;
+ (*input) += incr;
+ (*inputLeft) -= incr;
+ (*output)++;
+ (*outputLeft)--;
+ }
+ }
+ else {
+ // wchar_t isn't unicode, so the best we can do is treat the
+ // input as if it is isolatin1 :(
+ isolatin1_to_utf16(input, inputLeft, output, outputLeft);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsNativeCharsetConverter::UnicodeToNative(const PRUnichar **input,
+ uint32_t *inputLeft,
+ char **output,
+ uint32_t *outputLeft)
+{
+ if (gWCharIsUnicode) {
+ int incr;
+
+ while (*inputLeft && *outputLeft >= MB_CUR_MAX) {
+#ifdef HAVE_WCRTOMB
+ incr = (int) wcrtomb(*output, (wchar_t) **input, &ps);
+#else
+ // XXX is this thread-safe?
+ incr = (int) wctomb(*output, (wchar_t) **input);
+#endif
+ if (incr < 0) {
+ NS_WARNING("mbtowc failed: possible charset mismatch");
+ **output = (unsigned char) **input; // truncate
+ incr = 1;
+ }
+ // most likely we're dead anyways if this assertion should fire
+ NS_ASSERTION(uint32_t(incr) <= *outputLeft, "wrote beyond end of string");
+ (*output) += incr;
+ (*outputLeft) -= incr;
+ (*input)++;
+ (*inputLeft)--;
+ }
+ }
+ else {
+ // wchar_t isn't unicode, so the best we can do is treat the
+ // input as if it is isolatin1 :(
+ utf16_to_isolatin1(input, inputLeft, output, outputLeft);
+ }
+
+ return NS_OK;
+}
+
+// XXX : for now, return false
+bool
+nsNativeCharsetConverter::IsNativeUTF8()
+{
+ return false;
+}
+
+#endif // USE_STDCONV
+
+//-----------------------------------------------------------------------------
+// API implementation
+//-----------------------------------------------------------------------------
+
+nsresult
+NS_CopyNativeToUnicode(const nsACString &input, nsAString &output)
+{
+ output.Truncate();
+
+ uint32_t inputLen = input.Length();
+
+ nsACString::const_iterator iter;
+ input.BeginReading(iter);
+
+ //
+ // OPTIMIZATION: preallocate space for largest possible result; convert
+ // directly into the result buffer to avoid intermediate buffer copy.
+ //
+ // this will generally result in a larger allocation, but that seems
+ // better than an extra buffer copy.
+ //
+ if (!output.SetLength(inputLen, fallible_t()))
+ return NS_ERROR_OUT_OF_MEMORY;
+ nsAString::iterator out_iter;
+ output.BeginWriting(out_iter);
+
+ PRUnichar *result = out_iter.get();
+ uint32_t resultLeft = inputLen;
+
+ const char *buf = iter.get();
+ uint32_t bufLeft = inputLen;
+
+ nsNativeCharsetConverter conv;
+ nsresult rv = conv.NativeToUnicode(&buf, &bufLeft, &result, &resultLeft);
+ if (NS_SUCCEEDED(rv)) {
+ NS_ASSERTION(bufLeft == 0, "did not consume entire input buffer");
+ output.SetLength(inputLen - resultLeft);
+ }
+ return rv;
+}
+
+nsresult
+NS_CopyUnicodeToNative(const nsAString &input, nsACString &output)
+{
+ output.Truncate();
+
+ nsAString::const_iterator iter, end;
+ input.BeginReading(iter);
+ input.EndReading(end);
+
+ // cannot easily avoid intermediate buffer copy.
+ char temp[4096];
+
+ nsNativeCharsetConverter conv;
+
+ const PRUnichar *buf = iter.get();
+ uint32_t bufLeft = Distance(iter, end);
+ while (bufLeft) {
+ char *p = temp;
+ uint32_t tempLeft = sizeof(temp);
+
+ nsresult rv = conv.UnicodeToNative(&buf, &bufLeft, &p, &tempLeft);
+ if (NS_FAILED(rv)) return rv;
+
+ if (tempLeft < sizeof(temp))
+ output.Append(temp, sizeof(temp) - tempLeft);
+ }
+ return NS_OK;
+}
+
+bool
+NS_IsNativeUTF8()
+{
+ return nsNativeCharsetConverter::IsNativeUTF8();
+}
+
+void
+NS_StartupNativeCharsetUtils()
+{
+ //
+ // need to initialize the locale or else charset conversion will fail.
+ // better not delay this in case some other component alters the locale
+ // settings.
+ //
+ // XXX we assume that we are called early enough that we should
+ // always be the first to care about the locale's charset.
+ //
+ setlocale(LC_CTYPE, "");
+
+ nsNativeCharsetConverter::GlobalInit();
+}
+
+void
+NS_ShutdownNativeCharsetUtils()
+{
+ nsNativeCharsetConverter::GlobalShutdown();
+}
+
+//-----------------------------------------------------------------------------
+// XP_WIN
+//-----------------------------------------------------------------------------
+#elif defined(XP_WIN)
+
+#include <windows.h>
+#include "nsAString.h"
+#include "nsReadableUtils.h"
+
+using namespace mozilla;
+
+nsresult
+NS_CopyNativeToUnicode(const nsACString &input, nsAString &output)
+{
+ uint32_t inputLen = input.Length();
+
+ nsACString::const_iterator iter;
+ input.BeginReading(iter);
+
+ const char *buf = iter.get();
+
+ // determine length of result
+ uint32_t resultLen = 0;
+ int n = ::MultiByteToWideChar(CP_ACP, 0, buf, inputLen, NULL, 0);
+ if (n > 0)
+ resultLen += n;
+
+ // allocate sufficient space
+ if (!output.SetLength(resultLen, fallible_t()))
+ return NS_ERROR_OUT_OF_MEMORY;
+ if (resultLen > 0) {
+ nsAString::iterator out_iter;
+ output.BeginWriting(out_iter);
+
+ PRUnichar *result = out_iter.get();
+
+ ::MultiByteToWideChar(CP_ACP, 0, buf, inputLen, result, resultLen);
+ }
+ return NS_OK;
+}
+
+nsresult
+NS_CopyUnicodeToNative(const nsAString &input, nsACString &output)
+{
+ uint32_t inputLen = input.Length();
+
+ nsAString::const_iterator iter;
+ input.BeginReading(iter);
+
+ const PRUnichar *buf = iter.get();
+
+ // determine length of result
+ uint32_t resultLen = 0;
+
+ int n = ::WideCharToMultiByte(CP_ACP, 0, buf, inputLen, NULL, 0, NULL, NULL);
+ if (n > 0)
+ resultLen += n;
+
+ // allocate sufficient space
+ if (!output.SetLength(resultLen, fallible_t()))
+ return NS_ERROR_OUT_OF_MEMORY;
+ if (resultLen > 0) {
+ nsACString::iterator out_iter;
+ output.BeginWriting(out_iter);
+
+ // default "defaultChar" is '?', which is an illegal character on windows
+ // file system. That will cause file uncreatable. Change it to '_'
+ const char defaultChar = '_';
+
+ char *result = out_iter.get();
+
+ ::WideCharToMultiByte(CP_ACP, 0, buf, inputLen, result, resultLen,
+ &defaultChar, NULL);
+ }
+ return NS_OK;
+}
+
+// moved from widget/windows/nsToolkit.cpp
+int32_t
+NS_ConvertAtoW(const char *aStrInA, int aBufferSize, PRUnichar *aStrOutW)
+{
+ return MultiByteToWideChar(CP_ACP, 0, aStrInA, -1, aStrOutW, aBufferSize);
+}
+
+int32_t
+NS_ConvertWtoA(const PRUnichar *aStrInW, int aBufferSizeOut,
+ char *aStrOutA, const char *aDefault)
+{
+ if ((!aStrInW) || (!aStrOutA) || (aBufferSizeOut <= 0))
+ return 0;
+
+ int numCharsConverted = WideCharToMultiByte(CP_ACP, 0, aStrInW, -1,
+ aStrOutA, aBufferSizeOut,
+ aDefault, NULL);
+
+ if (!numCharsConverted) {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ // Overflow, add missing null termination but return 0
+ aStrOutA[aBufferSizeOut-1] = '\0';
+ }
+ else {
+ // Other error, clear string and return 0
+ aStrOutA[0] = '\0';
+ }
+ }
+ else if (numCharsConverted < aBufferSizeOut) {
+ // Add 2nd null (really necessary?)
+ aStrOutA[numCharsConverted] = '\0';
+ }
+
+ return numCharsConverted;
+}
+
+//-----------------------------------------------------------------------------
+// XP_OS2
+//-----------------------------------------------------------------------------
+#elif defined(XP_OS2)
+
+#define INCL_DOS
+#include <os2.h>
+#include <uconv.h>
+#include "nsAString.h"
+#include "nsReadableUtils.h"
+#include <ulserrno.h>
+#include "nsNativeCharsetUtils.h"
+
+using namespace mozilla;
+
+static UconvObject UnicodeConverter = NULL;
+
+nsresult
+NS_CopyNativeToUnicode(const nsACString &input, nsAString &output)
+{
+ uint32_t inputLen = input.Length();
+
+ nsACString::const_iterator iter;
+ input.BeginReading(iter);
+ const char *inputStr = iter.get();
+
+ // determine length of result
+ uint32_t resultLen = inputLen;
+ if (!output.SetLength(resultLen, fallible_t()))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsAString::iterator out_iter;
+ output.BeginWriting(out_iter);
+ UniChar *result = (UniChar*)out_iter.get();
+
+ size_t cSubs = 0;
+ size_t resultLeft = resultLen;
+
+ if (!UnicodeConverter)
+ NS_StartupNativeCharsetUtils();
+
+ int unirc = ::UniUconvToUcs(UnicodeConverter, (void**)&inputStr, &inputLen,
+ &result, &resultLeft, &cSubs);
+
+ NS_ASSERTION(unirc != UCONV_E2BIG, "Path too big");
+
+ if (unirc != ULS_SUCCESS) {
+ output.Truncate();
+ return NS_ERROR_FAILURE;
+ }
+
+ // Need to update string length to reflect how many bytes were actually
+ // written.
+ output.Truncate(resultLen - resultLeft);
+ return NS_OK;
+}
+
+nsresult
+NS_CopyUnicodeToNative(const nsAString &input, nsACString &output)
+{
+ size_t inputLen = input.Length();
+
+ nsAString::const_iterator iter;
+ input.BeginReading(iter);
+ UniChar* inputStr = (UniChar*) const_cast<PRUnichar*>(iter.get());
+
+ // maximum length of unicode string of length x converted to native
+ // codepage is x*2
+ size_t resultLen = inputLen * 2;
+ if (!output.SetLength(resultLen, fallible_t()))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsACString::iterator out_iter;
+ output.BeginWriting(out_iter);
+ char *result = out_iter.get();
+
+ size_t cSubs = 0;
+ size_t resultLeft = resultLen;
+
+ if (!UnicodeConverter)
+ NS_StartupNativeCharsetUtils();
+
+ int unirc = ::UniUconvFromUcs(UnicodeConverter, &inputStr, &inputLen,
+ (void**)&result, &resultLeft, &cSubs);
+
+ NS_ASSERTION(unirc != UCONV_E2BIG, "Path too big");
+
+ if (unirc != ULS_SUCCESS) {
+ output.Truncate();
+ return NS_ERROR_FAILURE;
+ }
+
+ // Need to update string length to reflect how many bytes were actually
+ // written.
+ output.Truncate(resultLen - resultLeft);
+ return NS_OK;
+}
+
+void
+NS_StartupNativeCharsetUtils()
+{
+ ULONG ulLength;
+ ULONG ulCodePage;
+ DosQueryCp(sizeof(ULONG), &ulCodePage, &ulLength);
+
+ UniChar codepage[20];
+ int unirc = ::UniMapCpToUcsCp(ulCodePage, codepage, 20);
+ if (unirc == ULS_SUCCESS) {
+ unirc = ::UniCreateUconvObject(codepage, &UnicodeConverter);
+ if (unirc == ULS_SUCCESS) {
+ uconv_attribute_t attr;
+ ::UniQueryUconvObject(UnicodeConverter, &attr, sizeof(uconv_attribute_t),
+ NULL, NULL, NULL);
+ attr.options = UCONV_OPTION_SUBSTITUTE_BOTH;
+ attr.subchar_len=1;
+ attr.subchar[0]='_';
+ ::UniSetUconvObject(UnicodeConverter, &attr);
+ }
+ }
+}
+
+void
+NS_ShutdownNativeCharsetUtils()
+{
+ ::UniFreeUconvObject(UnicodeConverter);
+}
+
+#else
+
+#include "nsReadableUtils.h"
+
+nsresult
+NS_CopyNativeToUnicode(const nsACString &input, nsAString &output)
+{
+ CopyASCIItoUTF16(input, output);
+ return NS_OK;
+}
+
+nsresult
+NS_CopyUnicodeToNative(const nsAString &input, nsACString &output)
+{
+ LossyCopyUTF16toASCII(input, output);
+ return NS_OK;
+}
+
+void
+NS_StartupNativeCharsetUtils()
+{
+}
+
+void
+NS_ShutdownNativeCharsetUtils()
+{
+}
+
+#endif
diff --git a/xpcom/io/nsNativeCharsetUtils.h b/xpcom/io/nsNativeCharsetUtils.h
new file mode 100644
index 000000000..0dc6aec15
--- /dev/null
+++ b/xpcom/io/nsNativeCharsetUtils.h
@@ -0,0 +1,60 @@
+/* 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 nsNativeCharsetUtils_h__
+#define nsNativeCharsetUtils_h__
+
+
+/*****************************************************************************\
+ * *
+ * **** NOTICE **** *
+ * *
+ * *** THESE ARE NOT GENERAL PURPOSE CONVERTERS *** *
+ * *
+ * NS_CopyNativeToUnicode / NS_CopyUnicodeToNative should only be used *
+ * for converting *FILENAMES* between native and unicode. They are not *
+ * designed or tested for general encoding converter use. *
+ * *
+\*****************************************************************************/
+
+/**
+ * thread-safe conversion routines that do not depend on uconv libraries.
+ */
+nsresult NS_CopyNativeToUnicode(const nsACString &input, nsAString &output);
+nsresult NS_CopyUnicodeToNative(const nsAString &input, nsACString &output);
+
+/*
+ * This function indicates whether the character encoding used in the file
+ * system (more exactly what's used for |GetNativeFoo| and |SetNativeFoo|
+ * of |nsIFile|) is UTF-8 or not. Knowing that helps us avoid an
+ * unncessary encoding conversion in some cases. For instance, to get the leaf
+ * name in UTF-8 out of nsIFile, we can just use |GetNativeLeafName| rather
+ * than using |GetLeafName| and converting the result to UTF-8 if the file
+ * system encoding is UTF-8.
+ * On Unix (but not on Mac OS X), it depends on the locale and is not known
+ * in advance (at the compilation time) so that this function needs to be
+ * a real function. On Mac OS X it's always UTF-8 while on Windows
+ * and other platforms (e.g. OS2), it's never UTF-8.
+ */
+#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(ANDROID)
+bool NS_IsNativeUTF8();
+#else
+inline bool NS_IsNativeUTF8()
+{
+#if defined(XP_MACOSX) || defined(ANDROID)
+ return true;
+#else
+ return false;
+#endif
+}
+#endif
+
+
+/**
+ * internal
+ */
+void NS_StartupNativeCharsetUtils();
+void NS_ShutdownNativeCharsetUtils();
+
+#endif // nsNativeCharsetUtils_h__
diff --git a/xpcom/io/nsPipe.h b/xpcom/io/nsPipe.h
new file mode 100644
index 000000000..383065bd0
--- /dev/null
+++ b/xpcom/io/nsPipe.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 nsPipe_h__
+#define nsPipe_h__
+
+#define NS_PIPE_CONTRACTID \
+ "@mozilla.org/pipe;1"
+#define NS_PIPE_CID \
+{ /* e4a0ee4e-0775-457b-9118-b3ae97a7c758 */ \
+ 0xe4a0ee4e, \
+ 0x0775, \
+ 0x457b, \
+ {0x91,0x18,0xb3,0xae,0x97,0xa7,0xc7,0x58} \
+}
+
+// Generic factory constructor for the nsPipe class
+nsresult NS_HIDDEN
+nsPipeConstructor(nsISupports *outer, REFNSIID iid, void **result);
+
+#endif // !defined(nsPipe_h__)
diff --git a/xpcom/io/nsPipe3.cpp b/xpcom/io/nsPipe3.cpp
new file mode 100644
index 000000000..adc225f98
--- /dev/null
+++ b/xpcom/io/nsPipe3.cpp
@@ -0,0 +1,1308 @@
+/* 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 "mozilla/Attributes.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "nsIPipe.h"
+#include "nsIEventTarget.h"
+#include "nsISeekableStream.h"
+#include "nsIProgrammingLanguage.h"
+#include "nsSegmentedBuffer.h"
+#include "nsStreamUtils.h"
+#include "nsCOMPtr.h"
+#include "nsCRT.h"
+#include "prlog.h"
+#include "nsIClassInfoImpl.h"
+#include "nsAtomicRefcnt.h"
+#include "nsAlgorithm.h"
+
+using namespace mozilla;
+
+#if defined(PR_LOGGING)
+//
+// set NSPR_LOG_MODULES=nsPipe:5
+//
+static PRLogModuleInfo *
+GetPipeLog()
+{
+ static PRLogModuleInfo *sLog;
+ if (!sLog)
+ sLog = PR_NewLogModule("nsPipe");
+ return sLog;
+}
+#define LOG(args) PR_LOG(GetPipeLog(), PR_LOG_DEBUG, args)
+#else
+#define LOG(args)
+#endif
+
+#define DEFAULT_SEGMENT_SIZE 4096
+#define DEFAULT_SEGMENT_COUNT 16
+
+class nsPipe;
+class nsPipeEvents;
+class nsPipeInputStream;
+class nsPipeOutputStream;
+
+//-----------------------------------------------------------------------------
+
+// this class is used to delay notifications until the end of a particular
+// scope. it helps avoid the complexity of issuing callbacks while inside
+// a critical section.
+class nsPipeEvents
+{
+public:
+ nsPipeEvents() { }
+ ~nsPipeEvents();
+
+ inline void NotifyInputReady(nsIAsyncInputStream *stream,
+ nsIInputStreamCallback *callback)
+ {
+ NS_ASSERTION(!mInputCallback, "already have an input event");
+ mInputStream = stream;
+ mInputCallback = callback;
+ }
+
+ inline void NotifyOutputReady(nsIAsyncOutputStream *stream,
+ nsIOutputStreamCallback *callback)
+ {
+ NS_ASSERTION(!mOutputCallback, "already have an output event");
+ mOutputStream = stream;
+ mOutputCallback = callback;
+ }
+
+private:
+ nsCOMPtr<nsIAsyncInputStream> mInputStream;
+ nsCOMPtr<nsIInputStreamCallback> mInputCallback;
+ nsCOMPtr<nsIAsyncOutputStream> mOutputStream;
+ nsCOMPtr<nsIOutputStreamCallback> mOutputCallback;
+};
+
+//-----------------------------------------------------------------------------
+
+// the input end of a pipe (allocated as a member of the pipe).
+class nsPipeInputStream : public nsIAsyncInputStream
+ , public nsISeekableStream
+ , public nsISearchableInputStream
+ , public nsIClassInfo
+{
+public:
+ // since this class will be allocated as a member of the pipe, we do not
+ // need our own ref count. instead, we share the lifetime (the ref count)
+ // of the entire pipe. this macro is just convenience since it does not
+ // declare a mRefCount variable; however, don't let the name fool you...
+ // we are not inheriting from nsPipe ;-)
+ NS_DECL_ISUPPORTS_INHERITED
+
+ NS_DECL_NSIINPUTSTREAM
+ NS_DECL_NSIASYNCINPUTSTREAM
+ NS_DECL_NSISEEKABLESTREAM
+ NS_DECL_NSISEARCHABLEINPUTSTREAM
+ NS_DECL_NSICLASSINFO
+
+ nsPipeInputStream(nsPipe *pipe)
+ : mPipe(pipe)
+ , mReaderRefCnt(0)
+ , mLogicalOffset(0)
+ , mBlocking(true)
+ , mBlocked(false)
+ , mAvailable(0)
+ , mCallbackFlags(0)
+ { }
+
+ nsresult Fill();
+ void SetNonBlocking(bool aNonBlocking) { mBlocking = !aNonBlocking; }
+
+ uint32_t Available() { return mAvailable; }
+ void ReduceAvailable(uint32_t avail) { mAvailable -= avail; }
+
+ // synchronously wait for the pipe to become readable.
+ nsresult Wait();
+
+ // these functions return true to indicate that the pipe's monitor should
+ // be notified, to wake up a blocked reader if any.
+ bool OnInputReadable(uint32_t bytesWritten, nsPipeEvents &);
+ bool OnInputException(nsresult, nsPipeEvents &);
+
+private:
+ nsPipe *mPipe;
+
+ // separate refcnt so that we know when to close the consumer
+ nsrefcnt mReaderRefCnt;
+ int64_t mLogicalOffset;
+ bool mBlocking;
+
+ // these variables can only be accessed while inside the pipe's monitor
+ bool mBlocked;
+ uint32_t mAvailable;
+ nsCOMPtr<nsIInputStreamCallback> mCallback;
+ uint32_t mCallbackFlags;
+};
+
+//-----------------------------------------------------------------------------
+
+// the output end of a pipe (allocated as a member of the pipe).
+class nsPipeOutputStream : public nsIAsyncOutputStream
+ , public nsIClassInfo
+{
+public:
+ // since this class will be allocated as a member of the pipe, we do not
+ // need our own ref count. instead, we share the lifetime (the ref count)
+ // of the entire pipe. this macro is just convenience since it does not
+ // declare a mRefCount variable; however, don't let the name fool you...
+ // we are not inheriting from nsPipe ;-)
+ NS_DECL_ISUPPORTS_INHERITED
+
+ NS_DECL_NSIOUTPUTSTREAM
+ NS_DECL_NSIASYNCOUTPUTSTREAM
+ NS_DECL_NSICLASSINFO
+
+ nsPipeOutputStream(nsPipe *pipe)
+ : mPipe(pipe)
+ , mWriterRefCnt(0)
+ , mLogicalOffset(0)
+ , mBlocking(true)
+ , mBlocked(false)
+ , mWritable(true)
+ , mCallbackFlags(0)
+ { }
+
+ void SetNonBlocking(bool aNonBlocking) { mBlocking = !aNonBlocking; }
+ void SetWritable(bool writable) { mWritable = writable; }
+
+ // synchronously wait for the pipe to become writable.
+ nsresult Wait();
+
+ // these functions return true to indicate that the pipe's monitor should
+ // be notified, to wake up a blocked writer if any.
+ bool OnOutputWritable(nsPipeEvents &);
+ bool OnOutputException(nsresult, nsPipeEvents &);
+
+private:
+ nsPipe *mPipe;
+
+ // separate refcnt so that we know when to close the producer
+ nsrefcnt mWriterRefCnt;
+ int64_t mLogicalOffset;
+ bool mBlocking;
+
+ // these variables can only be accessed while inside the pipe's monitor
+ bool mBlocked;
+ bool mWritable;
+ nsCOMPtr<nsIOutputStreamCallback> mCallback;
+ uint32_t mCallbackFlags;
+};
+
+//-----------------------------------------------------------------------------
+
+class nsPipe MOZ_FINAL : public nsIPipe
+{
+public:
+ friend class nsPipeInputStream;
+ friend class nsPipeOutputStream;
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPIPE
+
+ // nsPipe methods:
+ nsPipe();
+
+private:
+ ~nsPipe();
+
+public:
+ //
+ // methods below may only be called while inside the pipe's monitor
+ //
+
+ void PeekSegment(uint32_t n, char *&cursor, char *&limit);
+
+ //
+ // methods below may be called while outside the pipe's monitor
+ //
+
+ nsresult GetReadSegment(const char *&segment, uint32_t &segmentLen);
+ void AdvanceReadCursor(uint32_t count);
+
+ nsresult GetWriteSegment(char *&segment, uint32_t &segmentLen);
+ void AdvanceWriteCursor(uint32_t count);
+
+ void OnPipeException(nsresult reason, bool outputOnly = false);
+
+protected:
+ // We can't inherit from both nsIInputStream and nsIOutputStream
+ // because they collide on their Close method. Consequently we nest their
+ // implementations to avoid the extra object allocation.
+ nsPipeInputStream mInput;
+ nsPipeOutputStream mOutput;
+
+ ReentrantMonitor mReentrantMonitor;
+ nsSegmentedBuffer mBuffer;
+
+ char* mReadCursor;
+ char* mReadLimit;
+
+ int32_t mWriteSegment;
+ char* mWriteCursor;
+ char* mWriteLimit;
+
+ nsresult mStatus;
+ bool mInited;
+};
+
+//
+// NOTES on buffer architecture:
+//
+// +-----------------+ - - mBuffer.GetSegment(0)
+// | |
+// + - - - - - - - - + - - mReadCursor
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// +-----------------+ - - mReadLimit
+// |
+// +-----------------+
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// +-----------------+
+// |
+// +-----------------+ - - mBuffer.GetSegment(mWriteSegment)
+// |/////////////////|
+// |/////////////////|
+// |/////////////////|
+// + - - - - - - - - + - - mWriteCursor
+// | |
+// | |
+// +-----------------+ - - mWriteLimit
+//
+// (shaded region contains data)
+//
+// NOTE: on some systems (notably OS/2), the heap allocator uses an arena for
+// small allocations (e.g., 64 byte allocations). this means that buffers may
+// be allocated back-to-back. in the diagram above, for example, mReadLimit
+// would actually be pointing at the beginning of the next segment. when
+// making changes to this file, please keep this fact in mind.
+//
+
+//-----------------------------------------------------------------------------
+// nsPipe methods:
+//-----------------------------------------------------------------------------
+
+nsPipe::nsPipe()
+ : mInput(this)
+ , mOutput(this)
+ , mReentrantMonitor("nsPipe.mReentrantMonitor")
+ , mReadCursor(nullptr)
+ , mReadLimit(nullptr)
+ , mWriteSegment(-1)
+ , mWriteCursor(nullptr)
+ , mWriteLimit(nullptr)
+ , mStatus(NS_OK)
+ , mInited(false)
+{
+}
+
+nsPipe::~nsPipe()
+{
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsPipe, nsIPipe)
+
+NS_IMETHODIMP
+nsPipe::Init(bool nonBlockingIn,
+ bool nonBlockingOut,
+ uint32_t segmentSize,
+ uint32_t segmentCount,
+ nsIMemory *segmentAlloc)
+{
+ mInited = true;
+
+ if (segmentSize == 0)
+ segmentSize = DEFAULT_SEGMENT_SIZE;
+ if (segmentCount == 0)
+ segmentCount = DEFAULT_SEGMENT_COUNT;
+
+ // protect against overflow
+ uint32_t maxCount = uint32_t(-1) / segmentSize;
+ if (segmentCount > maxCount)
+ segmentCount = maxCount;
+
+ nsresult rv = mBuffer.Init(segmentSize, segmentSize * segmentCount, segmentAlloc);
+ if (NS_FAILED(rv))
+ return rv;
+
+ mInput.SetNonBlocking(nonBlockingIn);
+ mOutput.SetNonBlocking(nonBlockingOut);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipe::GetInputStream(nsIAsyncInputStream **aInputStream)
+{
+ NS_ADDREF(*aInputStream = &mInput);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipe::GetOutputStream(nsIAsyncOutputStream **aOutputStream)
+{
+ NS_ENSURE_TRUE(mInited, NS_ERROR_NOT_INITIALIZED);
+ NS_ADDREF(*aOutputStream = &mOutput);
+ return NS_OK;
+}
+
+void
+nsPipe::PeekSegment(uint32_t index, char *&cursor, char *&limit)
+{
+ if (index == 0) {
+ NS_ASSERTION(!mReadCursor || mBuffer.GetSegmentCount(), "unexpected state");
+ cursor = mReadCursor;
+ limit = mReadLimit;
+ }
+ else {
+ uint32_t numSegments = mBuffer.GetSegmentCount();
+ if (index >= numSegments)
+ cursor = limit = nullptr;
+ else {
+ cursor = mBuffer.GetSegment(index);
+ if (mWriteSegment == (int32_t) index)
+ limit = mWriteCursor;
+ else
+ limit = cursor + mBuffer.GetSegmentSize();
+ }
+ }
+}
+
+nsresult
+nsPipe::GetReadSegment(const char *&segment, uint32_t &segmentLen)
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ if (mReadCursor == mReadLimit)
+ return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_WOULD_BLOCK;
+
+ segment = mReadCursor;
+ segmentLen = mReadLimit - mReadCursor;
+ return NS_OK;
+}
+
+void
+nsPipe::AdvanceReadCursor(uint32_t bytesRead)
+{
+ NS_ASSERTION(bytesRead, "don't call if no bytes read");
+
+ nsPipeEvents events;
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ LOG(("III advancing read cursor by %u\n", bytesRead));
+ NS_ASSERTION(bytesRead <= mBuffer.GetSegmentSize(), "read too much");
+
+ mReadCursor += bytesRead;
+ NS_ASSERTION(mReadCursor <= mReadLimit, "read cursor exceeds limit");
+
+ mInput.ReduceAvailable(bytesRead);
+
+ if (mReadCursor == mReadLimit) {
+ // we've reached the limit of how much we can read from this segment.
+ // if at the end of this segment, then we must discard this segment.
+
+ // if still writing in this segment then bail because we're not done
+ // with the segment and have to wait for now...
+ if (mWriteSegment == 0 && mWriteLimit > mWriteCursor) {
+ NS_ASSERTION(mReadLimit == mWriteCursor, "unexpected state");
+ return;
+ }
+
+ // shift write segment index (-1 indicates an empty buffer).
+ --mWriteSegment;
+
+ // done with this segment
+ mBuffer.DeleteFirstSegment();
+ LOG(("III deleting first segment\n"));
+
+ if (mWriteSegment == -1) {
+ // buffer is completely empty
+ mReadCursor = nullptr;
+ mReadLimit = nullptr;
+ mWriteCursor = nullptr;
+ mWriteLimit = nullptr;
+ }
+ else {
+ // advance read cursor and limit to next buffer segment
+ mReadCursor = mBuffer.GetSegment(0);
+ if (mWriteSegment == 0)
+ mReadLimit = mWriteCursor;
+ else
+ mReadLimit = mReadCursor + mBuffer.GetSegmentSize();
+ }
+
+ // we've free'd up a segment, so notify output stream that pipe has
+ // room for a new segment.
+ if (mOutput.OnOutputWritable(events))
+ mon.Notify();
+ }
+ }
+}
+
+nsresult
+nsPipe::GetWriteSegment(char *&segment, uint32_t &segmentLen)
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ if (NS_FAILED(mStatus))
+ return mStatus;
+
+ // write cursor and limit may both be null indicating an empty buffer.
+ if (mWriteCursor == mWriteLimit) {
+ char *seg = mBuffer.AppendNewSegment();
+ // pipe is full
+ if (seg == nullptr)
+ return NS_BASE_STREAM_WOULD_BLOCK;
+ LOG(("OOO appended new segment\n"));
+ mWriteCursor = seg;
+ mWriteLimit = mWriteCursor + mBuffer.GetSegmentSize();
+ ++mWriteSegment;
+ }
+
+ // make sure read cursor is initialized
+ if (mReadCursor == nullptr) {
+ NS_ASSERTION(mWriteSegment == 0, "unexpected null read cursor");
+ mReadCursor = mReadLimit = mWriteCursor;
+ }
+
+ // check to see if we can roll-back our read and write cursors to the
+ // beginning of the current/first segment. this is purely an optimization.
+ if (mReadCursor == mWriteCursor && mWriteSegment == 0) {
+ char *head = mBuffer.GetSegment(0);
+ LOG(("OOO rolling back write cursor %u bytes\n", mWriteCursor - head));
+ mWriteCursor = mReadCursor = mReadLimit = head;
+ }
+
+ segment = mWriteCursor;
+ segmentLen = mWriteLimit - mWriteCursor;
+ return NS_OK;
+}
+
+void
+nsPipe::AdvanceWriteCursor(uint32_t bytesWritten)
+{
+ NS_ASSERTION(bytesWritten, "don't call if no bytes written");
+
+ nsPipeEvents events;
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ LOG(("OOO advancing write cursor by %u\n", bytesWritten));
+
+ char *newWriteCursor = mWriteCursor + bytesWritten;
+ NS_ASSERTION(newWriteCursor <= mWriteLimit, "write cursor exceeds limit");
+
+ // update read limit if reading in the same segment
+ if (mWriteSegment == 0 && mReadLimit == mWriteCursor)
+ mReadLimit = newWriteCursor;
+
+ mWriteCursor = newWriteCursor;
+
+ // The only way mReadCursor == mWriteCursor is if:
+ //
+ // - mReadCursor is at the start of a segment (which, based on how
+ // nsSegmentedBuffer works, means that this segment is the "first"
+ // segment)
+ // - mWriteCursor points at the location past the end of the current
+ // write segment (so the current write filled the current write
+ // segment, so we've incremented mWriteCursor to point past the end
+ // of it)
+ // - the segment to which data has just been written is located
+ // exactly one segment's worth of bytes before the first segment
+ // where mReadCursor is located
+ //
+ // Consequently, the byte immediately after the end of the current
+ // write segment is the first byte of the first segment, so
+ // mReadCursor == mWriteCursor. (Another way to think about this is
+ // to consider the buffer architecture diagram above, but consider it
+ // with an arena allocator which allocates from the *end* of the
+ // arena to the *beginning* of the arena.)
+ NS_ASSERTION(mReadCursor != mWriteCursor ||
+ (mBuffer.GetSegment(0) == mReadCursor &&
+ mWriteCursor == mWriteLimit),
+ "read cursor is bad");
+
+ // update the writable flag on the output stream
+ if (mWriteCursor == mWriteLimit) {
+ if (mBuffer.GetSize() >= mBuffer.GetMaxSize())
+ mOutput.SetWritable(false);
+ }
+
+ // notify input stream that pipe now contains additional data
+ if (mInput.OnInputReadable(bytesWritten, events))
+ mon.Notify();
+ }
+}
+
+void
+nsPipe::OnPipeException(nsresult reason, bool outputOnly)
+{
+ LOG(("PPP nsPipe::OnPipeException [reason=%x output-only=%d]\n",
+ reason, outputOnly));
+
+ nsPipeEvents events;
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+ // if we've already hit an exception, then ignore this one.
+ if (NS_FAILED(mStatus))
+ return;
+
+ mStatus = reason;
+
+ // an output-only exception applies to the input end if the pipe has
+ // zero bytes available.
+ if (outputOnly && !mInput.Available())
+ outputOnly = false;
+
+ if (!outputOnly)
+ if (mInput.OnInputException(reason, events))
+ mon.Notify();
+
+ if (mOutput.OnOutputException(reason, events))
+ mon.Notify();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// nsPipeEvents methods:
+//-----------------------------------------------------------------------------
+
+nsPipeEvents::~nsPipeEvents()
+{
+ // dispatch any pending events
+
+ if (mInputCallback) {
+ mInputCallback->OnInputStreamReady(mInputStream);
+ mInputCallback = 0;
+ mInputStream = 0;
+ }
+ if (mOutputCallback) {
+ mOutputCallback->OnOutputStreamReady(mOutputStream);
+ mOutputCallback = 0;
+ mOutputStream = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// nsPipeInputStream methods:
+//-----------------------------------------------------------------------------
+
+NS_IMPL_QUERY_INTERFACE5(nsPipeInputStream,
+ nsIInputStream,
+ nsIAsyncInputStream,
+ nsISeekableStream,
+ nsISearchableInputStream,
+ nsIClassInfo)
+
+NS_IMPL_CI_INTERFACE_GETTER4(nsPipeInputStream,
+ nsIInputStream,
+ nsIAsyncInputStream,
+ nsISeekableStream,
+ nsISearchableInputStream)
+
+NS_IMPL_THREADSAFE_CI(nsPipeInputStream)
+
+nsresult
+nsPipeInputStream::Wait()
+{
+ NS_ASSERTION(mBlocking, "wait on non-blocking pipe input stream");
+
+ ReentrantMonitorAutoEnter mon(mPipe->mReentrantMonitor);
+
+ while (NS_SUCCEEDED(mPipe->mStatus) && (mAvailable == 0)) {
+ LOG(("III pipe input: waiting for data\n"));
+
+ mBlocked = true;
+ mon.Wait();
+ mBlocked = false;
+
+ LOG(("III pipe input: woke up [pipe-status=%x available=%u]\n",
+ mPipe->mStatus, mAvailable));
+ }
+
+ return mPipe->mStatus == NS_BASE_STREAM_CLOSED ? NS_OK : mPipe->mStatus;
+}
+
+bool
+nsPipeInputStream::OnInputReadable(uint32_t bytesWritten, nsPipeEvents &events)
+{
+ bool result = false;
+
+ mAvailable += bytesWritten;
+
+ if (mCallback && !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
+ events.NotifyInputReady(this, mCallback);
+ mCallback = 0;
+ mCallbackFlags = 0;
+ }
+ else if (mBlocked)
+ result = true;
+
+ return result;
+}
+
+bool
+nsPipeInputStream::OnInputException(nsresult reason, nsPipeEvents &events)
+{
+ LOG(("nsPipeInputStream::OnInputException [this=%x reason=%x]\n",
+ this, reason));
+
+ bool result = false;
+
+ NS_ASSERTION(NS_FAILED(reason), "huh? successful exception");
+
+ // force count of available bytes to zero.
+ mAvailable = 0;
+
+ if (mCallback) {
+ events.NotifyInputReady(this, mCallback);
+ mCallback = 0;
+ mCallbackFlags = 0;
+ }
+ else if (mBlocked)
+ result = true;
+
+ return result;
+}
+
+NS_IMETHODIMP_(nsrefcnt)
+nsPipeInputStream::AddRef(void)
+{
+ NS_AtomicIncrementRefcnt(mReaderRefCnt);
+ return mPipe->AddRef();
+}
+
+NS_IMETHODIMP_(nsrefcnt)
+nsPipeInputStream::Release(void)
+{
+ if (NS_AtomicDecrementRefcnt(mReaderRefCnt) == 0)
+ Close();
+ return mPipe->Release();
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::CloseWithStatus(nsresult reason)
+{
+ LOG(("III CloseWithStatus [this=%x reason=%x]\n", this, reason));
+
+ if (NS_SUCCEEDED(reason))
+ reason = NS_BASE_STREAM_CLOSED;
+
+ mPipe->OnPipeException(reason);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::Close()
+{
+ return CloseWithStatus(NS_BASE_STREAM_CLOSED);
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::Available(uint64_t *result)
+{
+ // nsPipeInputStream supports under 4GB stream only
+ ReentrantMonitorAutoEnter mon(mPipe->mReentrantMonitor);
+
+ // return error if pipe closed
+ if (!mAvailable && NS_FAILED(mPipe->mStatus))
+ return mPipe->mStatus;
+
+ *result = (uint64_t)mAvailable;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::ReadSegments(nsWriteSegmentFun writer,
+ void *closure,
+ uint32_t count,
+ uint32_t *readCount)
+{
+ LOG(("III ReadSegments [this=%x count=%u]\n", this, count));
+
+ nsresult rv = NS_OK;
+
+ const char *segment;
+ uint32_t segmentLen;
+
+ *readCount = 0;
+ while (count) {
+ rv = mPipe->GetReadSegment(segment, segmentLen);
+ if (NS_FAILED(rv)) {
+ // ignore this error if we've already read something.
+ if (*readCount > 0) {
+ rv = NS_OK;
+ break;
+ }
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+ // pipe is empty
+ if (!mBlocking)
+ break;
+ // wait for some data to be written to the pipe
+ rv = Wait();
+ if (NS_SUCCEEDED(rv))
+ continue;
+ }
+ // ignore this error, just return.
+ if (rv == NS_BASE_STREAM_CLOSED) {
+ rv = NS_OK;
+ break;
+ }
+ mPipe->OnPipeException(rv);
+ break;
+ }
+
+ // read no more than count
+ if (segmentLen > count)
+ segmentLen = count;
+
+ uint32_t writeCount, originalLen = segmentLen;
+ while (segmentLen) {
+ writeCount = 0;
+
+ rv = writer(this, closure, segment, *readCount, segmentLen, &writeCount);
+
+ if (NS_FAILED(rv) || writeCount == 0) {
+ count = 0;
+ // any errors returned from the writer end here: do not
+ // propagate to the caller of ReadSegments.
+ rv = NS_OK;
+ break;
+ }
+
+ NS_ASSERTION(writeCount <= segmentLen, "wrote more than expected");
+ segment += writeCount;
+ segmentLen -= writeCount;
+ count -= writeCount;
+ *readCount += writeCount;
+ mLogicalOffset += writeCount;
+ }
+
+ if (segmentLen < originalLen)
+ mPipe->AdvanceReadCursor(originalLen - segmentLen);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::Read(char* toBuf, uint32_t bufLen, uint32_t *readCount)
+{
+ return ReadSegments(NS_CopySegmentToBuffer, toBuf, bufLen, readCount);
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::IsNonBlocking(bool *aNonBlocking)
+{
+ *aNonBlocking = !mBlocking;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::AsyncWait(nsIInputStreamCallback *callback,
+ uint32_t flags,
+ uint32_t requestedCount,
+ nsIEventTarget *target)
+{
+ LOG(("III AsyncWait [this=%x]\n", this));
+
+ nsPipeEvents pipeEvents;
+ {
+ ReentrantMonitorAutoEnter mon(mPipe->mReentrantMonitor);
+
+ // replace a pending callback
+ mCallback = 0;
+ mCallbackFlags = 0;
+
+ if (!callback)
+ return NS_OK;
+
+ nsCOMPtr<nsIInputStreamCallback> proxy;
+ if (target) {
+ proxy = NS_NewInputStreamReadyEvent(callback, target);
+ callback = proxy;
+ }
+
+ if (NS_FAILED(mPipe->mStatus) ||
+ (mAvailable && !(flags & WAIT_CLOSURE_ONLY))) {
+ // stream is already closed or readable; post event.
+ pipeEvents.NotifyInputReady(this, callback);
+ }
+ else {
+ // queue up callback object to be notified when data becomes available
+ mCallback = callback;
+ mCallbackFlags = flags;
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::Seek(int32_t whence, int64_t offset)
+{
+ NS_NOTREACHED("nsPipeInputStream::Seek");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::Tell(int64_t *offset)
+{
+ ReentrantMonitorAutoEnter mon(mPipe->mReentrantMonitor);
+
+ // return error if pipe closed
+ if (!mAvailable && NS_FAILED(mPipe->mStatus))
+ return mPipe->mStatus;
+
+ *offset = mLogicalOffset;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeInputStream::SetEOF()
+{
+ NS_NOTREACHED("nsPipeInputStream::SetEOF");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+#define COMPARE(s1, s2, i) \
+ (ignoreCase \
+ ? nsCRT::strncasecmp((const char *)s1, (const char *)s2, (uint32_t)i) \
+ : nsCRT::strncmp((const char *)s1, (const char *)s2, (uint32_t)i))
+
+NS_IMETHODIMP
+nsPipeInputStream::Search(const char *forString,
+ bool ignoreCase,
+ bool *found,
+ uint32_t *offsetSearchedTo)
+{
+ LOG(("III Search [for=%s ic=%u]\n", forString, ignoreCase));
+
+ ReentrantMonitorAutoEnter mon(mPipe->mReentrantMonitor);
+
+ char *cursor1, *limit1;
+ uint32_t index = 0, offset = 0;
+ uint32_t strLen = strlen(forString);
+
+ mPipe->PeekSegment(0, cursor1, limit1);
+ if (cursor1 == limit1) {
+ *found = false;
+ *offsetSearchedTo = 0;
+ LOG((" result [found=%u offset=%u]\n", *found, *offsetSearchedTo));
+ return NS_OK;
+ }
+
+ while (true) {
+ uint32_t i, len1 = limit1 - cursor1;
+
+ // check if the string is in the buffer segment
+ for (i = 0; i < len1 - strLen + 1; i++) {
+ if (COMPARE(&cursor1[i], forString, strLen) == 0) {
+ *found = true;
+ *offsetSearchedTo = offset + i;
+ LOG((" result [found=%u offset=%u]\n", *found, *offsetSearchedTo));
+ return NS_OK;
+ }
+ }
+
+ // get the next segment
+ char *cursor2, *limit2;
+ uint32_t len2;
+
+ index++;
+ offset += len1;
+
+ mPipe->PeekSegment(index, cursor2, limit2);
+ if (cursor2 == limit2) {
+ *found = false;
+ *offsetSearchedTo = offset - strLen + 1;
+ LOG((" result [found=%u offset=%u]\n", *found, *offsetSearchedTo));
+ return NS_OK;
+ }
+ len2 = limit2 - cursor2;
+
+ // check if the string is straddling the next buffer segment
+ uint32_t lim = XPCOM_MIN(strLen, len2 + 1);
+ for (i = 0; i < lim; ++i) {
+ uint32_t strPart1Len = strLen - i - 1;
+ uint32_t strPart2Len = strLen - strPart1Len;
+ const char* strPart2 = &forString[strLen - strPart2Len];
+ uint32_t bufSeg1Offset = len1 - strPart1Len;
+ if (COMPARE(&cursor1[bufSeg1Offset], forString, strPart1Len) == 0 &&
+ COMPARE(cursor2, strPart2, strPart2Len) == 0) {
+ *found = true;
+ *offsetSearchedTo = offset - strPart1Len;
+ LOG((" result [found=%u offset=%u]\n", *found, *offsetSearchedTo));
+ return NS_OK;
+ }
+ }
+
+ // finally continue with the next buffer
+ cursor1 = cursor2;
+ limit1 = limit2;
+ }
+
+ NS_NOTREACHED("can't get here");
+ return NS_ERROR_UNEXPECTED; // keep compiler happy
+}
+
+//-----------------------------------------------------------------------------
+// nsPipeOutputStream methods:
+//-----------------------------------------------------------------------------
+
+NS_IMPL_QUERY_INTERFACE3(nsPipeOutputStream,
+ nsIOutputStream,
+ nsIAsyncOutputStream,
+ nsIClassInfo)
+
+NS_IMPL_CI_INTERFACE_GETTER2(nsPipeOutputStream,
+ nsIOutputStream,
+ nsIAsyncOutputStream)
+
+NS_IMPL_THREADSAFE_CI(nsPipeOutputStream)
+
+nsresult
+nsPipeOutputStream::Wait()
+{
+ NS_ASSERTION(mBlocking, "wait on non-blocking pipe output stream");
+
+ ReentrantMonitorAutoEnter mon(mPipe->mReentrantMonitor);
+
+ if (NS_SUCCEEDED(mPipe->mStatus) && !mWritable) {
+ LOG(("OOO pipe output: waiting for space\n"));
+ mBlocked = true;
+ mon.Wait();
+ mBlocked = false;
+ LOG(("OOO pipe output: woke up [pipe-status=%x writable=%u]\n",
+ mPipe->mStatus, mWritable));
+ }
+
+ return mPipe->mStatus == NS_BASE_STREAM_CLOSED ? NS_OK : mPipe->mStatus;
+}
+
+bool
+nsPipeOutputStream::OnOutputWritable(nsPipeEvents &events)
+{
+ bool result = false;
+
+ mWritable = true;
+
+ if (mCallback && !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
+ events.NotifyOutputReady(this, mCallback);
+ mCallback = 0;
+ mCallbackFlags = 0;
+ }
+ else if (mBlocked)
+ result = true;
+
+ return result;
+}
+
+bool
+nsPipeOutputStream::OnOutputException(nsresult reason, nsPipeEvents &events)
+{
+ LOG(("nsPipeOutputStream::OnOutputException [this=%x reason=%x]\n",
+ this, reason));
+
+ bool result = false;
+
+ NS_ASSERTION(NS_FAILED(reason), "huh? successful exception");
+ mWritable = false;
+
+ if (mCallback) {
+ events.NotifyOutputReady(this, mCallback);
+ mCallback = 0;
+ mCallbackFlags = 0;
+ }
+ else if (mBlocked)
+ result = true;
+
+ return result;
+}
+
+
+NS_IMETHODIMP_(nsrefcnt)
+nsPipeOutputStream::AddRef()
+{
+ NS_AtomicIncrementRefcnt(mWriterRefCnt);
+ return mPipe->AddRef();
+}
+
+NS_IMETHODIMP_(nsrefcnt)
+nsPipeOutputStream::Release()
+{
+ if (NS_AtomicDecrementRefcnt(mWriterRefCnt) == 0)
+ Close();
+ return mPipe->Release();
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::CloseWithStatus(nsresult reason)
+{
+ LOG(("OOO CloseWithStatus [this=%x reason=%x]\n", this, reason));
+
+ if (NS_SUCCEEDED(reason))
+ reason = NS_BASE_STREAM_CLOSED;
+
+ // input stream may remain open
+ mPipe->OnPipeException(reason, true);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::Close()
+{
+ return CloseWithStatus(NS_BASE_STREAM_CLOSED);
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::WriteSegments(nsReadSegmentFun reader,
+ void* closure,
+ uint32_t count,
+ uint32_t *writeCount)
+{
+ LOG(("OOO WriteSegments [this=%x count=%u]\n", this, count));
+
+ nsresult rv = NS_OK;
+
+ char *segment;
+ uint32_t segmentLen;
+
+ *writeCount = 0;
+ while (count) {
+ rv = mPipe->GetWriteSegment(segment, segmentLen);
+ if (NS_FAILED(rv)) {
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+ // pipe is full
+ if (!mBlocking) {
+ // ignore this error if we've already written something
+ if (*writeCount > 0)
+ rv = NS_OK;
+ break;
+ }
+ // wait for the pipe to have an empty segment.
+ rv = Wait();
+ if (NS_SUCCEEDED(rv))
+ continue;
+ }
+ mPipe->OnPipeException(rv);
+ break;
+ }
+
+ // write no more than count
+ if (segmentLen > count)
+ segmentLen = count;
+
+ uint32_t readCount, originalLen = segmentLen;
+ while (segmentLen) {
+ readCount = 0;
+
+ rv = reader(this, closure, segment, *writeCount, segmentLen, &readCount);
+
+ if (NS_FAILED(rv) || readCount == 0) {
+ count = 0;
+ // any errors returned from the reader end here: do not
+ // propagate to the caller of WriteSegments.
+ rv = NS_OK;
+ break;
+ }
+
+ NS_ASSERTION(readCount <= segmentLen, "read more than expected");
+ segment += readCount;
+ segmentLen -= readCount;
+ count -= readCount;
+ *writeCount += readCount;
+ mLogicalOffset += readCount;
+ }
+
+ if (segmentLen < originalLen)
+ mPipe->AdvanceWriteCursor(originalLen - segmentLen);
+ }
+
+ return rv;
+}
+
+static NS_METHOD
+nsReadFromRawBuffer(nsIOutputStream* outStr,
+ void* closure,
+ char* toRawSegment,
+ uint32_t offset,
+ uint32_t count,
+ uint32_t *readCount)
+{
+ const char* fromBuf = (const char*)closure;
+ memcpy(toRawSegment, &fromBuf[offset], count);
+ *readCount = count;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::Write(const char* fromBuf,
+ uint32_t bufLen,
+ uint32_t *writeCount)
+{
+ return WriteSegments(nsReadFromRawBuffer, (void*)fromBuf, bufLen, writeCount);
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::Flush(void)
+{
+ // nothing to do
+ return NS_OK;
+}
+
+static NS_METHOD
+nsReadFromInputStream(nsIOutputStream* outStr,
+ void* closure,
+ char* toRawSegment,
+ uint32_t offset,
+ uint32_t count,
+ uint32_t *readCount)
+{
+ nsIInputStream* fromStream = (nsIInputStream*)closure;
+ return fromStream->Read(toRawSegment, count, readCount);
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::WriteFrom(nsIInputStream* fromStream,
+ uint32_t count,
+ uint32_t *writeCount)
+{
+ return WriteSegments(nsReadFromInputStream, fromStream, count, writeCount);
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::IsNonBlocking(bool *aNonBlocking)
+{
+ *aNonBlocking = !mBlocking;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPipeOutputStream::AsyncWait(nsIOutputStreamCallback *callback,
+ uint32_t flags,
+ uint32_t requestedCount,
+ nsIEventTarget *target)
+{
+ LOG(("OOO AsyncWait [this=%x]\n", this));
+
+ nsPipeEvents pipeEvents;
+ {
+ ReentrantMonitorAutoEnter mon(mPipe->mReentrantMonitor);
+
+ // replace a pending callback
+ mCallback = 0;
+ mCallbackFlags = 0;
+
+ if (!callback)
+ return NS_OK;
+
+ nsCOMPtr<nsIOutputStreamCallback> proxy;
+ if (target) {
+ proxy = NS_NewOutputStreamReadyEvent(callback, target);
+ callback = proxy;
+ }
+
+ if (NS_FAILED(mPipe->mStatus) ||
+ (mWritable && !(flags & WAIT_CLOSURE_ONLY))) {
+ // stream is already closed or writable; post event.
+ pipeEvents.NotifyOutputReady(this, callback);
+ }
+ else {
+ // queue up callback object to be notified when data becomes available
+ mCallback = callback;
+ mCallbackFlags = flags;
+ }
+ }
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+nsresult
+NS_NewPipe(nsIInputStream **pipeIn,
+ nsIOutputStream **pipeOut,
+ uint32_t segmentSize,
+ uint32_t maxSize,
+ bool nonBlockingInput,
+ bool nonBlockingOutput,
+ nsIMemory *segmentAlloc)
+{
+ if (segmentSize == 0)
+ segmentSize = DEFAULT_SEGMENT_SIZE;
+
+ // Handle maxSize of UINT32_MAX as a special case
+ uint32_t segmentCount;
+ if (maxSize == UINT32_MAX)
+ segmentCount = UINT32_MAX;
+ else
+ segmentCount = maxSize / segmentSize;
+
+ nsIAsyncInputStream *in;
+ nsIAsyncOutputStream *out;
+ nsresult rv = NS_NewPipe2(&in, &out, nonBlockingInput, nonBlockingOutput,
+ segmentSize, segmentCount, segmentAlloc);
+ if (NS_FAILED(rv)) return rv;
+
+ *pipeIn = in;
+ *pipeOut = out;
+ return NS_OK;
+}
+
+nsresult
+NS_NewPipe2(nsIAsyncInputStream **pipeIn,
+ nsIAsyncOutputStream **pipeOut,
+ bool nonBlockingInput,
+ bool nonBlockingOutput,
+ uint32_t segmentSize,
+ uint32_t segmentCount,
+ nsIMemory *segmentAlloc)
+{
+ nsresult rv;
+
+ nsPipe *pipe = new nsPipe();
+ if (!pipe)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = pipe->Init(nonBlockingInput,
+ nonBlockingOutput,
+ segmentSize,
+ segmentCount,
+ segmentAlloc);
+ if (NS_FAILED(rv)) {
+ NS_ADDREF(pipe);
+ NS_RELEASE(pipe);
+ return rv;
+ }
+
+ pipe->GetInputStream(pipeIn);
+ pipe->GetOutputStream(pipeOut);
+ return NS_OK;
+}
+
+nsresult
+nsPipeConstructor(nsISupports *outer, REFNSIID iid, void **result)
+{
+ if (outer)
+ return NS_ERROR_NO_AGGREGATION;
+ nsPipe *pipe = new nsPipe();
+ if (!pipe)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(pipe);
+ nsresult rv = pipe->QueryInterface(iid, result);
+ NS_RELEASE(pipe);
+ return rv;
+}
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/xpcom/io/nsScriptableBase64Encoder.cpp b/xpcom/io/nsScriptableBase64Encoder.cpp
new file mode 100644
index 000000000..8f54d98e6
--- /dev/null
+++ b/xpcom/io/nsScriptableBase64Encoder.cpp
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsScriptableBase64Encoder.h"
+#include "mozilla/Base64.h"
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS1(nsScriptableBase64Encoder, nsIScriptableBase64Encoder)
+
+/* ACString encodeToCString (in nsIInputStream stream, in unsigned long length); */
+NS_IMETHODIMP
+nsScriptableBase64Encoder::EncodeToCString(nsIInputStream *aStream,
+ uint32_t aLength,
+ nsACString & _retval)
+{
+ Base64EncodeInputStream(aStream, _retval, aLength);
+ return NS_OK;
+}
+
+/* AString encodeToString (in nsIInputStream stream, in unsigned long length); */
+NS_IMETHODIMP
+nsScriptableBase64Encoder::EncodeToString(nsIInputStream *aStream,
+ uint32_t aLength,
+ nsAString & _retval)
+{
+ Base64EncodeInputStream(aStream, _retval, aLength);
+ return NS_OK;
+}
diff --git a/xpcom/io/nsScriptableBase64Encoder.h b/xpcom/io/nsScriptableBase64Encoder.h
new file mode 100644
index 000000000..4436344de
--- /dev/null
+++ b/xpcom/io/nsScriptableBase64Encoder.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 nsScriptableBase64Encoder_h__
+#define nsScriptableBase64Encoder_h__
+
+#include "nsIScriptableBase64Encoder.h"
+#include "mozilla/Attributes.h"
+
+#define NS_SCRIPTABLEBASE64ENCODER_CID \
+ {0xaaf68860, 0xf849, 0x40ee, \
+ {0xbb, 0x7a, 0xb2, 0x29, 0xbc, 0xe0, 0x36, 0xa3} }
+#define NS_SCRIPTABLEBASE64ENCODER_CONTRACTID \
+ "@mozilla.org/scriptablebase64encoder;1"
+
+class nsScriptableBase64Encoder MOZ_FINAL : public nsIScriptableBase64Encoder
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISCRIPTABLEBASE64ENCODER
+private:
+ ~nsScriptableBase64Encoder() {}
+};
+
+#endif
diff --git a/xpcom/io/nsScriptableInputStream.cpp b/xpcom/io/nsScriptableInputStream.cpp
new file mode 100644
index 000000000..36f43ea5e
--- /dev/null
+++ b/xpcom/io/nsScriptableInputStream.cpp
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsScriptableInputStream.h"
+#include "nsMemory.h"
+#include "nsString.h"
+
+NS_IMPL_ISUPPORTS1(nsScriptableInputStream, nsIScriptableInputStream)
+
+// nsIScriptableInputStream methods
+NS_IMETHODIMP
+nsScriptableInputStream::Close(void) {
+ if (!mInputStream) return NS_ERROR_NOT_INITIALIZED;
+ return mInputStream->Close();
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::Init(nsIInputStream *aInputStream) {
+ if (!aInputStream) return NS_ERROR_NULL_POINTER;
+ mInputStream = aInputStream;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::Available(uint64_t *_retval) {
+ if (!mInputStream) return NS_ERROR_NOT_INITIALIZED;
+ return mInputStream->Available(_retval);
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::Read(uint32_t aCount, char **_retval) {
+ nsresult rv = NS_OK;
+ uint64_t count64 = 0;
+ char *buffer = nullptr;
+
+ if (!mInputStream) return NS_ERROR_NOT_INITIALIZED;
+
+ rv = mInputStream->Available(&count64);
+ if (NS_FAILED(rv)) return rv;
+
+ // bug716556 - Ensure count+1 doesn't overflow
+ uint32_t count = XPCOM_MIN((uint32_t)XPCOM_MIN<uint64_t>(count64, aCount), UINT32_MAX - 1);
+ buffer = (char*)nsMemory::Alloc(count+1); // make room for '\0'
+ if (!buffer) return NS_ERROR_OUT_OF_MEMORY;
+
+ uint32_t amtRead = 0;
+ rv = mInputStream->Read(buffer, count, &amtRead);
+ if (NS_FAILED(rv)) {
+ nsMemory::Free(buffer);
+ return rv;
+ }
+
+ buffer[amtRead] = '\0';
+ *_retval = buffer;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptableInputStream::ReadBytes(uint32_t aCount, nsACString &_retval) {
+ if (!mInputStream) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ _retval.SetLength(aCount);
+ if (_retval.Length() != aCount) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ char *ptr = _retval.BeginWriting();
+ uint32_t totalBytesRead = 0;
+ while (1) {
+ uint32_t bytesRead;
+ nsresult rv = mInputStream->Read(ptr + totalBytesRead,
+ aCount - totalBytesRead,
+ &bytesRead);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ totalBytesRead += bytesRead;
+ if (totalBytesRead == aCount) {
+ break;
+ }
+
+ // If we have read zero bytes, we have hit EOF.
+ if (bytesRead == 0) {
+ _retval.Truncate();
+ return NS_ERROR_FAILURE;
+ }
+
+ }
+ return NS_OK;
+}
+
+nsresult
+nsScriptableInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) {
+ if (aOuter) return NS_ERROR_NO_AGGREGATION;
+
+ nsScriptableInputStream *sis = new nsScriptableInputStream();
+ if (!sis) return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(sis);
+ nsresult rv = sis->QueryInterface(aIID, aResult);
+ NS_RELEASE(sis);
+ return rv;
+}
diff --git a/xpcom/io/nsScriptableInputStream.h b/xpcom/io/nsScriptableInputStream.h
new file mode 100644
index 000000000..080816c7f
--- /dev/null
+++ b/xpcom/io/nsScriptableInputStream.h
@@ -0,0 +1,39 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 ___nsscriptableinputstream___h_
+#define ___nsscriptableinputstream___h_
+
+#include "nsIScriptableInputStream.h"
+#include "nsIInputStream.h"
+#include "nsCOMPtr.h"
+#include "mozilla/Attributes.h"
+
+#define NS_SCRIPTABLEINPUTSTREAM_CID \
+{ 0x7225c040, 0xa9bf, 0x11d3, { 0xa1, 0x97, 0x0, 0x50, 0x4, 0x1c, 0xaf, 0x44 } }
+
+#define NS_SCRIPTABLEINPUTSTREAM_CONTRACTID "@mozilla.org/scriptableinputstream;1"
+
+class nsScriptableInputStream MOZ_FINAL : public nsIScriptableInputStream {
+public:
+ // nsISupports methods
+ NS_DECL_ISUPPORTS
+
+ // nsIScriptableInputStream methods
+ NS_DECL_NSISCRIPTABLEINPUTSTREAM
+
+ // nsScriptableInputStream methods
+ nsScriptableInputStream() {}
+
+ static nsresult
+ Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
+
+private:
+ ~nsScriptableInputStream() {}
+
+ nsCOMPtr<nsIInputStream> mInputStream;
+};
+
+#endif // ___nsscriptableinputstream___h_
diff --git a/xpcom/io/nsSegmentedBuffer.cpp b/xpcom/io/nsSegmentedBuffer.cpp
new file mode 100644
index 000000000..1938fa16f
--- /dev/null
+++ b/xpcom/io/nsSegmentedBuffer.cpp
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsSegmentedBuffer.h"
+#include "nsCRT.h"
+
+nsresult
+nsSegmentedBuffer::Init(uint32_t segmentSize, uint32_t maxSize,
+ nsIMemory* allocator)
+{
+ if (mSegmentArrayCount != 0)
+ return NS_ERROR_FAILURE; // initialized more than once
+ mSegmentSize = segmentSize;
+ mMaxSize = maxSize;
+ mSegAllocator = allocator;
+ if (mSegAllocator == nullptr) {
+ mSegAllocator = nsMemory::GetGlobalMemoryService();
+ }
+ else {
+ NS_ADDREF(mSegAllocator);
+ }
+#if 0 // testing...
+ mSegmentArrayCount = 2;
+#else
+ mSegmentArrayCount = NS_SEGMENTARRAY_INITIAL_COUNT;
+#endif
+ return NS_OK;
+}
+
+char*
+nsSegmentedBuffer::AppendNewSegment()
+{
+ if (GetSize() >= mMaxSize)
+ return nullptr;
+
+ if (mSegmentArray == nullptr) {
+ uint32_t bytes = mSegmentArrayCount * sizeof(char*);
+ mSegmentArray = (char**)nsMemory::Alloc(bytes);
+ if (mSegmentArray == nullptr)
+ return nullptr;
+ memset(mSegmentArray, 0, bytes);
+ }
+
+ if (IsFull()) {
+ uint32_t newArraySize = mSegmentArrayCount * 2;
+ uint32_t bytes = newArraySize * sizeof(char*);
+ char** newSegArray = (char**)nsMemory::Realloc(mSegmentArray, bytes);
+ if (newSegArray == nullptr)
+ return nullptr;
+ mSegmentArray = newSegArray;
+ // copy wrapped content to new extension
+ if (mFirstSegmentIndex > mLastSegmentIndex) {
+ // deal with wrap around case
+ memcpy(&mSegmentArray[mSegmentArrayCount],
+ mSegmentArray,
+ mLastSegmentIndex * sizeof(char*));
+ memset(mSegmentArray, 0, mLastSegmentIndex * sizeof(char*));
+ mLastSegmentIndex += mSegmentArrayCount;
+ memset(&mSegmentArray[mLastSegmentIndex], 0,
+ (newArraySize - mLastSegmentIndex) * sizeof(char*));
+ }
+ else {
+ memset(&mSegmentArray[mLastSegmentIndex], 0,
+ (newArraySize - mLastSegmentIndex) * sizeof(char*));
+ }
+ mSegmentArrayCount = newArraySize;
+ }
+
+ char* seg = (char*)mSegAllocator->Alloc(mSegmentSize);
+ if (seg == nullptr) {
+ return nullptr;
+ }
+ mSegmentArray[mLastSegmentIndex] = seg;
+ mLastSegmentIndex = ModSegArraySize(mLastSegmentIndex + 1);
+ return seg;
+}
+
+bool
+nsSegmentedBuffer::DeleteFirstSegment()
+{
+ NS_ASSERTION(mSegmentArray[mFirstSegmentIndex] != nullptr, "deleting bad segment");
+ (void)mSegAllocator->Free(mSegmentArray[mFirstSegmentIndex]);
+ mSegmentArray[mFirstSegmentIndex] = nullptr;
+ int32_t last = ModSegArraySize(mLastSegmentIndex - 1);
+ if (mFirstSegmentIndex == last) {
+ mLastSegmentIndex = last;
+ return true;
+ }
+ else {
+ mFirstSegmentIndex = ModSegArraySize(mFirstSegmentIndex + 1);
+ return false;
+ }
+}
+
+bool
+nsSegmentedBuffer::DeleteLastSegment()
+{
+ int32_t last = ModSegArraySize(mLastSegmentIndex - 1);
+ NS_ASSERTION(mSegmentArray[last] != nullptr, "deleting bad segment");
+ (void)mSegAllocator->Free(mSegmentArray[last]);
+ mSegmentArray[last] = nullptr;
+ mLastSegmentIndex = last;
+ return (bool)(mLastSegmentIndex == mFirstSegmentIndex);
+}
+
+bool
+nsSegmentedBuffer::ReallocLastSegment(size_t newSize)
+{
+ int32_t last = ModSegArraySize(mLastSegmentIndex - 1);
+ NS_ASSERTION(mSegmentArray[last] != nullptr, "realloc'ing bad segment");
+ char *newSegment =
+ (char*)mSegAllocator->Realloc(mSegmentArray[last], newSize);
+ if (newSegment) {
+ mSegmentArray[last] = newSegment;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void
+nsSegmentedBuffer::Empty()
+{
+ if (mSegmentArray) {
+ for (uint32_t i = 0; i < mSegmentArrayCount; i++) {
+ if (mSegmentArray[i])
+ mSegAllocator->Free(mSegmentArray[i]);
+ }
+ nsMemory::Free(mSegmentArray);
+ mSegmentArray = nullptr;
+ }
+ mSegmentArrayCount = NS_SEGMENTARRAY_INITIAL_COUNT;
+ mFirstSegmentIndex = mLastSegmentIndex = 0;
+}
+
+#if 0
+void
+TestSegmentedBuffer()
+{
+ nsSegmentedBuffer* buf = new nsSegmentedBuffer();
+ NS_ASSERTION(buf, "out of memory");
+ buf->Init(4, 16);
+ char* seg;
+ bool empty;
+ seg = buf->AppendNewSegment();
+ NS_ASSERTION(seg, "AppendNewSegment failed");
+ seg = buf->AppendNewSegment();
+ NS_ASSERTION(seg, "AppendNewSegment failed");
+ seg = buf->AppendNewSegment();
+ NS_ASSERTION(seg, "AppendNewSegment failed");
+ empty = buf->DeleteFirstSegment();
+ NS_ASSERTION(!empty, "DeleteFirstSegment failed");
+ empty = buf->DeleteFirstSegment();
+ NS_ASSERTION(!empty, "DeleteFirstSegment failed");
+ seg = buf->AppendNewSegment();
+ NS_ASSERTION(seg, "AppendNewSegment failed");
+ seg = buf->AppendNewSegment();
+ NS_ASSERTION(seg, "AppendNewSegment failed");
+ seg = buf->AppendNewSegment();
+ NS_ASSERTION(seg, "AppendNewSegment failed");
+ empty = buf->DeleteFirstSegment();
+ NS_ASSERTION(!empty, "DeleteFirstSegment failed");
+ empty = buf->DeleteFirstSegment();
+ NS_ASSERTION(!empty, "DeleteFirstSegment failed");
+ empty = buf->DeleteFirstSegment();
+ NS_ASSERTION(!empty, "DeleteFirstSegment failed");
+ empty = buf->DeleteFirstSegment();
+ NS_ASSERTION(empty, "DeleteFirstSegment failed");
+ delete buf;
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/xpcom/io/nsSegmentedBuffer.h b/xpcom/io/nsSegmentedBuffer.h
new file mode 100644
index 000000000..82ef31266
--- /dev/null
+++ b/xpcom/io/nsSegmentedBuffer.h
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 nsSegmentedBuffer_h__
+#define nsSegmentedBuffer_h__
+
+#include "nsMemory.h"
+#include "prclist.h"
+
+class nsSegmentedBuffer
+{
+public:
+ nsSegmentedBuffer()
+ : mSegmentSize(0), mMaxSize(0),
+ mSegAllocator(nullptr), mSegmentArray(nullptr),
+ mSegmentArrayCount(0),
+ mFirstSegmentIndex(0), mLastSegmentIndex(0) {}
+
+ ~nsSegmentedBuffer() {
+ Empty();
+ NS_IF_RELEASE(mSegAllocator);
+ }
+
+
+ nsresult Init(uint32_t segmentSize, uint32_t maxSize,
+ nsIMemory* allocator = nullptr);
+
+ char* AppendNewSegment(); // pushes at end
+
+ // returns true if no more segments remain:
+ bool DeleteFirstSegment(); // pops from beginning
+
+ // returns true if no more segments remain:
+ bool DeleteLastSegment(); // pops from beginning
+
+ // Call Realloc() on last segment. This is used to reduce memory
+ // consumption when data is not an exact multiple of segment size.
+ bool ReallocLastSegment(size_t newSize);
+
+ void Empty(); // frees all segments
+
+ inline uint32_t GetSegmentCount() {
+ if (mFirstSegmentIndex <= mLastSegmentIndex)
+ return mLastSegmentIndex - mFirstSegmentIndex;
+ else
+ return mSegmentArrayCount + mLastSegmentIndex - mFirstSegmentIndex;
+ }
+
+ inline uint32_t GetSegmentSize() { return mSegmentSize; }
+ inline uint32_t GetMaxSize() { return mMaxSize; }
+ inline uint32_t GetSize() { return GetSegmentCount() * mSegmentSize; }
+
+ inline char* GetSegment(uint32_t indx) {
+ NS_ASSERTION(indx < GetSegmentCount(), "index out of bounds");
+ int32_t i = ModSegArraySize(mFirstSegmentIndex + (int32_t)indx);
+ return mSegmentArray[i];
+ }
+
+protected:
+ inline int32_t ModSegArraySize(int32_t n) {
+ uint32_t result = n & (mSegmentArrayCount - 1);
+ NS_ASSERTION(result == n % mSegmentArrayCount,
+ "non-power-of-2 mSegmentArrayCount");
+ return result;
+ }
+
+ inline bool IsFull() {
+ return ModSegArraySize(mLastSegmentIndex + 1) == mFirstSegmentIndex;
+ }
+
+protected:
+ uint32_t mSegmentSize;
+ uint32_t mMaxSize;
+ nsIMemory* mSegAllocator;
+ char** mSegmentArray;
+ uint32_t mSegmentArrayCount;
+ int32_t mFirstSegmentIndex;
+ int32_t mLastSegmentIndex;
+};
+
+// NS_SEGMENTARRAY_INITIAL_SIZE: This number needs to start out as a
+// power of 2 given how it gets used. We double the segment array
+// when we overflow it, and use that fact that it's a power of 2
+// to compute a fast modulus operation in IsFull.
+//
+// 32 segment array entries can accommodate 128k of data if segments
+// are 4k in size. That seems like a reasonable amount that will avoid
+// needing to grow the segment array.
+#define NS_SEGMENTARRAY_INITIAL_COUNT 32
+
+#endif // nsSegmentedBuffer_h__
diff --git a/xpcom/io/nsStorageStream.cpp b/xpcom/io/nsStorageStream.cpp
new file mode 100644
index 000000000..107e292c9
--- /dev/null
+++ b/xpcom/io/nsStorageStream.cpp
@@ -0,0 +1,537 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set ts=4 sts=4 sw=4 cin et: */
+/* 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/. */
+
+/*
+ * The storage stream provides an internal buffer that can be filled by a
+ * client using a single output stream. One or more independent input streams
+ * can be created to read the data out non-destructively. The implementation
+ * uses a segmented buffer internally to avoid realloc'ing of large buffers,
+ * with the attendant performance loss and heap fragmentation.
+ */
+
+#include "nsAlgorithm.h"
+#include "nsStorageStream.h"
+#include "nsSegmentedBuffer.h"
+#include "nsStreamUtils.h"
+#include "nsCOMPtr.h"
+#include "prbit.h"
+#include "nsIInputStream.h"
+#include "nsISeekableStream.h"
+#include "prlog.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Likely.h"
+
+#if defined(PR_LOGGING)
+//
+// Log module for StorageStream logging...
+//
+// To enable logging (see prlog.h for full details):
+//
+// set NSPR_LOG_MODULES=StorageStreamLog:5
+// set NSPR_LOG_FILE=nspr.log
+//
+// this enables PR_LOG_DEBUG level information and places all output in
+// the file nspr.log
+//
+static PRLogModuleInfo*
+GetStorageStreamLog()
+{
+ static PRLogModuleInfo *sLog;
+ if (!sLog)
+ sLog = PR_NewLogModule("nsStorageStream");
+ return sLog;
+}
+#endif
+#define LOG(args) PR_LOG(GetStorageStreamLog(), PR_LOG_DEBUG, args)
+
+nsStorageStream::nsStorageStream()
+ : mSegmentedBuffer(0), mSegmentSize(0), mWriteInProgress(false),
+ mLastSegmentNum(-1), mWriteCursor(0), mSegmentEnd(0), mLogicalLength(0)
+{
+ LOG(("Creating nsStorageStream [%p].\n", this));
+}
+
+nsStorageStream::~nsStorageStream()
+{
+ delete mSegmentedBuffer;
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsStorageStream,
+ nsIStorageStream,
+ nsIOutputStream)
+
+NS_IMETHODIMP
+nsStorageStream::Init(uint32_t segmentSize, uint32_t maxSize,
+ nsIMemory *segmentAllocator)
+{
+ mSegmentedBuffer = new nsSegmentedBuffer();
+ if (!mSegmentedBuffer)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ mSegmentSize = segmentSize;
+ mSegmentSizeLog2 = PR_FloorLog2(segmentSize);
+
+ // Segment size must be a power of two
+ if (mSegmentSize != ((uint32_t)1 << mSegmentSizeLog2))
+ return NS_ERROR_INVALID_ARG;
+
+ return mSegmentedBuffer->Init(segmentSize, maxSize, segmentAllocator);
+}
+
+NS_IMETHODIMP
+nsStorageStream::GetOutputStream(int32_t aStartingOffset,
+ nsIOutputStream * *aOutputStream)
+{
+ NS_ENSURE_ARG(aOutputStream);
+ NS_ENSURE_TRUE(mSegmentedBuffer, NS_ERROR_NOT_INITIALIZED);
+
+ if (mWriteInProgress)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ nsresult rv = Seek(aStartingOffset);
+ if (NS_FAILED(rv)) return rv;
+
+ // Enlarge the last segment in the buffer so that it is the same size as
+ // all the other segments in the buffer. (It may have been realloc'ed
+ // smaller in the Close() method.)
+ if (mLastSegmentNum >= 0)
+ if (mSegmentedBuffer->ReallocLastSegment(mSegmentSize)) {
+ // Need to re-Seek, since realloc changed segment base pointer
+ rv = Seek(aStartingOffset);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ NS_ADDREF(this);
+ *aOutputStream = static_cast<nsIOutputStream*>(this);
+ mWriteInProgress = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageStream::Close()
+{
+ NS_ENSURE_TRUE(mSegmentedBuffer, NS_ERROR_NOT_INITIALIZED);
+
+ mWriteInProgress = false;
+
+ int32_t segmentOffset = SegOffset(mLogicalLength);
+
+ // Shrink the final segment in the segmented buffer to the minimum size
+ // needed to contain the data, so as to conserve memory.
+ if (segmentOffset)
+ mSegmentedBuffer->ReallocLastSegment(segmentOffset);
+
+ mWriteCursor = 0;
+ mSegmentEnd = 0;
+
+ LOG(("nsStorageStream [%p] Close mWriteCursor=%x mSegmentEnd=%x\n",
+ this, mWriteCursor, mSegmentEnd));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageStream::Flush()
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageStream::Write(const char *aBuffer, uint32_t aCount, uint32_t *aNumWritten)
+{
+ NS_ENSURE_TRUE(mSegmentedBuffer, NS_ERROR_NOT_INITIALIZED);
+
+ const char* readCursor;
+ uint32_t count, availableInSegment, remaining;
+ nsresult rv = NS_OK;
+
+ NS_ENSURE_ARG_POINTER(aNumWritten);
+ NS_ENSURE_ARG(aBuffer);
+
+ LOG(("nsStorageStream [%p] Write mWriteCursor=%x mSegmentEnd=%x aCount=%d\n",
+ this, mWriteCursor, mSegmentEnd, aCount));
+
+ remaining = aCount;
+ readCursor = aBuffer;
+ // If no segments have been created yet, create one even if we don't have
+ // to write any data; this enables creating an input stream which reads from
+ // the very end of the data for any amount of data in the stream (i.e.
+ // this stream contains N bytes of data and newInputStream(N) is called),
+ // even for N=0 (with the caveat that we require .write("", 0) be called to
+ // initialize internal buffers).
+ bool firstTime = mSegmentedBuffer->GetSegmentCount() == 0;
+ while (remaining || MOZ_UNLIKELY(firstTime)) {
+ firstTime = false;
+ availableInSegment = mSegmentEnd - mWriteCursor;
+ if (!availableInSegment) {
+ mWriteCursor = mSegmentedBuffer->AppendNewSegment();
+ if (!mWriteCursor) {
+ mSegmentEnd = 0;
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ goto out;
+ }
+ mLastSegmentNum++;
+ mSegmentEnd = mWriteCursor + mSegmentSize;
+ availableInSegment = mSegmentEnd - mWriteCursor;
+ LOG(("nsStorageStream [%p] Write (new seg) mWriteCursor=%x mSegmentEnd=%x\n",
+ this, mWriteCursor, mSegmentEnd));
+ }
+
+ count = XPCOM_MIN(availableInSegment, remaining);
+ memcpy(mWriteCursor, readCursor, count);
+ remaining -= count;
+ readCursor += count;
+ mWriteCursor += count;
+ LOG(("nsStorageStream [%p] Writing mWriteCursor=%x mSegmentEnd=%x count=%d\n",
+ this, mWriteCursor, mSegmentEnd, count));
+ };
+
+ out:
+ *aNumWritten = aCount - remaining;
+ mLogicalLength += *aNumWritten;
+
+ LOG(("nsStorageStream [%p] Wrote mWriteCursor=%x mSegmentEnd=%x numWritten=%d\n",
+ this, mWriteCursor, mSegmentEnd, *aNumWritten));
+ return rv;
+}
+
+NS_IMETHODIMP
+nsStorageStream::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsStorageStream::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsStorageStream::IsNonBlocking(bool *aNonBlocking)
+{
+ *aNonBlocking = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageStream::GetLength(uint32_t *aLength)
+{
+ NS_ENSURE_ARG(aLength);
+ *aLength = mLogicalLength;
+ return NS_OK;
+}
+
+// Truncate the buffer by deleting the end segments
+NS_IMETHODIMP
+nsStorageStream::SetLength(uint32_t aLength)
+{
+ NS_ENSURE_TRUE(mSegmentedBuffer, NS_ERROR_NOT_INITIALIZED);
+
+ if (mWriteInProgress)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ if (aLength > mLogicalLength)
+ return NS_ERROR_INVALID_ARG;
+
+ int32_t newLastSegmentNum = SegNum(aLength);
+ int32_t segmentOffset = SegOffset(aLength);
+ if (segmentOffset == 0)
+ newLastSegmentNum--;
+
+ while (newLastSegmentNum < mLastSegmentNum) {
+ mSegmentedBuffer->DeleteLastSegment();
+ mLastSegmentNum--;
+ }
+
+ mLogicalLength = aLength;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageStream::GetWriteInProgress(bool *aWriteInProgress)
+{
+ NS_ENSURE_ARG(aWriteInProgress);
+
+ *aWriteInProgress = mWriteInProgress;
+ return NS_OK;
+}
+
+NS_METHOD
+nsStorageStream::Seek(int32_t aPosition)
+{
+ NS_ENSURE_TRUE(mSegmentedBuffer, NS_ERROR_NOT_INITIALIZED);
+
+ // An argument of -1 means "seek to end of stream"
+ if (aPosition == -1)
+ aPosition = mLogicalLength;
+
+ // Seeking beyond the buffer end is illegal
+ if ((uint32_t)aPosition > mLogicalLength)
+ return NS_ERROR_INVALID_ARG;
+
+ // Seeking backwards in the write stream results in truncation
+ SetLength(aPosition);
+
+ // Special handling for seek to start-of-buffer
+ if (aPosition == 0) {
+ mWriteCursor = 0;
+ mSegmentEnd = 0;
+ LOG(("nsStorageStream [%p] Seek mWriteCursor=%x mSegmentEnd=%x\n",
+ this, mWriteCursor, mSegmentEnd));
+ return NS_OK;
+ }
+
+ // Segment may have changed, so reset pointers
+ mWriteCursor = mSegmentedBuffer->GetSegment(mLastSegmentNum);
+ NS_ASSERTION(mWriteCursor, "null mWriteCursor");
+ mSegmentEnd = mWriteCursor + mSegmentSize;
+
+ // Adjust write cursor for current segment offset. This test is necessary
+ // because SegNum may reference the next-to-be-allocated segment, in which
+ // case we need to be pointing at the end of the last segment.
+ int32_t segmentOffset = SegOffset(aPosition);
+ if (segmentOffset == 0 && (SegNum(aPosition) > (uint32_t) mLastSegmentNum))
+ mWriteCursor = mSegmentEnd;
+ else
+ mWriteCursor += segmentOffset;
+
+ LOG(("nsStorageStream [%p] Seek mWriteCursor=%x mSegmentEnd=%x\n",
+ this, mWriteCursor, mSegmentEnd));
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// There can be many nsStorageInputStreams for a single nsStorageStream
+class nsStorageInputStream MOZ_FINAL : public nsIInputStream
+ , public nsISeekableStream
+{
+public:
+ nsStorageInputStream(nsStorageStream *aStorageStream, uint32_t aSegmentSize)
+ : mStorageStream(aStorageStream), mReadCursor(0),
+ mSegmentEnd(0), mSegmentNum(0),
+ mSegmentSize(aSegmentSize), mLogicalCursor(0),
+ mStatus(NS_OK)
+ {
+ NS_ADDREF(mStorageStream);
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAM
+ NS_DECL_NSISEEKABLESTREAM
+
+private:
+ ~nsStorageInputStream()
+ {
+ NS_IF_RELEASE(mStorageStream);
+ }
+
+protected:
+ NS_METHOD Seek(uint32_t aPosition);
+
+ friend class nsStorageStream;
+
+private:
+ nsStorageStream* mStorageStream;
+ uint32_t mReadCursor; // Next memory location to read byte, or NULL
+ uint32_t mSegmentEnd; // One byte past end of current buffer segment
+ uint32_t mSegmentNum; // Segment number containing read cursor
+ uint32_t mSegmentSize; // All segments, except the last, are of this size
+ uint32_t mLogicalCursor; // Logical offset into stream
+ nsresult mStatus;
+
+ uint32_t SegNum(uint32_t aPosition) {return aPosition >> mStorageStream->mSegmentSizeLog2;}
+ uint32_t SegOffset(uint32_t aPosition) {return aPosition & (mSegmentSize - 1);}
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsStorageInputStream,
+ nsIInputStream,
+ nsISeekableStream)
+
+NS_IMETHODIMP
+nsStorageStream::NewInputStream(int32_t aStartingOffset, nsIInputStream* *aInputStream)
+{
+ NS_ENSURE_TRUE(mSegmentedBuffer, NS_ERROR_NOT_INITIALIZED);
+
+ nsStorageInputStream *inputStream = new nsStorageInputStream(this, mSegmentSize);
+ if (!inputStream)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(inputStream);
+
+ nsresult rv = inputStream->Seek(aStartingOffset);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(inputStream);
+ return rv;
+ }
+
+ *aInputStream = inputStream;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::Close()
+{
+ mStatus = NS_BASE_STREAM_CLOSED;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::Available(uint64_t *aAvailable)
+{
+ if (NS_FAILED(mStatus))
+ return mStatus;
+
+ *aAvailable = mStorageStream->mLogicalLength - mLogicalCursor;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t *aNumRead)
+{
+ return ReadSegments(NS_CopySegmentToBuffer, aBuffer, aCount, aNumRead);
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure, uint32_t aCount, uint32_t *aNumRead)
+{
+ *aNumRead = 0;
+ if (mStatus == NS_BASE_STREAM_CLOSED)
+ return NS_OK;
+ if (NS_FAILED(mStatus))
+ return mStatus;
+
+ uint32_t count, availableInSegment, remainingCapacity, bytesConsumed;
+ nsresult rv;
+
+ remainingCapacity = aCount;
+ while (remainingCapacity) {
+ availableInSegment = mSegmentEnd - mReadCursor;
+ if (!availableInSegment) {
+ uint32_t available = mStorageStream->mLogicalLength - mLogicalCursor;
+ if (!available)
+ goto out;
+
+ mSegmentNum++;
+ mReadCursor = 0;
+ mSegmentEnd = XPCOM_MIN(mSegmentSize, available);
+ availableInSegment = mSegmentEnd;
+ }
+ const char *cur = mStorageStream->mSegmentedBuffer->GetSegment(mSegmentNum);
+
+ count = XPCOM_MIN(availableInSegment, remainingCapacity);
+ rv = writer(this, closure, cur + mReadCursor, aCount - remainingCapacity,
+ count, &bytesConsumed);
+ if (NS_FAILED(rv) || (bytesConsumed == 0))
+ break;
+ remainingCapacity -= bytesConsumed;
+ mReadCursor += bytesConsumed;
+ mLogicalCursor += bytesConsumed;
+ };
+
+ out:
+ *aNumRead = aCount - remainingCapacity;
+
+ bool isWriteInProgress = false;
+ if (NS_FAILED(mStorageStream->GetWriteInProgress(&isWriteInProgress)))
+ isWriteInProgress = false;
+
+ if (*aNumRead == 0 && isWriteInProgress)
+ return NS_BASE_STREAM_WOULD_BLOCK;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::IsNonBlocking(bool *aNonBlocking)
+{
+ // TODO: This class should implement nsIAsyncInputStream so that callers
+ // have some way of dealing with NS_BASE_STREAM_WOULD_BLOCK errors.
+
+ *aNonBlocking = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::Seek(int32_t aWhence, int64_t aOffset)
+{
+ if (NS_FAILED(mStatus))
+ return mStatus;
+
+ int64_t pos = aOffset;
+
+ switch (aWhence) {
+ case NS_SEEK_SET:
+ break;
+ case NS_SEEK_CUR:
+ pos += mLogicalCursor;
+ break;
+ case NS_SEEK_END:
+ pos += mStorageStream->mLogicalLength;
+ break;
+ default:
+ NS_NOTREACHED("unexpected whence value");
+ return NS_ERROR_UNEXPECTED;
+ }
+ if (pos == int64_t(mLogicalCursor))
+ return NS_OK;
+
+ return Seek(pos);
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::Tell(int64_t *aResult)
+{
+ if (NS_FAILED(mStatus))
+ return mStatus;
+
+ *aResult = mLogicalCursor;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStorageInputStream::SetEOF()
+{
+ NS_NOTREACHED("nsStorageInputStream::SetEOF");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_METHOD
+nsStorageInputStream::Seek(uint32_t aPosition)
+{
+ uint32_t length = mStorageStream->mLogicalLength;
+ if (aPosition > length)
+ return NS_ERROR_INVALID_ARG;
+
+ if (length == 0)
+ return NS_OK;
+
+ mSegmentNum = SegNum(aPosition);
+ mReadCursor = SegOffset(aPosition);
+ uint32_t available = length - aPosition;
+ mSegmentEnd = mReadCursor + XPCOM_MIN(mSegmentSize - mReadCursor, available);
+ mLogicalCursor = aPosition;
+ return NS_OK;
+}
+
+nsresult
+NS_NewStorageStream(uint32_t segmentSize, uint32_t maxSize, nsIStorageStream **result)
+{
+ NS_ENSURE_ARG(result);
+
+ nsStorageStream* storageStream = new nsStorageStream();
+ if (!storageStream) return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(storageStream);
+ nsresult rv = storageStream->Init(segmentSize, maxSize, nullptr);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(storageStream);
+ return rv;
+ }
+ *result = storageStream;
+ return NS_OK;
+}
diff --git a/xpcom/io/nsStorageStream.h b/xpcom/io/nsStorageStream.h
new file mode 100644
index 000000000..e50ce3ffc
--- /dev/null
+++ b/xpcom/io/nsStorageStream.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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/. */
+
+/*
+ * The storage stream provides an internal buffer that can be filled by a
+ * client using a single output stream. One or more independent input streams
+ * can be created to read the data out non-destructively. The implementation
+ * uses a segmented buffer internally to avoid realloc'ing of large buffers,
+ * with the attendant performance loss and heap fragmentation.
+ */
+
+#ifndef _nsStorageStream_h_
+#define _nsStorageStream_h_
+
+#include "nsIStorageStream.h"
+#include "nsIOutputStream.h"
+#include "nsMemory.h"
+#include "mozilla/Attributes.h"
+
+#define NS_STORAGESTREAM_CID \
+{ /* 669a9795-6ff7-4ed4-9150-c34ce2971b63 */ \
+ 0x669a9795, \
+ 0x6ff7, \
+ 0x4ed4, \
+ {0x91, 0x50, 0xc3, 0x4c, 0xe2, 0x97, 0x1b, 0x63} \
+}
+
+#define NS_STORAGESTREAM_CONTRACTID "@mozilla.org/storagestream;1"
+
+class nsSegmentedBuffer;
+
+class nsStorageStream MOZ_FINAL : public nsIStorageStream,
+ public nsIOutputStream
+{
+public:
+ nsStorageStream();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISTORAGESTREAM
+ NS_DECL_NSIOUTPUTSTREAM
+
+ friend class nsStorageInputStream;
+
+private:
+ ~nsStorageStream();
+
+ nsSegmentedBuffer* mSegmentedBuffer;
+ uint32_t mSegmentSize; // All segments, except possibly the last, are of this size
+ // Must be power-of-2
+ uint32_t mSegmentSizeLog2; // log2(mSegmentSize)
+ bool mWriteInProgress; // true, if an un-Close'ed output stream exists
+ int32_t mLastSegmentNum; // Last segment # in use, -1 initially
+ char* mWriteCursor; // Pointer to next byte to be written
+ char* mSegmentEnd; // Pointer to one byte after end of segment
+ // containing the write cursor
+ uint32_t mLogicalLength; // Number of bytes written to stream
+
+ NS_METHOD Seek(int32_t aPosition);
+ uint32_t SegNum(uint32_t aPosition) {return aPosition >> mSegmentSizeLog2;}
+ uint32_t SegOffset(uint32_t aPosition) {return aPosition & (mSegmentSize - 1);}
+};
+
+#endif // _nsStorageStream_h_
diff --git a/xpcom/io/nsStreamUtils.cpp b/xpcom/io/nsStreamUtils.cpp
new file mode 100644
index 000000000..fe79bc0c9
--- /dev/null
+++ b/xpcom/io/nsStreamUtils.cpp
@@ -0,0 +1,775 @@
+/* vim:set ts=4 sw=4 sts=4 et cin: */
+/* 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 "mozilla/Mutex.h"
+#include "mozilla/Attributes.h"
+#include "nsStreamUtils.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsIPipe.h"
+#include "nsIEventTarget.h"
+#include "nsIRunnable.h"
+#include "nsISafeOutputStream.h"
+#include "nsString.h"
+
+using namespace mozilla;
+
+//-----------------------------------------------------------------------------
+
+class nsInputStreamReadyEvent MOZ_FINAL : public nsIRunnable
+ , public nsIInputStreamCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ nsInputStreamReadyEvent(nsIInputStreamCallback *callback,
+ nsIEventTarget *target)
+ : mCallback(callback)
+ , mTarget(target)
+ {
+ }
+
+private:
+ ~nsInputStreamReadyEvent()
+ {
+ if (!mCallback)
+ return;
+ //
+ // whoa!! looks like we never posted this event. take care to
+ // release mCallback on the correct thread. if mTarget lives on the
+ // calling thread, then we are ok. otherwise, we have to try to
+ // proxy the Release over the right thread. if that thread is dead,
+ // then there's nothing we can do... better to leak than crash.
+ //
+ bool val;
+ nsresult rv = mTarget->IsOnCurrentThread(&val);
+ if (NS_FAILED(rv) || !val) {
+ nsCOMPtr<nsIInputStreamCallback> event =
+ NS_NewInputStreamReadyEvent(mCallback, mTarget);
+ mCallback = nullptr;
+ if (event) {
+ rv = event->OnInputStreamReady(nullptr);
+ if (NS_FAILED(rv)) {
+ NS_NOTREACHED("leaking stream event");
+ nsISupports *sup = event;
+ NS_ADDREF(sup);
+ }
+ }
+ }
+ }
+
+public:
+ NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *stream)
+ {
+ mStream = stream;
+
+ nsresult rv =
+ mTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Dispatch failed");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+ }
+
+ NS_IMETHOD Run()
+ {
+ if (mCallback) {
+ if (mStream)
+ mCallback->OnInputStreamReady(mStream);
+ mCallback = nullptr;
+ }
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIAsyncInputStream> mStream;
+ nsCOMPtr<nsIInputStreamCallback> mCallback;
+ nsCOMPtr<nsIEventTarget> mTarget;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsInputStreamReadyEvent, nsIRunnable,
+ nsIInputStreamCallback)
+
+//-----------------------------------------------------------------------------
+
+class nsOutputStreamReadyEvent MOZ_FINAL : public nsIRunnable
+ , public nsIOutputStreamCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ nsOutputStreamReadyEvent(nsIOutputStreamCallback *callback,
+ nsIEventTarget *target)
+ : mCallback(callback)
+ , mTarget(target)
+ {
+ }
+
+private:
+ ~nsOutputStreamReadyEvent()
+ {
+ if (!mCallback)
+ return;
+ //
+ // whoa!! looks like we never posted this event. take care to
+ // release mCallback on the correct thread. if mTarget lives on the
+ // calling thread, then we are ok. otherwise, we have to try to
+ // proxy the Release over the right thread. if that thread is dead,
+ // then there's nothing we can do... better to leak than crash.
+ //
+ bool val;
+ nsresult rv = mTarget->IsOnCurrentThread(&val);
+ if (NS_FAILED(rv) || !val) {
+ nsCOMPtr<nsIOutputStreamCallback> event =
+ NS_NewOutputStreamReadyEvent(mCallback, mTarget);
+ mCallback = nullptr;
+ if (event) {
+ rv = event->OnOutputStreamReady(nullptr);
+ if (NS_FAILED(rv)) {
+ NS_NOTREACHED("leaking stream event");
+ nsISupports *sup = event;
+ NS_ADDREF(sup);
+ }
+ }
+ }
+ }
+
+public:
+ NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *stream)
+ {
+ mStream = stream;
+
+ nsresult rv =
+ mTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("PostEvent failed");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+ }
+
+ NS_IMETHOD Run()
+ {
+ if (mCallback) {
+ if (mStream)
+ mCallback->OnOutputStreamReady(mStream);
+ mCallback = nullptr;
+ }
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIAsyncOutputStream> mStream;
+ nsCOMPtr<nsIOutputStreamCallback> mCallback;
+ nsCOMPtr<nsIEventTarget> mTarget;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsOutputStreamReadyEvent, nsIRunnable,
+ nsIOutputStreamCallback)
+
+//-----------------------------------------------------------------------------
+
+already_AddRefed<nsIInputStreamCallback>
+NS_NewInputStreamReadyEvent(nsIInputStreamCallback *callback,
+ nsIEventTarget *target)
+{
+ NS_ASSERTION(callback, "null callback");
+ NS_ASSERTION(target, "null target");
+ nsRefPtr<nsInputStreamReadyEvent> ev =
+ new nsInputStreamReadyEvent(callback, target);
+ return ev.forget();
+}
+
+already_AddRefed<nsIOutputStreamCallback>
+NS_NewOutputStreamReadyEvent(nsIOutputStreamCallback *callback,
+ nsIEventTarget *target)
+{
+ NS_ASSERTION(callback, "null callback");
+ NS_ASSERTION(target, "null target");
+ nsRefPtr<nsOutputStreamReadyEvent> ev =
+ new nsOutputStreamReadyEvent(callback, target);
+ return ev.forget();
+}
+
+//-----------------------------------------------------------------------------
+// NS_AsyncCopy implementation
+
+// abstract stream copier...
+class nsAStreamCopier : public nsIInputStreamCallback
+ , public nsIOutputStreamCallback
+ , public nsIRunnable
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ nsAStreamCopier()
+ : mLock("nsAStreamCopier.mLock")
+ , mCallback(nullptr)
+ , mProgressCallback(nullptr)
+ , mClosure(nullptr)
+ , mChunkSize(0)
+ , mEventInProcess(false)
+ , mEventIsPending(false)
+ , mCloseSource(true)
+ , mCloseSink(true)
+ , mCanceled(false)
+ , mCancelStatus(NS_OK)
+ {
+ }
+
+ // virtual since subclasses call superclass Release()
+ virtual ~nsAStreamCopier()
+ {
+ }
+
+ // kick off the async copy...
+ nsresult Start(nsIInputStream *source,
+ nsIOutputStream *sink,
+ nsIEventTarget *target,
+ nsAsyncCopyCallbackFun callback,
+ void *closure,
+ uint32_t chunksize,
+ bool closeSource,
+ bool closeSink,
+ nsAsyncCopyProgressFun progressCallback)
+ {
+ mSource = source;
+ mSink = sink;
+ mTarget = target;
+ mCallback = callback;
+ mClosure = closure;
+ mChunkSize = chunksize;
+ mCloseSource = closeSource;
+ mCloseSink = closeSink;
+ mProgressCallback = progressCallback;
+
+ mAsyncSource = do_QueryInterface(mSource);
+ mAsyncSink = do_QueryInterface(mSink);
+
+ return PostContinuationEvent();
+ }
+
+ // implemented by subclasses, returns number of bytes copied and
+ // sets source and sink condition before returning.
+ virtual uint32_t DoCopy(nsresult *sourceCondition, nsresult *sinkCondition) = 0;
+
+ void Process()
+ {
+ if (!mSource || !mSink)
+ return;
+
+ nsresult sourceCondition, sinkCondition;
+ nsresult cancelStatus;
+ bool canceled;
+ {
+ MutexAutoLock lock(mLock);
+ canceled = mCanceled;
+ cancelStatus = mCancelStatus;
+ }
+
+ // Copy data from the source to the sink until we hit failure or have
+ // copied all the data.
+ for (;;) {
+ // Note: copyFailed will be true if the source or the sink have
+ // reported an error, or if we failed to write any bytes
+ // because we have consumed all of our data.
+ bool copyFailed = false;
+ if (!canceled) {
+ uint32_t n = DoCopy(&sourceCondition, &sinkCondition);
+ if (n > 0 && mProgressCallback) {
+ mProgressCallback(mClosure, n);
+ }
+ copyFailed = NS_FAILED(sourceCondition) ||
+ NS_FAILED(sinkCondition) || n == 0;
+
+ MutexAutoLock lock(mLock);
+ canceled = mCanceled;
+ cancelStatus = mCancelStatus;
+ }
+ if (copyFailed && !canceled) {
+ if (sourceCondition == NS_BASE_STREAM_WOULD_BLOCK && mAsyncSource) {
+ // need to wait for more data from source. while waiting for
+ // more source data, be sure to observe failures on output end.
+ mAsyncSource->AsyncWait(this, 0, 0, nullptr);
+
+ if (mAsyncSink)
+ mAsyncSink->AsyncWait(this,
+ nsIAsyncOutputStream::WAIT_CLOSURE_ONLY,
+ 0, nullptr);
+ break;
+ }
+ else if (sinkCondition == NS_BASE_STREAM_WOULD_BLOCK && mAsyncSink) {
+ // need to wait for more room in the sink. while waiting for
+ // more room in the sink, be sure to observer failures on the
+ // input end.
+ mAsyncSink->AsyncWait(this, 0, 0, nullptr);
+
+ if (mAsyncSource)
+ mAsyncSource->AsyncWait(this,
+ nsIAsyncInputStream::WAIT_CLOSURE_ONLY,
+ 0, nullptr);
+ break;
+ }
+ }
+ if (copyFailed || canceled) {
+ if (mCloseSource) {
+ // close source
+ if (mAsyncSource)
+ mAsyncSource->CloseWithStatus(canceled ? cancelStatus :
+ sinkCondition);
+ else
+ mSource->Close();
+ }
+ mAsyncSource = nullptr;
+ mSource = nullptr;
+
+ if (mCloseSink) {
+ // close sink
+ if (mAsyncSink)
+ mAsyncSink->CloseWithStatus(canceled ? cancelStatus :
+ sourceCondition);
+ else {
+ // If we have an nsISafeOutputStream, and our
+ // sourceCondition and sinkCondition are not set to a
+ // failure state, finish writing.
+ nsCOMPtr<nsISafeOutputStream> sostream =
+ do_QueryInterface(mSink);
+ if (sostream && NS_SUCCEEDED(sourceCondition) &&
+ NS_SUCCEEDED(sinkCondition))
+ sostream->Finish();
+ else
+ mSink->Close();
+ }
+ }
+ mAsyncSink = nullptr;
+ mSink = nullptr;
+
+ // notify state complete...
+ if (mCallback) {
+ nsresult status;
+ if (!canceled) {
+ status = sourceCondition;
+ if (NS_SUCCEEDED(status))
+ status = sinkCondition;
+ if (status == NS_BASE_STREAM_CLOSED)
+ status = NS_OK;
+ } else {
+ status = cancelStatus;
+ }
+ mCallback(mClosure, status);
+ }
+ break;
+ }
+ }
+ }
+
+ nsresult Cancel(nsresult aReason)
+ {
+ MutexAutoLock lock(mLock);
+ if (mCanceled)
+ return NS_ERROR_FAILURE;
+
+ if (NS_SUCCEEDED(aReason)) {
+ NS_WARNING("cancel with non-failure status code");
+ aReason = NS_BASE_STREAM_CLOSED;
+ }
+
+ mCanceled = true;
+ mCancelStatus = aReason;
+ return NS_OK;
+ }
+
+ NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *source)
+ {
+ PostContinuationEvent();
+ return NS_OK;
+ }
+
+ NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *sink)
+ {
+ PostContinuationEvent();
+ return NS_OK;
+ }
+
+ // continuation event handler
+ NS_IMETHOD Run()
+ {
+ Process();
+
+ // clear "in process" flag and post any pending continuation event
+ MutexAutoLock lock(mLock);
+ mEventInProcess = false;
+ if (mEventIsPending) {
+ mEventIsPending = false;
+ PostContinuationEvent_Locked();
+ }
+
+ return NS_OK;
+ }
+
+ nsresult PostContinuationEvent()
+ {
+ // we cannot post a continuation event if there is currently
+ // an event in process. doing so could result in Process being
+ // run simultaneously on multiple threads, so we mark the event
+ // as pending, and if an event is already in process then we
+ // just let that existing event take care of posting the real
+ // continuation event.
+
+ MutexAutoLock lock(mLock);
+ return PostContinuationEvent_Locked();
+ }
+
+ nsresult PostContinuationEvent_Locked()
+ {
+ nsresult rv = NS_OK;
+ if (mEventInProcess)
+ mEventIsPending = true;
+ else {
+ rv = mTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+ if (NS_SUCCEEDED(rv))
+ mEventInProcess = true;
+ else
+ NS_WARNING("unable to post continuation event");
+ }
+ return rv;
+ }
+
+protected:
+ nsCOMPtr<nsIInputStream> mSource;
+ nsCOMPtr<nsIOutputStream> mSink;
+ nsCOMPtr<nsIAsyncInputStream> mAsyncSource;
+ nsCOMPtr<nsIAsyncOutputStream> mAsyncSink;
+ nsCOMPtr<nsIEventTarget> mTarget;
+ Mutex mLock;
+ nsAsyncCopyCallbackFun mCallback;
+ nsAsyncCopyProgressFun mProgressCallback;
+ void *mClosure;
+ uint32_t mChunkSize;
+ bool mEventInProcess;
+ bool mEventIsPending;
+ bool mCloseSource;
+ bool mCloseSink;
+ bool mCanceled;
+ nsresult mCancelStatus;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS3(nsAStreamCopier,
+ nsIInputStreamCallback,
+ nsIOutputStreamCallback,
+ nsIRunnable)
+
+class nsStreamCopierIB MOZ_FINAL : public nsAStreamCopier
+{
+public:
+ nsStreamCopierIB() : nsAStreamCopier() {}
+ virtual ~nsStreamCopierIB() {}
+
+ struct ReadSegmentsState {
+ nsIOutputStream *mSink;
+ nsresult mSinkCondition;
+ };
+
+ static NS_METHOD ConsumeInputBuffer(nsIInputStream *inStr,
+ void *closure,
+ const char *buffer,
+ uint32_t offset,
+ uint32_t count,
+ uint32_t *countWritten)
+ {
+ ReadSegmentsState *state = (ReadSegmentsState *) closure;
+
+ nsresult rv = state->mSink->Write(buffer, count, countWritten);
+ if (NS_FAILED(rv))
+ state->mSinkCondition = rv;
+ else if (*countWritten == 0)
+ state->mSinkCondition = NS_BASE_STREAM_CLOSED;
+
+ return state->mSinkCondition;
+ }
+
+ uint32_t DoCopy(nsresult *sourceCondition, nsresult *sinkCondition)
+ {
+ ReadSegmentsState state;
+ state.mSink = mSink;
+ state.mSinkCondition = NS_OK;
+
+ uint32_t n;
+ *sourceCondition =
+ mSource->ReadSegments(ConsumeInputBuffer, &state, mChunkSize, &n);
+ *sinkCondition = state.mSinkCondition;
+ return n;
+ }
+};
+
+class nsStreamCopierOB MOZ_FINAL : public nsAStreamCopier
+{
+public:
+ nsStreamCopierOB() : nsAStreamCopier() {}
+ virtual ~nsStreamCopierOB() {}
+
+ struct WriteSegmentsState {
+ nsIInputStream *mSource;
+ nsresult mSourceCondition;
+ };
+
+ static NS_METHOD FillOutputBuffer(nsIOutputStream *outStr,
+ void *closure,
+ char *buffer,
+ uint32_t offset,
+ uint32_t count,
+ uint32_t *countRead)
+ {
+ WriteSegmentsState *state = (WriteSegmentsState *) closure;
+
+ nsresult rv = state->mSource->Read(buffer, count, countRead);
+ if (NS_FAILED(rv))
+ state->mSourceCondition = rv;
+ else if (*countRead == 0)
+ state->mSourceCondition = NS_BASE_STREAM_CLOSED;
+
+ return state->mSourceCondition;
+ }
+
+ uint32_t DoCopy(nsresult *sourceCondition, nsresult *sinkCondition)
+ {
+ WriteSegmentsState state;
+ state.mSource = mSource;
+ state.mSourceCondition = NS_OK;
+
+ uint32_t n;
+ *sinkCondition =
+ mSink->WriteSegments(FillOutputBuffer, &state, mChunkSize, &n);
+ *sourceCondition = state.mSourceCondition;
+ return n;
+ }
+};
+
+//-----------------------------------------------------------------------------
+
+nsresult
+NS_AsyncCopy(nsIInputStream *source,
+ nsIOutputStream *sink,
+ nsIEventTarget *target,
+ nsAsyncCopyMode mode,
+ uint32_t chunkSize,
+ nsAsyncCopyCallbackFun callback,
+ void *closure,
+ bool closeSource,
+ bool closeSink,
+ nsISupports **aCopierCtx,
+ nsAsyncCopyProgressFun progressCallback)
+{
+ NS_ASSERTION(target, "non-null target required");
+
+ nsresult rv;
+ nsAStreamCopier *copier;
+
+ if (mode == NS_ASYNCCOPY_VIA_READSEGMENTS)
+ copier = new nsStreamCopierIB();
+ else
+ copier = new nsStreamCopierOB();
+
+ if (!copier)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Start() takes an owning ref to the copier...
+ NS_ADDREF(copier);
+ rv = copier->Start(source, sink, target, callback, closure, chunkSize,
+ closeSource, closeSink, progressCallback);
+
+ if (aCopierCtx) {
+ *aCopierCtx = static_cast<nsISupports*>(
+ static_cast<nsIRunnable*>(copier));
+ NS_ADDREF(*aCopierCtx);
+ }
+ NS_RELEASE(copier);
+
+ return rv;
+}
+
+//-----------------------------------------------------------------------------
+
+nsresult
+NS_CancelAsyncCopy(nsISupports *aCopierCtx, nsresult aReason)
+{
+ nsAStreamCopier *copier = static_cast<nsAStreamCopier *>(
+ static_cast<nsIRunnable *>(aCopierCtx));
+ return copier->Cancel(aReason);
+}
+
+//-----------------------------------------------------------------------------
+
+nsresult
+NS_ConsumeStream(nsIInputStream *stream, uint32_t maxCount, nsACString &result)
+{
+ nsresult rv = NS_OK;
+ result.Truncate();
+
+ while (maxCount) {
+ uint64_t avail64;
+ rv = stream->Available(&avail64);
+ if (NS_FAILED(rv)) {
+ if (rv == NS_BASE_STREAM_CLOSED)
+ rv = NS_OK;
+ break;
+ }
+ if (avail64 == 0)
+ break;
+
+ uint32_t avail = (uint32_t)XPCOM_MIN<uint64_t>(avail64, maxCount);
+
+ // resize result buffer
+ uint32_t length = result.Length();
+ if (avail > UINT32_MAX - length)
+ return NS_ERROR_FILE_TOO_BIG;
+
+ result.SetLength(length + avail);
+ if (result.Length() != (length + avail))
+ return NS_ERROR_OUT_OF_MEMORY;
+ char *buf = result.BeginWriting() + length;
+
+ uint32_t n;
+ rv = stream->Read(buf, avail, &n);
+ if (NS_FAILED(rv))
+ break;
+ if (n != avail)
+ result.SetLength(length + n);
+ if (n == 0)
+ break;
+ maxCount -= n;
+ }
+
+ return rv;
+}
+
+//-----------------------------------------------------------------------------
+
+static NS_METHOD
+TestInputStream(nsIInputStream *inStr,
+ void *closure,
+ const char *buffer,
+ uint32_t offset,
+ uint32_t count,
+ uint32_t *countWritten)
+{
+ bool *result = static_cast<bool *>(closure);
+ *result = true;
+ return NS_ERROR_ABORT; // don't call me anymore
+}
+
+bool
+NS_InputStreamIsBuffered(nsIInputStream *stream)
+{
+ bool result = false;
+ uint32_t n;
+ nsresult rv = stream->ReadSegments(TestInputStream,
+ &result, 1, &n);
+ return result || NS_SUCCEEDED(rv);
+}
+
+static NS_METHOD
+TestOutputStream(nsIOutputStream *outStr,
+ void *closure,
+ char *buffer,
+ uint32_t offset,
+ uint32_t count,
+ uint32_t *countRead)
+{
+ bool *result = static_cast<bool *>(closure);
+ *result = true;
+ return NS_ERROR_ABORT; // don't call me anymore
+}
+
+bool
+NS_OutputStreamIsBuffered(nsIOutputStream *stream)
+{
+ bool result = false;
+ uint32_t n;
+ stream->WriteSegments(TestOutputStream, &result, 1, &n);
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+
+NS_METHOD
+NS_CopySegmentToStream(nsIInputStream *inStr,
+ void *closure,
+ const char *buffer,
+ uint32_t offset,
+ uint32_t count,
+ uint32_t *countWritten)
+{
+ nsIOutputStream *outStr = static_cast<nsIOutputStream *>(closure);
+ *countWritten = 0;
+ while (count) {
+ uint32_t n;
+ nsresult rv = outStr->Write(buffer, count, &n);
+ if (NS_FAILED(rv))
+ return rv;
+ buffer += n;
+ count -= n;
+ *countWritten += n;
+ }
+ return NS_OK;
+}
+
+NS_METHOD
+NS_CopySegmentToBuffer(nsIInputStream *inStr,
+ void *closure,
+ const char *buffer,
+ uint32_t offset,
+ uint32_t count,
+ uint32_t *countWritten)
+{
+ char *toBuf = static_cast<char *>(closure);
+ memcpy(&toBuf[offset], buffer, count);
+ *countWritten = count;
+ return NS_OK;
+}
+
+NS_METHOD
+NS_CopySegmentToBuffer(nsIOutputStream *outStr,
+ void *closure,
+ char *buffer,
+ uint32_t offset,
+ uint32_t count,
+ uint32_t *countRead)
+{
+ const char* fromBuf = static_cast<const char*>(closure);
+ memcpy(buffer, &fromBuf[offset], count);
+ *countRead = count;
+ return NS_OK;
+}
+
+NS_METHOD
+NS_DiscardSegment(nsIInputStream *inStr,
+ void *closure,
+ const char *buffer,
+ uint32_t offset,
+ uint32_t count,
+ uint32_t *countWritten)
+{
+ *countWritten = count;
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+NS_METHOD
+NS_WriteSegmentThunk(nsIInputStream *inStr,
+ void *closure,
+ const char *buffer,
+ uint32_t offset,
+ uint32_t count,
+ uint32_t *countWritten)
+{
+ nsWriteSegmentThunk *thunk = static_cast<nsWriteSegmentThunk *>(closure);
+ return thunk->mFun(thunk->mStream, thunk->mClosure, buffer, offset, count,
+ countWritten);
+}
diff --git a/xpcom/io/nsStreamUtils.h b/xpcom/io/nsStreamUtils.h
new file mode 100644
index 000000000..fcb47052a
--- /dev/null
+++ b/xpcom/io/nsStreamUtils.h
@@ -0,0 +1,235 @@
+/* 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 nsStreamUtils_h__
+#define nsStreamUtils_h__
+
+#include "nsCOMPtr.h"
+#include "nsStringFwd.h"
+#include "nsIInputStream.h"
+
+class nsIOutputStream;
+class nsIInputStreamCallback;
+class nsIOutputStreamCallback;
+class nsIEventTarget;
+
+/**
+ * A "one-shot" proxy of the OnInputStreamReady callback. The resulting
+ * proxy object's OnInputStreamReady function may only be called once! The
+ * proxy object ensures that the real notify object will be free'd on the
+ * thread corresponding to the given event target regardless of what thread
+ * the proxy object is destroyed on.
+ *
+ * This function is designed to be used to implement AsyncWait when the
+ * aTarget parameter is non-null.
+ */
+extern already_AddRefed<nsIInputStreamCallback>
+NS_NewInputStreamReadyEvent(nsIInputStreamCallback *aNotify,
+ nsIEventTarget *aTarget);
+
+/**
+ * A "one-shot" proxy of the OnOutputStreamReady callback. The resulting
+ * proxy object's OnOutputStreamReady function may only be called once! The
+ * proxy object ensures that the real notify object will be free'd on the
+ * thread corresponding to the given event target regardless of what thread
+ * the proxy object is destroyed on.
+ *
+ * This function is designed to be used to implement AsyncWait when the
+ * aTarget parameter is non-null.
+ */
+extern already_AddRefed<nsIOutputStreamCallback>
+NS_NewOutputStreamReadyEvent(nsIOutputStreamCallback *aNotify,
+ nsIEventTarget *aTarget);
+
+/* ------------------------------------------------------------------------- */
+
+enum nsAsyncCopyMode {
+ NS_ASYNCCOPY_VIA_READSEGMENTS,
+ NS_ASYNCCOPY_VIA_WRITESEGMENTS
+};
+
+/**
+ * This function is called when a new chunk of data has been copied. The
+ * reported count is the size of the current chunk.
+ */
+typedef void (* nsAsyncCopyProgressFun)(void *closure, uint32_t count);
+
+/**
+ * This function is called when the async copy process completes. The reported
+ * status is NS_OK on success and some error code on failure.
+ */
+typedef void (* nsAsyncCopyCallbackFun)(void *closure, nsresult status);
+
+/**
+ * This function asynchronously copies data from the source to the sink. All
+ * data transfer occurs on the thread corresponding to the given event target.
+ * A null event target is not permitted.
+ *
+ * The copier handles blocking or non-blocking streams transparently. If a
+ * stream operation returns NS_BASE_STREAM_WOULD_BLOCK, then the stream will
+ * be QI'd to nsIAsync{In,Out}putStream and its AsyncWait method will be used
+ * to determine when to resume copying.
+ *
+ * Source and sink are closed by default when copying finishes or when error
+ * occurs. Caller can prevent closing source or sink by setting aCloseSource
+ * or aCloseSink to false.
+ *
+ * Caller can obtain aCopierCtx to be able to cancel copying.
+ */
+extern nsresult
+NS_AsyncCopy(nsIInputStream *aSource,
+ nsIOutputStream *aSink,
+ nsIEventTarget *aTarget,
+ nsAsyncCopyMode aMode = NS_ASYNCCOPY_VIA_READSEGMENTS,
+ uint32_t aChunkSize = 4096,
+ nsAsyncCopyCallbackFun aCallbackFun = nullptr,
+ void *aCallbackClosure = nullptr,
+ bool aCloseSource = true,
+ bool aCloseSink = true,
+ nsISupports **aCopierCtx = nullptr,
+ nsAsyncCopyProgressFun aProgressCallbackFun = nullptr);
+
+/**
+ * This function cancels copying started by function NS_AsyncCopy.
+ *
+ * @param aCopierCtx
+ * Copier context returned by NS_AsyncCopy.
+ * @param aReason
+ * A failure code indicating why the operation is being canceled.
+ * It is an error to pass a success code.
+ */
+extern nsresult
+NS_CancelAsyncCopy(nsISupports *aCopierCtx, nsresult aReason);
+
+/**
+ * This function copies all of the available data from the stream (up to at
+ * most aMaxCount bytes) into the given buffer. The buffer is truncated at
+ * the start of the function.
+ *
+ * If an error occurs while reading from the stream or while attempting to
+ * resize the buffer, then the corresponding error code is returned from this
+ * function, and any data that has already been read will be returned in the
+ * output buffer. This allows one to use this function with a non-blocking
+ * input stream that may return NS_BASE_STREAM_WOULD_BLOCK if it only has
+ * partial data available.
+ *
+ * @param aSource
+ * The input stream to read.
+ * @param aMaxCount
+ * The maximum number of bytes to consume from the stream. Pass the
+ * value UINT32_MAX to consume the entire stream. The number of
+ * bytes actually read is given by the length of aBuffer upon return.
+ * @param aBuffer
+ * The string object that will contain the stream data upon return.
+ * Note: The data copied to the string may contain null bytes and may
+ * contain non-ASCII values.
+ */
+extern nsresult
+NS_ConsumeStream(nsIInputStream *aSource, uint32_t aMaxCount,
+ nsACString &aBuffer);
+
+/**
+ * This function tests whether or not the input stream is buffered. A buffered
+ * input stream is one that implements readSegments. The test for this is to
+ * simply call readSegments, without actually consuming any data from the
+ * stream, to verify that it functions.
+ *
+ * NOTE: If the stream is non-blocking and has no data available yet, then this
+ * test will fail. In that case, we return false even though the test is not
+ * really conclusive.
+ *
+ * @param aInputStream
+ * The input stream to test.
+ */
+extern bool
+NS_InputStreamIsBuffered(nsIInputStream *aInputStream);
+
+/**
+ * This function tests whether or not the output stream is buffered. A
+ * buffered output stream is one that implements writeSegments. The test for
+ * this is to simply call writeSegments, without actually writing any data into
+ * the stream, to verify that it functions.
+ *
+ * NOTE: If the stream is non-blocking and has no available space yet, then
+ * this test will fail. In that case, we return false even though the test is
+ * not really conclusive.
+ *
+ * @param aOutputStream
+ * The output stream to test.
+ */
+extern bool
+NS_OutputStreamIsBuffered(nsIOutputStream *aOutputStream);
+
+/**
+ * This function is intended to be passed to nsIInputStream::ReadSegments to
+ * copy data from the nsIInputStream into a nsIOutputStream passed as the
+ * aClosure parameter to the ReadSegments function.
+ *
+ * @see nsIInputStream.idl for a description of this function's parameters.
+ */
+extern NS_METHOD
+NS_CopySegmentToStream(nsIInputStream *aInputStream, void *aClosure,
+ const char *aFromSegment, uint32_t aToOffset,
+ uint32_t aCount, uint32_t *aWriteCount);
+
+/**
+ * This function is intended to be passed to nsIInputStream::ReadSegments to
+ * copy data from the nsIInputStream into a character buffer passed as the
+ * aClosure parameter to the ReadSegments function. The character buffer
+ * must be at least as large as the aCount parameter passed to ReadSegments.
+ *
+ * @see nsIInputStream.idl for a description of this function's parameters.
+ */
+extern NS_METHOD
+NS_CopySegmentToBuffer(nsIInputStream *aInputStream, void *aClosure,
+ const char *aFromSegment, uint32_t aToOffset,
+ uint32_t aCount, uint32_t *aWriteCount);
+
+/**
+ * This function is intended to be passed to nsIOutputStream::WriteSegments to
+ * copy data into the nsIOutputStream from a character buffer passed as the
+ * aClosure parameter to the WriteSegments function.
+ *
+ * @see nsIOutputStream.idl for a description of this function's parameters.
+ */
+extern NS_METHOD
+NS_CopySegmentToBuffer(nsIOutputStream *aOutputStream, void *aClosure,
+ char *aToSegment, uint32_t aFromOffset,
+ uint32_t aCount, uint32_t *aReadCount);
+
+/**
+ * This function is intended to be passed to nsIInputStream::ReadSegments to
+ * discard data from the nsIInputStream. This can be used to efficiently read
+ * data from the stream without actually copying any bytes.
+ *
+ * @see nsIInputStream.idl for a description of this function's parameters.
+ */
+extern NS_METHOD
+NS_DiscardSegment(nsIInputStream *aInputStream, void *aClosure,
+ const char *aFromSegment, uint32_t aToOffset,
+ uint32_t aCount, uint32_t *aWriteCount);
+
+/**
+ * This function is intended to be passed to nsIInputStream::ReadSegments to
+ * adjust the aInputStream parameter passed to a consumer's WriteSegmentFun.
+ * The aClosure parameter must be a pointer to a nsWriteSegmentThunk object.
+ * The mStream and mClosure members of that object will be passed to the mFun
+ * function, with the remainder of the parameters being what are passed to
+ * NS_WriteSegmentThunk.
+ *
+ * This function comes in handy when implementing ReadSegments in terms of an
+ * inner stream's ReadSegments.
+ */
+extern NS_METHOD
+NS_WriteSegmentThunk(nsIInputStream *aInputStream, void *aClosure,
+ const char *aFromSegment, uint32_t aToOffset,
+ uint32_t aCount, uint32_t *aWriteCount);
+
+struct nsWriteSegmentThunk {
+ nsIInputStream *mStream;
+ nsWriteSegmentFun mFun;
+ void *mClosure;
+};
+
+#endif // !nsStreamUtils_h__
diff --git a/xpcom/io/nsStringStream.cpp b/xpcom/io/nsStringStream.cpp
new file mode 100644
index 000000000..591e130b3
--- /dev/null
+++ b/xpcom/io/nsStringStream.cpp
@@ -0,0 +1,400 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set ts=4 sts=4 sw=4 cin et: */
+/* 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/. */
+
+/**
+ * Based on original code from nsIStringStream.cpp
+ */
+
+#include "ipc/IPCMessageUtils.h"
+
+#include "nsStringStream.h"
+#include "nsStreamUtils.h"
+#include "nsReadableUtils.h"
+#include "nsISeekableStream.h"
+#include "nsISupportsPrimitives.h"
+#include "nsCRT.h"
+#include "prerror.h"
+#include "plstr.h"
+#include "nsIClassInfoImpl.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "nsIIPCSerializableInputStream.h"
+
+using namespace mozilla::ipc;
+
+//-----------------------------------------------------------------------------
+// nsIStringInputStream implementation
+//-----------------------------------------------------------------------------
+
+class nsStringInputStream MOZ_FINAL : public nsIStringInputStream
+ , public nsISeekableStream
+ , public nsISupportsCString
+ , public nsIIPCSerializableInputStream
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAM
+ NS_DECL_NSISTRINGINPUTSTREAM
+ NS_DECL_NSISEEKABLESTREAM
+ NS_DECL_NSISUPPORTSPRIMITIVE
+ NS_DECL_NSISUPPORTSCSTRING
+ NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
+
+ nsStringInputStream()
+ {
+ Clear();
+ }
+
+private:
+ ~nsStringInputStream()
+ {}
+
+ uint32_t Length() const
+ {
+ return mData.Length();
+ }
+
+ uint32_t LengthRemaining() const
+ {
+ return Length() - mOffset;
+ }
+
+ void Clear()
+ {
+ mData.SetIsVoid(true);
+ }
+
+ bool Closed()
+ {
+ return mData.IsVoid();
+ }
+
+ nsDependentCSubstring mData;
+ uint32_t mOffset;
+};
+
+// This class needs to support threadsafe refcounting since people often
+// allocate a string stream, and then read it from a background thread.
+NS_IMPL_THREADSAFE_ADDREF(nsStringInputStream)
+NS_IMPL_THREADSAFE_RELEASE(nsStringInputStream)
+
+NS_IMPL_CLASSINFO(nsStringInputStream, NULL, nsIClassInfo::THREADSAFE,
+ NS_STRINGINPUTSTREAM_CID)
+NS_IMPL_QUERY_INTERFACE5_CI(nsStringInputStream,
+ nsIStringInputStream,
+ nsIInputStream,
+ nsISupportsCString,
+ nsISeekableStream,
+ nsIIPCSerializableInputStream)
+NS_IMPL_CI_INTERFACE_GETTER4(nsStringInputStream,
+ nsIStringInputStream,
+ nsIInputStream,
+ nsISupportsCString,
+ nsISeekableStream)
+
+/////////
+// nsISupportsCString implementation
+/////////
+
+NS_IMETHODIMP
+nsStringInputStream::GetType(uint16_t *type)
+{
+ *type = TYPE_CSTRING;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::GetData(nsACString &data)
+{
+ // The stream doesn't have any data when it is closed. We could fake it
+ // and return an empty string here, but it seems better to keep this return
+ // value consistent with the behavior of the other 'getter' methods.
+ NS_ENSURE_TRUE(!Closed(), NS_BASE_STREAM_CLOSED);
+
+ data.Assign(mData);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::SetData(const nsACString &data)
+{
+ mData.Assign(data);
+ mOffset = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::ToString(char **result)
+{
+ // NOTE: This method may result in data loss, so we do not implement it.
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/////////
+// nsIStringInputStream implementation
+/////////
+
+NS_IMETHODIMP
+nsStringInputStream::SetData(const char *data, int32_t dataLen)
+{
+ NS_ENSURE_ARG_POINTER(data);
+ mData.Assign(data, dataLen);
+ mOffset = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::AdoptData(char *data, int32_t dataLen)
+{
+ NS_ENSURE_ARG_POINTER(data);
+ mData.Adopt(data, dataLen);
+ mOffset = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::ShareData(const char *data, int32_t dataLen)
+{
+ NS_ENSURE_ARG_POINTER(data);
+
+ if (dataLen < 0)
+ dataLen = strlen(data);
+
+ mData.Rebind(data, dataLen);
+ mOffset = 0;
+ return NS_OK;
+}
+
+/////////
+// nsIInputStream implementation
+/////////
+
+NS_IMETHODIMP
+nsStringInputStream::Close()
+{
+ Clear();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::Available(uint64_t *aLength)
+{
+ NS_ASSERTION(aLength, "null ptr");
+
+ if (Closed())
+ return NS_BASE_STREAM_CLOSED;
+
+ *aLength = LengthRemaining();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::Read(char* aBuf, uint32_t aCount, uint32_t *aReadCount)
+{
+ NS_ASSERTION(aBuf, "null ptr");
+ return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, aReadCount);
+}
+
+NS_IMETHODIMP
+nsStringInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure,
+ uint32_t aCount, uint32_t *result)
+{
+ NS_ASSERTION(result, "null ptr");
+ NS_ASSERTION(Length() >= mOffset, "bad stream state");
+
+ if (Closed())
+ return NS_BASE_STREAM_CLOSED;
+
+ // We may be at end-of-file
+ uint32_t maxCount = LengthRemaining();
+ if (maxCount == 0) {
+ *result = 0;
+ return NS_OK;
+ }
+
+ if (aCount > maxCount)
+ aCount = maxCount;
+ nsresult rv = writer(this, closure, mData.BeginReading() + mOffset, 0, aCount, result);
+ if (NS_SUCCEEDED(rv)) {
+ NS_ASSERTION(*result <= aCount,
+ "writer should not write more than we asked it to write");
+ mOffset += *result;
+ }
+
+ // errors returned from the writer end here!
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::IsNonBlocking(bool *aNonBlocking)
+{
+ *aNonBlocking = true;
+ return NS_OK;
+}
+
+/////////
+// nsISeekableStream implementation
+/////////
+
+NS_IMETHODIMP
+nsStringInputStream::Seek(int32_t whence, int64_t offset)
+{
+ if (Closed())
+ return NS_BASE_STREAM_CLOSED;
+
+ // Compute new stream position. The given offset may be a negative value.
+
+ int64_t newPos = offset;
+ switch (whence) {
+ case NS_SEEK_SET:
+ break;
+ case NS_SEEK_CUR:
+ newPos += mOffset;
+ break;
+ case NS_SEEK_END:
+ newPos += Length();
+ break;
+ default:
+ NS_ERROR("invalid whence");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ NS_ENSURE_ARG(newPos >= 0);
+ NS_ENSURE_ARG(newPos <= Length());
+
+ mOffset = (uint32_t)newPos;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::Tell(int64_t* outWhere)
+{
+ if (Closed())
+ return NS_BASE_STREAM_CLOSED;
+
+ *outWhere = mOffset;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStringInputStream::SetEOF()
+{
+ if (Closed())
+ return NS_BASE_STREAM_CLOSED;
+
+ mOffset = Length();
+ return NS_OK;
+}
+
+void
+nsStringInputStream::Serialize(InputStreamParams& aParams)
+{
+ StringInputStreamParams params;
+ params.data() = PromiseFlatCString(mData);
+ aParams = params;
+}
+
+bool
+nsStringInputStream::Deserialize(const InputStreamParams& aParams)
+{
+ if (aParams.type() != InputStreamParams::TStringInputStreamParams) {
+ NS_ERROR("Received unknown parameters from the other process!");
+ return false;
+ }
+
+ const StringInputStreamParams& params =
+ aParams.get_StringInputStreamParams();
+
+ if (NS_FAILED(SetData(params.data()))) {
+ NS_WARNING("SetData failed!");
+ return false;
+ }
+
+ return true;
+}
+
+nsresult
+NS_NewByteInputStream(nsIInputStream** aStreamResult,
+ const char* aStringToRead, int32_t aLength,
+ nsAssignmentType aAssignment)
+{
+ NS_PRECONDITION(aStreamResult, "null out ptr");
+
+ nsStringInputStream* stream = new nsStringInputStream();
+ if (! stream)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(stream);
+
+ nsresult rv;
+ switch (aAssignment) {
+ case NS_ASSIGNMENT_COPY:
+ rv = stream->SetData(aStringToRead, aLength);
+ break;
+ case NS_ASSIGNMENT_DEPEND:
+ rv = stream->ShareData(aStringToRead, aLength);
+ break;
+ case NS_ASSIGNMENT_ADOPT:
+ rv = stream->AdoptData(const_cast<char*>(aStringToRead), aLength);
+ break;
+ default:
+ NS_ERROR("invalid assignment type");
+ rv = NS_ERROR_INVALID_ARG;
+ }
+
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(stream);
+ return rv;
+ }
+
+ *aStreamResult = stream;
+ return NS_OK;
+}
+
+nsresult
+NS_NewStringInputStream(nsIInputStream** aStreamResult,
+ const nsAString& aStringToRead)
+{
+ NS_LossyConvertUTF16toASCII data(aStringToRead); // truncates high-order bytes
+ return NS_NewCStringInputStream(aStreamResult, data);
+}
+
+nsresult
+NS_NewCStringInputStream(nsIInputStream** aStreamResult,
+ const nsACString& aStringToRead)
+{
+ NS_PRECONDITION(aStreamResult, "null out ptr");
+
+ nsStringInputStream* stream = new nsStringInputStream();
+ if (! stream)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(stream);
+
+ stream->SetData(aStringToRead);
+
+ *aStreamResult = stream;
+ return NS_OK;
+}
+
+// factory method for constructing a nsStringInputStream object
+nsresult
+nsStringInputStreamConstructor(nsISupports *outer, REFNSIID iid, void **result)
+{
+ *result = nullptr;
+
+ NS_ENSURE_TRUE(!outer, NS_ERROR_NO_AGGREGATION);
+
+ nsStringInputStream *inst = new nsStringInputStream();
+ if (!inst)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(inst);
+ nsresult rv = inst->QueryInterface(iid, result);
+ NS_RELEASE(inst);
+
+ return rv;
+}
diff --git a/xpcom/io/nsStringStream.h b/xpcom/io/nsStringStream.h
new file mode 100644
index 000000000..5b5c66eef
--- /dev/null
+++ b/xpcom/io/nsStringStream.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 nsStringStream_h__
+#define nsStringStream_h__
+
+#include "nsIStringStream.h"
+#include "nsStringGlue.h"
+#include "nsMemory.h"
+
+/**
+ * Implements:
+ * nsIStringInputStream
+ * nsIInputStream
+ * nsISeekableStream
+ * nsISupportsCString
+ */
+#define NS_STRINGINPUTSTREAM_CONTRACTID "@mozilla.org/io/string-input-stream;1"
+#define NS_STRINGINPUTSTREAM_CID \
+{ /* 0abb0835-5000-4790-af28-61b3ba17c295 */ \
+ 0x0abb0835, \
+ 0x5000, \
+ 0x4790, \
+ {0xaf, 0x28, 0x61, 0xb3, 0xba, 0x17, 0xc2, 0x95} \
+}
+
+/**
+ * Factory method to get an nsInputStream from a byte buffer. Result will
+ * implement nsIStringInputStream and nsISeekableStream.
+ *
+ * If aAssignment is NS_ASSIGNMENT_COPY, then the resulting stream holds a copy
+ * of the given buffer (aStringToRead), and the caller is free to discard
+ * aStringToRead after this function returns.
+ *
+ * If aAssignment is NS_ASSIGNMENT_DEPEND, then the resulting stream refers
+ * directly to the given buffer (aStringToRead), so the caller must ensure that
+ * the buffer remains valid for the lifetime of the stream object. Use with
+ * care!!
+ *
+ * If aAssignment is NS_ASSIGNMENT_ADOPT, then the resulting stream refers
+ * directly to the given buffer (aStringToRead) and will free aStringToRead
+ * once the stream is closed.
+ *
+ * If aLength is less than zero, then the length of aStringToRead will be
+ * determined by scanning the buffer for the first null byte.
+ */
+extern nsresult
+NS_NewByteInputStream(nsIInputStream** aStreamResult,
+ const char* aStringToRead, int32_t aLength = -1,
+ nsAssignmentType aAssignment = NS_ASSIGNMENT_DEPEND);
+
+/**
+ * Factory method to get an nsInputStream from an nsAString. Result will
+ * implement nsIStringInputStream and nsISeekableStream.
+ *
+ * The given string data will be converted to a single-byte data buffer via
+ * truncation (i.e., the high-order byte of each character will be discarded).
+ * This could result in data-loss, so be careful when using this function.
+ */
+extern nsresult
+NS_NewStringInputStream(nsIInputStream** aStreamResult,
+ const nsAString& aStringToRead);
+
+/**
+ * Factory method to get an nsInputStream from an nsACString. Result will
+ * implement nsIStringInputStream and nsISeekableStream.
+ */
+extern nsresult
+NS_NewCStringInputStream(nsIInputStream** aStreamResult,
+ const nsACString& aStringToRead);
+
+#endif // nsStringStream_h__
diff --git a/xpcom/io/nsUnicharInputStream.cpp b/xpcom/io/nsUnicharInputStream.cpp
new file mode 100644
index 000000000..a1e76a4c3
--- /dev/null
+++ b/xpcom/io/nsUnicharInputStream.cpp
@@ -0,0 +1,434 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsUnicharInputStream.h"
+#include "nsIInputStream.h"
+#include "nsIByteBuffer.h"
+#include "nsIUnicharBuffer.h"
+#include "nsIServiceManager.h"
+#include "nsString.h"
+#include "nsAutoPtr.h"
+#include "nsCRT.h"
+#include "nsUTF8Utils.h"
+#include "mozilla/Attributes.h"
+#include <fcntl.h>
+#if defined(XP_WIN)
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+#define STRING_BUFFER_SIZE 8192
+
+class StringUnicharInputStream MOZ_FINAL : public nsIUnicharInputStream {
+public:
+ StringUnicharInputStream(const nsAString& aString) :
+ mString(aString), mPos(0), mLen(aString.Length()) { }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIUNICHARINPUTSTREAM
+
+ nsString mString;
+ uint32_t mPos;
+ uint32_t mLen;
+
+private:
+ ~StringUnicharInputStream() { }
+};
+
+NS_IMETHODIMP
+StringUnicharInputStream::Read(PRUnichar* aBuf,
+ uint32_t aCount,
+ uint32_t *aReadCount)
+{
+ if (mPos >= mLen) {
+ *aReadCount = 0;
+ return NS_OK;
+ }
+ nsAString::const_iterator iter;
+ mString.BeginReading(iter);
+ const PRUnichar* us = iter.get();
+ uint32_t amount = mLen - mPos;
+ if (amount > aCount) {
+ amount = aCount;
+ }
+ memcpy(aBuf, us + mPos, sizeof(PRUnichar) * amount);
+ mPos += amount;
+ *aReadCount = amount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+StringUnicharInputStream::ReadSegments(nsWriteUnicharSegmentFun aWriter,
+ void* aClosure,
+ uint32_t aCount, uint32_t *aReadCount)
+{
+ uint32_t bytesWritten;
+ uint32_t totalBytesWritten = 0;
+
+ nsresult rv;
+ aCount = XPCOM_MIN(mString.Length() - mPos, aCount);
+
+ nsAString::const_iterator iter;
+ mString.BeginReading(iter);
+
+ while (aCount) {
+ rv = aWriter(this, aClosure, iter.get() + mPos,
+ totalBytesWritten, aCount, &bytesWritten);
+
+ if (NS_FAILED(rv)) {
+ // don't propagate errors to the caller
+ break;
+ }
+
+ aCount -= bytesWritten;
+ totalBytesWritten += bytesWritten;
+ mPos += bytesWritten;
+ }
+
+ *aReadCount = totalBytesWritten;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+StringUnicharInputStream::ReadString(uint32_t aCount, nsAString& aString,
+ uint32_t* aReadCount)
+{
+ if (mPos >= mLen) {
+ *aReadCount = 0;
+ return NS_OK;
+ }
+ uint32_t amount = mLen - mPos;
+ if (amount > aCount) {
+ amount = aCount;
+ }
+ aString = Substring(mString, mPos, amount);
+ mPos += amount;
+ *aReadCount = amount;
+ return NS_OK;
+}
+
+nsresult StringUnicharInputStream::Close()
+{
+ mPos = mLen;
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS1(StringUnicharInputStream, nsIUnicharInputStream)
+
+//----------------------------------------------------------------------
+
+class UTF8InputStream MOZ_FINAL : public nsIUnicharInputStream {
+public:
+ UTF8InputStream();
+ nsresult Init(nsIInputStream* aStream);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIUNICHARINPUTSTREAM
+
+private:
+ ~UTF8InputStream();
+
+protected:
+ int32_t Fill(nsresult * aErrorCode);
+
+ static void CountValidUTF8Bytes(const char *aBuf, uint32_t aMaxBytes, uint32_t& aValidUTF8bytes, uint32_t& aValidUTF16CodeUnits);
+
+ nsCOMPtr<nsIInputStream> mInput;
+ nsCOMPtr<nsIByteBuffer> mByteData;
+ nsCOMPtr<nsIUnicharBuffer> mUnicharData;
+
+ uint32_t mByteDataOffset;
+ uint32_t mUnicharDataOffset;
+ uint32_t mUnicharDataLength;
+};
+
+UTF8InputStream::UTF8InputStream() :
+ mByteDataOffset(0),
+ mUnicharDataOffset(0),
+ mUnicharDataLength(0)
+{
+}
+
+nsresult
+UTF8InputStream::Init(nsIInputStream* aStream)
+{
+ nsresult rv = NS_NewByteBuffer(getter_AddRefs(mByteData), nullptr,
+ STRING_BUFFER_SIZE);
+ if (NS_FAILED(rv)) return rv;
+ rv = NS_NewUnicharBuffer(getter_AddRefs(mUnicharData), nullptr,
+ STRING_BUFFER_SIZE);
+ if (NS_FAILED(rv)) return rv;
+
+ mInput = aStream;
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS1(UTF8InputStream,nsIUnicharInputStream)
+
+UTF8InputStream::~UTF8InputStream()
+{
+ Close();
+}
+
+nsresult UTF8InputStream::Close()
+{
+ mInput = nullptr;
+ mByteData = nullptr;
+ mUnicharData = nullptr;
+
+ return NS_OK;
+}
+
+nsresult UTF8InputStream::Read(PRUnichar* aBuf,
+ uint32_t aCount,
+ uint32_t *aReadCount)
+{
+ NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
+ uint32_t readCount = mUnicharDataLength - mUnicharDataOffset;
+ nsresult errorCode;
+ if (0 == readCount) {
+ // Fill the unichar buffer
+ int32_t bytesRead = Fill(&errorCode);
+ if (bytesRead <= 0) {
+ *aReadCount = 0;
+ return errorCode;
+ }
+ readCount = bytesRead;
+ }
+ if (readCount > aCount) {
+ readCount = aCount;
+ }
+ memcpy(aBuf, mUnicharData->GetBuffer() + mUnicharDataOffset,
+ readCount * sizeof(PRUnichar));
+ mUnicharDataOffset += readCount;
+ *aReadCount = readCount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UTF8InputStream::ReadSegments(nsWriteUnicharSegmentFun aWriter,
+ void* aClosure,
+ uint32_t aCount, uint32_t *aReadCount)
+{
+ NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
+ uint32_t bytesToWrite = mUnicharDataLength - mUnicharDataOffset;
+ nsresult rv = NS_OK;
+ if (0 == bytesToWrite) {
+ // Fill the unichar buffer
+ int32_t bytesRead = Fill(&rv);
+ if (bytesRead <= 0) {
+ *aReadCount = 0;
+ return rv;
+ }
+ bytesToWrite = bytesRead;
+ }
+
+ if (bytesToWrite > aCount)
+ bytesToWrite = aCount;
+
+ uint32_t bytesWritten;
+ uint32_t totalBytesWritten = 0;
+
+ while (bytesToWrite) {
+ rv = aWriter(this, aClosure,
+ mUnicharData->GetBuffer() + mUnicharDataOffset,
+ totalBytesWritten, bytesToWrite, &bytesWritten);
+
+ if (NS_FAILED(rv)) {
+ // don't propagate errors to the caller
+ break;
+ }
+
+ bytesToWrite -= bytesWritten;
+ totalBytesWritten += bytesWritten;
+ mUnicharDataOffset += bytesWritten;
+ }
+
+ *aReadCount = totalBytesWritten;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UTF8InputStream::ReadString(uint32_t aCount, nsAString& aString,
+ uint32_t* aReadCount)
+{
+ NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
+ uint32_t readCount = mUnicharDataLength - mUnicharDataOffset;
+ nsresult errorCode;
+ if (0 == readCount) {
+ // Fill the unichar buffer
+ int32_t bytesRead = Fill(&errorCode);
+ if (bytesRead <= 0) {
+ *aReadCount = 0;
+ return errorCode;
+ }
+ readCount = bytesRead;
+ }
+ if (readCount > aCount) {
+ readCount = aCount;
+ }
+ const PRUnichar* buf = reinterpret_cast<const PRUnichar*>(mUnicharData->GetBuffer() +
+ mUnicharDataOffset);
+ aString.Assign(buf, readCount);
+
+ mUnicharDataOffset += readCount;
+ *aReadCount = readCount;
+ return NS_OK;
+}
+
+
+int32_t UTF8InputStream::Fill(nsresult * aErrorCode)
+{
+ if (nullptr == mInput) {
+ // We already closed the stream!
+ *aErrorCode = NS_BASE_STREAM_CLOSED;
+ return -1;
+ }
+
+ NS_ASSERTION(mByteData->GetLength() >= mByteDataOffset, "unsigned madness");
+ uint32_t remainder = mByteData->GetLength() - mByteDataOffset;
+ mByteDataOffset = remainder;
+ int32_t nb = mByteData->Fill(aErrorCode, mInput, remainder);
+ if (nb <= 0) {
+ // Because we assume a many to one conversion, the lingering data
+ // in the byte buffer must be a partial conversion
+ // fragment. Because we know that we have received no more new
+ // data to add to it, we can't convert it. Therefore, we discard
+ // it.
+ return nb;
+ }
+ NS_ASSERTION(remainder + nb == mByteData->GetLength(), "bad nb");
+
+ // Now convert as much of the byte buffer to unicode as possible
+ uint32_t srcLen, dstLen;
+ CountValidUTF8Bytes(mByteData->GetBuffer(),remainder + nb, srcLen, dstLen);
+
+ // the number of UCS2 characters should always be <= the number of
+ // UTF8 chars
+ NS_ASSERTION( (remainder+nb >= srcLen), "cannot be longer than out buffer");
+ NS_ASSERTION(int32_t(dstLen) <= mUnicharData->GetBufferSize(),
+ "Ouch. I would overflow my buffer if I wasn't so careful.");
+ if (int32_t(dstLen) > mUnicharData->GetBufferSize()) return 0;
+
+ ConvertUTF8toUTF16 converter(mUnicharData->GetBuffer());
+
+ nsASingleFragmentCString::const_char_iterator start = mByteData->GetBuffer();
+ nsASingleFragmentCString::const_char_iterator end = mByteData->GetBuffer() + srcLen;
+
+ copy_string(start, end, converter);
+ if (converter.Length() != dstLen) {
+ *aErrorCode = NS_BASE_STREAM_BAD_CONVERSION;
+ return -1;
+ }
+
+ mUnicharDataOffset = 0;
+ mUnicharDataLength = dstLen;
+ mByteDataOffset = srcLen;
+
+ return dstLen;
+}
+
+void
+UTF8InputStream::CountValidUTF8Bytes(const char* aBuffer, uint32_t aMaxBytes, uint32_t& aValidUTF8bytes, uint32_t& aValidUTF16CodeUnits)
+{
+ const char *c = aBuffer;
+ const char *end = aBuffer + aMaxBytes;
+ const char *lastchar = c; // pre-initialize in case of 0-length buffer
+ uint32_t utf16length = 0;
+ while (c < end && *c) {
+ lastchar = c;
+ utf16length++;
+
+ if (UTF8traits::isASCII(*c))
+ c++;
+ else if (UTF8traits::is2byte(*c))
+ c += 2;
+ else if (UTF8traits::is3byte(*c))
+ c += 3;
+ else if (UTF8traits::is4byte(*c)) {
+ c += 4;
+ utf16length++; // add 1 more because this will be converted to a
+ // surrogate pair.
+ }
+ else if (UTF8traits::is5byte(*c))
+ c += 5;
+ else if (UTF8traits::is6byte(*c))
+ c += 6;
+ else {
+ NS_WARNING("Unrecognized UTF8 string in UTF8InputStream::CountValidUTF8Bytes()");
+ break; // Otherwise we go into an infinite loop. But what happens now?
+ }
+ }
+ if (c > end) {
+ c = lastchar;
+ utf16length--;
+ }
+
+ aValidUTF8bytes = c - aBuffer;
+ aValidUTF16CodeUnits = utf16length;
+}
+
+NS_IMPL_QUERY_INTERFACE2(nsSimpleUnicharStreamFactory,
+ nsIFactory,
+ nsISimpleUnicharStreamFactory)
+
+NS_IMETHODIMP_(nsrefcnt) nsSimpleUnicharStreamFactory::AddRef() { return 2; }
+NS_IMETHODIMP_(nsrefcnt) nsSimpleUnicharStreamFactory::Release() { return 1; }
+
+NS_IMETHODIMP
+nsSimpleUnicharStreamFactory::CreateInstance(nsISupports* aOuter, REFNSIID aIID,
+ void **aResult)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsSimpleUnicharStreamFactory::LockFactory(bool aLock)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSimpleUnicharStreamFactory::CreateInstanceFromString(const nsAString& aString,
+ nsIUnicharInputStream* *aResult)
+{
+ StringUnicharInputStream* it = new StringUnicharInputStream(aString);
+ if (!it) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ NS_ADDREF(*aResult = it);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSimpleUnicharStreamFactory::CreateInstanceFromUTF8Stream(nsIInputStream* aStreamToWrap,
+ nsIUnicharInputStream* *aResult)
+{
+ *aResult = nullptr;
+
+ // Create converter input stream
+ nsRefPtr<UTF8InputStream> it = new UTF8InputStream();
+ if (!it)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv = it->Init(aStreamToWrap);
+ if (NS_FAILED(rv))
+ return rv;
+
+ NS_ADDREF(*aResult = it);
+ return NS_OK;
+}
+
+nsSimpleUnicharStreamFactory*
+nsSimpleUnicharStreamFactory::GetInstance()
+{
+ static const nsSimpleUnicharStreamFactory kInstance;
+ return const_cast<nsSimpleUnicharStreamFactory*>(&kInstance);
+}
diff --git a/xpcom/io/nsUnicharInputStream.h b/xpcom/io/nsUnicharInputStream.h
new file mode 100644
index 000000000..9b8e6c004
--- /dev/null
+++ b/xpcom/io/nsUnicharInputStream.h
@@ -0,0 +1,29 @@
+/* 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 nsUnicharInputStream_h__
+#define nsUnicharInputStream_h__
+
+#include "nsISimpleUnicharStreamFactory.h"
+#include "nsIUnicharInputStream.h"
+#include "nsIFactory.h"
+
+// {428DCA6F-1A0F-4cda-B516-0D5244745A6A}
+#define NS_SIMPLE_UNICHAR_STREAM_FACTORY_CID \
+{ 0x428dca6f, 0x1a0f, 0x4cda, { 0xb5, 0x16, 0xd, 0x52, 0x44, 0x74, 0x5a, 0x6a } }
+
+class nsSimpleUnicharStreamFactory :
+ public nsIFactory,
+ private nsISimpleUnicharStreamFactory
+{
+public:
+ nsSimpleUnicharStreamFactory() {}
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIFACTORY
+ NS_DECL_NSISIMPLEUNICHARSTREAMFACTORY
+
+ static nsSimpleUnicharStreamFactory* GetInstance();
+};
+
+#endif // nsUnicharInputStream_h__
diff --git a/xpcom/io/nsWildCard.cpp b/xpcom/io/nsWildCard.cpp
new file mode 100644
index 000000000..3c228daff
--- /dev/null
+++ b/xpcom/io/nsWildCard.cpp
@@ -0,0 +1,439 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set ts=4 sts=4 sw=4 cin et: */
+/* 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/. */
+
+/* *
+ *
+ *
+ * nsWildCard.cpp: shell-like wildcard match routines
+ *
+ * See nsIZipReader.findEntries documentation in nsIZipReader.idl for
+ * a description of the syntax supported by the routines in this file.
+ *
+ * Rob McCool
+ *
+ */
+
+#include "nsWildCard.h"
+#include "nsXPCOM.h"
+#include "nsCRTGlue.h"
+#include "nsCharTraits.h"
+
+/* -------------------- ASCII-specific character methods ------------------- */
+
+typedef int static_assert_character_code_arrangement['a' > 'A' ? 1 : -1];
+
+template<class T>
+static int
+alpha(T c)
+{
+ return ('a' <= c && c <= 'z') ||
+ ('A' <= c && c <= 'Z');
+}
+
+template<class T>
+static int
+alphanumeric(T c)
+{
+ return ('0' <= c && c <= '9') || ::alpha(c);
+}
+
+template<class T>
+static int
+lower(T c)
+{
+ return ('A' <= c && c <= 'Z') ? c + ('a' - 'A') : c;
+}
+
+template<class T>
+static int
+upper(T c)
+{
+ return ('a' <= c && c <= 'z') ? c - ('a' - 'A') : c;
+}
+
+/* ----------------------------- _valid_subexp ---------------------------- */
+
+template<class T>
+static int
+_valid_subexp(const T *expr, T stop1, T stop2)
+{
+ register int x;
+ int nsc = 0; /* Number of special characters */
+ int np; /* Number of pipe characters in union */
+ int tld = 0; /* Number of tilde characters */
+
+ for (x = 0; expr[x] && (expr[x] != stop1) && (expr[x] != stop2); ++x) {
+ switch(expr[x]) {
+ case '~':
+ if(tld) /* at most one exclusion */
+ return INVALID_SXP;
+ if (stop1) /* no exclusions within unions */
+ return INVALID_SXP;
+ if (!expr[x+1]) /* exclusion cannot be last character */
+ return INVALID_SXP;
+ if (!x) /* exclusion cannot be first character */
+ return INVALID_SXP;
+ ++tld;
+ /* fall through */
+ case '*':
+ case '?':
+ case '$':
+ ++nsc;
+ break;
+ case '[':
+ ++nsc;
+ if((!expr[++x]) || (expr[x] == ']'))
+ return INVALID_SXP;
+ for(; expr[x] && (expr[x] != ']'); ++x) {
+ if(expr[x] == '\\' && !expr[++x])
+ return INVALID_SXP;
+ }
+ if(!expr[x])
+ return INVALID_SXP;
+ break;
+ case '(':
+ ++nsc;
+ if (stop1) /* no nested unions */
+ return INVALID_SXP;
+ np = -1;
+ do {
+ int t = ::_valid_subexp(&expr[++x], T(')'), T('|'));
+ if(t == 0 || t == INVALID_SXP)
+ return INVALID_SXP;
+ x+=t;
+ if(!expr[x])
+ return INVALID_SXP;
+ ++np;
+ } while (expr[x] == '|' );
+ if(np < 1) /* must be at least one pipe */
+ return INVALID_SXP;
+ break;
+ case ')':
+ case ']':
+ case '|':
+ return INVALID_SXP;
+ case '\\':
+ ++nsc;
+ if(!expr[++x])
+ return INVALID_SXP;
+ break;
+ default:
+ break;
+ }
+ }
+ if((!stop1) && (!nsc)) /* must be at least one special character */
+ return NON_SXP;
+ return ((expr[x] == stop1 || expr[x] == stop2) ? x : INVALID_SXP);
+}
+
+
+template<class T>
+int
+NS_WildCardValid_(const T *expr)
+{
+ int x = ::_valid_subexp(expr, T('\0'), T('\0'));
+ return (x < 0 ? x : VALID_SXP);
+}
+
+int
+NS_WildCardValid(const char *expr)
+{
+ return NS_WildCardValid_(expr);
+}
+
+int
+NS_WildCardValid(const PRUnichar *expr)
+{
+ return NS_WildCardValid_(expr);
+}
+
+/* ----------------------------- _shexp_match ----------------------------- */
+
+
+#define MATCH 0
+#define NOMATCH 1
+#define ABORTED -1
+
+template<class T>
+static int
+_shexp_match(const T *str, const T *expr, bool case_insensitive, unsigned int level);
+
+/**
+ * Count characters until we reach a NUL character or either of the
+ * two delimiter characters, stop1 or stop2. If we encounter a bracketed
+ * expression, look only for NUL or ']' inside it. Do not look for stop1
+ * or stop2 inside it. Return ABORTED if bracketed expression is unterminated.
+ * Handle all escaping.
+ * Return index in input string of first stop found, or ABORTED if not found.
+ * If "dest" is non-NULL, copy counted characters to it and NUL terminate.
+ */
+template<class T>
+static int
+_scan_and_copy(const T *expr, T stop1, T stop2, T *dest)
+{
+ register int sx; /* source index */
+ register T cc;
+
+ for (sx = 0; (cc = expr[sx]) && cc != stop1 && cc != stop2; sx++) {
+ if (cc == '\\') {
+ if (!expr[++sx])
+ return ABORTED; /* should be impossible */
+ }
+ else if (cc == '[') {
+ while ((cc = expr[++sx]) && cc != ']') {
+ if(cc == '\\' && !expr[++sx])
+ return ABORTED;
+ }
+ if (!cc)
+ return ABORTED; /* should be impossible */
+ }
+ }
+ if (dest && sx) {
+ /* Copy all but the closing delimiter. */
+ memcpy(dest, expr, sx * sizeof(T));
+ dest[sx] = 0;
+ }
+ return cc ? sx : ABORTED; /* index of closing delimiter */
+}
+
+/* On input, expr[0] is the opening parenthesis of a union.
+ * See if any of the alternatives in the union matches as a pattern.
+ * The strategy is to take each of the alternatives, in turn, and append
+ * the rest of the expression (after the closing ')' that marks the end of
+ * this union) to that alternative, and then see if the resultant expression
+ * matches the input string. Repeat this until some alternative matches,
+ * or we have an abort.
+ */
+template<class T>
+static int
+_handle_union(const T *str, const T *expr, bool case_insensitive,
+ unsigned int level)
+{
+ register int sx; /* source index */
+ int cp; /* source index of closing parenthesis */
+ int count;
+ int ret = NOMATCH;
+ T *e2;
+
+ /* Find the closing parenthesis that ends this union in the expression */
+ cp = ::_scan_and_copy(expr, T(')'), T('\0'), static_cast<T*>(NULL));
+ if (cp == ABORTED || cp < 4) /* must be at least "(a|b" before ')' */
+ return ABORTED;
+ ++cp; /* now index of char after closing parenthesis */
+ e2 = (T *) NS_Alloc((1 + nsCharTraits<T>::length(expr)) * sizeof(T));
+ if (!e2)
+ return ABORTED;
+ for (sx = 1; ; ++sx) {
+ /* Here, expr[sx] is one character past the preceding '(' or '|'. */
+ /* Copy everything up to the next delimiter to e2 */
+ count = ::_scan_and_copy(expr + sx, T(')'), T('|'), e2);
+ if (count == ABORTED || !count) {
+ ret = ABORTED;
+ break;
+ }
+ sx += count;
+ /* Append everything after closing parenthesis to e2. This is safe. */
+ nsCharTraits<T>::copy(e2 + count, expr + cp, nsCharTraits<T>::length(expr + cp) + 1);
+ ret = ::_shexp_match(str, e2, case_insensitive, level + 1);
+ if (ret != NOMATCH || !expr[sx] || expr[sx] == ')')
+ break;
+ }
+ NS_Free(e2);
+ if (sx < 2)
+ ret = ABORTED;
+ return ret;
+}
+
+/* returns 1 if val is in range from start..end, case insensitive. */
+static int
+_is_char_in_range(unsigned char start, unsigned char end, unsigned char val)
+{
+ char map[256];
+ memset(map, 0, sizeof map);
+ while (start <= end)
+ map[lower(start++)] = 1;
+ return map[lower(val)];
+}
+
+template<class T>
+static int
+_shexp_match(const T *str, const T *expr, bool case_insensitive,
+ unsigned int level)
+{
+ register int x; /* input string index */
+ register int y; /* expression index */
+ int ret,neg;
+
+ if (level > 20) /* Don't let the stack get too deep. */
+ return ABORTED;
+ for(x = 0, y = 0; expr[y]; ++y, ++x) {
+ if((!str[x]) && (expr[y] != '$') && (expr[y] != '*')) {
+ return NOMATCH;
+ }
+ switch(expr[y]) {
+ case '$':
+ if(str[x])
+ return NOMATCH;
+ --x; /* we don't want loop to increment x */
+ break;
+ case '*':
+ while(expr[++y] == '*'){}
+ if(!expr[y])
+ return MATCH;
+ while(str[x]) {
+ ret = ::_shexp_match(&str[x++], &expr[y], case_insensitive,
+ level + 1);
+ switch(ret) {
+ case NOMATCH:
+ continue;
+ case ABORTED:
+ return ABORTED;
+ default:
+ return MATCH;
+ }
+ }
+ if((expr[y] == '$') && (expr[y+1] == '\0') && (!str[x]))
+ return MATCH;
+ else
+ return NOMATCH;
+ case '[': {
+ T start, end = 0;
+ int i;
+ neg = ((expr[++y] == '^') && (expr[y+1] != ']'));
+ if (neg)
+ ++y;
+ i = y;
+ start = expr[i++];
+ if (start == '\\')
+ start = expr[i++];
+ if (::alphanumeric(start) && expr[i++] == '-') {
+ end = expr[i++];
+ if (end == '\\')
+ end = expr[i++];
+ }
+ if (::alphanumeric(end) && expr[i] == ']') {
+ /* This is a range form: a-b */
+ T val = str[x];
+ if (end < start) { /* swap them */
+ T tmp = end;
+ end = start;
+ start = tmp;
+ }
+ if (case_insensitive && ::alpha(val)) {
+ val = ::_is_char_in_range((unsigned char) start,
+ (unsigned char) end,
+ (unsigned char) val);
+ if (neg == val)
+ return NOMATCH;
+ }
+ else if (neg != ((val < start) || (val > end))) {
+ return NOMATCH;
+ }
+ y = i;
+ }
+ else {
+ /* Not range form */
+ int matched = 0;
+ for (; expr[y] != ']'; y++) {
+ if (expr[y] == '\\')
+ ++y;
+ if(case_insensitive)
+ matched |= (::upper(str[x]) == ::upper(expr[y]));
+ else
+ matched |= (str[x] == expr[y]);
+ }
+ if (neg == matched)
+ return NOMATCH;
+ }
+ }
+ break;
+ case '(':
+ if (!expr[y+1])
+ return ABORTED;
+ return ::_handle_union(&str[x], &expr[y], case_insensitive, level + 1);
+ case '?':
+ break;
+ case ')':
+ case ']':
+ case '|':
+ return ABORTED;
+ case '\\':
+ ++y;
+ /* fall through */
+ default:
+ if(case_insensitive) {
+ if(::upper(str[x]) != ::upper(expr[y]))
+ return NOMATCH;
+ }
+ else {
+ if(str[x] != expr[y])
+ return NOMATCH;
+ }
+ break;
+ }
+ }
+ return (str[x] ? NOMATCH : MATCH);
+}
+
+
+template<class T>
+static int
+ns_WildCardMatch(const T *str, const T *xp, bool case_insensitive)
+{
+ T *expr = NULL;
+ int x, ret = MATCH;
+
+ if (!nsCharTraits<T>::find(xp, nsCharTraits<T>::length(xp), T('~')))
+ return ::_shexp_match(str, xp, case_insensitive, 0);
+
+ expr = (T *) NS_Alloc((nsCharTraits<T>::length(xp) + 1) * sizeof(T));
+ if(!expr)
+ return NOMATCH;
+ memcpy(expr, xp, (nsCharTraits<T>::length(xp) + 1) * sizeof(T));
+
+ x = ::_scan_and_copy(expr, T('~'), T('\0'), static_cast<T*>(NULL));
+ if (x != ABORTED && expr[x] == '~') {
+ expr[x++] = '\0';
+ ret = ::_shexp_match(str, &expr[x], case_insensitive, 0);
+ switch (ret) {
+ case NOMATCH: ret = MATCH; break;
+ case MATCH: ret = NOMATCH; break;
+ default: break;
+ }
+ }
+ if (ret == MATCH)
+ ret = ::_shexp_match(str, expr, case_insensitive, 0);
+
+ NS_Free(expr);
+ return ret;
+}
+
+template<class T>
+int
+NS_WildCardMatch_(const T *str, const T *expr, bool case_insensitive)
+{
+ int is_valid = NS_WildCardValid(expr);
+ switch(is_valid) {
+ case INVALID_SXP:
+ return -1;
+ default:
+ return ::ns_WildCardMatch(str, expr, case_insensitive);
+ }
+}
+
+int
+NS_WildCardMatch(const char *str, const char *xp,
+ bool case_insensitive)
+{
+ return NS_WildCardMatch_(str, xp, case_insensitive);
+}
+
+int
+NS_WildCardMatch(const PRUnichar *str, const PRUnichar *xp,
+ bool case_insensitive)
+{
+ return NS_WildCardMatch_(str, xp, case_insensitive);
+}
diff --git a/xpcom/io/nsWildCard.h b/xpcom/io/nsWildCard.h
new file mode 100644
index 000000000..ef6fd349d
--- /dev/null
+++ b/xpcom/io/nsWildCard.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/*
+ * nsWildCard.h: Defines and prototypes for shell exp. match routines
+ *
+ * See nsIZipReader.findEntries docs in nsIZipReader.idl for a description of
+ * the supported expression syntax.
+ *
+ * Note that the syntax documentation explicitly says the results of certain
+ * expressions are undefined. This is intentional to require less robustness
+ * in the code. Regular expression parsing is hard; the smaller the set of
+ * features and interactions this code must support, the easier it is to
+ * ensure it works.
+ *
+ */
+
+#ifndef nsWildCard_h__
+#define nsWildCard_h__
+
+#include "prtypes.h"
+#include "nscore.h"
+
+/* --------------------------- Public routines ---------------------------- */
+
+
+/*
+ * NS_WildCardValid takes a shell expression exp as input. It returns:
+ *
+ * NON_SXP if exp is a standard string
+ * INVALID_SXP if exp is a shell expression, but invalid
+ * VALID_SXP if exp is a valid shell expression
+ */
+
+#define NON_SXP -1
+#define INVALID_SXP -2
+#define VALID_SXP 1
+
+int NS_WildCardValid(const char *expr);
+
+int NS_WildCardValid(const PRUnichar *expr);
+
+/* return values for the search routines */
+#define MATCH 0
+#define NOMATCH 1
+#define ABORTED -1
+
+/*
+ * NS_WildCardMatch
+ *
+ * Takes a prevalidated shell expression exp, and a string str.
+ *
+ * Returns 0 on match and 1 on non-match.
+ */
+
+int NS_WildCardMatch(const char *str, const char *expr,
+ bool case_insensitive);
+
+int NS_WildCardMatch(const PRUnichar *str, const PRUnichar *expr,
+ bool case_insensitive);
+
+#endif /* nsWildCard_h__ */