import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

645
xpcom/io/Base64.cpp Normal file
View file

@ -0,0 +1,645 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "mozilla/UniquePtrExtensions.h"
#include "nsIInputStream.h"
#include "nsString.h"
#include "nsTArray.h"
#include "plbase64.h"
namespace {
// BEGIN base64 encode code copied and modified from NSPR
const unsigned char* base =
(unsigned char*)"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
template<typename T>
static void
Encode3to4(const unsigned char* aSrc, T* aDest)
{
uint32_t b32 = (uint32_t)0;
int i, j = 18;
for (i = 0; i < 3; ++i) {
b32 <<= 8;
b32 |= (uint32_t)aSrc[i];
}
for (i = 0; i < 4; ++i) {
aDest[i] = base[(uint32_t)((b32 >> j) & 0x3F)];
j -= 6;
}
}
template<typename T>
static void
Encode2to4(const unsigned char* aSrc, T* aDest)
{
aDest[0] = base[(uint32_t)((aSrc[0] >> 2) & 0x3F)];
aDest[1] = base[(uint32_t)(((aSrc[0] & 0x03) << 4) | ((aSrc[1] >> 4) & 0x0F))];
aDest[2] = base[(uint32_t)((aSrc[1] & 0x0F) << 2)];
aDest[3] = (unsigned char)'=';
}
template<typename T>
static void
Encode1to4(const unsigned char* aSrc, T* aDest)
{
aDest[0] = base[(uint32_t)((aSrc[0] >> 2) & 0x3F)];
aDest[1] = base[(uint32_t)((aSrc[0] & 0x03) << 4)];
aDest[2] = (unsigned char)'=';
aDest[3] = (unsigned char)'=';
}
template<typename T>
static void
Encode(const unsigned char* aSrc, uint32_t aSrcLen, T* aDest)
{
while (aSrcLen >= 3) {
Encode3to4(aSrc, aDest);
aSrc += 3;
aDest += 4;
aSrcLen -= 3;
}
switch (aSrcLen) {
case 2:
Encode2to4(aSrc, aDest);
break;
case 1:
Encode1to4(aSrc, aDest);
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>
nsresult
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;
MOZ_ASSERT(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.
MOZ_ASSERT(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);
if (NS_WARN_IF(NS_FAILED(rv))) {
return 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);
if (!aDest.SetLength(count + aOffset, mozilla::fallible)) {
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);
}
if (aDest.Length()) {
// May belong to an nsCString with an unallocated buffer, so only null
// terminate if there is a need to.
*aDest.EndWriting() = '\0';
}
return NS_OK;
}
static const char kBase64URLAlphabet[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
// Maps an encoded character to a value in the Base64 URL alphabet, per
// RFC 4648, Table 2. Invalid input characters map to UINT8_MAX.
static const uint8_t kBase64URLDecodeTable[] = {
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255,
62 /* - */,
255, 255,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, /* 0 - 9 */
255, 255, 255, 255, 255, 255, 255,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, /* A - Z */
255, 255, 255, 255,
63 /* _ */,
255,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
42, 43, 44, 45, 46, 47, 48, 49, 50, 51, /* a - z */
255, 255, 255, 255,
};
bool
Base64URLCharToValue(char aChar, uint8_t* aValue) {
uint8_t index = static_cast<uint8_t>(aChar);
*aValue = kBase64URLDecodeTable[index & 0x7f];
return (*aValue != 255) && !(index & ~0x7f);
}
} // namespace
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 char* aBinary, uint32_t aBinaryLen, char** aBase64)
{
// Check for overflow.
if (aBinaryLen > (UINT32_MAX / 4) * 3) {
return NS_ERROR_FAILURE;
}
// Don't ask PR_Base64Encode to encode empty strings.
if (aBinaryLen == 0) {
*aBase64 = (char*)moz_xmalloc(1);
(*aBase64)[0] = '\0';
return NS_OK;
}
*aBase64 = nullptr;
uint32_t base64Len = ((aBinaryLen + 2) / 3) * 4;
// Add one byte for null termination.
UniqueFreePtr<char[]> base64((char*)malloc(base64Len + 1));
if (!base64) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (!PL_Base64Encode(aBinary, aBinaryLen, base64.get())) {
return NS_ERROR_INVALID_ARG;
}
// PL_Base64Encode doesn't null terminate the buffer for us when we pass
// the buffer in. Do that manually.
base64[base64Len] = '\0';
*aBase64 = base64.release();
return NS_OK;
}
nsresult
Base64Encode(const nsACString& aBinary, nsACString& aBase64)
{
// Check for overflow.
if (aBinary.Length() > (UINT32_MAX / 4) * 3) {
return NS_ERROR_FAILURE;
}
// Don't ask PR_Base64Encode to encode empty strings.
if (aBinary.IsEmpty()) {
aBase64.Truncate();
return NS_OK;
}
uint32_t base64Len = ((aBinary.Length() + 2) / 3) * 4;
// Add one byte for null termination.
if (!aBase64.SetCapacity(base64Len + 1, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
char* base64 = aBase64.BeginWriting();
if (!PL_Base64Encode(aBinary.BeginReading(), aBinary.Length(), base64)) {
aBase64.Truncate();
return NS_ERROR_INVALID_ARG;
}
// PL_Base64Encode doesn't null terminate the buffer for us when we pass
// the buffer in. Do that manually.
base64[base64Len] = '\0';
aBase64.SetLength(base64Len);
return NS_OK;
}
nsresult
Base64Encode(const nsAString& aBinary, nsAString& aBase64)
{
NS_LossyConvertUTF16toASCII binary(aBinary);
nsAutoCString base64;
nsresult rv = Base64Encode(binary, base64);
if (NS_SUCCEEDED(rv)) {
CopyASCIItoUTF16(base64, aBase64);
} else {
aBase64.Truncate();
}
return rv;
}
static nsresult
Base64DecodeHelper(const char* aBase64, uint32_t aBase64Len, char* aBinary,
uint32_t* aBinaryLen)
{
MOZ_ASSERT(aBinary);
if (!PL_Base64Decode(aBase64, aBase64Len, aBinary)) {
return NS_ERROR_INVALID_ARG;
}
// 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 (aBase64Len != 0 && aBase64[aBase64Len - 1] == '=') {
if (aBase64Len > 1 && aBase64[aBase64Len - 2] == '=') {
*aBinaryLen -= 2;
} else {
*aBinaryLen -= 1;
}
}
aBinary[*aBinaryLen] = '\0';
return NS_OK;
}
nsresult
Base64Decode(const char* aBase64, uint32_t aBase64Len, char** aBinary,
uint32_t* aBinaryLen)
{
// Check for overflow.
if (aBase64Len > UINT32_MAX / 3) {
return NS_ERROR_FAILURE;
}
// Don't ask PR_Base64Decode to decode the empty string.
if (aBase64Len == 0) {
*aBinary = (char*)moz_xmalloc(1);
(*aBinary)[0] = '\0';
*aBinaryLen = 0;
return NS_OK;
}
*aBinary = nullptr;
*aBinaryLen = (aBase64Len * 3) / 4;
// Add one byte for null termination.
UniqueFreePtr<char[]> binary((char*)malloc(*aBinaryLen + 1));
if (!binary) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsresult rv =
Base64DecodeHelper(aBase64, aBase64Len, binary.get(), aBinaryLen);
if (NS_FAILED(rv)) {
return rv;
}
*aBinary = binary.release();
return NS_OK;
}
nsresult
Base64Decode(const nsACString& aBase64, nsACString& aBinary)
{
// Check for overflow.
if (aBase64.Length() > UINT32_MAX / 3) {
return NS_ERROR_FAILURE;
}
// Don't ask PR_Base64Decode to decode the empty string
if (aBase64.IsEmpty()) {
aBinary.Truncate();
return NS_OK;
}
uint32_t binaryLen = ((aBase64.Length() * 3) / 4);
// Add one byte for null termination.
if (!aBinary.SetCapacity(binaryLen + 1, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
char* binary = aBinary.BeginWriting();
nsresult rv = Base64DecodeHelper(aBase64.BeginReading(), aBase64.Length(),
binary, &binaryLen);
if (NS_FAILED(rv)) {
aBinary.Truncate();
return rv;
}
aBinary.SetLength(binaryLen);
return NS_OK;
}
nsresult
Base64Decode(const nsAString& aBase64, nsAString& aBinary)
{
NS_LossyConvertUTF16toASCII base64(aBase64);
nsAutoCString binary;
nsresult rv = Base64Decode(base64, binary);
if (NS_SUCCEEDED(rv)) {
CopyASCIItoUTF16(binary, aBinary);
} else {
aBinary.Truncate();
}
return rv;
}
nsresult
Base64URLDecode(const nsACString& aBase64,
Base64URLDecodePaddingPolicy aPaddingPolicy,
FallibleTArray<uint8_t>& aBinary)
{
// Don't decode empty strings.
if (aBase64.IsEmpty()) {
aBinary.Clear();
return NS_OK;
}
// Check for overflow.
uint32_t base64Len = aBase64.Length();
if (base64Len > UINT32_MAX / 3) {
return NS_ERROR_FAILURE;
}
const char* base64 = aBase64.BeginReading();
// The decoded length may be 1-2 bytes over, depending on the final quantum.
uint32_t binaryLen = (base64Len * 3) / 4;
// Determine whether to check for and ignore trailing padding.
bool maybePadded = false;
switch (aPaddingPolicy) {
case Base64URLDecodePaddingPolicy::Require:
if (base64Len % 4) {
// Padded input length must be a multiple of 4.
return NS_ERROR_INVALID_ARG;
}
maybePadded = true;
break;
case Base64URLDecodePaddingPolicy::Ignore:
// Check for padding only if the length is a multiple of 4.
maybePadded = !(base64Len % 4);
break;
// If we're expecting unpadded input, no need for additional checks.
// `=` isn't in the decode table, so padded strings will fail to decode.
default:
MOZ_FALLTHROUGH_ASSERT("Invalid decode padding policy");
case Base64URLDecodePaddingPolicy::Reject:
break;
}
if (maybePadded && base64[base64Len - 1] == '=') {
if (base64[base64Len - 2] == '=') {
base64Len -= 2;
} else {
base64Len -= 1;
}
}
if (NS_WARN_IF(!aBinary.SetCapacity(binaryLen, mozilla::fallible))) {
return NS_ERROR_OUT_OF_MEMORY;
}
aBinary.SetLengthAndRetainStorage(binaryLen);
uint8_t* binary = aBinary.Elements();
for (; base64Len >= 4; base64Len -= 4) {
uint8_t w, x, y, z;
if (!Base64URLCharToValue(*base64++, &w) ||
!Base64URLCharToValue(*base64++, &x) ||
!Base64URLCharToValue(*base64++, &y) ||
!Base64URLCharToValue(*base64++, &z)) {
return NS_ERROR_INVALID_ARG;
}
*binary++ = w << 2 | x >> 4;
*binary++ = x << 4 | y >> 2;
*binary++ = y << 6 | z;
}
if (base64Len == 3) {
uint8_t w, x, y;
if (!Base64URLCharToValue(*base64++, &w) ||
!Base64URLCharToValue(*base64++, &x) ||
!Base64URLCharToValue(*base64++, &y)) {
return NS_ERROR_INVALID_ARG;
}
*binary++ = w << 2 | x >> 4;
*binary++ = x << 4 | y >> 2;
} else if (base64Len == 2) {
uint8_t w, x;
if (!Base64URLCharToValue(*base64++, &w) ||
!Base64URLCharToValue(*base64++, &x)) {
return NS_ERROR_INVALID_ARG;
}
*binary++ = w << 2 | x >> 4;
} else if (base64Len) {
return NS_ERROR_INVALID_ARG;
}
// Set the length to the actual number of decoded bytes.
aBinary.TruncateLength(binary - aBinary.Elements());
return NS_OK;
}
nsresult
Base64URLEncode(uint32_t aBinaryLen, const uint8_t* aBinary,
Base64URLEncodePaddingPolicy aPaddingPolicy,
nsACString& aBase64)
{
// Don't encode empty strings.
if (aBinaryLen == 0) {
aBase64.Truncate();
return NS_OK;
}
// Check for overflow.
if (aBinaryLen > (UINT32_MAX / 4) * 3) {
return NS_ERROR_FAILURE;
}
// Allocate a buffer large enough to hold the encoded string with padding.
// Add one byte for null termination.
uint32_t base64Len = ((aBinaryLen + 2) / 3) * 4;
if (NS_WARN_IF(!aBase64.SetCapacity(base64Len + 1, fallible))) {
aBase64.Truncate();
return NS_ERROR_FAILURE;
}
char* base64 = aBase64.BeginWriting();
uint32_t index = 0;
for (; index + 3 <= aBinaryLen; index += 3) {
*base64++ = kBase64URLAlphabet[aBinary[index] >> 2];
*base64++ = kBase64URLAlphabet[((aBinary[index] & 0x3) << 4) |
(aBinary[index + 1] >> 4)];
*base64++ = kBase64URLAlphabet[((aBinary[index + 1] & 0xf) << 2) |
(aBinary[index + 2] >> 6)];
*base64++ = kBase64URLAlphabet[aBinary[index + 2] & 0x3f];
}
uint32_t remaining = aBinaryLen - index;
if (remaining == 1) {
*base64++ = kBase64URLAlphabet[aBinary[index] >> 2];
*base64++ = kBase64URLAlphabet[((aBinary[index] & 0x3) << 4)];
} else if (remaining == 2) {
*base64++ = kBase64URLAlphabet[aBinary[index] >> 2];
*base64++ = kBase64URLAlphabet[((aBinary[index] & 0x3) << 4) |
(aBinary[index + 1] >> 4)];
*base64++ = kBase64URLAlphabet[((aBinary[index + 1] & 0xf) << 2)];
}
uint32_t length = base64 - aBase64.BeginWriting();
if (aPaddingPolicy == Base64URLEncodePaddingPolicy::Include) {
if (length % 4 == 2) {
*base64++ = '=';
*base64++ = '=';
length += 2;
} else if (length % 4 == 3) {
*base64++ = '=';
length += 1;
}
} else {
MOZ_ASSERT(aPaddingPolicy == Base64URLEncodePaddingPolicy::Omit,
"Invalid encode padding policy");
}
// Null terminate and truncate to the actual number of characters.
*base64 = '\0';
aBase64.SetLength(length);
return NS_OK;
}
} // namespace mozilla

73
xpcom/io/Base64.h Normal file
View file

@ -0,0 +1,73 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 {
MOZ_MUST_USE nsresult
Base64EncodeInputStream(nsIInputStream* aInputStream,
nsACString& aDest,
uint32_t aCount,
uint32_t aOffset = 0);
MOZ_MUST_USE nsresult
Base64EncodeInputStream(nsIInputStream* aInputStream,
nsAString& aDest,
uint32_t aCount,
uint32_t aOffset = 0);
MOZ_MUST_USE nsresult
Base64Encode(const char* aBinary, uint32_t aBinaryLen, char** aBase64);
MOZ_MUST_USE nsresult
Base64Encode(const nsACString& aBinary, nsACString& aBase64);
MOZ_MUST_USE nsresult
Base64Encode(const nsAString& aBinary, nsAString& aBase64);
MOZ_MUST_USE nsresult
Base64Decode(const char* aBase64, uint32_t aBase64Len, char** aBinary,
uint32_t* aBinaryLen);
MOZ_MUST_USE nsresult
Base64Decode(const nsACString& aBase64, nsACString& aBinary);
MOZ_MUST_USE nsresult
Base64Decode(const nsAString& aBase64, nsAString& aBinary);
enum class Base64URLEncodePaddingPolicy {
Include,
Omit,
};
/**
* Converts |aBinary| to an unpadded, Base64 URL-encoded string per RFC 4648.
* Aims to encode the data in constant time. The caller retains ownership
* of |aBinary|.
*/
MOZ_MUST_USE nsresult
Base64URLEncode(uint32_t aBinaryLen, const uint8_t* aBinary,
Base64URLEncodePaddingPolicy aPaddingPolicy,
nsACString& aBase64);
enum class Base64URLDecodePaddingPolicy {
Require,
Ignore,
Reject,
};
/**
* Decodes a Base64 URL-encoded |aBase64| into |aBinary|.
*/
MOZ_MUST_USE nsresult
Base64URLDecode(const nsACString& aBase64,
Base64URLDecodePaddingPolicy aPaddingPolicy,
FallibleTArray<uint8_t>& aBinary);
} // namespace mozilla
#endif

35
xpcom/io/CocoaFileUtils.h Normal file
View file

@ -0,0 +1,35 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 aUrl);
nsresult OpenURL(CFURLRef aUrl);
nsresult GetFileCreatorCode(CFURLRef aUrl, OSType* aCreatorCode);
nsresult SetFileCreatorCode(CFURLRef aUrl, OSType aCreatorCode);
nsresult GetFileTypeCode(CFURLRef aUrl, OSType* aTypeCode);
nsresult SetFileTypeCode(CFURLRef aUrl, OSType aTypeCode);
void AddOriginMetadataToFile(const CFStringRef filePath,
const CFURLRef sourceURL,
const CFURLRef referrerURL);
void AddQuarantineMetadataToFile(const CFStringRef filePath,
const CFURLRef sourceURL,
const CFURLRef referrerURL,
const bool isFromWeb);
CFURLRef GetTemporaryFolderCFURLRef();
} // namespace CocoaFileUtils
#endif

267
xpcom/io/CocoaFileUtils.mm Normal file
View file

@ -0,0 +1,267 @@
/* -*- 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 "nsCocoaFeatures.h"
#include "nsCocoaUtils.h"
#include <Cocoa/Cocoa.h>
#include "nsObjCExceptions.h"
#include "nsDebug.h"
// Need to cope with us using old versions of the SDK and needing this on 10.10+
#if !defined(MAC_OS_X_VERSION_10_10) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10)
const CFStringRef kCFURLQuarantinePropertiesKey = CFSTR("NSURLQuarantinePropertiesKey");
#endif
namespace CocoaFileUtils {
nsresult RevealFileInFinder(CFURLRef url)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
if (NS_WARN_IF(!url))
return NS_ERROR_INVALID_ARG;
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;
if (NS_WARN_IF(!url))
return NS_ERROR_INVALID_ARG;
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;
if (NS_WARN_IF(!url) || NS_WARN_IF(!creatorCode))
return NS_ERROR_INVALID_ARG;
nsAutoreleasePool localPool;
NSString *resolvedPath = [[(NSURL*)url path] stringByResolvingSymlinksInPath];
if (!resolvedPath) {
return NS_ERROR_FAILURE;
}
NSDictionary* dict = [[NSFileManager defaultManager] attributesOfItemAtPath:resolvedPath error:nil];
if (!dict) {
return NS_ERROR_FAILURE;
}
NSNumber* creatorNum = (NSNumber*)[dict objectForKey:NSFileHFSCreatorCode];
if (!creatorNum) {
return NS_ERROR_FAILURE;
}
*creatorCode = [creatorNum unsignedLongValue];
return NS_OK;
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
}
nsresult SetFileCreatorCode(CFURLRef url, OSType creatorCode)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
if (NS_WARN_IF(!url))
return NS_ERROR_INVALID_ARG;
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;
if (NS_WARN_IF(!url) || NS_WARN_IF(!typeCode))
return NS_ERROR_INVALID_ARG;
nsAutoreleasePool localPool;
NSString *resolvedPath = [[(NSURL*)url path] stringByResolvingSymlinksInPath];
if (!resolvedPath) {
return NS_ERROR_FAILURE;
}
NSDictionary* dict = [[NSFileManager defaultManager] attributesOfItemAtPath:resolvedPath error:nil];
if (!dict) {
return NS_ERROR_FAILURE;
}
NSNumber* typeNum = (NSNumber*)[dict objectForKey:NSFileHFSTypeCode];
if (!typeNum) {
return NS_ERROR_FAILURE;
}
*typeCode = [typeNum unsignedLongValue];
return NS_OK;
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
}
nsresult SetFileTypeCode(CFURLRef url, OSType typeCode)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
if (NS_WARN_IF(!url))
return NS_ERROR_INVALID_ARG;
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;
}
void AddOriginMetadataToFile(const CFStringRef filePath,
const CFURLRef sourceURL,
const CFURLRef referrerURL) {
typedef OSStatus (*MDItemSetAttribute_type)(MDItemRef, CFStringRef, CFTypeRef);
static MDItemSetAttribute_type mdItemSetAttributeFunc = NULL;
static bool did_symbol_lookup = false;
if (!did_symbol_lookup) {
did_symbol_lookup = true;
CFBundleRef metadata_bundle = ::CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Metadata"));
if (!metadata_bundle) {
return;
}
mdItemSetAttributeFunc = (MDItemSetAttribute_type)
::CFBundleGetFunctionPointerForName(metadata_bundle, CFSTR("MDItemSetAttribute"));
}
if (!mdItemSetAttributeFunc) {
return;
}
MDItemRef mdItem = ::MDItemCreate(NULL, filePath);
if (!mdItem) {
return;
}
CFMutableArrayRef list = ::CFArrayCreateMutable(kCFAllocatorDefault, 2, NULL);
if (!list) {
::CFRelease(mdItem);
return;
}
// The first item in the list is the source URL of the downloaded file.
if (sourceURL) {
::CFArrayAppendValue(list, ::CFURLGetString(sourceURL));
}
// If the referrer is known, store that in the second position.
if (referrerURL) {
::CFArrayAppendValue(list, ::CFURLGetString(referrerURL));
}
mdItemSetAttributeFunc(mdItem, kMDItemWhereFroms, list);
::CFRelease(list);
::CFRelease(mdItem);
}
void AddQuarantineMetadataToFile(const CFStringRef filePath,
const CFURLRef sourceURL,
const CFURLRef referrerURL,
const bool isFromWeb) {
CFURLRef fileURL = ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
filePath,
kCFURLPOSIXPathStyle,
false);
// The properties key changed in 10.10:
CFStringRef quarantinePropKey;
if (nsCocoaFeatures::OnYosemiteOrLater()) {
quarantinePropKey = kCFURLQuarantinePropertiesKey;
} else {
quarantinePropKey = kLSItemQuarantineProperties;
}
CFDictionaryRef quarantineProps = NULL;
Boolean success = ::CFURLCopyResourcePropertyForKey(fileURL,
quarantinePropKey,
&quarantineProps,
NULL);
// If there aren't any quarantine properties then the user probably
// set up an exclusion and we don't need to add metadata.
if (!success || !quarantineProps) {
::CFRelease(fileURL);
return;
}
// We don't know what to do if the props aren't a dictionary.
if (::CFGetTypeID(quarantineProps) != ::CFDictionaryGetTypeID()) {
::CFRelease(fileURL);
::CFRelease(quarantineProps);
return;
}
// Make a mutable copy of the properties.
CFMutableDictionaryRef mutQuarantineProps =
::CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, (CFDictionaryRef)quarantineProps);
::CFRelease(quarantineProps);
// Add metadata that the OS couldn't infer.
if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineTypeKey)) {
CFStringRef type = isFromWeb ? kLSQuarantineTypeWebDownload : kLSQuarantineTypeOtherDownload;
::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineTypeKey, type);
}
if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineOriginURLKey) && referrerURL) {
::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineOriginURLKey, referrerURL);
}
if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineDataURLKey) && sourceURL) {
::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineDataURLKey, sourceURL);
}
// Set quarantine properties on file.
::CFURLSetResourcePropertyForKey(fileURL,
quarantinePropKey,
mutQuarantineProps,
NULL);
::CFRelease(fileURL);
::CFRelease(mutQuarantineProps);
}
CFURLRef GetTemporaryFolderCFURLRef()
{
NSString* tempDir = ::NSTemporaryDirectory();
return tempDir == nil ? NULL : (CFURLRef)[NSURL fileURLWithPath:tempDir
isDirectory:YES];
}
} // namespace CocoaFileUtils

75
xpcom/io/FileUtilsWin.cpp Normal file
View file

@ -0,0 +1,75 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "FileUtilsWin.h"
#include <windows.h>
#include <psapi.h>
#include "mozilla/Unused.h"
#include "nsWindowsHelpers.h"
#include "GeckoProfiler.h"
namespace {
// Scoped type used by HandleToFilename
struct ScopedMappedViewTraits
{
typedef void* type;
static void* empty()
{
return nullptr;
}
static void release(void* aPtr)
{
if (aPtr) {
mozilla::Unused << UnmapViewOfFile(aPtr);
}
}
};
typedef mozilla::Scoped<ScopedMappedViewTraits> ScopedMappedView;
} // namespace
namespace mozilla {
bool
HandleToFilename(HANDLE aHandle, const LARGE_INTEGER& aOffset,
nsAString& aFilename)
{
PROFILER_LABEL_FUNC(js::ProfileEntry::Category::NETWORK);
aFilename.Truncate();
// This implementation is nice because it uses fully documented APIs that
// are available on all Windows versions that we support.
nsAutoHandle fileMapping(CreateFileMapping(aHandle, nullptr, PAGE_READONLY,
0, 1, nullptr));
if (!fileMapping) {
return false;
}
ScopedMappedView view(MapViewOfFile(fileMapping, FILE_MAP_READ,
aOffset.HighPart, aOffset.LowPart, 1));
if (!view) {
return false;
}
nsAutoString mappedFilename;
DWORD len = 0;
SetLastError(ERROR_SUCCESS);
do {
mappedFilename.SetLength(mappedFilename.Length() + MAX_PATH);
len = GetMappedFileNameW(GetCurrentProcess(), view,
wwc(mappedFilename.BeginWriting()),
mappedFilename.Length());
} while (!len && GetLastError() == ERROR_INSUFFICIENT_BUFFER);
if (!len) {
return false;
}
mappedFilename.Truncate(len);
return NtPathToDosPath(mappedFilename, aFilename);
}
} // namespace mozilla

144
xpcom/io/FileUtilsWin.h Normal file
View file

@ -0,0 +1,144 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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_FileUtilsWin_h
#define mozilla_FileUtilsWin_h
#include <windows.h>
#include "mozilla/Scoped.h"
#include "nsStringGlue.h"
namespace mozilla {
inline bool
EnsureLongPath(nsAString& aDosPath)
{
uint32_t aDosPathOriginalLen = aDosPath.Length();
auto inputPath = PromiseFlatString(aDosPath);
// Try to get the long path, or else get the required length of the long path
DWORD longPathLen = GetLongPathNameW(inputPath.get(),
reinterpret_cast<wchar_t*>(aDosPath.BeginWriting()),
aDosPathOriginalLen);
if (longPathLen == 0) {
return false;
}
aDosPath.SetLength(longPathLen);
if (longPathLen <= aDosPathOriginalLen) {
// Our string happened to be long enough for the first call to succeed.
return true;
}
// Now we have a large enough buffer, get the actual string
longPathLen = GetLongPathNameW(inputPath.get(),
reinterpret_cast<wchar_t*>(aDosPath.BeginWriting()), aDosPath.Length());
if (longPathLen == 0) {
return false;
}
// This success check should always be less-than because longPathLen excludes
// the null terminator on success, but includes it in the first call that
// returned the required size.
if (longPathLen < aDosPath.Length()) {
aDosPath.SetLength(longPathLen);
return true;
}
// We shouldn't reach this, but if we do then it's a failure!
return false;
}
inline bool
NtPathToDosPath(const nsAString& aNtPath, nsAString& aDosPath)
{
aDosPath.Truncate();
if (aNtPath.IsEmpty()) {
return true;
}
NS_NAMED_LITERAL_STRING(symLinkPrefix, "\\??\\");
uint32_t ntPathLen = aNtPath.Length();
uint32_t symLinkPrefixLen = symLinkPrefix.Length();
if (ntPathLen >= 6 && aNtPath.CharAt(5) == L':' &&
ntPathLen >= symLinkPrefixLen &&
Substring(aNtPath, 0, symLinkPrefixLen).Equals(symLinkPrefix)) {
// Symbolic link for DOS device. Just strip off the prefix.
aDosPath = aNtPath;
aDosPath.Cut(0, 4);
return true;
}
nsAutoString logicalDrives;
DWORD len = 0;
while (true) {
len = GetLogicalDriveStringsW(
len, reinterpret_cast<wchar_t*>(logicalDrives.BeginWriting()));
if (!len) {
return false;
} else if (len > logicalDrives.Length()) {
logicalDrives.SetLength(len);
} else {
break;
}
}
const char16_t* cur = logicalDrives.BeginReading();
const char16_t* end = logicalDrives.EndReading();
nsString targetPath;
targetPath.SetLength(MAX_PATH);
wchar_t driveTemplate[] = L" :";
do {
// Unfortunately QueryDosDevice doesn't support the idiom for querying the
// output buffer size, so it may require retries.
driveTemplate[0] = *cur;
DWORD targetPathLen = 0;
SetLastError(ERROR_SUCCESS);
while (true) {
targetPathLen = QueryDosDeviceW(driveTemplate,
reinterpret_cast<wchar_t*>(targetPath.BeginWriting()),
targetPath.Length());
if (targetPathLen || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
break;
}
targetPath.SetLength(targetPath.Length() * 2);
}
if (targetPathLen) {
// Need to use wcslen here because targetPath contains embedded NULL chars
size_t firstTargetPathLen = wcslen(targetPath.get());
const char16_t* pathComponent = aNtPath.BeginReading() +
firstTargetPathLen;
bool found = _wcsnicmp(char16ptr_t(aNtPath.BeginReading()), targetPath.get(),
firstTargetPathLen) == 0 &&
*pathComponent == L'\\';
if (found) {
aDosPath = driveTemplate;
aDosPath += pathComponent;
return EnsureLongPath(aDosPath);
}
}
// Advance to the next NUL character in logicalDrives
while (*cur++);
} while (cur != end);
// Try to handle UNC paths. NB: This must happen after we've checked drive
// mappings in case a UNC path is mapped to a drive!
NS_NAMED_LITERAL_STRING(uncPrefix, "\\\\");
NS_NAMED_LITERAL_STRING(deviceMupPrefix, "\\Device\\Mup\\");
if (StringBeginsWith(aNtPath, deviceMupPrefix)) {
aDosPath = uncPrefix;
aDosPath += Substring(aNtPath, deviceMupPrefix.Length());
return true;
}
NS_NAMED_LITERAL_STRING(deviceLanmanRedirectorPrefix,
"\\Device\\LanmanRedirector\\");
if (StringBeginsWith(aNtPath, deviceLanmanRedirectorPrefix)) {
aDosPath = uncPrefix;
aDosPath += Substring(aNtPath, deviceLanmanRedirectorPrefix.Length());
return true;
}
return false;
}
bool
HandleToFilename(HANDLE aHandle, const LARGE_INTEGER& aOffset,
nsAString& aFilename);
} // namespace mozilla
#endif // mozilla_FileUtilsWin_h

View file

@ -0,0 +1,209 @@
/* -*- 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 "SlicedInputStream.h"
#include "nsISeekableStream.h"
#include "nsStreamUtils.h"
NS_IMPL_ISUPPORTS(SlicedInputStream, nsIInputStream,
nsICloneableInputStream, nsIAsyncInputStream)
SlicedInputStream::SlicedInputStream(nsIInputStream* aInputStream,
uint64_t aStart, uint64_t aLength)
: mInputStream(aInputStream)
, mStart(aStart)
, mLength(aLength)
, mCurPos(0)
, mClosed(false)
{
MOZ_ASSERT(aInputStream);
}
SlicedInputStream::~SlicedInputStream()
{}
NS_IMETHODIMP
SlicedInputStream::Close()
{
mClosed = true;
return NS_OK;
}
// nsIInputStream interface
NS_IMETHODIMP
SlicedInputStream::Available(uint64_t* aLength)
{
if (mClosed) {
return NS_BASE_STREAM_CLOSED;
}
nsresult rv = mInputStream->Available(aLength);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Let's remove extra length from the end.
if (*aLength + mCurPos > mStart + mLength) {
*aLength -= XPCOM_MIN(*aLength, (*aLength + mCurPos) - (mStart + mLength));
}
// Let's remove extra length from the begin.
if (mCurPos < mStart) {
*aLength -= XPCOM_MIN(*aLength, mStart - mCurPos);
}
return NS_OK;
}
NS_IMETHODIMP
SlicedInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount)
{
return ReadSegments(NS_CopySegmentToBuffer, aBuffer, aCount, aReadCount);
}
NS_IMETHODIMP
SlicedInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
uint32_t aCount, uint32_t *aResult)
{
uint32_t result;
if (!aResult) {
aResult = &result;
}
*aResult = 0;
if (mClosed) {
return NS_BASE_STREAM_CLOSED;
}
if (mCurPos < mStart) {
nsCOMPtr<nsISeekableStream> seekableStream =
do_QueryInterface(mInputStream);
if (seekableStream) {
nsresult rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_SET,
mStart);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mCurPos = mStart;
} else {
char buf[4096];
while (mCurPos < mStart) {
uint32_t bytesRead;
uint64_t bufCount = XPCOM_MIN(mStart - mCurPos, (uint64_t)sizeof(buf));
nsresult rv = mInputStream->Read(buf, bufCount, &bytesRead);
if (NS_WARN_IF(NS_FAILED(rv)) || bytesRead == 0) {
return rv;
}
mCurPos += bytesRead;
}
}
}
// Let's reduce aCount in case it's too big.
if (mCurPos + aCount > mStart + mLength) {
aCount = mStart + mLength - mCurPos;
}
char buf[4096];
while (mCurPos < mStart + mLength && *aResult < aCount) {
uint32_t bytesRead;
uint64_t bufCount = XPCOM_MIN(aCount - *aResult, (uint32_t)sizeof(buf));
nsresult rv = mInputStream->Read(buf, bufCount, &bytesRead);
if (NS_WARN_IF(NS_FAILED(rv)) || bytesRead == 0) {
return rv;
}
mCurPos += bytesRead;
uint32_t bytesWritten = 0;
while (bytesWritten < bytesRead) {
uint32_t writerCount = 0;
rv = aWriter(this, aClosure, buf + bytesWritten, *aResult,
bytesRead - bytesWritten, &writerCount);
if (NS_FAILED(rv) || writerCount == 0) {
return NS_OK;
}
MOZ_ASSERT(writerCount <= bytesRead - bytesWritten);
bytesWritten += writerCount;
*aResult += writerCount;
}
}
return NS_OK;
}
NS_IMETHODIMP
SlicedInputStream::IsNonBlocking(bool* aNonBlocking)
{
return mInputStream->IsNonBlocking(aNonBlocking);
}
// nsICloneableInputStream interface
NS_IMETHODIMP
SlicedInputStream::GetCloneable(bool* aCloneable)
{
*aCloneable = true;
return NS_OK;
}
NS_IMETHODIMP
SlicedInputStream::Clone(nsIInputStream** aResult)
{
nsCOMPtr<nsIInputStream> clonedStream;
nsCOMPtr<nsIInputStream> replacementStream;
nsresult rv = NS_CloneInputStream(mInputStream, getter_AddRefs(clonedStream),
getter_AddRefs(replacementStream));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (replacementStream) {
mInputStream = replacementStream.forget();
}
nsCOMPtr<nsIInputStream> sis =
new SlicedInputStream(clonedStream, mStart, mLength);
sis.forget(aResult);
return NS_OK;
}
// nsIAsyncInputStream interface
NS_IMETHODIMP
SlicedInputStream::CloseWithStatus(nsresult aStatus)
{
nsCOMPtr<nsIAsyncInputStream> asyncStream =
do_QueryInterface(mInputStream);
if (!asyncStream) {
return NS_ERROR_FAILURE;
}
return asyncStream->CloseWithStatus(aStatus);
}
NS_IMETHODIMP
SlicedInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
uint32_t aFlags,
uint32_t aRequestedCount,
nsIEventTarget* aEventTarget)
{
nsCOMPtr<nsIAsyncInputStream> asyncStream =
do_QueryInterface(mInputStream);
if (!asyncStream) {
return NS_ERROR_FAILURE;
}
return asyncStream->AsyncWait(aCallback, aFlags, aRequestedCount,
aEventTarget);
}

View file

@ -0,0 +1,50 @@
/* -*- 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 SlicedInputStream_h
#define SlicedInputStream_h
#include "mozilla/Attributes.h"
#include "nsCOMPtr.h"
#include "nsIAsyncInputStream.h"
#include "nsICloneableInputStream.h"
// A wrapper for a slice of an underlying input stream.
class SlicedInputStream final : public nsIAsyncInputStream
, public nsICloneableInputStream
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
NS_DECL_NSICLONEABLEINPUTSTREAM
NS_DECL_NSIASYNCINPUTSTREAM
// Create an input stream whose data comes from a slice of aInputStream. The
// slice begins at aStart bytes beyond aInputStream's current position, and
// extends for a maximum of aLength bytes. If aInputStream contains fewer
// than aStart bytes, reading from SlicedInputStream returns no data. If
// aInputStream contains more than aStart bytes, but fewer than aStart +
// aLength bytes, reading from SlicedInputStream returns as many bytes as can
// be consumed from aInputStream after reading aLength bytes.
//
// aInputStream should not be read from after constructing a
// SlicedInputStream wrapper around it.
SlicedInputStream(nsIInputStream* aInputStream,
uint64_t aStart, uint64_t aLength);
private:
~SlicedInputStream();
nsCOMPtr<nsIInputStream> mInputStream;
uint64_t mStart;
uint64_t mLength;
uint64_t mCurPos;
bool mClosed;
};
#endif // SlicedInputStream_h

View file

@ -0,0 +1,256 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/SnappyCompressOutputStream.h"
#include <algorithm>
#include "nsStreamUtils.h"
#include "snappy/snappy.h"
namespace mozilla {
NS_IMPL_ISUPPORTS(SnappyCompressOutputStream, nsIOutputStream);
// static
const size_t
SnappyCompressOutputStream::kMaxBlockSize = snappy::kBlockSize;
SnappyCompressOutputStream::SnappyCompressOutputStream(nsIOutputStream* aBaseStream,
size_t aBlockSize)
: mBaseStream(aBaseStream)
, mBlockSize(std::min(aBlockSize, kMaxBlockSize))
, mNextByte(0)
, mCompressedBufferLength(0)
, mStreamIdentifierWritten(false)
{
MOZ_ASSERT(mBlockSize > 0);
// This implementation only supports sync base streams. Verify this in debug
// builds. Note, this can be simpler than the check in
// SnappyUncompressInputStream because we don't have to deal with the
// nsStringInputStream oddness of being non-blocking and sync.
#ifdef DEBUG
bool baseNonBlocking;
nsresult rv = mBaseStream->IsNonBlocking(&baseNonBlocking);
MOZ_ASSERT(NS_SUCCEEDED(rv));
MOZ_ASSERT(!baseNonBlocking);
#endif
}
size_t
SnappyCompressOutputStream::BlockSize() const
{
return mBlockSize;
}
NS_IMETHODIMP
SnappyCompressOutputStream::Close()
{
if (!mBaseStream) {
return NS_OK;
}
nsresult rv = Flush();
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
mBaseStream->Close();
mBaseStream = nullptr;
mBuffer = nullptr;
mCompressedBuffer = nullptr;
return NS_OK;
}
NS_IMETHODIMP
SnappyCompressOutputStream::Flush()
{
if (!mBaseStream) {
return NS_BASE_STREAM_CLOSED;
}
nsresult rv = FlushToBaseStream();
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
mBaseStream->Flush();
return NS_OK;
}
NS_IMETHODIMP
SnappyCompressOutputStream::Write(const char* aBuf, uint32_t aCount,
uint32_t* aResultOut)
{
return WriteSegments(NS_CopySegmentToBuffer, const_cast<char*>(aBuf), aCount,
aResultOut);
}
NS_IMETHODIMP
SnappyCompressOutputStream::WriteFrom(nsIInputStream*, uint32_t, uint32_t*)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
SnappyCompressOutputStream::WriteSegments(nsReadSegmentFun aReader,
void* aClosure,
uint32_t aCount,
uint32_t* aBytesWrittenOut)
{
*aBytesWrittenOut = 0;
if (!mBaseStream) {
return NS_BASE_STREAM_CLOSED;
}
if (!mBuffer) {
mBuffer.reset(new (fallible) char[mBlockSize]);
if (NS_WARN_IF(!mBuffer)) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
while (aCount > 0) {
// Determine how much space is left in our flat, uncompressed buffer.
MOZ_ASSERT(mNextByte <= mBlockSize);
uint32_t remaining = mBlockSize - mNextByte;
// If it is full, then compress and flush the data to the base stream.
if (remaining == 0) {
nsresult rv = FlushToBaseStream();
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
// Now the entire buffer should be available for copying.
MOZ_ASSERT(!mNextByte);
remaining = mBlockSize;
}
uint32_t numToRead = std::min(remaining, aCount);
uint32_t numRead = 0;
nsresult rv = aReader(this, aClosure, &mBuffer[mNextByte],
*aBytesWrittenOut, numToRead, &numRead);
// As defined in nsIOutputStream.idl, do not pass reader func errors.
if (NS_FAILED(rv)) {
return NS_OK;
}
// End-of-file
if (numRead == 0) {
return NS_OK;
}
mNextByte += numRead;
*aBytesWrittenOut += numRead;
aCount -= numRead;
}
return NS_OK;
}
NS_IMETHODIMP
SnappyCompressOutputStream::IsNonBlocking(bool* aNonBlockingOut)
{
*aNonBlockingOut = false;
return NS_OK;
}
SnappyCompressOutputStream::~SnappyCompressOutputStream()
{
Close();
}
nsresult
SnappyCompressOutputStream::FlushToBaseStream()
{
MOZ_ASSERT(mBaseStream);
// Lazily create the compressed buffer on our first flush. This
// allows us to report OOM during stream operation. This buffer
// will then get re-used until the stream is closed.
if (!mCompressedBuffer) {
mCompressedBufferLength = MaxCompressedBufferLength(mBlockSize);
mCompressedBuffer.reset(new (fallible) char[mCompressedBufferLength]);
if (NS_WARN_IF(!mCompressedBuffer)) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
// The first chunk must be a StreamIdentifier chunk. Write it out
// if we have not done so already.
nsresult rv = MaybeFlushStreamIdentifier();
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
// Compress the data to our internal compressed buffer.
size_t compressedLength;
rv = WriteCompressedData(mCompressedBuffer.get(), mCompressedBufferLength,
mBuffer.get(), mNextByte, &compressedLength);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
MOZ_ASSERT(compressedLength > 0);
mNextByte = 0;
// Write the compressed buffer out to the base stream.
uint32_t numWritten = 0;
rv = WriteAll(mCompressedBuffer.get(), compressedLength, &numWritten);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
MOZ_ASSERT(compressedLength == numWritten);
return NS_OK;
}
nsresult
SnappyCompressOutputStream::MaybeFlushStreamIdentifier()
{
MOZ_ASSERT(mCompressedBuffer);
if (mStreamIdentifierWritten) {
return NS_OK;
}
// Build the StreamIdentifier in our compressed buffer.
size_t compressedLength;
nsresult rv = WriteStreamIdentifier(mCompressedBuffer.get(),
mCompressedBufferLength,
&compressedLength);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
// Write the compressed buffer out to the base stream.
uint32_t numWritten = 0;
rv = WriteAll(mCompressedBuffer.get(), compressedLength, &numWritten);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
MOZ_ASSERT(compressedLength == numWritten);
mStreamIdentifierWritten = true;
return NS_OK;
}
nsresult
SnappyCompressOutputStream::WriteAll(const char* aBuf, uint32_t aCount,
uint32_t* aBytesWrittenOut)
{
*aBytesWrittenOut = 0;
if (!mBaseStream) {
return NS_BASE_STREAM_CLOSED;
}
uint32_t offset = 0;
while (aCount > 0) {
uint32_t numWritten = 0;
nsresult rv = mBaseStream->Write(aBuf + offset, aCount, &numWritten);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
offset += numWritten;
aCount -= numWritten;
*aBytesWrittenOut += numWritten;
}
return NS_OK;
}
} // namespace mozilla

View file

@ -0,0 +1,69 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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_SnappyCompressOutputStream_h__
#define mozilla_SnappyCompressOutputStream_h__
#include "mozilla/Attributes.h"
#include "mozilla/UniquePtr.h"
#include "nsCOMPtr.h"
#include "nsIOutputStream.h"
#include "nsISupportsImpl.h"
#include "SnappyFrameUtils.h"
namespace mozilla {
class SnappyCompressOutputStream final : public nsIOutputStream
, protected detail::SnappyFrameUtils
{
public:
// Maximum compression block size.
static const size_t kMaxBlockSize;
// Construct a new blocking output stream to compress data to
// the given base stream. The base stream must also be blocking.
// The compression block size may optionally be set to a value
// up to kMaxBlockSize.
explicit SnappyCompressOutputStream(nsIOutputStream* aBaseStream,
size_t aBlockSize = kMaxBlockSize);
// The compression block size. To optimize stream performance
// try to write to the stream in segments at least this size.
size_t BlockSize() const;
private:
virtual ~SnappyCompressOutputStream();
nsresult FlushToBaseStream();
nsresult MaybeFlushStreamIdentifier();
nsresult WriteAll(const char* aBuf, uint32_t aCount,
uint32_t* aBytesWrittenOut);
nsCOMPtr<nsIOutputStream> mBaseStream;
const size_t mBlockSize;
// Buffer holding copied uncompressed data. This must be copied here
// so that the compression can be performed on a single flat buffer.
mozilla::UniquePtr<char[]> mBuffer;
// The next byte in the uncompressed data to copy incoming data to.
size_t mNextByte;
// Buffer holding the resulting compressed data.
mozilla::UniquePtr<char[]> mCompressedBuffer;
size_t mCompressedBufferLength;
// The first thing written to the stream must be a stream identifier.
bool mStreamIdentifierWritten;
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIOUTPUTSTREAM
};
} // namespace mozilla
#endif // mozilla_SnappyCompressOutputStream_h__

View file

@ -0,0 +1,258 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/SnappyFrameUtils.h"
#include "crc32c.h"
#include "mozilla/EndianUtils.h"
#include "nsDebug.h"
#include "snappy/snappy.h"
namespace {
using mozilla::detail::SnappyFrameUtils;
using mozilla::NativeEndian;
SnappyFrameUtils::ChunkType ReadChunkType(uint8_t aByte)
{
if (aByte == 0xff) {
return SnappyFrameUtils::StreamIdentifier;
} else if (aByte == 0x00) {
return SnappyFrameUtils::CompressedData;
} else if (aByte == 0x01) {
return SnappyFrameUtils::UncompressedData;
} else if (aByte == 0xfe) {
return SnappyFrameUtils::Padding;
}
return SnappyFrameUtils::Reserved;
}
void WriteChunkType(char* aDest, SnappyFrameUtils::ChunkType aType)
{
unsigned char* dest = reinterpret_cast<unsigned char*>(aDest);
if (aType == SnappyFrameUtils::StreamIdentifier) {
*dest = 0xff;
} else if (aType == SnappyFrameUtils::CompressedData) {
*dest = 0x00;
} else if (aType == SnappyFrameUtils::UncompressedData) {
*dest = 0x01;
} else if (aType == SnappyFrameUtils::Padding) {
*dest = 0xfe;
} else {
*dest = 0x02;
}
}
void WriteUInt24(char* aBuf, uint32_t aVal)
{
MOZ_ASSERT(!(aVal & 0xff000000));
uint32_t tmp = NativeEndian::swapToLittleEndian(aVal);
memcpy(aBuf, &tmp, 3);
}
uint32_t ReadUInt24(const char* aBuf)
{
uint32_t val = 0;
memcpy(&val, aBuf, 3);
return NativeEndian::swapFromLittleEndian(val);
}
// This mask is explicitly defined in the snappy framing_format.txt file.
uint32_t MaskChecksum(uint32_t aValue)
{
return ((aValue >> 15) | (aValue << 17)) + 0xa282ead8;
}
} // namespace
namespace mozilla {
namespace detail {
using mozilla::LittleEndian;
// static
nsresult
SnappyFrameUtils::WriteStreamIdentifier(char* aDest, size_t aDestLength,
size_t* aBytesWrittenOut)
{
if (NS_WARN_IF(aDestLength < (kHeaderLength + kStreamIdentifierDataLength))) {
return NS_ERROR_NOT_AVAILABLE;
}
WriteChunkType(aDest, StreamIdentifier);
aDest[1] = 0x06; // Data length
aDest[2] = 0x00;
aDest[3] = 0x00;
aDest[4] = 0x73; // "sNaPpY"
aDest[5] = 0x4e;
aDest[6] = 0x61;
aDest[7] = 0x50;
aDest[8] = 0x70;
aDest[9] = 0x59;
static_assert(kHeaderLength + kStreamIdentifierDataLength == 10,
"StreamIdentifier chunk should be exactly 10 bytes long");
*aBytesWrittenOut = kHeaderLength + kStreamIdentifierDataLength;
return NS_OK;
}
// static
nsresult
SnappyFrameUtils::WriteCompressedData(char* aDest, size_t aDestLength,
const char* aData, size_t aDataLength,
size_t* aBytesWrittenOut)
{
*aBytesWrittenOut = 0;
size_t neededLength = MaxCompressedBufferLength(aDataLength);
if (NS_WARN_IF(aDestLength < neededLength)) {
return NS_ERROR_NOT_AVAILABLE;
}
size_t offset = 0;
WriteChunkType(aDest, CompressedData);
offset += kChunkTypeLength;
// Skip length for now and write it out after we know the compressed length.
size_t lengthOffset = offset;
offset += kChunkLengthLength;
uint32_t crc = ComputeCrc32c(~0, reinterpret_cast<const unsigned char*>(aData),
aDataLength);
uint32_t maskedCrc = MaskChecksum(crc);
LittleEndian::writeUint32(aDest + offset, maskedCrc);
offset += kCRCLength;
size_t compressedLength;
snappy::RawCompress(aData, aDataLength, aDest + offset, &compressedLength);
// Go back and write the data length.
size_t dataLength = compressedLength + kCRCLength;
WriteUInt24(aDest + lengthOffset, dataLength);
*aBytesWrittenOut = kHeaderLength + dataLength;
return NS_OK;
}
// static
nsresult
SnappyFrameUtils::ParseHeader(const char* aSource, size_t aSourceLength,
ChunkType* aTypeOut, size_t* aDataLengthOut)
{
if (NS_WARN_IF(aSourceLength < kHeaderLength)) {
return NS_ERROR_NOT_AVAILABLE;
}
*aTypeOut = ReadChunkType(aSource[0]);
*aDataLengthOut = ReadUInt24(aSource + kChunkTypeLength);
return NS_OK;
}
// static
nsresult
SnappyFrameUtils::ParseData(char* aDest, size_t aDestLength,
ChunkType aType, const char* aData,
size_t aDataLength,
size_t* aBytesWrittenOut, size_t* aBytesReadOut)
{
switch(aType) {
case StreamIdentifier:
return ParseStreamIdentifier(aDest, aDestLength, aData, aDataLength,
aBytesWrittenOut, aBytesReadOut);
case CompressedData:
return ParseCompressedData(aDest, aDestLength, aData, aDataLength,
aBytesWrittenOut, aBytesReadOut);
// TODO: support other snappy chunk types
default:
MOZ_ASSERT_UNREACHABLE("Unsupported snappy framing chunk type.");
return NS_ERROR_NOT_IMPLEMENTED;
}
}
// static
nsresult
SnappyFrameUtils::ParseStreamIdentifier(char*, size_t,
const char* aData, size_t aDataLength,
size_t* aBytesWrittenOut,
size_t* aBytesReadOut)
{
*aBytesWrittenOut = 0;
*aBytesReadOut = 0;
if (NS_WARN_IF(aDataLength != kStreamIdentifierDataLength ||
aData[0] != 0x73 ||
aData[1] != 0x4e ||
aData[2] != 0x61 ||
aData[3] != 0x50 ||
aData[4] != 0x70 ||
aData[5] != 0x59)) {
return NS_ERROR_CORRUPTED_CONTENT;
}
*aBytesReadOut = aDataLength;
return NS_OK;
}
// static
nsresult
SnappyFrameUtils::ParseCompressedData(char* aDest, size_t aDestLength,
const char* aData, size_t aDataLength,
size_t* aBytesWrittenOut,
size_t* aBytesReadOut)
{
*aBytesWrittenOut = 0;
*aBytesReadOut = 0;
size_t offset = 0;
uint32_t readCrc = LittleEndian::readUint32(aData + offset);
offset += kCRCLength;
size_t uncompressedLength;
if (NS_WARN_IF(!snappy::GetUncompressedLength(aData + offset,
aDataLength - offset,
&uncompressedLength))) {
return NS_ERROR_CORRUPTED_CONTENT;
}
if (NS_WARN_IF(aDestLength < uncompressedLength)) {
return NS_ERROR_NOT_AVAILABLE;
}
if (NS_WARN_IF(!snappy::RawUncompress(aData + offset, aDataLength - offset,
aDest))) {
return NS_ERROR_CORRUPTED_CONTENT;
}
uint32_t crc = ComputeCrc32c(~0, reinterpret_cast<const unsigned char*>(aDest),
uncompressedLength);
uint32_t maskedCrc = MaskChecksum(crc);
if (NS_WARN_IF(readCrc != maskedCrc)) {
return NS_ERROR_CORRUPTED_CONTENT;
}
*aBytesWrittenOut = uncompressedLength;
*aBytesReadOut = aDataLength;
return NS_OK;
}
// static
size_t
SnappyFrameUtils::MaxCompressedBufferLength(size_t aSourceLength)
{
size_t neededLength = kHeaderLength;
neededLength += kCRCLength;
neededLength += snappy::MaxCompressedLength(aSourceLength);
return neededLength;
}
} // namespace detail
} // namespace mozilla

View file

@ -0,0 +1,85 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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_SnappyFrameUtils_h__
#define mozilla_SnappyFrameUtils_h__
#include "mozilla/Attributes.h"
#include "nsError.h"
namespace mozilla {
namespace detail {
//
// Utility class providing primitives necessary to build streams based
// on the snappy compressor. This essentially abstracts the framing format
// defined in:
//
// other-licences/snappy/src/framing_format.txt
//
// NOTE: Currently only the StreamIdentifier and CompressedData chunks are
// supported.
//
class SnappyFrameUtils
{
public:
enum ChunkType
{
Unknown,
StreamIdentifier,
CompressedData,
UncompressedData,
Padding,
Reserved,
ChunkTypeCount
};
static const size_t kChunkTypeLength = 1;
static const size_t kChunkLengthLength = 3;
static const size_t kHeaderLength = kChunkTypeLength + kChunkLengthLength;
static const size_t kStreamIdentifierDataLength = 6;
static const size_t kCRCLength = 4;
static nsresult
WriteStreamIdentifier(char* aDest, size_t aDestLength,
size_t* aBytesWrittenOut);
static nsresult
WriteCompressedData(char* aDest, size_t aDestLength,
const char* aData, size_t aDataLength,
size_t* aBytesWrittenOut);
static nsresult
ParseHeader(const char* aSource, size_t aSourceLength, ChunkType* aTypeOut,
size_t* aDataLengthOut);
static nsresult
ParseData(char* aDest, size_t aDestLength,
ChunkType aType, const char* aData, size_t aDataLength,
size_t* aBytesWrittenOut, size_t* aBytesReadOut);
static nsresult
ParseStreamIdentifier(char* aDest, size_t aDestLength,
const char* aData, size_t aDataLength,
size_t* aBytesWrittenOut, size_t* aBytesReadOut);
static nsresult
ParseCompressedData(char* aDest, size_t aDestLength,
const char* aData, size_t aDataLength,
size_t* aBytesWrittenOut, size_t* aBytesReadOut);
static size_t
MaxCompressedBufferLength(size_t aSourceLength);
protected:
SnappyFrameUtils() { }
virtual ~SnappyFrameUtils() { }
};
} // namespace detail
} // namespace mozilla
#endif // mozilla_SnappyFrameUtils_h__

View file

@ -0,0 +1,362 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/SnappyUncompressInputStream.h"
#include <algorithm>
#include "nsIAsyncInputStream.h"
#include "nsStreamUtils.h"
#include "snappy/snappy.h"
namespace mozilla {
NS_IMPL_ISUPPORTS(SnappyUncompressInputStream,
nsIInputStream);
// Putting kCompressedBufferLength inside a function avoids a static
// constructor.
static size_t CompressedBufferLength()
{
static size_t kCompressedBufferLength =
detail::SnappyFrameUtils::MaxCompressedBufferLength(snappy::kBlockSize);
MOZ_ASSERT(kCompressedBufferLength > 0);
return kCompressedBufferLength;
}
SnappyUncompressInputStream::SnappyUncompressInputStream(nsIInputStream* aBaseStream)
: mBaseStream(aBaseStream)
, mUncompressedBytes(0)
, mNextByte(0)
, mNextChunkType(Unknown)
, mNextChunkDataLength(0)
, mNeedFirstStreamIdentifier(true)
{
// This implementation only supports sync base streams. Verify this in debug
// builds. Note, this is a bit complicated because the streams we support
// advertise different capabilities:
// - nsFileInputStream - blocking and sync
// - nsStringInputStream - non-blocking and sync
// - nsPipeInputStream - can be blocking, but provides async interface
#ifdef DEBUG
bool baseNonBlocking;
nsresult rv = mBaseStream->IsNonBlocking(&baseNonBlocking);
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (baseNonBlocking) {
nsCOMPtr<nsIAsyncInputStream> async = do_QueryInterface(mBaseStream);
MOZ_ASSERT(!async);
}
#endif
}
NS_IMETHODIMP
SnappyUncompressInputStream::Close()
{
if (!mBaseStream) {
return NS_OK;
}
mBaseStream->Close();
mBaseStream = nullptr;
mUncompressedBuffer = nullptr;
mCompressedBuffer = nullptr;
return NS_OK;
}
NS_IMETHODIMP
SnappyUncompressInputStream::Available(uint64_t* aLengthOut)
{
if (!mBaseStream) {
return NS_BASE_STREAM_CLOSED;
}
// If we have uncompressed bytes, then we are done.
*aLengthOut = UncompressedLength();
if (*aLengthOut > 0) {
return NS_OK;
}
// Otherwise, attempt to uncompress bytes until we get something or the
// underlying stream is drained. We loop here because some chunks can
// be StreamIdentifiers, padding, etc with no data.
uint32_t bytesRead;
do {
nsresult rv = ParseNextChunk(&bytesRead);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
*aLengthOut = UncompressedLength();
} while(*aLengthOut == 0 && bytesRead);
return NS_OK;
}
NS_IMETHODIMP
SnappyUncompressInputStream::Read(char* aBuf, uint32_t aCount,
uint32_t* aBytesReadOut)
{
return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, aBytesReadOut);
}
NS_IMETHODIMP
SnappyUncompressInputStream::ReadSegments(nsWriteSegmentFun aWriter,
void* aClosure, uint32_t aCount,
uint32_t* aBytesReadOut)
{
*aBytesReadOut = 0;
if (!mBaseStream) {
return NS_BASE_STREAM_CLOSED;
}
nsresult rv;
// Do not try to use the base stream's ReadSegements here. Its very
// unlikely we will get a single buffer that contains all of the compressed
// data and therefore would have to copy into our own buffer anyways.
// Instead, focus on making efficient use of the Read() interface.
while (aCount > 0) {
// We have some decompressed data in our buffer. Provide it to the
// callers writer function.
if (mUncompressedBytes > 0) {
MOZ_ASSERT(mUncompressedBuffer);
uint32_t remaining = UncompressedLength();
uint32_t numToWrite = std::min(aCount, remaining);
uint32_t numWritten;
rv = aWriter(this, aClosure, &mUncompressedBuffer[mNextByte], *aBytesReadOut,
numToWrite, &numWritten);
// As defined in nsIInputputStream.idl, do not pass writer func errors.
if (NS_FAILED(rv)) {
return NS_OK;
}
// End-of-file
if (numWritten == 0) {
return NS_OK;
}
*aBytesReadOut += numWritten;
mNextByte += numWritten;
MOZ_ASSERT(mNextByte <= mUncompressedBytes);
if (mNextByte == mUncompressedBytes) {
mNextByte = 0;
mUncompressedBytes = 0;
}
aCount -= numWritten;
continue;
}
// Otherwise uncompress the next chunk and loop. Any resulting data
// will set mUncompressedBytes which we check at the top of the loop.
uint32_t bytesRead;
rv = ParseNextChunk(&bytesRead);
if (NS_FAILED(rv)) { return rv; }
// If we couldn't read anything and there is no more data to provide
// to the caller, then this is eof.
if (bytesRead == 0 && mUncompressedBytes == 0) {
return NS_OK;
}
}
return NS_OK;
}
NS_IMETHODIMP
SnappyUncompressInputStream::IsNonBlocking(bool* aNonBlockingOut)
{
*aNonBlockingOut = false;
return NS_OK;
}
SnappyUncompressInputStream::~SnappyUncompressInputStream()
{
Close();
}
nsresult
SnappyUncompressInputStream::ParseNextChunk(uint32_t* aBytesReadOut)
{
// There must not be any uncompressed data already in mUncompressedBuffer.
MOZ_ASSERT(mUncompressedBytes == 0);
MOZ_ASSERT(mNextByte == 0);
nsresult rv;
*aBytesReadOut = 0;
// Lazily create our two buffers so we can report OOM during stream
// operation. These allocations only happens once. The buffers are reused
// until the stream is closed.
if (!mUncompressedBuffer) {
mUncompressedBuffer.reset(new (fallible) char[snappy::kBlockSize]);
if (NS_WARN_IF(!mUncompressedBuffer)) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
if (!mCompressedBuffer) {
mCompressedBuffer.reset(new (fallible) char[CompressedBufferLength()]);
if (NS_WARN_IF(!mCompressedBuffer)) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
// We have no decompressed data and we also have not seen the start of stream
// yet. Read and validate the StreamIdentifier chunk. Also read the next
// header to determine the size of the first real data chunk.
if (mNeedFirstStreamIdentifier) {
const uint32_t firstReadLength = kHeaderLength +
kStreamIdentifierDataLength +
kHeaderLength;
MOZ_ASSERT(firstReadLength <= CompressedBufferLength());
rv = ReadAll(mCompressedBuffer.get(), firstReadLength, firstReadLength,
aBytesReadOut);
if (NS_WARN_IF(NS_FAILED(rv)) || *aBytesReadOut == 0) { return rv; }
rv = ParseHeader(mCompressedBuffer.get(), kHeaderLength,
&mNextChunkType, &mNextChunkDataLength);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
if (NS_WARN_IF(mNextChunkType != StreamIdentifier ||
mNextChunkDataLength != kStreamIdentifierDataLength)) {
return NS_ERROR_CORRUPTED_CONTENT;
}
size_t offset = kHeaderLength;
mNeedFirstStreamIdentifier = false;
size_t numRead;
size_t numWritten;
rv = ParseData(mUncompressedBuffer.get(), snappy::kBlockSize, mNextChunkType,
&mCompressedBuffer[offset],
mNextChunkDataLength, &numWritten, &numRead);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
MOZ_ASSERT(numWritten == 0);
MOZ_ASSERT(numRead == mNextChunkDataLength);
offset += numRead;
rv = ParseHeader(&mCompressedBuffer[offset], *aBytesReadOut - offset,
&mNextChunkType, &mNextChunkDataLength);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
return NS_OK;
}
// We have no compressed data and we don't know how big the next chunk is.
// This happens when we get an EOF pause in the middle of a stream and also
// at the end of the stream. Simply read the next header and return. The
// chunk body will be read on the next entry into this method.
if (mNextChunkType == Unknown) {
rv = ReadAll(mCompressedBuffer.get(), kHeaderLength, kHeaderLength,
aBytesReadOut);
if (NS_WARN_IF(NS_FAILED(rv)) || *aBytesReadOut == 0) { return rv; }
rv = ParseHeader(mCompressedBuffer.get(), kHeaderLength,
&mNextChunkType, &mNextChunkDataLength);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
return NS_OK;
}
// We have no decompressed data, but we do know the size of the next chunk.
// Read at least that much from the base stream.
uint32_t readLength = mNextChunkDataLength;
MOZ_ASSERT(readLength <= CompressedBufferLength());
// However, if there is enough data in the base stream, also read the next
// chunk header. This helps optimize the stream by avoiding many small reads.
uint64_t avail;
rv = mBaseStream->Available(&avail);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
if (avail >= (readLength + kHeaderLength)) {
readLength += kHeaderLength;
MOZ_ASSERT(readLength <= CompressedBufferLength());
}
rv = ReadAll(mCompressedBuffer.get(), readLength, mNextChunkDataLength,
aBytesReadOut);
if (NS_WARN_IF(NS_FAILED(rv)) || *aBytesReadOut == 0) { return rv; }
size_t numRead;
size_t numWritten;
rv = ParseData(mUncompressedBuffer.get(), snappy::kBlockSize, mNextChunkType,
mCompressedBuffer.get(), mNextChunkDataLength,
&numWritten, &numRead);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
MOZ_ASSERT(numRead == mNextChunkDataLength);
mUncompressedBytes = numWritten;
// If we were unable to directly read the next chunk header, then clear
// our internal state. We will have to perform a small read to get the
// header the next time we enter this method.
if (*aBytesReadOut <= mNextChunkDataLength) {
mNextChunkType = Unknown;
mNextChunkDataLength = 0;
return NS_OK;
}
// We got the next chunk header. Parse it so that we are ready to for the
// next call into this method.
rv = ParseHeader(&mCompressedBuffer[numRead], *aBytesReadOut - numRead,
&mNextChunkType, &mNextChunkDataLength);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
return NS_OK;
}
nsresult
SnappyUncompressInputStream::ReadAll(char* aBuf, uint32_t aCount,
uint32_t aMinValidCount,
uint32_t* aBytesReadOut)
{
MOZ_ASSERT(aCount >= aMinValidCount);
*aBytesReadOut = 0;
if (!mBaseStream) {
return NS_BASE_STREAM_CLOSED;
}
uint32_t offset = 0;
while (aCount > 0) {
uint32_t bytesRead = 0;
nsresult rv = mBaseStream->Read(aBuf + offset, aCount, &bytesRead);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
// EOF, but don't immediately return. We need to validate min read bytes
// below.
if (bytesRead == 0) {
break;
}
*aBytesReadOut += bytesRead;
offset += bytesRead;
aCount -= bytesRead;
}
// Reading zero bytes is not an error. Its the expected EOF condition.
// Only compare to the minimum valid count if we read at least one byte.
if (*aBytesReadOut != 0 && *aBytesReadOut < aMinValidCount) {
return NS_ERROR_CORRUPTED_CONTENT;
}
return NS_OK;
}
size_t
SnappyUncompressInputStream::UncompressedLength() const
{
MOZ_ASSERT(mNextByte <= mUncompressedBytes);
return mUncompressedBytes - mNextByte;
}
} // namespace mozilla

View file

@ -0,0 +1,90 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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_SnappyUncompressInputStream_h__
#define mozilla_SnappyUncompressInputStream_h__
#include "mozilla/Attributes.h"
#include "mozilla/UniquePtr.h"
#include "nsCOMPtr.h"
#include "nsIInputStream.h"
#include "nsISupportsImpl.h"
#include "SnappyFrameUtils.h"
namespace mozilla {
class SnappyUncompressInputStream final : public nsIInputStream
, protected detail::SnappyFrameUtils
{
public:
// Construct a new blocking stream to uncompress the given base stream. The
// base stream must also be blocking. The base stream does not have to be
// buffered.
explicit SnappyUncompressInputStream(nsIInputStream* aBaseStream);
private:
virtual ~SnappyUncompressInputStream();
// Parse the next chunk of data. This may populate mBuffer and set
// mBufferFillSize. This should not be called when mBuffer already
// contains data.
nsresult ParseNextChunk(uint32_t* aBytesReadOut);
// Convenience routine to Read() from the base stream until we get
// the given number of bytes or reach EOF.
//
// aBuf - The buffer to write the bytes into.
// aCount - Max number of bytes to read. If the stream closes
// fewer bytes my be read.
// aMinValidCount - A minimum expected number of bytes. If we find
// fewer than this many bytes, then return
// NS_ERROR_CORRUPTED_CONTENT. If nothing was read due
// due to EOF (aBytesReadOut == 0), then NS_OK is returned.
// aBytesReadOut - An out parameter indicating how many bytes were read.
nsresult ReadAll(char* aBuf, uint32_t aCount, uint32_t aMinValidCount,
uint32_t* aBytesReadOut);
// Convenience routine to determine how many bytes of uncompressed data
// we currently have in our buffer.
size_t UncompressedLength() const;
nsCOMPtr<nsIInputStream> mBaseStream;
// Buffer to hold compressed data. Must copy here since we need a large
// flat buffer to run the uncompress process on. Always the same length
// of SnappyFrameUtils::MaxCompressedBufferLength(snappy::kBlockSize)
// bytes long.
mozilla::UniquePtr<char[]> mCompressedBuffer;
// Buffer storing the resulting uncompressed data. Exactly snappy::kBlockSize
// bytes long.
mozilla::UniquePtr<char[]> mUncompressedBuffer;
// Number of bytes of uncompressed data in mBuffer.
size_t mUncompressedBytes;
// Next byte of mBuffer to return in ReadSegments(). Must be less than
// mBufferFillSize
size_t mNextByte;
// Next chunk in the stream that has been parsed during read-ahead.
ChunkType mNextChunkType;
// Length of next chunk's length that has been determined during read-ahead.
size_t mNextChunkDataLength;
// The stream must begin with a StreamIdentifier chunk. Are we still
// expecting it?
bool mNeedFirstStreamIdentifier;
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
};
} // namespace mozilla
#endif // mozilla_SnappyUncompressInputStream_h__

View file

@ -0,0 +1,830 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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>
#include "mozilla/WindowsVersion.h"
using mozilla::IsWin7OrLater;
#elif defined(XP_UNIX)
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/param.h>
#include "prenv.h"
#if defined(MOZ_WIDGET_COCOA)
#include "CocoaFileUtils.h"
#endif
#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 = nullptr;
#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* aGuid, nsIFile** aFile)
{
if (!aGuid || !gGetKnownFolderPath) {
return NS_ERROR_FAILURE;
}
PWSTR path = nullptr;
gGetKnownFolderPath(*aGuid, 0, nullptr, &path);
if (!path) {
return NS_ERROR_FAILURE;
}
nsresult rv = NS_NewLocalFile(nsDependentString(path),
true,
aFile);
CoTaskMemFree(path);
return rv;
}
static nsresult
GetWindowsFolder(int aFolder, nsIFile** aFile)
{
WCHAR path_orig[MAX_PATH + 3];
WCHAR* path = path_orig + 1;
HRESULT result = SHGetSpecialFolderPathW(nullptr, path, aFolder, 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 = nullptr;
IShellLibrary* plib;
HRESULT hr = CoCreateInstance(CLSID_ShellLibrary, nullptr,
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.
if (!IsWin7OrLater()) {
return GetWindowsFolder(aFallbackFolderId, aFile);
}
RefPtr<IShellLibrary> shellLib;
RefPtr<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)))) {
wchar_t* str = nullptr;
if (SUCCEEDED(savePath->GetDisplayName(SIGDN_FILESYSPATH, &str))) {
nsAutoString path;
path.Assign(str);
path.Append('\\');
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"),
nullptr, &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)
{
#if 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* aType)
{
FILE* file;
char* home_dir;
char* config_home;
char* config_file;
char buffer[512];
char* user_dir;
char* p;
char* d;
int len;
int relative;
home_dir = getenv("HOME");
if (!home_dir) {
goto error;
}
config_home = getenv("XDG_CONFIG_HOME");
if (!config_home || config_home[0] == 0) {
config_file = (char*)malloc(strlen(home_dir) +
strlen("/.config/user-dirs.dirs") + 1);
if (!config_file) {
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) {
goto error;
}
strcpy(config_file, config_home);
strcat(config_file, "/user-dirs.dirs");
}
file = fopen(config_file, "r");
free(config_file);
if (!file) {
goto error;
}
user_dir = nullptr;
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, aType, strlen(aType)) != 0) {
continue;
}
p += strlen(aType);
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) {
goto error2;
}
strcpy(user_dir, home_dir);
strcat(user_dir, "/");
} else {
user_dir = (char*)malloc(strlen(p) + 1);
if (!user_dir) {
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 nullptr;
}
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"));
} 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);
#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] == char16_t(':') && path[2] == char16_t('\\')) {
path[3] = 0;
}
return NS_NewLocalFile(nsDependentString(path),
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(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;
}
#if defined(MOZ_CONTENT_SANDBOX)
case Win_LocalAppdataLow: {
// This should only really fail on versions pre-Vista, in which case this
// shouldn't have been used in the first place.
GUID localAppDataLowGuid = FOLDERID_LocalAppDataLow;
return GetKnownFolder(&localAppDataLowGuid, aFile);
}
#endif
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
default:
break;
}
return NS_ERROR_NOT_AVAILABLE;
}
#if defined (MOZ_WIDGET_COCOA)
nsresult
GetOSXFolderType(short aDomain, OSType aFolderType, nsIFile** aLocalFile)
{
nsresult rv = NS_ERROR_FAILURE;
if (aFolderType == kTemporaryFolderType) {
NS_NewLocalFile(EmptyString(), true, aLocalFile);
nsCOMPtr<nsILocalFileMac> localMacFile(do_QueryInterface(*aLocalFile));
if (localMacFile) {
rv = localMacFile->InitWithCFURL(
CocoaFileUtils::GetTemporaryFolderCFURLRef());
}
return rv;
}
OSErr err;
FSRef fsRef;
err = ::FSFindFolder(aDomain, aFolderType, kCreateFolder, &fsRef);
if (err == noErr) {
NS_NewLocalFile(EmptyString(), true, aLocalFile);
nsCOMPtr<nsILocalFileMac> localMacFile(do_QueryInterface(*aLocalFile));
if (localMacFile) {
rv = localMacFile->InitWithFSRef(&fsRef);
}
}
return rv;
}
#endif

View file

@ -0,0 +1,107 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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,
#if defined(MOZ_CONTENT_SANDBOX)
Win_LocalAppdataLow = 236,
#endif
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
};
nsresult
GetSpecialSystemDirectory(SystemDirectories aSystemSystemDirectory,
nsIFile** aFile);
#ifdef MOZ_WIDGET_COCOA
nsresult
GetOSXFolderType(short aDomain, OSType aFolderType, nsIFile** aLocalFile);
#endif
#endif

154
xpcom/io/crc32c.c Normal file
View file

@ -0,0 +1,154 @@
/*
* Based on file found here:
*
* https://svnweb.freebsd.org/base/stable/10/sys/libkern/crc32.c?revision=256281
*/
/*-
* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
* code or tables extracted from it, as desired without restriction.
*/
/*
* First, the polynomial itself and its table of feedback terms. The
* polynomial is
* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
*
* Note that we take it "backwards" and put the highest-order term in
* the lowest-order bit. The X^32 term is "implied"; the LSB is the
* X^31 term, etc. The X^0 term (usually shown as "+1") results in
* the MSB being 1
*
* Note that the usual hardware shift register implementation, which
* is what we're using (we're merely optimizing it by doing eight-bit
* chunks at a time) shifts bits into the lowest-order term. In our
* implementation, that means shifting towards the right. Why do we
* do it this way? Because the calculated CRC must be transmitted in
* order from highest-order term to lowest-order term. UARTs transmit
* characters in order from LSB to MSB. By storing the CRC this way
* we hand it to the UART in the order low-byte to high-byte; the UART
* sends each low-bit to hight-bit; and the result is transmission bit
* by bit from highest- to lowest-order term without requiring any bit
* shuffling on our part. Reception works similarly
*
* The feedback terms table consists of 256, 32-bit entries. Notes
*
* The table can be generated at runtime if desired; code to do so
* is shown later. It might not be obvious, but the feedback
* terms simply represent the results of eight shift/xor opera
* tions for all combinations of data and CRC register values
*
* The values must be right-shifted by eight bits by the "updcrc
* logic; the shift must be unsigned (bring in zeroes). On some
* hardware you could probably optimize the shift in assembler by
* using byte-swap instructions
* polynomial $edb88320
*
*
* CRC32 code derived from work by Gary S. Brown.
*/
#include "crc32c.h"
/* CRC32C routines, these use a different polynomial */
/*****************************************************************/
/* */
/* CRC LOOKUP TABLE */
/* ================ */
/* The following CRC lookup table was generated automagically */
/* by the Rocksoft^tm Model CRC Algorithm Table Generation */
/* Program V1.0 using the following model parameters: */
/* */
/* Width : 4 bytes. */
/* Poly : 0x1EDC6F41L */
/* Reverse : TRUE. */
/* */
/* For more information on the Rocksoft^tm Model CRC Algorithm, */
/* see the document titled "A Painless Guide to CRC Error */
/* Detection Algorithms" by Ross Williams */
/* (ross@guest.adelaide.edu.au.). This document is likely to be */
/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */
/* */
/*****************************************************************/
static const uint32_t crc32Table[256] = {
0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
};
// NOTE: See source URL at top of this file for multitable implementation which
// offers a performance boost at the cost of ~8KB of static tables.
uint32_t
ComputeCrc32c(uint32_t crc, const void *buf, size_t size)
{
const uint8_t *p = buf;
while (size--)
crc = crc32Table[(crc ^ *p++) & 0xff] ^ (crc >> 8);
return crc;
}

23
xpcom/io/crc32c.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef crc32c_h
#define crc32c_h
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
// Compute a CRC32c as defined in RFC3720. This is a different polynomial than
// what is used in the crc for zlib, etc. Typical usage to calculate a new CRC:
//
// ComputeCrc32c(~0, buffer, bufferLength);
//
uint32_t
ComputeCrc32c(uint32_t aCrc, const void *aBuf, size_t aSize);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // crc32c_h

140
xpcom/io/moz.build Normal file
View file

@ -0,0 +1,140 @@
# -*- Mode: python; 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',
'nsICloneableInputStream.idl',
'nsIConverterInputStream.idl',
'nsIConverterOutputStream.idl',
'nsIDirectoryEnumerator.idl',
'nsIDirectoryService.idl',
'nsIFile.idl',
'nsIInputStream.idl',
'nsIInputStreamTee.idl',
'nsIIOUtil.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',
'nsIStorageStream.idl',
'nsIStreamBufferAccess.idl',
'nsIStringStream.idl',
'nsIUnicharInputStream.idl',
'nsIUnicharLineInputStream.idl',
'nsIUnicharOutputStream.idl',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
XPIDL_SOURCES += [
'nsILocalFileMac.idl',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
EXPORTS += ['nsLocalFileWin.h']
EXPORTS.mozilla += [
'FileUtilsWin.h',
]
SOURCES += [
'FileUtilsWin.cpp',
'nsLocalFileWin.cpp',
]
else:
EXPORTS += ['nsLocalFileUnix.h']
SOURCES += [
'nsLocalFileUnix.cpp',
]
XPIDL_MODULE = 'xpcom_io'
EXPORTS += [
'nsAnonymousTemporaryFile.h',
'nsAppDirectoryServiceDefs.h',
'nsDirectoryService.h',
'nsDirectoryServiceAtomList.h',
'nsDirectoryServiceDefs.h',
'nsDirectoryServiceUtils.h',
'nsEscape.h',
'nsLinebreakConverter.h',
'nsLocalFile.h',
'nsMultiplexInputStream.h',
'nsNativeCharsetUtils.h',
'nsScriptableInputStream.h',
'nsStorageStream.h',
'nsStreamUtils.h',
'nsStringStream.h',
'nsUnicharInputStream.h',
'nsWildCard.h',
'SlicedInputStream.h',
'SpecialSystemDirectory.h',
]
EXPORTS.mozilla += [
'Base64.h',
'SnappyCompressOutputStream.h',
'SnappyFrameUtils.h',
'SnappyUncompressInputStream.h',
]
UNIFIED_SOURCES += [
'Base64.cpp',
'crc32c.c',
'nsAnonymousTemporaryFile.cpp',
'nsAppFileLocationProvider.cpp',
'nsBinaryStream.cpp',
'nsDirectoryService.cpp',
'nsEscape.cpp',
'nsInputStreamTee.cpp',
'nsIOUtil.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',
'SlicedInputStream.cpp',
'SnappyCompressOutputStream.cpp',
'SnappyFrameUtils.cpp',
'SnappyUncompressInputStream.cpp',
'SpecialSystemDirectory.cpp',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
SOURCES += [
'CocoaFileUtils.mm',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
if CONFIG['OS_ARCH'] == 'Linux' and 'lib64' in CONFIG['libdir']:
DEFINES['HAVE_USR_LIB64_DIR'] = True
LOCAL_INCLUDES += ['!..']
if CONFIG['_MSC_VER']:
# This is intended as a temporary hack to support building with VS2015.
# '_snwprintf' : format string '%s' requires an argument of type 'wchar_t *',
# but variadic argument 3 has type 'char16ptr_t'
CXXFLAGS += ['-wd4477']

View file

@ -0,0 +1,314 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/dom/ContentChild.h"
#include "mozilla/SyncRunnable.h"
#include "nsAnonymousTemporaryFile.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsXULAppAPI.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsAppDirectoryServiceDefs.h"
#include "prio.h"
#include "private/pprio.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"
#endif
using namespace mozilla;
// 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)
{
if (NS_WARN_IF(!aTempDir)) {
return NS_ERROR_INVALID_ARG;
}
nsCOMPtr<nsIFile> tmpFile;
nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile));
if (NS_WARN_IF(NS_FAILED(rv))) {
return 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"));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700);
if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
#endif
tmpFile.forget(aTempDir);
return NS_OK;
}
namespace {
class nsRemoteAnonymousTemporaryFileRunnable : public Runnable
{
public:
dom::FileDescOrError *mResultPtr;
explicit nsRemoteAnonymousTemporaryFileRunnable(dom::FileDescOrError *aResultPtr)
: mResultPtr(aResultPtr)
{ }
protected:
NS_IMETHOD Run() override {
dom::ContentChild* child = dom::ContentChild::GetSingleton();
MOZ_ASSERT(child);
child->SendOpenAnonymousTemporaryFile(mResultPtr);
return NS_OK;
}
};
} // namespace
nsresult
NS_OpenAnonymousTemporaryFile(PRFileDesc** aOutFileDesc)
{
if (NS_WARN_IF(!aOutFileDesc)) {
return NS_ERROR_INVALID_ARG;
}
if (dom::ContentChild* child = dom::ContentChild::GetSingleton()) {
dom::FileDescOrError fd = NS_OK;
if (NS_IsMainThread()) {
child->SendOpenAnonymousTemporaryFile(&fd);
} else {
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
MOZ_ASSERT(mainThread);
SyncRunnable::DispatchToThread(mainThread,
new nsRemoteAnonymousTemporaryFileRunnable(&fd));
}
if (fd.type() == dom::FileDescOrError::Tnsresult) {
nsresult rv = fd.get_nsresult();
MOZ_ASSERT(NS_FAILED(rv));
return rv;
}
auto rawFD = fd.get_FileDescriptor().ClonePlatformHandle();
*aOutFileDesc = PR_ImportFile(PROsfd(rawFD.release()));
return NS_OK;
}
nsresult rv;
nsCOMPtr<nsIFile> tmpFile;
rv = GetTempDir(getter_AddRefs(tmpFile));
if (NS_WARN_IF(NS_FAILED(rv))) {
return 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);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0700);
if (NS_WARN_IF(NS_FAILED(rv))) {
return 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 final : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
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);
if (NS_WARN_IF(!mTimer)) {
return NS_ERROR_FAILURE;
}
nsresult rv = mTimer->Init(this,
SCHEDULE_TIMEOUT_MS,
nsITimer::TYPE_ONE_SHOT);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Register shutdown observer so we can cancel the timer if we shutdown before
// the timer runs.
nsCOMPtr<nsIObserverService> obsSrv = services::GetObserverService();
if (NS_WARN_IF(!obsSrv)) {
return 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 char16_t* 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));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
// Remove the directory recursively.
tmpDir->Remove(true);
}
private:
~nsAnonTempFileRemover() {}
nsCOMPtr<nsITimer> mTimer;
};
NS_IMPL_ISUPPORTS(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_IsParentProcess()) {
return NS_OK;
}
RefPtr<nsAnonTempFileRemover> tempRemover = new nsAnonTempFileRemover();
return tempRemover->Init();
}
#endif

View file

@ -0,0 +1,31 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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);

View file

@ -0,0 +1,118 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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_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"
#define NS_APP_DISTRIBUTION_SEARCH_DIR_LIST "SrchPluginsDistDL"
// --------------------------------------------------------------------------------------
// 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_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_INDEXEDDB_PARENT_DIR "indexedDBPDir"
#define NS_APP_PERMISSION_PARENT_DIR "permissionDBPDir"
#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
//
// NS_APP_CONTENT_PROCESS_TEMP_DIR refers to a directory that is read and
// write accessible from a sandboxed content process. The key may be used in
// either process, but the directory is intended to be used for short-lived
// files that need to be saved to the filesystem by the content process and
// don't need to survive browser restarts. The directory is reset on startup.
// The key is only valid when MOZ_CONTENT_SANDBOX is defined. When
// MOZ_CONTENT_SANDBOX is defined, the directory the key refers to differs
// depending on whether or not content sandboxing is enabled.
//
// When MOZ_CONTENT_SANDBOX is defined and sandboxing is enabled (versus
// manually disabled via prefs), the content process replaces NS_OS_TEMP_DIR
// with NS_APP_CONTENT_PROCESS_TEMP_DIR so that legacy code in content
// attempting to write to NS_OS_TEMP_DIR will write to
// NS_APP_CONTENT_PROCESS_TEMP_DIR instead. When MOZ_CONTENT_SANDBOX is
// defined but sandboxing is disabled, NS_APP_CONTENT_PROCESS_TEMP_DIR
// falls back to NS_OS_TEMP_DIR in both content and chrome processes.
//
// New code should avoid writing to the filesystem from the content process
// and should instead proxy through the parent process whenever possible.
//
// At present, all sandboxed content processes use the same directory for
// NS_APP_CONTENT_PROCESS_TEMP_DIR, but that should not be relied upon.
//
#define NS_APP_CONTENT_PROCESS_TEMP_DIR "ContentTmpD"
#else
// Otherwise NS_APP_CONTENT_PROCESS_TEMP_DIR must match NS_OS_TEMP_DIR.
#define NS_APP_CONTENT_PROCESS_TEMP_DIR "TmpD"
#endif // (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
#endif // nsAppDirectoryServiceDefs_h___

View file

@ -0,0 +1,609 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "nsEnumeratorUtils.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_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)
#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 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_ISUPPORTS(nsAppFileLocationProvider,
nsIDirectoryServiceProvider,
nsIDirectoryServiceProvider2)
//*****************************************************************************
// nsAppFileLocationProvider::nsIDirectoryServiceProvider
//*****************************************************************************
NS_IMETHODIMP
nsAppFileLocationProvider::GetFile(const char* aProp, bool* aPersistent,
nsIFile** aResult)
{
if (NS_WARN_IF(!aProp)) {
return NS_ERROR_INVALID_ARG;
}
nsCOMPtr<nsIFile> localFile;
nsresult rv = NS_ERROR_FAILURE;
*aResult = nullptr;
*aPersistent = true;
#ifdef MOZ_WIDGET_COCOA
FSRef fileRef;
nsCOMPtr<nsILocalFileMac> macFile;
#endif
if (nsCRT::strcmp(aProp, NS_APP_APPLICATION_REGISTRY_DIR) == 0) {
rv = GetProductDirectory(getter_AddRefs(localFile));
} else if (nsCRT::strcmp(aProp, 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(aProp, 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(aProp, 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(aProp, NS_APP_USER_PROFILES_ROOT_DIR) == 0) {
rv = GetDefaultUserProfileRoot(getter_AddRefs(localFile));
} else if (nsCRT::strcmp(aProp, NS_APP_USER_PROFILES_LOCAL_ROOT_DIR) == 0) {
rv = GetDefaultUserProfileRoot(getter_AddRefs(localFile), true);
} else if (nsCRT::strcmp(aProp, NS_APP_RES_DIR) == 0) {
rv = CloneMozBinDirectory(getter_AddRefs(localFile));
if (NS_SUCCEEDED(rv)) {
rv = localFile->AppendRelativeNativePath(RES_DIR_NAME);
}
} else if (nsCRT::strcmp(aProp, NS_APP_CHROME_DIR) == 0) {
rv = CloneMozBinDirectory(getter_AddRefs(localFile));
if (NS_SUCCEEDED(rv)) {
rv = localFile->AppendRelativeNativePath(CHROME_DIR_NAME);
}
} else if (nsCRT::strcmp(aProp, 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(aProp, 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(aProp, 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(aProp, 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(aProp, 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(aProp, 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(aProp, 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(aProp, NS_APP_SEARCH_DIR) == 0) {
rv = CloneMozBinDirectory(getter_AddRefs(localFile));
if (NS_SUCCEEDED(rv)) {
rv = localFile->AppendRelativeNativePath(SEARCH_DIR_NAME);
}
} else if (nsCRT::strcmp(aProp, NS_APP_USER_SEARCH_DIR) == 0) {
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, aResult);
if (NS_SUCCEEDED(rv)) {
rv = (*aResult)->AppendNative(SEARCH_DIR_NAME);
}
} else if (nsCRT::strcmp(aProp, 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)) {
localFile.forget(aResult);
return NS_OK;
}
return rv;
}
nsresult
nsAppFileLocationProvider::CloneMozBinDirectory(nsIFile** aLocalFile)
{
if (NS_WARN_IF(!aLocalFile)) {
return NS_ERROR_INVALID_ARG;
}
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:
//----------------------------------------------------------------------------------------
nsresult
nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile,
bool aLocal)
{
if (NS_WARN_IF(!aLocalFile)) {
return NS_ERROR_INVALID_ARG;
}
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_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;
}
localDir.forget(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:
//----------------------------------------------------------------------------------------
nsresult
nsAppFileLocationProvider::GetDefaultUserProfileRoot(nsIFile** aLocalFile,
bool aLocal)
{
if (NS_WARN_IF(!aLocalFile)) {
return NS_ERROR_INVALID_ARG;
}
nsresult rv;
nsCOMPtr<nsIFile> localDir;
rv = GetProductDirectory(getter_AddRefs(localDir), aLocal);
if (NS_FAILED(rv)) {
return rv;
}
#if defined(MOZ_WIDGET_COCOA) || 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
localDir.forget(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* aResult) override
{
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;
}
}
*aResult = mNext != nullptr;
return NS_OK;
}
NS_IMETHOD GetNext(nsISupports** aResult) override
{
if (NS_WARN_IF(!aResult)) {
return NS_ERROR_INVALID_ARG;
}
*aResult = nullptr;
bool hasMore;
HasMoreElements(&hasMore);
if (!hasMore) {
return NS_ERROR_FAILURE;
}
*aResult = mNext;
NS_IF_ADDREF(*aResult);
mNext = nullptr;
return *aResult ? NS_OK : NS_ERROR_FAILURE;
}
protected:
nsCOMPtr<nsIDirectoryServiceProvider> mProvider;
const char** mCurrentKey;
nsCOMPtr<nsIFile> mNext;
// Virtual destructor since subclass nsPathsDirectoryEnumerator
// does not re-implement Release()
virtual ~nsAppDirectoryEnumerator()
{
}
};
NS_IMPL_ISUPPORTS(nsAppDirectoryEnumerator, nsISimpleEnumerator)
/* nsPathsDirectoryEnumerator and PATH_SEPARATOR
* are not used on MacOS/X. */
#if defined(XP_WIN) /* Win32 */
#define PATH_SEPARATOR ';'
#else
#define PATH_SEPARATOR ':'
#endif
class nsPathsDirectoryEnumerator final
: public nsAppDirectoryEnumerator
{
~nsPathsDirectoryEnumerator() {}
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* aResult)
{
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) {
*aResult = true;
} else {
nsAppDirectoryEnumerator::HasMoreElements(aResult);
}
return NS_OK;
}
protected:
const char* mEndPath;
};
NS_IMETHODIMP
nsAppFileLocationProvider::GetFiles(const char* aProp,
nsISimpleEnumerator** aResult)
{
if (NS_WARN_IF(!aResult)) {
return NS_ERROR_INVALID_ARG;
}
*aResult = nullptr;
nsresult rv = NS_ERROR_FAILURE;
if (!nsCRT::strcmp(aProp, 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
};
*aResult = 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;
}
*aResult = new nsPathsDirectoryEnumerator(this, keys);
#endif
NS_ADDREF(*aResult);
rv = NS_OK;
}
if (!nsCRT::strcmp(aProp, NS_APP_SEARCH_DIR_LIST)) {
static const char* keys[] = { nullptr, 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;
}
*aResult = new nsPathsDirectoryEnumerator(this, keys);
NS_ADDREF(*aResult);
rv = NS_OK;
}
if (!strcmp(aProp, NS_APP_DISTRIBUTION_SEARCH_DIR_LIST)) {
return NS_NewEmptyEnumerator(aResult);
}
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

View file

@ -0,0 +1,55 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 nsAppFileLocationProvider_h
#define nsAppFileLocationProvider_h
#include "nsIDirectoryService.h"
#include "nsIFile.h"
#include "mozilla/Attributes.h"
class nsIFile;
//*****************************************************************************
// class nsAppFileLocationProvider
//*****************************************************************************
class nsAppFileLocationProvider final : public nsIDirectoryServiceProvider2
{
public:
nsAppFileLocationProvider();
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIDIRECTORYSERVICEPROVIDER
NS_DECL_NSIDIRECTORYSERVICEPROVIDER2
private:
~nsAppFileLocationProvider()
{
}
protected:
nsresult 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)
*/
nsresult GetProductDirectory(nsIFile** aLocalFile,
bool aLocal = false);
nsresult GetDefaultUserProfileRoot(nsIFile** aLocalFile,
bool aLocal = false);
#if defined(MOZ_WIDGET_COCOA)
static bool IsOSXLeopard();
#endif
nsCOMPtr<nsIFile> mMozBinDirectory;
};
#endif

1016
xpcom/io/nsBinaryStream.cpp Normal file

File diff suppressed because it is too large Load diff

101
xpcom/io/nsBinaryStream.h Normal file
View file

@ -0,0 +1,101 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 final : public nsIObjectOutputStream
{
public:
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;
private:
// virtual dtor since subclasses call our Release()
virtual ~nsBinaryOutputStream()
{
}
};
#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"
class nsBinaryInputStream final : public nsIObjectInputStream
{
public:
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;
private:
// virtual dtor since subclasses call our Release()
virtual ~nsBinaryInputStream()
{
}
};
#endif // nsBinaryStream_h___

View file

@ -0,0 +1,766 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/ArrayUtils.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
#endif
#include "SpecialSystemDirectory.h"
#include "nsAppFileLocationProvider.h"
using namespace mozilla;
// 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
#endif
//----------------------------------------------------------------------------------------
nsresult
nsDirectoryService::GetCurrentProcessDirectory(nsIFile** aFile)
//----------------------------------------------------------------------------------------
{
if (NS_WARN_IF(!aFile)) {
return NS_ERROR_INVALID_ARG;
}
*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> localFile;
dirService->Get(NS_XPCOM_INIT_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile),
getter_AddRefs(localFile));
if (localFile) {
localFile.forget(aFile);
return NS_OK;
}
}
RefPtr<nsLocalFile> localFile = new nsLocalFile;
#ifdef XP_WIN
wchar_t 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
wchar_t* lastSlash = wcsrchr(buf, L'\\');
if (lastSlash) {
*(lastSlash + 1) = L'\0';
}
localFile->InitWithPath(nsDependentString(buf));
localFile.forget(aFile);
return NS_OK;
}
#elif defined(MOZ_WIDGET_COCOA)
// Works even if we're not bundled.
CFBundleRef appBundle = CFBundleGetMainBundle();
if (appBundle) {
CFURLRef bundleURL = CFBundleCopyExecutableURL(appBundle);
if (bundleURL) {
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)) {
localFile.forget(aFile);
}
}
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));
localFile.forget(aFile);
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));
localFile.forget(aFile);
return NS_OK;
}
#endif
NS_ERROR("unable to get current process directory");
return NS_ERROR_FAILURE;
} // GetCurrentProcessDirectory()
StaticRefPtr<nsDirectoryService> nsDirectoryService::gService;
nsDirectoryService::nsDirectoryService()
: mHashtable(128)
{
}
nsresult
nsDirectoryService::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
{
if (NS_WARN_IF(!aResult)) {
return NS_ERROR_INVALID_ARG;
}
if (NS_WARN_IF(aOuter)) {
return NS_ERROR_NO_AGGREGATION;
}
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!");
gService = new nsDirectoryService();
NS_RegisterStaticAtoms(directory_atoms);
// Let the list hold the only reference to the provider.
nsAppFileLocationProvider* defaultProvider = new nsAppFileLocationProvider;
gService->mProviders.AppendElement(defaultProvider);
}
nsDirectoryService::~nsDirectoryService()
{
}
NS_IMPL_ISUPPORTS(nsDirectoryService,
nsIProperties,
nsIDirectoryService,
nsIDirectoryServiceProvider,
nsIDirectoryServiceProvider2)
NS_IMETHODIMP
nsDirectoryService::Undefine(const char* aProp)
{
if (NS_WARN_IF(!aProp)) {
return NS_ERROR_INVALID_ARG;
}
nsDependentCString key(aProp);
if (!mHashtable.Get(key, nullptr)) {
return NS_ERROR_FAILURE;
}
mHashtable.Remove(key);
return NS_OK;
}
NS_IMETHODIMP
nsDirectoryService::GetKeys(uint32_t* aCount, char*** aKeys)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
struct MOZ_STACK_CLASS FileData
{
FileData(const char* aProperty, const nsIID& aUUID)
: property(aProperty)
, data(nullptr)
, persistent(true)
, uuid(aUUID)
{
}
const char* property;
nsCOMPtr<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.get(), newFiles);
if (unionFiles) {
unionFiles.swap(*(nsISimpleEnumerator**)&aData->data);
}
} else {
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* aProp, const nsIID& aUuid, void** aResult)
{
if (NS_WARN_IF(!aProp)) {
return NS_ERROR_INVALID_ARG;
}
nsDependentCString key(aProp);
nsCOMPtr<nsIFile> cachedFile = mHashtable.Get(key);
if (cachedFile) {
nsCOMPtr<nsIFile> cloneFile;
cachedFile->Clone(getter_AddRefs(cloneFile));
return cloneFile->QueryInterface(aUuid, aResult);
}
// it is not one of our defaults, lets check any providers
FileData fileData(aProp, aUuid);
for (int32_t i = mProviders.Length() - 1; i >= 0; i--) {
if (!FindProviderFile(mProviders[i], &fileData)) {
break;
}
}
if (fileData.data) {
if (fileData.persistent) {
Set(aProp, static_cast<nsIFile*>(fileData.data.get()));
}
nsresult rv = (fileData.data)->QueryInterface(aUuid, aResult);
fileData.data = nullptr; // AddRef occurs in FindProviderFile()
return rv;
}
FindProviderFile(static_cast<nsIDirectoryServiceProvider*>(this), &fileData);
if (fileData.data) {
if (fileData.persistent) {
Set(aProp, static_cast<nsIFile*>(fileData.data.get()));
}
nsresult rv = (fileData.data)->QueryInterface(aUuid, aResult);
fileData.data = nullptr; // AddRef occurs in FindProviderFile()
return rv;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsDirectoryService::Set(const char* aProp, nsISupports* aValue)
{
if (NS_WARN_IF(!aProp)) {
return NS_ERROR_INVALID_ARG;
}
nsDependentCString key(aProp);
if (mHashtable.Get(key, nullptr) || !aValue) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIFile> ourFile = do_QueryInterface(aValue);
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* aProp, bool* aResult)
{
if (NS_WARN_IF(!aProp)) {
return NS_ERROR_INVALID_ARG;
}
*aResult = false;
nsCOMPtr<nsIFile> value;
nsresult rv = Get(aProp, NS_GET_IID(nsIFile), getter_AddRefs(value));
if (NS_FAILED(rv)) {
return NS_OK;
}
if (value) {
*aResult = true;
}
return rv;
}
NS_IMETHODIMP
nsDirectoryService::RegisterProvider(nsIDirectoryServiceProvider* aProv)
{
if (!aProv) {
return NS_ERROR_FAILURE;
}
mProviders.AppendElement(aProv);
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* aProv)
{
if (!aProv) {
return NS_ERROR_FAILURE;
}
mProviders.RemoveElement(aProv);
return NS_OK;
}
#if defined(MOZ_CONTENT_SANDBOX) && defined(XP_WIN)
static nsresult
GetLowIntegrityTempBase(nsIFile** aLowIntegrityTempBase)
{
nsCOMPtr<nsIFile> localFile;
nsresult rv = GetSpecialSystemDirectory(Win_LocalAppdataLow,
getter_AddRefs(localFile));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = localFile->Append(NS_LITERAL_STRING(MOZ_USER_DIR));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
localFile.forget(aLowIntegrityTempBase);
return rv;
}
#endif
// 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* aProp, bool* aPersistent,
nsIFile** aResult)
{
nsCOMPtr<nsIFile> localFile;
nsresult rv = NS_ERROR_FAILURE;
*aResult = nullptr;
*aPersistent = true;
nsCOMPtr<nsIAtom> inAtom = NS_Atomize(aProp);
// 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 ||
inAtom == nsDirectoryService::sGRE_BinDirectory) {
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));
#if defined(MOZ_CONTENT_SANDBOX)
} else if (inAtom == nsDirectoryService::sLocalAppdataLow) {
rv = GetSpecialSystemDirectory(Win_LocalAppdataLow, getter_AddRefs(localFile));
} else if (inAtom == nsDirectoryService::sLowIntegrityTempBase) {
rv = GetLowIntegrityTempBase(getter_AddRefs(localFile));
#endif
} 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));
*aPersistent = false;
} else if (inAtom == nsDirectoryService::sXDGDocuments) {
rv = GetSpecialSystemDirectory(Unix_XDG_Documents, getter_AddRefs(localFile));
*aPersistent = false;
} else if (inAtom == nsDirectoryService::sXDGDownload ||
inAtom == nsDirectoryService::sDefaultDownloadDirectory) {
rv = GetSpecialSystemDirectory(Unix_XDG_Download, getter_AddRefs(localFile));
*aPersistent = false;
} else if (inAtom == nsDirectoryService::sXDGMusic) {
rv = GetSpecialSystemDirectory(Unix_XDG_Music, getter_AddRefs(localFile));
*aPersistent = false;
} else if (inAtom == nsDirectoryService::sXDGPictures) {
rv = GetSpecialSystemDirectory(Unix_XDG_Pictures, getter_AddRefs(localFile));
*aPersistent = false;
} else if (inAtom == nsDirectoryService::sXDGPublicShare) {
rv = GetSpecialSystemDirectory(Unix_XDG_PublicShare, getter_AddRefs(localFile));
*aPersistent = false;
} else if (inAtom == nsDirectoryService::sXDGTemplates) {
rv = GetSpecialSystemDirectory(Unix_XDG_Templates, getter_AddRefs(localFile));
*aPersistent = false;
} else if (inAtom == nsDirectoryService::sXDGVideos) {
rv = GetSpecialSystemDirectory(Unix_XDG_Videos, getter_AddRefs(localFile));
*aPersistent = false;
}
#endif
if (NS_FAILED(rv)) {
return rv;
}
if (!localFile) {
return NS_ERROR_FAILURE;
}
localFile.forget(aResult);
return NS_OK;
}
NS_IMETHODIMP
nsDirectoryService::GetFiles(const char* aProp, nsISimpleEnumerator** aResult)
{
if (NS_WARN_IF(!aResult)) {
return NS_ERROR_INVALID_ARG;
}
*aResult = nullptr;
return NS_ERROR_FAILURE;
}

View file

@ -0,0 +1,66 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "nsInterfaceHashtable.h"
#include "nsIFile.h"
#include "nsIAtom.h"
#include "nsTArray.h"
#include "mozilla/Attributes.h"
#include "mozilla/StaticPtr.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 final
: public nsIDirectoryService
, public nsIProperties
, public nsIDirectoryServiceProvider2
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIPROPERTIES
NS_DECL_NSIDIRECTORYSERVICE
NS_DECL_NSIDIRECTORYSERVICEPROVIDER
NS_DECL_NSIDIRECTORYSERVICEPROVIDER2
nsDirectoryService();
static void RealInit();
void RegisterCategoryProviders();
static nsresult
Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
static mozilla::StaticRefPtr<nsDirectoryService> gService;
private:
~nsDirectoryService();
nsresult GetCurrentProcessDirectory(nsIFile** aFile);
nsInterfaceHashtable<nsCStringHashKey, nsIFile> mHashtable;
nsTArray<nsCOMPtr<nsIDirectoryServiceProvider>> mProviders;
public:
#define DIR_ATOM(name_, value_) static nsIAtom* name_;
#include "nsDirectoryServiceAtomList.h"
#undef DIR_ATOM
};
#endif

View file

@ -0,0 +1,98 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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(sGRE_BinDirectory, NS_GRE_BIN_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)
#if defined(MOZ_CONTENT_SANDBOX)
DIR_ATOM(sLocalAppdataLow, NS_WIN_LOCAL_APPDATA_LOW_DIR)
DIR_ATOM(sLowIntegrityTempBase, NS_WIN_LOW_INTEGRITY_TEMP_BASE)
#endif
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)
#endif

View file

@ -0,0 +1,168 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 GRE directory.
* On OSX, this typically points to Contents/Resources in the app bundle.
* If no GRE is used, this propery will behave like
* NS_XPCOM_CURRENT_PROCESS_DIR.
*/
#define NS_GRE_DIR "GreD"
/* Property will return the current location of the GRE-binaries directory.
* On OSX, this typically points to Contents/MacOS in the app bundle. On
* all other platforms, this will be identical to NS_GRE_DIR.
* Since this property is based on the NS_GRE_DIR, if no GRE is used, this
* propery will behave like NS_XPCOM_CURRENT_PROCESS_DIR.
*/
#define NS_GRE_BIN_DIR "GreBinD"
/* 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"
#if defined(MOZ_CONTENT_SANDBOX)
#define NS_WIN_LOCAL_APPDATA_LOW_DIR "LocalAppDataLow"
#define NS_WIN_LOW_INTEGRITY_TEMP_BASE "LowTmpDBase"
#endif
#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"
#endif
/* Deprecated */
#define NS_OS_DRIVE_DIR "DrvD"
#endif

View file

@ -0,0 +1,31 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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* aSpecialDirName, nsIFile** aResult)
{
nsresult rv;
nsCOMPtr<nsIProperties> serv(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,
&rv));
if (NS_FAILED(rv)) {
return rv;
}
return serv->Get(aSpecialDirName, NS_GET_IID(nsIFile),
reinterpret_cast<void**>(aResult));
}
#endif

633
xpcom/io/nsEscape.cpp Normal file
View file

@ -0,0 +1,633 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "nsEscape.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/BinarySearch.h"
#include "nsTArray.h"
#include "nsCRT.h"
#include "plstr.h"
static const char hexCharsUpper[] = "0123456789ABCDEF";
static const char hexCharsUpperLower[] = "0123456789ABCDEFabcdef";
static 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))] & (aFlags))
#define HEX_ESCAPE '%'
static const uint32_t ENCODE_MAX_LEN = 6; // %uABCD
static uint32_t
AppendPercentHex(char* aBuffer, unsigned char aChar)
{
uint32_t i = 0;
aBuffer[i++] = '%';
aBuffer[i++] = hexCharsUpper[aChar >> 4]; // high nibble
aBuffer[i++] = hexCharsUpper[aChar & 0xF]; // low nibble
return i;
}
static uint32_t
AppendPercentHex(char16_t* aBuffer, char16_t aChar)
{
uint32_t i = 0;
aBuffer[i++] = '%';
if (aChar & 0xff00) {
aBuffer[i++] = 'u';
aBuffer[i++] = hexCharsUpper[aChar >> 12]; // high-byte high nibble
aBuffer[i++] = hexCharsUpper[(aChar >> 8) & 0xF]; // high-byte low nibble
}
aBuffer[i++] = hexCharsUpper[(aChar >> 4) & 0xF]; // low-byte high nibble
aBuffer[i++] = hexCharsUpper[aChar & 0xF]; // low-byte low nibble
return i;
}
//----------------------------------------------------------------------------------------
char*
nsEscape(const char* aStr, size_t aLength, size_t* aOutputLength,
nsEscapeMask aFlags)
//----------------------------------------------------------------------------------------
{
if (!aStr) {
return nullptr;
}
size_t charsToEscape = 0;
const unsigned char* src = (const unsigned char*)aStr;
for (size_t i = 0; i < aLength; ++i) {
if (!IS_OK(src[i])) {
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 = aLength + 1 + charsToEscape;
if (dstSize <= aLength) {
return nullptr;
}
dstSize += charsToEscape;
if (dstSize < aLength) {
return nullptr;
}
// fail if we need more than 4GB
if (dstSize > UINT32_MAX) {
return nullptr;
}
char* result = (char*)moz_xmalloc(dstSize);
if (!result) {
return nullptr;
}
unsigned char* dst = (unsigned char*)result;
src = (const unsigned char*)aStr;
if (aFlags == url_XPAlphas) {
for (size_t i = 0; i < aLength; ++i) {
unsigned char c = *src++;
if (IS_OK(c)) {
*dst++ = c;
} else if (c == ' ') {
*dst++ = '+'; /* convert spaces to pluses */
} else {
*dst++ = HEX_ESCAPE;
*dst++ = hexCharsUpper[c >> 4]; /* high nibble */
*dst++ = hexCharsUpper[c & 0x0f]; /* low nibble */
}
}
} else {
for (size_t i = 0; i < aLength; ++i) {
unsigned char c = *src++;
if (IS_OK(c)) {
*dst++ = c;
} else {
*dst++ = HEX_ESCAPE;
*dst++ = hexCharsUpper[c >> 4]; /* high nibble */
*dst++ = hexCharsUpper[c & 0x0f]; /* low nibble */
}
}
}
*dst = '\0'; /* tack on eos */
if (aOutputLength) {
*aOutputLength = dst - (unsigned char*)result;
}
return result;
}
//----------------------------------------------------------------------------------------
char*
nsUnescape(char* aStr)
//----------------------------------------------------------------------------------------
{
nsUnescapeCount(aStr);
return aStr;
}
//----------------------------------------------------------------------------------------
int32_t
nsUnescapeCount(char* aStr)
//----------------------------------------------------------------------------------------
{
char* src = aStr;
char* dst = aStr;
char c1[] = " ";
char c2[] = " ";
char* const pc1 = c1;
char* const pc2 = c2;
if (!*src) {
// A null string was passed in. Nothing to escape.
// Returns early as the string might not actually be mutable with
// length 0.
return 0;
}
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, hexCharsUpperLower) == 0 ||
PL_strpbrk(pc2, hexCharsUpperLower) == 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 - aStr);
} /* NET_UnEscapeCnt */
char*
nsEscapeHTML(const char* aString)
{
char* rv = nullptr;
/* XXX Hardcoded max entity len. The +1 is for the trailing null. */
uint32_t len = strlen(aString);
if (len >= (UINT32_MAX / 6)) {
return nullptr;
}
rv = (char*)moz_xmalloc((6 * len) + 1);
char* ptr = rv;
if (rv) {
for (; *aString != '\0'; ++aString) {
if (*aString == '<') {
*ptr++ = '&';
*ptr++ = 'l';
*ptr++ = 't';
*ptr++ = ';';
} else if (*aString == '>') {
*ptr++ = '&';
*ptr++ = 'g';
*ptr++ = 't';
*ptr++ = ';';
} else if (*aString == '&') {
*ptr++ = '&';
*ptr++ = 'a';
*ptr++ = 'm';
*ptr++ = 'p';
*ptr++ = ';';
} else if (*aString == '"') {
*ptr++ = '&';
*ptr++ = 'q';
*ptr++ = 'u';
*ptr++ = 'o';
*ptr++ = 't';
*ptr++ = ';';
} else if (*aString == '\'') {
*ptr++ = '&';
*ptr++ = '#';
*ptr++ = '3';
*ptr++ = '9';
*ptr++ = ';';
} else {
*ptr++ = *aString;
}
}
*ptr = '\0';
}
return rv;
}
char16_t*
nsEscapeHTML2(const char16_t* 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(char16_t)) / (6 * sizeof(char16_t)))) {
return nullptr;
}
char16_t* resultBuffer = (char16_t*)moz_xmalloc(
aSourceBufferLen * 6 * sizeof(char16_t) + sizeof(char16_t('\0')));
char16_t* 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;
}
//----------------------------------------------------------------------------------------
//
// The following table encodes which characters needs to be escaped for which
// parts of an URL. The bits are the "url components" in the enum EscapeMask,
// see nsEscape.h.
//
// 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
static const uint32_t 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, 112,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,1008, 896,1008, 896,1023, // 5x PQRSTUVWXYZ[\]^_
384,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{|}~ DEL
0 // 80 to FF are zero
};
static uint16_t dontNeedEscape(unsigned char aChar, uint32_t aFlags)
{
return EscapeChars[(uint32_t)aChar] & aFlags;
}
static uint16_t dontNeedEscape(uint16_t aChar, uint32_t aFlags)
{
return aChar < mozilla::ArrayLength(EscapeChars) ?
(EscapeChars[(uint32_t)aChar] & aFlags) : 0;
}
//----------------------------------------------------------------------------------------
/**
* Templated helper for URL escaping a portion of a string.
*
* @param aPart The pointer to the beginning of the portion of the string to
* escape.
* @param aPartLen The length of the string to escape.
* @param aFlags Flags used to configure escaping. @see EscapeMask
* @param aResult String that has the URL escaped portion appended to. Only
* altered if the string is URL escaped or |esc_AlwaysCopy| is specified.
* @param aDidAppend Indicates whether or not data was appended to |aResult|.
* @return NS_ERROR_INVALID_ARG, NS_ERROR_OUT_OF_MEMORY on failure.
*/
template<class T>
static nsresult
T_EscapeURL(const typename T::char_type* aPart, size_t aPartLen,
uint32_t aFlags, T& aResult, bool& aDidAppend)
{
typedef nsCharTraits<typename T::char_type> traits;
typedef typename traits::unsigned_char_type unsigned_char_type;
static_assert(sizeof(*aPart) == 1 || sizeof(*aPart) == 2,
"unexpected char type");
if (!aPart) {
NS_NOTREACHED("null pointer");
return NS_ERROR_INVALID_ARG;
}
bool forced = !!(aFlags & esc_Forced);
bool ignoreNonAscii = !!(aFlags & esc_OnlyASCII);
bool ignoreAscii = !!(aFlags & esc_OnlyNonASCII);
bool writing = !!(aFlags & esc_AlwaysCopy);
bool colon = !!(aFlags & esc_Colon);
auto src = reinterpret_cast<const unsigned_char_type*>(aPart);
typename T::char_type tempBuffer[100];
unsigned int tempBufferPos = 0;
bool previousIsNonASCII = false;
for (size_t i = 0; i < aPartLen; ++i) {
unsigned_char_type 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 aPart 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 ((dontNeedEscape(c, aFlags) || (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) {
if (!aResult.Append(aPart, i, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
writing = true;
}
uint32_t len = ::AppendPercentHex(tempBuffer + tempBufferPos, c);
tempBufferPos += len;
MOZ_ASSERT(len <= ENCODE_MAX_LEN, "potential buffer overflow");
}
// Flush the temp buffer if it doesnt't have room for another encoded char.
if (tempBufferPos >= mozilla::ArrayLength(tempBuffer) - ENCODE_MAX_LEN) {
NS_ASSERTION(writing, "should be writing");
if (!aResult.Append(tempBuffer, tempBufferPos, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
tempBufferPos = 0;
}
previousIsNonASCII = (c > 0x7f);
}
if (writing) {
if (!aResult.Append(tempBuffer, tempBufferPos, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
aDidAppend = writing;
return NS_OK;
}
bool
NS_EscapeURL(const char* aPart, int32_t aPartLen, uint32_t aFlags,
nsACString& aResult)
{
if (aPartLen < 0) {
aPartLen = strlen(aPart);
}
bool result = false;
nsresult rv = T_EscapeURL(aPart, aPartLen, aFlags, aResult, result);
if (NS_FAILED(rv)) {
::NS_ABORT_OOM(aResult.Length() * sizeof(nsACString::char_type));
}
return result;
}
nsresult
NS_EscapeURL(const nsCSubstring& aStr, uint32_t aFlags, nsCSubstring& aResult,
const mozilla::fallible_t&)
{
bool appended = false;
nsresult rv = T_EscapeURL(aStr.Data(), aStr.Length(), aFlags, aResult, appended);
if (NS_FAILED(rv)) {
aResult.Truncate();
return rv;
}
if (!appended) {
aResult = aStr;
}
return rv;
}
const nsSubstring&
NS_EscapeURL(const nsSubstring& aStr, uint32_t aFlags, nsSubstring& aResult)
{
bool result = false;
nsresult rv = T_EscapeURL<nsSubstring>(aStr.Data(), aStr.Length(), aFlags, aResult, result);
if (NS_FAILED(rv)) {
::NS_ABORT_OOM(aResult.Length() * sizeof(nsSubstring::char_type));
}
if (result) {
return aResult;
}
return aStr;
}
// Starting at aStr[aStart] find the first index in aStr that matches any
// character in aForbidden. Return false if not found.
static bool
FindFirstMatchFrom(const nsAFlatString& aStr, size_t aStart,
const nsTArray<char16_t>& aForbidden, size_t* aIndex)
{
const size_t len = aForbidden.Length();
for (size_t j = aStart, l = aStr.Length(); j < l; ++j) {
size_t unused;
if (mozilla::BinarySearch(aForbidden, 0, len, aStr[j], &unused)) {
*aIndex = j;
return true;
}
}
return false;
}
const nsSubstring&
NS_EscapeURL(const nsAFlatString& aStr, const nsTArray<char16_t>& aForbidden,
nsSubstring& aResult)
{
bool didEscape = false;
for (size_t i = 0, strLen = aStr.Length(); i < strLen; ) {
size_t j;
if (MOZ_UNLIKELY(FindFirstMatchFrom(aStr, i, aForbidden, &j))) {
if (i == 0) {
didEscape = true;
aResult.Truncate();
aResult.SetCapacity(aStr.Length());
}
if (j != i) {
// The substring from 'i' up to 'j' that needs no escaping.
aResult.Append(nsDependentSubstring(aStr, i, j - i));
}
char16_t buffer[ENCODE_MAX_LEN];
uint32_t bufferLen = ::AppendPercentHex(buffer, aStr[j]);
MOZ_ASSERT(bufferLen <= ENCODE_MAX_LEN, "buffer overflow");
aResult.Append(buffer, bufferLen);
i = j + 1;
} else {
if (MOZ_UNLIKELY(didEscape)) {
// The tail of the string that needs no escaping.
aResult.Append(nsDependentSubstring(aStr, i, strLen - i));
}
break;
}
}
if (MOZ_UNLIKELY(didEscape)) {
return aResult;
}
return aStr;
}
#define ISHEX(c) memchr(hexCharsUpperLower, c, sizeof(hexCharsUpperLower)-1)
bool
NS_UnescapeURL(const char* aStr, int32_t aLen, uint32_t aFlags,
nsACString& aResult)
{
if (!aStr) {
NS_NOTREACHED("null pointer");
return false;
}
MOZ_ASSERT(aResult.IsEmpty(),
"Passing a non-empty string as an out parameter!");
if (aLen < 0) {
aLen = strlen(aStr);
}
bool ignoreNonAscii = !!(aFlags & esc_OnlyASCII);
bool ignoreAscii = !!(aFlags & esc_OnlyNonASCII);
bool writing = !!(aFlags & esc_AlwaysCopy);
bool skipControl = !!(aFlags & esc_SkipControl);
bool skipInvalidHostChar = !!(aFlags & esc_Host);
if (writing) {
aResult.SetCapacity(aLen);
}
const char* last = aStr;
const char* p = aStr;
for (int i = 0; i < aLen; ++i, ++p) {
if (*p == HEX_ESCAPE && i < aLen - 2) {
unsigned char c1 = *((unsigned char*)p + 1);
unsigned char c2 = *((unsigned char*)p + 2);
unsigned char u = (UNHEX(c1) << 4) + UNHEX(c2);
if (ISHEX(c1) && ISHEX(c2) &&
(!skipInvalidHostChar || dontNeedEscape(u, aFlags) || c1 >= '8') &&
((c1 < '8' && !ignoreAscii) || (c1 >= '8' && !ignoreNonAscii)) &&
!(skipControl &&
(c1 < '2' || (c1 == '7' && (c2 == 'f' || c2 == 'F'))))) {
if (!writing) {
writing = true;
aResult.SetCapacity(aLen);
}
if (p > last) {
aResult.Append(last, p - last);
last = p;
}
aResult.Append(u);
i += 2;
p += 2;
last += 3;
}
}
}
if (writing && last < aStr + aLen) {
aResult.Append(last, aStr + aLen - last);
}
return writing;
}

224
xpcom/io/nsEscape.h Normal file
View file

@ -0,0 +1,224 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "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 aSstr The string to escape
* @param aLength The length of the string to escape
* @param aOutputLen A pointer that will be used to store the length of the
* output string, if not null
* @param aMask How to escape the string
* @return A newly allocated escaped string that must be free'd with
* nsCRT::free, or null on failure
* @note: Please, don't use this function. Use NS_Escape instead!
*/
char* nsEscape(const char* aStr, size_t aLength, size_t* aOutputLen,
nsEscapeMask aMask);
char* nsUnescape(char* aStr);
/* decode % escaped hex codes into character values,
* modifies the parameter, returns the same buffer
*/
int32_t nsUnescapeCount(char* aStr);
/* 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* aString);
char16_t*
nsEscapeHTML2(const char16_t* 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, unless aFlags has the esc_Forced bit in which
* case % will also be escaped. Iff some part of aStr is escaped is the
* final result appended to aResult. You can also request that aStr is
* always appended to aResult with esc_AlwaysCopy.
*
* @param aStr url segment string
* @param aLen url segment string length (-1 if unknown)
* @param aFlags url segment type flag (see EscapeMask above)
* @param aResult result buffer, untouched if aStr is already escaped unless
* aFlags has esc_AlwaysCopy
*
* @return true if aResult was written to (i.e. at least one character was
* escaped or esc_AlwaysCopy was requested), false otherwise.
*/
bool NS_EscapeURL(const char* aStr,
int32_t aLen,
uint32_t aFlags,
nsACString& aResult);
/**
* Expands URL escape sequences... beware embedded null bytes!
*
* @param aStr url string to unescape
* @param aLen length of aStr
* @param aFlags only esc_OnlyNonASCII, esc_SkipControl and esc_AlwaysCopy
* are recognized
* @param aResult result buffer, untouched if aStr is already unescaped unless
* aFlags has esc_AlwaysCopy
*
* @return true if aResult was written to (i.e. at least one character was
* unescaped or esc_AlwaysCopy was requested), false otherwise.
*/
bool NS_UnescapeURL(const char* aStr,
int32_t aLen,
uint32_t aFlags,
nsACString& aResult);
/** returns resultant string length **/
inline int32_t
NS_UnescapeURL(char* aStr)
{
return nsUnescapeCount(aStr);
}
/**
* String friendly versions...
*/
inline const nsCSubstring&
NS_EscapeURL(const nsCSubstring& aStr, uint32_t aFlags, nsCSubstring& aResult)
{
if (NS_EscapeURL(aStr.Data(), aStr.Length(), aFlags, aResult)) {
return aResult;
}
return aStr;
}
/**
* Fallible version of NS_EscapeURL. On success aResult will point to either
* the original string or an escaped copy.
*/
nsresult
NS_EscapeURL(const nsCSubstring& aStr, uint32_t aFlags, nsCSubstring& aResult,
const mozilla::fallible_t&);
inline const nsCSubstring&
NS_UnescapeURL(const nsCSubstring& aStr, uint32_t aFlags, nsCSubstring& aResult)
{
if (NS_UnescapeURL(aStr.Data(), aStr.Length(), aFlags, aResult)) {
return aResult;
}
return aStr;
}
const nsSubstring&
NS_EscapeURL(const nsSubstring& aStr, uint32_t aFlags, nsSubstring& aResult);
/**
* Percent-escapes all characters in aStr that occurs in aForbidden.
* @param aStr the input URL string
* @param aForbidden the characters that should be escaped if found in aStr
* @note that aForbidden MUST be sorted (low to high)
* @param aResult the result if some characters were escaped
* @return aResult if some characters were escaped, or aStr otherwise (aResult
* is unmodified in that case)
*/
const nsSubstring&
NS_EscapeURL(const nsAFlatString& aStr, const nsTArray<char16_t>& aForbidden,
nsSubstring& aResult);
/**
* 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 nsACString& aOriginal, nsACString& aEscaped,
nsEscapeMask aMask)
{
size_t escLen = 0;
char* esc = nsEscape(aOriginal.BeginReading(), aOriginal.Length(), &escLen,
aMask);
if (! esc) {
return false;
}
aEscaped.Adopt(esc, escLen);
return true;
}
/**
* Inline unescape of mutable string object.
*/
inline nsACString&
NS_UnescapeURL(nsACString& aStr)
{
aStr.SetLength(nsUnescapeCount(aStr.BeginWriting()));
return aStr;
}
#endif // _ESCAPE_H_

View file

@ -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);
};

View file

@ -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.
*/
[function, 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);
};

View file

@ -0,0 +1,119 @@
/* -*- 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"
/**
* 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(899b826b-2eb3-469c-8b31-4c29f5d341a6)]
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.
*
* @return The number of bytes actually read into aArrayBuffer.
*/
[implicit_jscontext]
unsigned long 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
%}

View file

@ -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/. */
#include "nsIOutputStream.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 char16_t* aString)
{
bool nonnull = (aString != nullptr);
nsresult rv = aStream->WriteBoolean(nonnull);
if (NS_SUCCEEDED(rv) && nonnull)
rv = aStream->WriteWStringZ(aString);
return rv;
}
%}

View file

@ -0,0 +1,22 @@
/* 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"
[scriptable, builtinclass, uuid(8149be1f-44d3-4f14-8b65-a57a5fbbeb97)]
interface nsICloneableInputStream : nsISupports
{
// Allow streams that implement the interface to determine if cloning
// possible at runtime. For example, this allows wrappers to check if
// their base stream supports cloning.
[infallible] readonly attribute boolean cloneable;
// Produce a copy of the current stream in the most efficient way possible.
// In this case "copy" means that both the original and cloned streams
// should produce the same bytes for all future reads. Bytes that have
// already been consumed from the original stream are not copied to the
// clone. Operations on the two streams should be completely independent
// after the clone() occurs.
nsIInputStream clone();
};

View file

@ -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 char16_t 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 char16_t aReplacementChar);
};

View file

@ -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 char16_t aReplacementCharacter);
};

View file

@ -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();
};

View file

@ -0,0 +1,103 @@
/* -*- 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 nsIFile;
interface nsISimpleEnumerator;
/**
* 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);
};

521
xpcom/io/nsIFile.idl Normal file
View file

@ -0,0 +1,521 @@
/* -*- 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++
struct PRFileDesc;
struct PRLibrary;
#include <stdio.h>
%}
[ptr] native PRFileDescStar(PRFileDesc);
[ptr] native PRLibraryStar(PRLibrary);
[ptr] native FILE(FILE);
interface nsISimpleEnumerator;
/**
* An nsIFile is an abstract representation of a filename. It manages
* filename encoding issues, pathname component separators ('/' vs. '\\'
* vs. ':') and weird stuff like differing volumes with identical names, as
* on pre-Darwin Macintoshes.
*
* This file has long introduced itself to new hackers with this opening
* paragraph:
*
* 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.
*
* While taking the pose struck here to heart would be uncalled for, one
* may safely conclude that writing cross-platform code is an embittering
* experience.
*
* 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, main_process_scriptable_only, uuid(2fa6884a-ae65-412a-9d4c-ce6e34544ba1), 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.
*/
[must_use] 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);
/**
* renameTo
*
* This method is identical to moveTo except that if this file or directory
* is moved to a a different volume, it fails and returns an error
* (NS_ERROR_FILE_ACCESS_DENIED).
* This object will still point to the old location after renaming.
*/
void renameTo(in nsIFile newParentDir, in AString newName);
[noscript] void renameToNative(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.
*/
[must_use]
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.
* This routine looks in subdirectories too.
*/
boolean contains(in nsIFile inFile);
/**
* 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. On success, the
* returned PRFileDescr must be non-null.
*
* @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, must_use] 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. On success, the
* returned FILE pointer must be non-null.
*/
[noscript, must_use] 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, must_use] PRLibraryStar load();
// number of bytes available on disk to non-superuser
[must_use] 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!
*/
[must_use] 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.
*/
[must_use] 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.
*/
[must_use] 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.
* Throws if fromFile is null.
*/
[must_use] 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
*/
[must_use]
void setRelativeDescriptor(in nsIFile fromFile, in ACString relativeDesc);
/**
* getRelativePath
*
* Returns a relative file from 'fromFile' to this file as a UTF-8 string.
* Going up the directory tree is represented via "../". '/' is used as
* the path segment separator. This is not a native path, since it's UTF-8
* encoded.
*
* @param fromFile
* the file from which the path is relative.
* Throws if fromFile is null.
*/
[must_use] AUTF8String getRelativePath(in nsIFile fromFile);
/**
* setRelativePath
*
* Initializes the file to the location relative to fromFile using
* a string returned by getRelativePath.
*
* @param fromFile
* the file from which the path is relative
* @param relative
* the relative path obtained from getRelativePath
*/
[must_use]
void setRelativePath(in nsIFile fromFile, in AUTF8String relativeDesc);
};
%{C++
#ifdef MOZILLA_INTERNAL_API
#include "nsDirectoryServiceUtils.h"
#endif
%}

34
xpcom/io/nsIIOUtil.idl Normal file
View file

@ -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);
};

147
xpcom/io/nsIInputStream.idl Normal file
View file

@ -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 nsresult (*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();
};

View file

@ -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);
%}

View file

@ -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(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);
};

17
xpcom/io/nsILocalFile.idl Normal file
View file

@ -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(7ba8c6ba-2ce2-48b1-bd60-4c32aac35f9c)]
interface nsILocalFile : nsIFile
{
};

View file

@ -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(623eca5b-c25d-4e27-be5a-789a66c4b2f7)]
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);
}
%}

View file

@ -0,0 +1,121 @@
/* -*- 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"
%{C++
struct PRFileDesc;
%}
[ptr] native PRFileDescStar(PRFileDesc);
[scriptable, builtinclass, uuid(e7a3a954-384b-4aeb-a5f7-55626b0de9be)]
interface nsILocalFileWin : nsILocalFile
{
/**
* initWithCommandLine
*
* Initialize this object based on the main app path of a commandline
* handler.
*
* @param aCommandLine
* the commandline to parse an app path out of.
*/
void initWithCommandLine(in AString aCommandLine);
/**
* 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);
/**
* Identical to nsIFile::openNSPRFileDesc except it also uses the
* FILE_SHARE_DELETE flag.
*/
[noscript] PRFileDescStar openNSPRFileDescShareDelete(in long flags,
in long mode);
};

View file

@ -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);
};

32
xpcom/io/nsIOUtil.cpp Normal file
View file

@ -0,0 +1,32 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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_ISUPPORTS(nsIOUtil, nsIIOUtil)
NS_IMETHODIMP
nsIOUtil::InputStreamIsBuffered(nsIInputStream* aStream, bool* aResult)
{
if (NS_WARN_IF(!aStream)) {
return NS_ERROR_INVALID_ARG;
}
*aResult = NS_InputStreamIsBuffered(aStream);
return NS_OK;
}
NS_IMETHODIMP
nsIOUtil::OutputStreamIsBuffered(nsIOutputStream* aStream, bool* aResult)
{
if (NS_WARN_IF(!aStream)) {
return NS_ERROR_INVALID_ARG;
}
*aResult = NS_OutputStreamIsBuffered(aStream);
return NS_OK;
}

27
xpcom/io/nsIOUtil.h Normal file
View file

@ -0,0 +1,27 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 final : public nsIIOUtil
{
~nsIOUtil() {}
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIIOUTIL
};
#endif /* nsIOUtil_h__ */

View file

@ -0,0 +1,53 @@
/* -*- 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"
/**
* @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;
}
%}

View file

@ -0,0 +1,97 @@
/* -*- 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"
/**
* @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;
}
%}

View file

@ -0,0 +1,145 @@
/* -*- 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 nsresult (*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). This failure
* means no bytes were transferred.
* @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). This failure
* means no bytes were transferred.
* @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();
};

171
xpcom/io/nsIPipe.idl Normal file
View file

@ -0,0 +1,171 @@
/* -*- 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 nsIAsyncInputStream;
interface nsIAsyncOutputStream;
/**
* 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(25d0de93-685e-4ea4-95d3-d884e31df63c)]
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.
*/
[must_use] void init(in boolean nonBlockingInput,
in boolean nonBlockingOutput,
in unsigned long segmentSize,
in unsigned long segmentCount);
/**
* The pipe's input end, which also implements nsISearchableInputStream.
* Getting fails if the pipe hasn't been initialized.
*/
[must_use] readonly attribute nsIAsyncInputStream inputStream;
/**
* The pipe's output end. Getting fails if the pipe hasn't been
* initialized.
*/
[must_use] 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++
class nsIInputStream;
class nsIOutputStream;
/**
* 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.
*/
extern MOZ_MUST_USE nsresult
NS_NewPipe2(nsIAsyncInputStream **pipeIn,
nsIAsyncOutputStream **pipeOut,
bool nonBlockingInput = false,
bool nonBlockingOutput = false,
uint32_t segmentSize = 0,
uint32_t segmentCount = 0);
/**
* 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
*/
extern MOZ_MUST_USE nsresult
NS_NewPipe(nsIInputStream **pipeIn,
nsIOutputStream **pipeOut,
uint32_t segmentSize = 0,
uint32_t maxSize = 0,
bool nonBlockingInput = false,
bool nonBlockingOutput = false);
%}

View file

@ -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();
};

View file

@ -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);
};

View file

@ -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);
};

View file

@ -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();
};

View file

@ -0,0 +1,69 @@
/* -*- 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 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(44a200fe-6c2b-4b41-b4e3-63e8c14e7c0d)]
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.
*/
void init(in uint32_t segmentSize, in uint32_t maxSize);
/**
* 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);
%}

View file

@ -0,0 +1,88 @@
/* -*- 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"
/**
* 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))
%}

View file

@ -0,0 +1,66 @@
/* -*- 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"
%{C++
#include "mozilla/MemoryReporting.h"
%}
native MallocSizeOf(mozilla::MallocSizeOf);
/**
* nsIStringInputStream
*
* Provides scriptable and specialized C++-only methods for initializing a
* nsIInputStream implementation with a simple character array.
*/
[scriptable, builtinclass, 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 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);
[noscript, notxpcom]
size_t SizeOfIncludingThis(in MallocSizeOf aMallocSizeOf);
};

View file

@ -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 nsresult (*nsWriteUnicharSegmentFun)(nsIUnicharInputStream *aInStream,
void *aClosure,
const char16_t *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 char16_t 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();
};

View file

@ -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);
};

View file

@ -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 char16_t 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();
};

View file

@ -0,0 +1,366 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "mozilla/Logging.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 LOG
#undef LOG
#endif
static LazyLogModule sTeeLog("nsInputStreamTee");
#define LOG(args) MOZ_LOG(sTeeLog, mozilla::LogLevel::Debug, args)
class nsInputStreamTee final : public nsIInputStreamTee
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
NS_DECL_NSIINPUTSTREAMTEE
nsInputStreamTee();
bool SinkIsValid();
void InvalidateSink();
private:
~nsInputStreamTee()
{
}
nsresult TeeSegment(const char* aBuf, uint32_t aCount);
static nsresult 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 Runnable
{
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() override
{
if (!mBuf) {
NS_WARNING("nsInputStreamTeeWriteEvent::Run() "
"memory not allocated\n");
return NS_OK;
}
MOZ_ASSERT(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
RefPtr<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* aBuf, uint32_t aCount)
{
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
}
nsCOMPtr<nsIRunnable> event =
new nsInputStreamTeeWriteEvent(aBuf, aCount, mSink, this);
LOG(("nsInputStreamTee::TeeSegment [%p] dispatching write %u bytes\n",
this, aCount));
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 (aCount) {
uint32_t bytesWritten = 0;
rv = mSink->Write(aBuf + totalBytesWritten, aCount, &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 = nullptr;
break;
}
totalBytesWritten += bytesWritten;
NS_ASSERTION(bytesWritten <= aCount, "wrote too much");
aCount -= bytesWritten;
}
return NS_OK;
}
}
nsresult
nsInputStreamTee::WriteSegmentFun(nsIInputStream* aIn, void* aClosure,
const char* aFromSegment, uint32_t aOffset,
uint32_t aCount, uint32_t* aWriteCount)
{
nsInputStreamTee* tee = reinterpret_cast<nsInputStreamTee*>(aClosure);
nsresult rv = tee->mWriter(aIn, tee->mClosure, aFromSegment, aOffset,
aCount, aWriteCount);
if (NS_FAILED(rv) || (*aWriteCount == 0)) {
NS_ASSERTION((NS_FAILED(rv) ? (*aWriteCount == 0) : true),
"writer returned an error with non-zero writeCount");
return rv;
}
return tee->TeeSegment(aFromSegment, *aWriteCount);
}
NS_IMPL_ISUPPORTS(nsInputStreamTee,
nsIInputStreamTee,
nsIInputStream)
NS_IMETHODIMP
nsInputStreamTee::Close()
{
if (NS_WARN_IF(!mSource)) {
return NS_ERROR_NOT_INITIALIZED;
}
nsresult rv = mSource->Close();
mSource = nullptr;
mSink = nullptr;
return rv;
}
NS_IMETHODIMP
nsInputStreamTee::Available(uint64_t* aAvail)
{
if (NS_WARN_IF(!mSource)) {
return NS_ERROR_NOT_INITIALIZED;
}
return mSource->Available(aAvail);
}
NS_IMETHODIMP
nsInputStreamTee::Read(char* aBuf, uint32_t aCount, uint32_t* aBytesRead)
{
if (NS_WARN_IF(!mSource)) {
return NS_ERROR_NOT_INITIALIZED;
}
nsresult rv = mSource->Read(aBuf, aCount, aBytesRead);
if (NS_FAILED(rv) || (*aBytesRead == 0)) {
return rv;
}
return TeeSegment(aBuf, *aBytesRead);
}
NS_IMETHODIMP
nsInputStreamTee::ReadSegments(nsWriteSegmentFun aWriter,
void* aClosure,
uint32_t aCount,
uint32_t* aBytesRead)
{
if (NS_WARN_IF(!mSource)) {
return NS_ERROR_NOT_INITIALIZED;
}
mWriter = aWriter;
mClosure = aClosure;
return mSource->ReadSegments(WriteSegmentFun, this, aCount, aBytesRead);
}
NS_IMETHODIMP
nsInputStreamTee::IsNonBlocking(bool* aResult)
{
if (NS_WARN_IF(!mSource)) {
return NS_ERROR_NOT_INITIALIZED;
}
return mSource->IsNonBlocking(aResult);
}
NS_IMETHODIMP
nsInputStreamTee::SetSource(nsIInputStream* aSource)
{
mSource = aSource;
return NS_OK;
}
NS_IMETHODIMP
nsInputStreamTee::GetSource(nsIInputStream** aSource)
{
NS_IF_ADDREF(*aSource = mSource);
return NS_OK;
}
NS_IMETHODIMP
nsInputStreamTee::SetSink(nsIOutputStream* aSink)
{
#ifdef DEBUG
if (aSink) {
bool nonBlocking;
nsresult rv = aSink->IsNonBlocking(&nonBlocking);
if (NS_FAILED(rv) || nonBlocking) {
NS_ERROR("aSink should be a blocking stream");
}
}
#endif
mSink = aSink;
return NS_OK;
}
NS_IMETHODIMP
nsInputStreamTee::GetSink(nsIOutputStream** aSink)
{
NS_IF_ADDREF(*aSink = mSink);
return NS_OK;
}
NS_IMETHODIMP
nsInputStreamTee::SetEventTarget(nsIEventTarget* aEventTarget)
{
mEventTarget = aEventTarget;
if (mEventTarget) {
// Only need synchronization if this is an async tee
mLock = new Mutex("nsInputStreamTee.mLock");
}
return NS_OK;
}
NS_IMETHODIMP
nsInputStreamTee::GetEventTarget(nsIEventTarget** aEventTarget)
{
NS_IF_ADDREF(*aEventTarget = mEventTarget);
return NS_OK;
}
nsresult
NS_NewInputStreamTeeAsync(nsIInputStream** aResult,
nsIInputStream* aSource,
nsIOutputStream* aSink,
nsIEventTarget* aEventTarget)
{
nsresult rv;
nsCOMPtr<nsIInputStreamTee> tee = new nsInputStreamTee();
rv = tee->SetSource(aSource);
if (NS_FAILED(rv)) {
return rv;
}
rv = tee->SetSink(aSink);
if (NS_FAILED(rv)) {
return rv;
}
rv = tee->SetEventTarget(aEventTarget);
if (NS_FAILED(rv)) {
return rv;
}
tee.forget(aResult);
return rv;
}
nsresult
NS_NewInputStreamTee(nsIInputStream** aResult,
nsIInputStream* aSource,
nsIOutputStream* aSink)
{
return NS_NewInputStreamTeeAsync(aResult, aSource, aSink, nullptr);
}
#undef LOG

View file

@ -0,0 +1,488 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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*& aIoDest, const char* aLineBreakStr)
{
*aIoDest++ = *aLineBreakStr;
if (aLineBreakStr[1]) {
*aIoDest++ = aLineBreakStr[1];
}
}
/*----------------------------------------------------------------------------
CountChars
Counts occurrences of breakStr in aSrc
----------------------------------------------------------------------------*/
template<class T>
int32_t
CountLinebreaks(const T* aSrc, int32_t aInLen, const char* aBreakStr)
{
const T* src = aSrc;
const T* srcEnd = aSrc + aInLen;
int32_t theCount = 0;
while (src < srcEnd) {
if (*src == *aBreakStr) {
src++;
if (aBreakStr[1]) {
if (src < srcEnd && *src == aBreakStr[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* aInSrc, int32_t& aIoLen, const char* aSrcBreak,
const char* aDestBreak)
{
NS_ASSERTION(aInSrc && aSrcBreak && aDestBreak, "Got a null string");
T* resultString = nullptr;
// handle the no conversion case
if (nsCRT::strcmp(aSrcBreak, aDestBreak) == 0) {
resultString = (T*)malloc(sizeof(T) * aIoLen);
if (!resultString) {
return nullptr;
}
memcpy(resultString, aInSrc, sizeof(T) * aIoLen); // includes the null, if any
return resultString;
}
int32_t srcBreakLen = strlen(aSrcBreak);
int32_t destBreakLen = strlen(aDestBreak);
// 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*)malloc(sizeof(T) * aIoLen);
if (!resultString) {
return nullptr;
}
const T* src = aInSrc;
const T* srcEnd = aInSrc + aIoLen; // includes null, if any
T* dst = resultString;
char srcBreakChar = *aSrcBreak; // we know it's one char long already
char dstBreakChar = *aDestBreak;
while (src < srcEnd) {
if (*src == srcBreakChar) {
*dst++ = dstBreakChar;
src++;
} else {
*dst++ = *src++;
}
}
// aIoLen 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(aInSrc, aIoLen, aSrcBreak);
int32_t newBufLen =
aIoLen - (numLinebreaks * srcBreakLen) + (numLinebreaks * destBreakLen);
resultString = (T*)malloc(sizeof(T) * newBufLen);
if (!resultString) {
return nullptr;
}
const T* src = aInSrc;
const T* srcEnd = aInSrc + aIoLen; // includes null, if any
T* dst = resultString;
while (src < srcEnd) {
if (*src == *aSrcBreak) {
*dst++ = *aDestBreak;
if (aDestBreak[1]) {
*dst++ = aDestBreak[1];
}
src++;
if (src < srcEnd && aSrcBreak[1] && *src == aSrcBreak[1]) {
src++;
}
} else {
*dst++ = *src++;
}
}
aIoLen = 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* aInSrc, int32_t aInLen, char aSrcBreak, char aDestBreak)
{
T* src = aInSrc;
T* srcEnd = aInSrc + aInLen;
while (src < srcEnd) {
if (*src == aSrcBreak) {
*src = aDestBreak;
}
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* aInSrc, int32_t& aIoLen, const char* aDestBreak)
{
const T* src = aInSrc;
const T* srcEnd = aInSrc + aIoLen; // includes null, if any
int32_t destBreakLen = strlen(aDestBreak);
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*)malloc(sizeof(T) * finalLen);
if (!resultString) {
return nullptr;
}
src = aInSrc;
srcEnd = aInSrc + aIoLen; // includes null, if any
T* dst = resultString;
while (src < srcEnd) {
if (*src == nsCRT::CR) {
if (src < srcEnd && src[1] == nsCRT::LF) {
// CRLF
AppendLinebreak(dst, aDestBreak);
src++;
} else {
// Lone CR
AppendLinebreak(dst, aDestBreak);
}
} else if (*src == nsCRT::LF) {
// Lone LF
AppendLinebreak(dst, aDestBreak);
} else {
*dst++ = *src;
}
src++;
}
aIoLen = finalLen;
return resultString;
}
/*----------------------------------------------------------------------------
ConvertLineBreaks
----------------------------------------------------------------------------*/
char*
nsLinebreakConverter::ConvertLineBreaks(const char* aSrc,
ELinebreakType aSrcBreaks,
ELinebreakType aDestBreaks,
int32_t aSrcLen, int32_t* aOutLen)
{
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 (aOutLen) {
*aOutLen = sourceLen;
}
return resultString;
}
/*----------------------------------------------------------------------------
ConvertLineBreaksInSitu
----------------------------------------------------------------------------*/
nsresult
nsLinebreakConverter::ConvertLineBreaksInSitu(char** aIoBuffer,
ELinebreakType aSrcBreaks,
ELinebreakType aDestBreaks,
int32_t aSrcLen, int32_t* aOutLen)
{
NS_ASSERTION(aIoBuffer && *aIoBuffer, "Null pointer passed");
if (!aIoBuffer || !*aIoBuffer) {
return NS_ERROR_NULL_POINTER;
}
NS_ASSERTION(aDestBreaks != eLinebreakAny &&
aSrcBreaks != eLinebreakSpace, "Invalid parameter");
int32_t sourceLen = (aSrcLen == kIgnoreLen) ? strlen(*aIoBuffer) + 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(*aIoBuffer, sourceLen, *srcBreaks, *dstBreaks);
if (aOutLen) {
*aOutLen = sourceLen;
}
} else {
char* destBuffer;
if (aSrcBreaks == eLinebreakAny) {
destBuffer = ConvertUnknownBreaks(*aIoBuffer, sourceLen, dstBreaks);
} else {
destBuffer = ConvertBreaks(*aIoBuffer, sourceLen, srcBreaks, dstBreaks);
}
if (!destBuffer) {
return NS_ERROR_OUT_OF_MEMORY;
}
*aIoBuffer = destBuffer;
if (aOutLen) {
*aOutLen = sourceLen;
}
}
return NS_OK;
}
/*----------------------------------------------------------------------------
ConvertUnicharLineBreaks
----------------------------------------------------------------------------*/
char16_t*
nsLinebreakConverter::ConvertUnicharLineBreaks(const char16_t* aSrc,
ELinebreakType aSrcBreaks,
ELinebreakType aDestBreaks,
int32_t aSrcLen,
int32_t* aOutLen)
{
NS_ASSERTION(aDestBreaks != eLinebreakAny &&
aSrcBreaks != eLinebreakSpace, "Invalid parameter");
if (!aSrc) {
return nullptr;
}
int32_t bufLen = (aSrcLen == kIgnoreLen) ? NS_strlen(aSrc) + 1 : aSrcLen;
char16_t* resultString;
if (aSrcBreaks == eLinebreakAny) {
resultString = ConvertUnknownBreaks(aSrc, bufLen,
GetLinebreakString(aDestBreaks));
} else
resultString = ConvertBreaks(aSrc, bufLen, GetLinebreakString(aSrcBreaks),
GetLinebreakString(aDestBreaks));
if (aOutLen) {
*aOutLen = bufLen;
}
return resultString;
}
/*----------------------------------------------------------------------------
ConvertStringLineBreaks
----------------------------------------------------------------------------*/
nsresult
nsLinebreakConverter::ConvertUnicharLineBreaksInSitu(
char16_t** aIoBuffer, ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks,
int32_t aSrcLen, int32_t* aOutLen)
{
NS_ASSERTION(aIoBuffer && *aIoBuffer, "Null pointer passed");
if (!aIoBuffer || !*aIoBuffer) {
return NS_ERROR_NULL_POINTER;
}
NS_ASSERTION(aDestBreaks != eLinebreakAny &&
aSrcBreaks != eLinebreakSpace, "Invalid parameter");
int32_t sourceLen =
(aSrcLen == kIgnoreLen) ? NS_strlen(*aIoBuffer) + 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(*aIoBuffer, sourceLen, *srcBreaks, *dstBreaks);
if (aOutLen) {
*aOutLen = sourceLen;
}
} else {
char16_t* destBuffer;
if (aSrcBreaks == eLinebreakAny) {
destBuffer = ConvertUnknownBreaks(*aIoBuffer, sourceLen, dstBreaks);
} else {
destBuffer = ConvertBreaks(*aIoBuffer, sourceLen, srcBreaks, dstBreaks);
}
if (!destBuffer) {
return NS_ERROR_OUT_OF_MEMORY;
}
*aIoBuffer = destBuffer;
if (aOutLen) {
*aOutLen = sourceLen;
}
}
return NS_OK;
}
/*----------------------------------------------------------------------------
ConvertStringLineBreaks
----------------------------------------------------------------------------*/
nsresult
nsLinebreakConverter::ConvertStringLineBreaks(nsString& aIoString,
ELinebreakType aSrcBreaks,
ELinebreakType aDestBreaks)
{
NS_ASSERTION(aDestBreaks != eLinebreakAny &&
aSrcBreaks != eLinebreakSpace, "Invalid parameter");
// nothing to do
if (aIoString.IsEmpty()) {
return NS_OK;
}
nsresult rv;
// remember the old buffer in case
// we blow it away later
nsString::char_iterator stringBuf;
if (!aIoString.BeginWriting(stringBuf, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
int32_t newLen;
rv = ConvertUnicharLineBreaksInSitu(&stringBuf,
aSrcBreaks, aDestBreaks,
aIoString.Length() + 1, &newLen);
if (NS_FAILED(rv)) {
return rv;
}
if (stringBuf != aIoString.get()) {
aIoString.Adopt(stringBuf, newLen - 1);
}
return NS_OK;
}

View file

@ -0,0 +1,131 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 char16_t* ConvertUnicharLineBreaks(const char16_t* 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& aIoString,
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** aIoBuffer,
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(char16_t** aIoBuffer,
ELinebreakType aSrcBreaks,
ELinebreakType aDestBreaks,
int32_t aSrcLen = kIgnoreLen,
int32_t* aOutLen = nullptr);
};
#endif // nsLinebreakConverter_h_

124
xpcom/io/nsLocalFile.h Normal file
View file

@ -0,0 +1,124 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 char16_t *aNode); \
nsresult GetUnicodeLeafName(char16_t **aLeafName); \
nsresult SetUnicodeLeafName(const char16_t *aLeafName); \
nsresult CopyToUnicode(nsIFile *aNewParentDir, const char16_t *aNewLeafName); \
nsresult CopyToFollowingLinksUnicode(nsIFile *aNewParentDir, const char16_t *aNewLeafName); \
nsresult MoveToUnicode(nsIFile *aNewParentDir, const char16_t *aNewLeafName); \
nsresult GetUnicodeTarget(char16_t **aTarget); \
nsresult GetUnicodePath(char16_t **aPath); \
nsresult InitWithUnicodePath(const char16_t *aPath); \
nsresult AppendRelativeUnicodePath(const char16_t *aRelativePath);
// XPCOMInit 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"
#else
#error NOT_IMPLEMENTED
#endif
#define NSRESULT_FOR_RETURN(ret) (((ret) < 0) ? NSRESULT_FOR_ERRNO() : NS_OK)
inline nsresult
nsresultForErrno(int aErr)
{
switch (aErr) {
case 0:
return NS_OK;
#ifdef EDQUOT
case EDQUOT: /* Quota exceeded */
// FALLTHROUGH to return NS_ERROR_FILE_DISK_FULL
#endif
case ENOSPC:
return NS_ERROR_FILE_DISK_FULL;
#ifdef EISDIR
case EISDIR: /* Is a directory. */
return NS_ERROR_FILE_IS_DIRECTORY;
#endif
case ENAMETOOLONG:
return NS_ERROR_FILE_NAME_TOO_LONG;
case ENOEXEC: /* Executable file format error. */
return NS_ERROR_FILE_EXECUTION_FAILED;
case ENOENT:
return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
case ENOTDIR:
return NS_ERROR_FILE_DESTINATION_NOT_DIR;
#ifdef ELOOP
case ELOOP:
return NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
#endif /* ELOOP */
#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;
#ifdef EROFS
case EROFS: /* Read-only file system. */
return NS_ERROR_FILE_READ_ONLY;
#endif
/*
* 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 */
/* Note that nsIFile.createUnique() returns
NS_ERROR_FILE_TOO_BIG when it cannot create a temporary
file with a unique filename.
See https://developer.mozilla.org/en-US/docs/Table_Of_Errors
Other usages of NS_ERROR_FILE_TOO_BIG in the source tree
are in line with the POSIX semantics of EFBIG.
So this is a reasonably good approximation.
*/
case EFBIG: /* File too large. */
return NS_ERROR_FILE_TOO_BIG;
default:
return NS_ERROR_FAILURE;
}
}
#define NSRESULT_FOR_ERRNO() nsresultForErrno(errno)
void NS_StartupLocalFile();
void NS_ShutdownLocalFile();
#endif

View file

@ -0,0 +1,328 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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)
{
if (NS_WARN_IF(!aFile)) {
return NS_ERROR_INVALID_ARG;
}
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 aType, uint32_t aAttributes)
{
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(aType, aAttributes);
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(char16_t('.'));
#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 rvCreate = Create(aType, aAttributes);
if (rvCreate != NS_ERROR_FILE_ALREADY_EXISTS) {
return rvCreate;
}
}
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(aType, aAttributes);
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)
static const char16_t kPathSeparatorChar = '\\';
#elif defined(XP_UNIX)
static const char16_t kPathSeparatorChar = '/';
#else
#error Need to define file path separator for your platform
#endif
static void
SplitPath(char16_t* aPath, nsTArray<char16_t*>& aNodeArray)
{
if (*aPath == 0) {
return;
}
if (*aPath == kPathSeparatorChar) {
aPath++;
}
aNodeArray.AppendElement(aPath);
for (char16_t* cp = aPath; *cp != 0; ++cp) {
if (*cp == kPathSeparatorChar) {
*cp++ = 0;
if (*cp == 0) {
break;
}
aNodeArray.AppendElement(cp);
}
}
}
NS_IMETHODIMP
nsLocalFile::GetRelativeDescriptor(nsIFile* aFromFile, nsACString& aResult)
{
if (NS_WARN_IF(!aFromFile)) {
return NS_ERROR_INVALID_ARG;
}
//
// aResult will be UTF-8 encoded
//
nsresult rv;
aResult.Truncate(0);
nsAutoString thisPath, fromPath;
AutoTArray<char16_t*, 32> thisNodes;
AutoTArray<char16_t*, 32> fromNodes;
rv = GetPath(thisPath);
if (NS_FAILED(rv)) {
return rv;
}
rv = aFromFile->GetPath(fromPath);
if (NS_FAILED(rv)) {
return rv;
}
// get raw pointer to mutable string buffer
char16_t* thisPathPtr;
thisPath.BeginWriting(thisPathPtr);
char16_t* fromPathPtr;
fromPath.BeginWriting(fromPathPtr);
SplitPath(thisPathPtr, thisNodes);
SplitPath(fromPathPtr, fromNodes);
size_t nodeIndex;
for (nodeIndex = 0;
nodeIndex < thisNodes.Length() && nodeIndex < fromNodes.Length();
++nodeIndex) {
#ifdef XP_WIN
if (_wcsicmp(char16ptr_t(thisNodes[nodeIndex]),
char16ptr_t(fromNodes[nodeIndex]))) {
break;
}
#else
if (nsCRT::strcmp(thisNodes[nodeIndex], fromNodes[nodeIndex])) {
break;
}
#endif
}
size_t branchIndex = nodeIndex;
for (nodeIndex = branchIndex; nodeIndex < fromNodes.Length(); ++nodeIndex) {
aResult.AppendLiteral("../");
}
for (nodeIndex = branchIndex; nodeIndex < thisNodes.Length(); ++nodeIndex) {
NS_ConvertUTF16toUTF8 nodeStr(thisNodes[nodeIndex]);
aResult.Append(nodeStr);
if (nodeIndex + 1 < thisNodes.Length()) {
aResult.Append('/');
}
}
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::SetRelativeDescriptor(nsIFile* aFromFile,
const nsACString& aRelativeDesc)
{
NS_NAMED_LITERAL_CSTRING(kParentDirStr, "../");
nsCOMPtr<nsIFile> targetFile;
nsresult rv = aFromFile->Clone(getter_AddRefs(targetFile));
if (NS_FAILED(rv)) {
return rv;
}
//
// aRelativeDesc is UTF-8 encoded
//
nsCString::const_iterator strBegin, strEnd;
aRelativeDesc.BeginReading(strBegin);
aRelativeDesc.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);
}
NS_IMETHODIMP
nsLocalFile::GetRelativePath(nsIFile* aFromFile, nsACString& aResult)
{
return GetRelativeDescriptor(aFromFile, aResult);
}
NS_IMETHODIMP
nsLocalFile::SetRelativePath(nsIFile* aFromFile,
const nsACString& aRelativePath)
{
return SetRelativeDescriptor(aFromFile, aRelativePath);
}

2715
xpcom/io/nsLocalFileUnix.cpp Normal file

File diff suppressed because it is too large Load diff

138
xpcom/io/nsLocalFileUnix.h Normal file
View file

@ -0,0 +1,138 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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
// stat64 and lstat64 are deprecated on OS X. Normal stat and lstat are
// 64-bit by default on OS X 10.6+.
#if defined(HAVE_STAT64) && defined(HAVE_LSTAT64) && !defined(XP_DARWIN)
#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 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* aOuter,
const nsIID& aIID,
void** aInstancePtr);
NS_DECL_THREADSAFE_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& aOther);
~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* aNewParent);
nsresult CreateAllAncestors(uint32_t aPermissions);
nsresult GetNativeTargetPathName(nsIFile* aNewParent,
const nsACString& aNewName,
nsACString& aResult);
bool FillStatCache();
nsresult CreateAndKeepOpen(uint32_t aType, int aFlags,
uint32_t aPermissions, PRFileDesc** aResult);
};
#endif /* _nsLocalFileUNIX_H_ */

3787
xpcom/io/nsLocalFileWin.cpp Normal file

File diff suppressed because it is too large Load diff

123
xpcom/io/nsLocalFileWin.h Normal file
View file

@ -0,0 +1,123 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "prio.h"
#include "mozilla/Attributes.h"
#include "windows.h"
#include "shlobj.h"
#include <sys/stat.h>
class nsLocalFile final
: public nsILocalFileWin
, public nsIHashable
{
public:
NS_DEFINE_STATIC_CID_ACCESSOR(NS_LOCAL_FILE_CID)
nsLocalFile();
static nsresult nsLocalFileConstructor(nsISupports* aOuter,
const nsIID& aIID,
void** aInstancePtr);
// nsISupports interface
NS_DECL_THREADSAFE_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();
// Removes registry command handler parameters, quotes, and expands environment strings.
static bool CleanupCmdHandlerPath(nsAString& aCommandHandler);
private:
// CopyMove and CopySingleFile constants for |options| parameter:
enum CopyFileOption {
FollowSymlinks = 1u << 0,
Move = 1u << 1,
SkipNtfsAclReset = 1u << 2,
Rename = 1u << 3
};
nsLocalFile(const nsLocalFile& aOther);
~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* aNewParentDir, const nsAString& aNewName,
uint32_t aOptions);
nsresult CopySingleFile(nsIFile* aSource, nsIFile* aDest,
const nsAString& aNewName, uint32_t aOptions);
nsresult SetModDate(int64_t aLastModifiedTime, const wchar_t* aFilePath);
nsresult HasFileAttribute(DWORD aFileAttrib, bool* aResult);
nsresult AppendInternal(const nsAFlatString& aNode,
bool aMultipleComponents);
nsresult OpenNSPRFileDescMaybeShareDelete(int32_t aFlags,
int32_t aMode,
bool aShareDelete,
PRFileDesc** aResult);
};
#endif

View file

@ -0,0 +1,835 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "mozilla/Mutex.h"
#include "base/basictypes.h"
#include "nsMultiplexInputStream.h"
#include "nsICloneableInputStream.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;
using namespace mozilla::ipc;
using mozilla::DeprecatedAbs;
using mozilla::Maybe;
using mozilla::Nothing;
using mozilla::Some;
class nsMultiplexInputStream final
: public nsIMultiplexInputStream
, public nsISeekableStream
, public nsIIPCSerializableInputStream
, public nsICloneableInputStream
{
public:
nsMultiplexInputStream();
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
NS_DECL_NSIMULTIPLEXINPUTSTREAM
NS_DECL_NSISEEKABLESTREAM
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
NS_DECL_NSICLONEABLEINPUTSTREAM
private:
~nsMultiplexInputStream()
{
}
struct MOZ_STACK_CLASS ReadSegmentsState
{
nsCOMPtr<nsIInputStream> mThisStream;
uint32_t mOffset;
nsWriteSegmentFun mWriter;
void* mClosure;
bool mDone;
};
static nsresult ReadSegCb(nsIInputStream* aIn, void* aClosure,
const char* aFromRawSegment, uint32_t aToOffset,
uint32_t aCount, uint32_t* aWriteCount);
Mutex mLock; // Protects access to all data members.
nsTArray<nsCOMPtr<nsIInputStream>> mStreams;
uint32_t mCurrentStream;
bool mStartedReadingCurrent;
nsresult mStatus;
};
NS_IMPL_ADDREF(nsMultiplexInputStream)
NS_IMPL_RELEASE(nsMultiplexInputStream)
NS_IMPL_CLASSINFO(nsMultiplexInputStream, nullptr, nsIClassInfo::THREADSAFE,
NS_MULTIPLEXINPUTSTREAM_CID)
NS_IMPL_QUERY_INTERFACE_CI(nsMultiplexInputStream,
nsIMultiplexInputStream,
nsIInputStream,
nsISeekableStream,
nsIIPCSerializableInputStream,
nsICloneableInputStream)
NS_IMPL_CI_INTERFACE_GETTER(nsMultiplexInputStream,
nsIMultiplexInputStream,
nsIInputStream,
nsISeekableStream)
static nsresult
AvailableMaybeSeek(nsIInputStream* aStream, uint64_t* aResult)
{
nsresult rv = aStream->Available(aResult);
if (rv == NS_BASE_STREAM_CLOSED) {
// Blindly seek to the current position if Available() returns
// NS_BASE_STREAM_CLOSED.
// If nsIFileInputStream is closed in Read() due to CLOSE_ON_EOF flag,
// Seek() could reopen the file if REOPEN_ON_REWIND flag is set.
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aStream);
if (seekable) {
nsresult rvSeek = seekable->Seek(nsISeekableStream::NS_SEEK_CUR, 0);
if (NS_SUCCEEDED(rvSeek)) {
rv = aStream->Available(aResult);
}
}
}
return rv;
}
static nsresult
TellMaybeSeek(nsISeekableStream* aSeekable, int64_t* aResult)
{
nsresult rv = aSeekable->Tell(aResult);
if (rv == NS_BASE_STREAM_CLOSED) {
// Blindly seek to the current position if Tell() returns
// NS_BASE_STREAM_CLOSED.
// If nsIFileInputStream is closed in Read() due to CLOSE_ON_EOF flag,
// Seek() could reopen the file if REOPEN_ON_REWIND flag is set.
nsresult rvSeek = aSeekable->Seek(nsISeekableStream::NS_SEEK_CUR, 0);
if (NS_SUCCEEDED(rvSeek)) {
rv = aSeekable->Tell(aResult);
}
}
return rv;
}
nsMultiplexInputStream::nsMultiplexInputStream()
: mLock("nsMultiplexInputStream lock"),
mCurrentStream(0),
mStartedReadingCurrent(false),
mStatus(NS_OK)
{
}
NS_IMETHODIMP
nsMultiplexInputStream::GetCount(uint32_t* aCount)
{
MutexAutoLock lock(mLock);
*aCount = mStreams.Length();
return NS_OK;
}
NS_IMETHODIMP
nsMultiplexInputStream::AppendStream(nsIInputStream* aStream)
{
MutexAutoLock lock(mLock);
return mStreams.AppendElement(aStream) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP
nsMultiplexInputStream::InsertStream(nsIInputStream* aStream, uint32_t aIndex)
{
MutexAutoLock lock(mLock);
mStreams.InsertElementAt(aIndex, aStream);
if (mCurrentStream > aIndex ||
(mCurrentStream == aIndex && mStartedReadingCurrent)) {
++mCurrentStream;
}
return NS_OK;
}
NS_IMETHODIMP
nsMultiplexInputStream::RemoveStream(uint32_t aIndex)
{
MutexAutoLock lock(mLock);
mStreams.RemoveElementAt(aIndex);
if (mCurrentStream > aIndex) {
--mCurrentStream;
} else if (mCurrentStream == aIndex) {
mStartedReadingCurrent = false;
}
return NS_OK;
}
NS_IMETHODIMP
nsMultiplexInputStream::GetStream(uint32_t aIndex, nsIInputStream** aResult)
{
MutexAutoLock lock(mLock);
*aResult = mStreams.SafeElementAt(aIndex, nullptr);
if (NS_WARN_IF(!*aResult)) {
return NS_ERROR_NOT_AVAILABLE;
}
NS_ADDREF(*aResult);
return NS_OK;
}
NS_IMETHODIMP
nsMultiplexInputStream::Close()
{
MutexAutoLock lock(mLock);
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;
}
NS_IMETHODIMP
nsMultiplexInputStream::Available(uint64_t* aResult)
{
MutexAutoLock lock(mLock);
if (NS_FAILED(mStatus)) {
return mStatus;
}
uint64_t avail = 0;
uint32_t len = mStreams.Length();
for (uint32_t i = mCurrentStream; i < len; i++) {
uint64_t streamAvail;
mStatus = AvailableMaybeSeek(mStreams[i], &streamAvail);
if (NS_WARN_IF(NS_FAILED(mStatus))) {
return mStatus;
}
avail += streamAvail;
}
*aResult = avail;
return NS_OK;
}
NS_IMETHODIMP
nsMultiplexInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* aResult)
{
MutexAutoLock lock(mLock);
// 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).
*aResult = 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");
*aResult += read;
aCount -= read;
aBuf += read;
mStartedReadingCurrent = true;
}
}
return *aResult ? NS_OK : rv;
}
NS_IMETHODIMP
nsMultiplexInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
uint32_t aCount, uint32_t* aResult)
{
MutexAutoLock lock(mLock);
if (mStatus == NS_BASE_STREAM_CLOSED) {
*aResult = 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.
*aResult = state.mOffset;
return state.mOffset ? NS_OK : rv;
}
nsresult
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;
}
NS_IMETHODIMP
nsMultiplexInputStream::IsNonBlocking(bool* aNonBlocking)
{
MutexAutoLock lock(mLock);
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);
if (NS_WARN_IF(NS_FAILED(rv))) {
return 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;
}
NS_IMETHODIMP
nsMultiplexInputStream::Seek(int32_t aWhence, int64_t aOffset)
{
MutexAutoLock lock(mLock);
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);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
continue;
} else {
break;
}
}
// Get position in current stream
int64_t streamPos;
if (i > oldCurrentStream ||
(i == oldCurrentStream && !oldStartedReadingCurrent)) {
streamPos = 0;
} else {
rv = TellMaybeSeek(stream, &streamPos);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
// See if we need to seek current stream forward or backward
if (remaining < streamPos) {
rv = stream->Seek(NS_SEEK_SET, remaining);
if (NS_WARN_IF(NS_FAILED(rv))) {
return 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 = AvailableMaybeSeek(mStreams[i], &avail);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
int64_t newPos = XPCOM_MIN(remaining, streamPos + (int64_t)avail);
rv = stream->Seek(NS_SEEK_SET, newPos);
if (NS_WARN_IF(NS_FAILED(rv))) {
return 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 = AvailableMaybeSeek(mStreams[i], &avail);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
int64_t seek = XPCOM_MIN((int64_t)avail, remaining);
rv = stream->Seek(NS_SEEK_CUR, seek);
if (NS_WARN_IF(NS_FAILED(rv))) {
return 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 = TellMaybeSeek(stream, &pos);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
int64_t seek = XPCOM_MIN(pos, remaining);
rv = stream->Seek(NS_SEEK_CUR, -seek);
if (NS_WARN_IF(NS_FAILED(rv))) {
return 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);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else {
break;
}
}
// Get position in current stream
int64_t streamPos;
if (i < oldCurrentStream) {
streamPos = 0;
} else {
uint64_t avail;
rv = AvailableMaybeSeek(mStreams[i], &avail);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
streamPos = avail;
}
// See if we have enough data in the current stream.
if (DeprecatedAbs(remaining) < streamPos) {
rv = stream->Seek(NS_SEEK_END, remaining);
if (NS_WARN_IF(NS_FAILED(rv))) {
return 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 = TellMaybeSeek(stream, &avail);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
int64_t newPos = streamPos + XPCOM_MIN(avail, DeprecatedAbs(remaining));
rv = stream->Seek(NS_SEEK_END, -newPos);
if (NS_WARN_IF(NS_FAILED(rv))) {
return 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;
}
NS_IMETHODIMP
nsMultiplexInputStream::Tell(int64_t* aResult)
{
MutexAutoLock lock(mLock);
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]);
if (NS_WARN_IF(!stream)) {
return NS_ERROR_NO_INTERFACE;
}
int64_t pos;
rv = TellMaybeSeek(stream, &pos);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
ret64 += pos;
}
*aResult = ret64;
return NS_OK;
}
NS_IMETHODIMP
nsMultiplexInputStream::SetEOF()
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsMultiplexInputStreamConstructor(nsISupports* aOuter,
REFNSIID aIID,
void** aResult)
{
*aResult = nullptr;
if (aOuter) {
return NS_ERROR_NO_AGGREGATION;
}
RefPtr<nsMultiplexInputStream> inst = new nsMultiplexInputStream();
return inst->QueryInterface(aIID, aResult);
}
void
nsMultiplexInputStream::Serialize(InputStreamParams& aParams,
FileDescriptorArray& aFileDescriptors)
{
MutexAutoLock lock(mLock);
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++) {
InputStreamParams childStreamParams;
SerializeInputStream(mStreams[index], childStreamParams,
aFileDescriptors);
streams.AppendElement(childStreamParams);
}
}
params.currentStream() = mCurrentStream;
params.status() = mStatus;
params.startedReadingCurrent() = mStartedReadingCurrent;
aParams = params;
}
bool
nsMultiplexInputStream::Deserialize(const InputStreamParams& aParams,
const FileDescriptorArray& aFileDescriptors)
{
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], aFileDescriptors);
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;
}
Maybe<uint64_t>
nsMultiplexInputStream::ExpectedSerializedLength()
{
MutexAutoLock lock(mLock);
bool lengthValueExists = false;
uint64_t expectedLength = 0;
uint32_t streamCount = mStreams.Length();
for (uint32_t index = 0; index < streamCount; index++) {
nsCOMPtr<nsIIPCSerializableInputStream> stream = do_QueryInterface(mStreams[index]);
if (!stream) {
continue;
}
Maybe<uint64_t> length = stream->ExpectedSerializedLength();
if (length.isNothing()) {
continue;
}
lengthValueExists = true;
expectedLength += length.value();
}
return lengthValueExists ? Some(expectedLength) : Nothing();
}
NS_IMETHODIMP
nsMultiplexInputStream::GetCloneable(bool* aCloneable)
{
MutexAutoLock lock(mLock);
//XXXnsm Cloning a multiplex stream which has started reading is not permitted
//right now.
if (mCurrentStream > 0 || mStartedReadingCurrent) {
*aCloneable = false;
return NS_OK;
}
uint32_t len = mStreams.Length();
for (uint32_t i = 0; i < len; ++i) {
nsCOMPtr<nsICloneableInputStream> cis = do_QueryInterface(mStreams[i]);
if (!cis || !cis->GetCloneable()) {
*aCloneable = false;
return NS_OK;
}
}
*aCloneable = true;
return NS_OK;
}
NS_IMETHODIMP
nsMultiplexInputStream::Clone(nsIInputStream** aClone)
{
MutexAutoLock lock(mLock);
//XXXnsm Cloning a multiplex stream which has started reading is not permitted
//right now.
if (mCurrentStream > 0 || mStartedReadingCurrent) {
return NS_ERROR_FAILURE;
}
RefPtr<nsMultiplexInputStream> clone = new nsMultiplexInputStream();
nsresult rv;
uint32_t len = mStreams.Length();
for (uint32_t i = 0; i < len; ++i) {
nsCOMPtr<nsICloneableInputStream> substream = do_QueryInterface(mStreams[i]);
if (NS_WARN_IF(!substream)) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIInputStream> clonedSubstream;
rv = substream->Clone(getter_AddRefs(clonedSubstream));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = clone->AppendStream(clonedSubstream);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
clone.forget(aClone);
return NS_OK;
}

View file

@ -0,0 +1,30 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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* aOuter,
REFNSIID aIID,
void** aResult);
#endif // _nsMultiplexInputStream_h_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,63 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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& aInput, nsAString& aOutput);
nsresult NS_CopyUnicodeToNative(const nsAString& aInput, nsACString& aOutput);
/*
* 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__

24
xpcom/io/nsPipe.h Normal file
View file

@ -0,0 +1,24 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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
nsPipeConstructor(nsISupports* outer, REFNSIID iid, void** result);
#endif // !defined(nsPipe_h__)

2007
xpcom/io/nsPipe3.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,28 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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_ISUPPORTS(nsScriptableBase64Encoder, nsIScriptableBase64Encoder)
NS_IMETHODIMP
nsScriptableBase64Encoder::EncodeToCString(nsIInputStream* aStream,
uint32_t aLength,
nsACString& aResult)
{
return Base64EncodeInputStream(aStream, aResult, aLength);
}
NS_IMETHODIMP
nsScriptableBase64Encoder::EncodeToString(nsIInputStream* aStream,
uint32_t aLength,
nsAString& aResult)
{
return Base64EncodeInputStream(aStream, aResult, aLength);
}

View file

@ -0,0 +1,30 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 final : public nsIScriptableBase64Encoder
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISCRIPTABLEBASE64ENCODER
private:
~nsScriptableBase64Encoder()
{
}
};
#endif

View file

@ -0,0 +1,134 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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_ISUPPORTS(nsScriptableInputStream, nsIScriptableInputStream)
// nsIScriptableInputStream methods
NS_IMETHODIMP
nsScriptableInputStream::Close()
{
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* aResult)
{
if (!mInputStream) {
return NS_ERROR_NOT_INITIALIZED;
}
return mInputStream->Available(aResult);
}
NS_IMETHODIMP
nsScriptableInputStream::Read(uint32_t aCount, char** aResult)
{
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*)malloc(count + 1); // make room for '\0'
if (!buffer) {
return NS_ERROR_OUT_OF_MEMORY;
}
rv = ReadHelper(buffer, count);
if (NS_FAILED(rv)) {
free(buffer);
return rv;
}
buffer[count] = '\0';
*aResult = buffer;
return NS_OK;
}
NS_IMETHODIMP
nsScriptableInputStream::ReadBytes(uint32_t aCount, nsACString& aResult)
{
if (!mInputStream) {
return NS_ERROR_NOT_INITIALIZED;
}
if (!aResult.SetLength(aCount, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
MOZ_ASSERT(aResult.Length() == aCount);
char* ptr = aResult.BeginWriting();
nsresult rv = ReadHelper(ptr, aCount);
if (NS_FAILED(rv)) {
aResult.Truncate();
}
return rv;
}
nsresult
nsScriptableInputStream::ReadHelper(char* aBuffer, uint32_t aCount)
{
uint32_t totalBytesRead = 0;
while (1) {
uint32_t bytesRead;
nsresult rv = mInputStream->Read(aBuffer + 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) {
return NS_ERROR_FAILURE;
}
}
return NS_OK;
}
nsresult
nsScriptableInputStream::Create(nsISupports* aOuter, REFNSIID aIID,
void** aResult)
{
if (aOuter) {
return NS_ERROR_NO_AGGREGATION;
}
RefPtr<nsScriptableInputStream> sis = new nsScriptableInputStream();
return sis->QueryInterface(aIID, aResult);
}

View file

@ -0,0 +1,47 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 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()
{
}
nsresult ReadHelper(char* aBuffer, uint32_t aCount);
nsCOMPtr<nsIInputStream> mInputStream;
};
#endif // ___nsscriptableinputstream___h_

View file

@ -0,0 +1,169 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "nsMemory.h"
nsresult
nsSegmentedBuffer::Init(uint32_t aSegmentSize, uint32_t aMaxSize)
{
if (mSegmentArrayCount != 0) {
return NS_ERROR_FAILURE; // initialized more than once
}
mSegmentSize = aSegmentSize;
mMaxSize = aMaxSize;
#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) {
uint32_t bytes = mSegmentArrayCount * sizeof(char*);
mSegmentArray = (char**)moz_xmalloc(bytes);
if (!mSegmentArray) {
return nullptr;
}
memset(mSegmentArray, 0, bytes);
}
if (IsFull()) {
uint32_t newArraySize = mSegmentArrayCount * 2;
uint32_t bytes = newArraySize * sizeof(char*);
char** newSegArray = (char**)moz_xrealloc(mSegmentArray, bytes);
if (!newSegArray) {
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*)malloc(mSegmentSize);
if (!seg) {
return nullptr;
}
mSegmentArray[mLastSegmentIndex] = seg;
mLastSegmentIndex = ModSegArraySize(mLastSegmentIndex + 1);
return seg;
}
bool
nsSegmentedBuffer::DeleteFirstSegment()
{
NS_ASSERTION(mSegmentArray[mFirstSegmentIndex] != nullptr, "deleting bad segment");
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");
free(mSegmentArray[last]);
mSegmentArray[last] = nullptr;
mLastSegmentIndex = last;
return (bool)(mLastSegmentIndex == mFirstSegmentIndex);
}
bool
nsSegmentedBuffer::ReallocLastSegment(size_t aNewSize)
{
int32_t last = ModSegArraySize(mLastSegmentIndex - 1);
NS_ASSERTION(mSegmentArray[last] != nullptr, "realloc'ing bad segment");
char* newSegment = (char*)realloc(mSegmentArray[last], aNewSize);
if (newSegment) {
mSegmentArray[last] = newSegment;
return true;
}
return false;
}
void
nsSegmentedBuffer::Empty()
{
if (mSegmentArray) {
for (uint32_t i = 0; i < mSegmentArrayCount; i++) {
if (mSegmentArray[i]) {
free(mSegmentArray[i]);
}
}
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
////////////////////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,109 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "nsIMemory.h"
class nsSegmentedBuffer
{
public:
nsSegmentedBuffer()
: mSegmentSize(0)
, mMaxSize(0)
, mSegmentArray(nullptr)
, mSegmentArrayCount(0)
, mFirstSegmentIndex(0)
, mLastSegmentIndex(0)
{
}
~nsSegmentedBuffer()
{
Empty();
}
nsresult Init(uint32_t aSegmentSize, uint32_t aMaxSize);
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 aNewSize);
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 aIndex)
{
NS_ASSERTION(aIndex < GetSegmentCount(), "index out of bounds");
int32_t i = ModSegArraySize(mFirstSegmentIndex + (int32_t)aIndex);
return mSegmentArray[i];
}
protected:
inline int32_t ModSegArraySize(int32_t aIndex)
{
uint32_t result = aIndex & (mSegmentArrayCount - 1);
NS_ASSERTION(result == aIndex % mSegmentArrayCount,
"non-power-of-2 mSegmentArrayCount");
return result;
}
inline bool IsFull()
{
return ModSegArraySize(mLastSegmentIndex + 1) == mFirstSegmentIndex;
}
protected:
uint32_t mSegmentSize;
uint32_t mMaxSize;
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__

View file

@ -0,0 +1,648 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "nsICloneableInputStream.h"
#include "nsIInputStream.h"
#include "nsIIPCSerializableInputStream.h"
#include "nsISeekableStream.h"
#include "mozilla/Logging.h"
#include "mozilla/Attributes.h"
#include "mozilla/Likely.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/ipc/InputStreamUtils.h"
using mozilla::ipc::InputStreamParams;
using mozilla::ipc::StringInputStreamParams;
using mozilla::Maybe;
using mozilla::Some;
//
// Log module for StorageStream logging...
//
// To enable logging (see prlog.h for full details):
//
// set MOZ_LOG=StorageStreamLog:5
// set MOZ_LOG_FILE=storage.log
//
// This enables LogLevel::Debug level information and places all output in
// the file storage.log.
//
static LazyLogModule sStorageStreamLog("nsStorageStream");
#ifdef LOG
#undef LOG
#endif
#define LOG(args) MOZ_LOG(sStorageStreamLog, mozilla::LogLevel::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_ISUPPORTS(nsStorageStream,
nsIStorageStream,
nsIOutputStream)
NS_IMETHODIMP
nsStorageStream::Init(uint32_t aSegmentSize, uint32_t aMaxSize)
{
mSegmentedBuffer = new nsSegmentedBuffer();
mSegmentSize = aSegmentSize;
mSegmentSizeLog2 = mozilla::FloorLog2(aSegmentSize);
// Segment size must be a power of two
if (mSegmentSize != ((uint32_t)1 << mSegmentSizeLog2)) {
return NS_ERROR_INVALID_ARG;
}
return mSegmentedBuffer->Init(aSegmentSize, aMaxSize);
}
NS_IMETHODIMP
nsStorageStream::GetOutputStream(int32_t aStartingOffset,
nsIOutputStream** aOutputStream)
{
if (NS_WARN_IF(!aOutputStream)) {
return NS_ERROR_INVALID_ARG;
}
if (NS_WARN_IF(!mSegmentedBuffer)) {
return 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()
{
if (NS_WARN_IF(!mSegmentedBuffer)) {
return 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)
{
if (NS_WARN_IF(!aNumWritten) || NS_WARN_IF(!aBuffer)) {
return NS_ERROR_INVALID_ARG;
}
if (NS_WARN_IF(!mSegmentedBuffer)) {
return NS_ERROR_NOT_INITIALIZED;
}
const char* readCursor;
uint32_t count, availableInSegment, remaining;
nsresult rv = NS_OK;
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* aInStr, uint32_t aCount,
uint32_t* aResult)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsStorageStream::WriteSegments(nsReadSegmentFun aReader, void* aClosure,
uint32_t aCount, uint32_t* aResult)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsStorageStream::IsNonBlocking(bool* aNonBlocking)
{
*aNonBlocking = false;
return NS_OK;
}
NS_IMETHODIMP
nsStorageStream::GetLength(uint32_t* aLength)
{
*aLength = mLogicalLength;
return NS_OK;
}
// Truncate the buffer by deleting the end segments
NS_IMETHODIMP
nsStorageStream::SetLength(uint32_t aLength)
{
if (NS_WARN_IF(!mSegmentedBuffer)) {
return 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)
{
*aWriteInProgress = mWriteInProgress;
return NS_OK;
}
nsresult
nsStorageStream::Seek(int32_t aPosition)
{
if (NS_WARN_IF(!mSegmentedBuffer)) {
return 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 final
: public nsIInputStream
, public nsISeekableStream
, public nsIIPCSerializableInputStream
, public nsICloneableInputStream
{
public:
nsStorageInputStream(nsStorageStream* aStorageStream, uint32_t aSegmentSize)
: mStorageStream(aStorageStream), mReadCursor(0),
mSegmentEnd(0), mSegmentNum(0),
mSegmentSize(aSegmentSize), mLogicalCursor(0),
mStatus(NS_OK)
{
}
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
NS_DECL_NSISEEKABLESTREAM
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
NS_DECL_NSICLONEABLEINPUTSTREAM
private:
~nsStorageInputStream()
{
}
protected:
nsresult Seek(uint32_t aPosition);
friend class nsStorageStream;
private:
RefPtr<nsStorageStream> mStorageStream;
uint32_t mReadCursor; // Next memory location to read byte, or 0
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_ISUPPORTS(nsStorageInputStream,
nsIInputStream,
nsISeekableStream,
nsIIPCSerializableInputStream,
nsICloneableInputStream)
NS_IMETHODIMP
nsStorageStream::NewInputStream(int32_t aStartingOffset,
nsIInputStream** aInputStream)
{
if (NS_WARN_IF(!mSegmentedBuffer)) {
return NS_ERROR_NOT_INITIALIZED;
}
RefPtr<nsStorageInputStream> inputStream =
new nsStorageInputStream(this, mSegmentSize);
nsresult rv = inputStream->Seek(aStartingOffset);
if (NS_FAILED(rv)) {
return rv;
}
inputStream.forget(aInputStream);
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 aWriter, void* aClosure,
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;
}
// We have data in the stream, but if mSegmentEnd is zero, then we
// were likely constructed prior to any data being written into
// the stream. Therefore, if mSegmentEnd is non-zero, we should
// move into the next segment; otherwise, we should stay in this
// segment so our input state can be updated and we can properly
// perform the initial read.
if (mSegmentEnd > 0) {
mSegmentNum++;
}
mReadCursor = 0;
mSegmentEnd = XPCOM_MIN(mSegmentSize, available);
availableInSegment = mSegmentEnd;
}
const char* cur = mStorageStream->mSegmentedBuffer->GetSegment(mSegmentNum);
count = XPCOM_MIN(availableInSegment, remainingCapacity);
rv = aWriter(this, aClosure, 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;
}
nsresult
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;
}
void
nsStorageInputStream::Serialize(InputStreamParams& aParams, FileDescriptorArray&)
{
nsCString combined;
int64_t offset;
mozilla::DebugOnly<nsresult> rv = Tell(&offset);
MOZ_ASSERT(NS_SUCCEEDED(rv));
uint64_t remaining;
rv = Available(&remaining);
MOZ_ASSERT(NS_SUCCEEDED(rv));
combined.SetCapacity(remaining);
uint32_t numRead = 0;
rv = Read(combined.BeginWriting(), remaining, &numRead);
MOZ_ASSERT(NS_SUCCEEDED(rv));
MOZ_ASSERT(numRead == remaining);
combined.SetLength(numRead);
rv = Seek(NS_SEEK_SET, offset);
MOZ_ASSERT(NS_SUCCEEDED(rv));
StringInputStreamParams params;
params.data() = combined;
aParams = params;
}
Maybe<uint64_t>
nsStorageInputStream::ExpectedSerializedLength()
{
uint64_t remaining = 0;
DebugOnly<nsresult> rv = Available(&remaining);
MOZ_ASSERT(NS_SUCCEEDED(rv));
return Some(remaining);
}
bool
nsStorageInputStream::Deserialize(const InputStreamParams& aParams,
const FileDescriptorArray&)
{
NS_NOTREACHED("We should never attempt to deserialize a storage input stream.");
return false;
}
NS_IMETHODIMP
nsStorageInputStream::GetCloneable(bool* aCloneableOut)
{
*aCloneableOut = true;
return NS_OK;
}
NS_IMETHODIMP
nsStorageInputStream::Clone(nsIInputStream** aCloneOut)
{
return mStorageStream->NewInputStream(mLogicalCursor, aCloneOut);
}
nsresult
NS_NewStorageStream(uint32_t aSegmentSize, uint32_t aMaxSize,
nsIStorageStream** aResult)
{
RefPtr<nsStorageStream> storageStream = new nsStorageStream();
nsresult rv = storageStream->Init(aSegmentSize, aMaxSize);
if (NS_FAILED(rv)) {
return rv;
}
storageStream.forget(aResult);
return NS_OK;
}
// Undefine LOG, so that other .cpp files (or their includes) won't complain
// about it already being defined, when we build in unified mode.
#undef LOG

View file

@ -0,0 +1,73 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 final
: public nsIStorageStream
, public nsIOutputStream
{
public:
nsStorageStream();
NS_DECL_THREADSAFE_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
nsresult 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_

957
xpcom/io/nsStreamUtils.cpp Normal file
View file

@ -0,0 +1,957 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "nsICloneableInputStream.h"
#include "nsIEventTarget.h"
#include "nsICancelableRunnable.h"
#include "nsISafeOutputStream.h"
#include "nsString.h"
#include "nsIAsyncInputStream.h"
#include "nsIAsyncOutputStream.h"
#include "nsIBufferedStreams.h"
#include "nsNetCID.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
using namespace mozilla;
//-----------------------------------------------------------------------------
// This is a nsICancelableRunnable because we can dispatch it to Workers and
// those can be shut down at any time, and in these cases, Cancel() is called
// instead of Run().
class nsInputStreamReadyEvent final
: public CancelableRunnable
, public nsIInputStreamCallback
{
public:
NS_DECL_ISUPPORTS_INHERITED
nsInputStreamReadyEvent(nsIInputStreamCallback* aCallback,
nsIEventTarget* aTarget)
: mCallback(aCallback)
, mTarget(aTarget)
{
}
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* aStream) override
{
mStream = aStream;
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() override
{
if (mCallback) {
if (mStream) {
mCallback->OnInputStreamReady(mStream);
}
mCallback = nullptr;
}
return NS_OK;
}
nsresult Cancel() override
{
mCallback = nullptr;
return NS_OK;
}
private:
nsCOMPtr<nsIAsyncInputStream> mStream;
nsCOMPtr<nsIInputStreamCallback> mCallback;
nsCOMPtr<nsIEventTarget> mTarget;
};
NS_IMPL_ISUPPORTS_INHERITED(nsInputStreamReadyEvent, CancelableRunnable,
nsIInputStreamCallback)
//-----------------------------------------------------------------------------
// This is a nsICancelableRunnable because we can dispatch it to Workers and
// those can be shut down at any time, and in these cases, Cancel() is called
// instead of Run().
class nsOutputStreamReadyEvent final
: public CancelableRunnable
, public nsIOutputStreamCallback
{
public:
NS_DECL_ISUPPORTS_INHERITED
nsOutputStreamReadyEvent(nsIOutputStreamCallback* aCallback,
nsIEventTarget* aTarget)
: mCallback(aCallback)
, mTarget(aTarget)
{
}
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* aStream) override
{
mStream = aStream;
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() override
{
if (mCallback) {
if (mStream) {
mCallback->OnOutputStreamReady(mStream);
}
mCallback = nullptr;
}
return NS_OK;
}
nsresult Cancel() override
{
mCallback = nullptr;
return NS_OK;
}
private:
nsCOMPtr<nsIAsyncOutputStream> mStream;
nsCOMPtr<nsIOutputStreamCallback> mCallback;
nsCOMPtr<nsIEventTarget> mTarget;
};
NS_IMPL_ISUPPORTS_INHERITED(nsOutputStreamReadyEvent, CancelableRunnable,
nsIOutputStreamCallback)
//-----------------------------------------------------------------------------
already_AddRefed<nsIInputStreamCallback>
NS_NewInputStreamReadyEvent(nsIInputStreamCallback* aCallback,
nsIEventTarget* aTarget)
{
NS_ASSERTION(aCallback, "null callback");
NS_ASSERTION(aTarget, "null target");
RefPtr<nsInputStreamReadyEvent> ev =
new nsInputStreamReadyEvent(aCallback, aTarget);
return ev.forget();
}
already_AddRefed<nsIOutputStreamCallback>
NS_NewOutputStreamReadyEvent(nsIOutputStreamCallback* aCallback,
nsIEventTarget* aTarget)
{
NS_ASSERTION(aCallback, "null callback");
NS_ASSERTION(aTarget, "null target");
RefPtr<nsOutputStreamReadyEvent> ev =
new nsOutputStreamReadyEvent(aCallback, aTarget);
return ev.forget();
}
//-----------------------------------------------------------------------------
// NS_AsyncCopy implementation
// abstract stream copier...
class nsAStreamCopier
: public nsIInputStreamCallback
, public nsIOutputStreamCallback
, public CancelableRunnable
{
public:
NS_DECL_ISUPPORTS_INHERITED
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)
{
}
// kick off the async copy...
nsresult Start(nsIInputStream* aSource,
nsIOutputStream* aSink,
nsIEventTarget* aTarget,
nsAsyncCopyCallbackFun aCallback,
void* aClosure,
uint32_t aChunksize,
bool aCloseSource,
bool aCloseSink,
nsAsyncCopyProgressFun aProgressCallback)
{
mSource = aSource;
mSink = aSink;
mTarget = aTarget;
mCallback = aCallback;
mClosure = aClosure;
mChunkSize = aChunksize;
mCloseSource = aCloseSource;
mCloseSink = aCloseSink;
mProgressCallback = aProgressCallback;
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* aSourceCondition,
nsresult* aSinkCondition) = 0;
void Process()
{
if (!mSource || !mSink) {
return;
}
nsresult cancelStatus;
bool canceled;
{
MutexAutoLock lock(mLock);
canceled = mCanceled;
cancelStatus = mCancelStatus;
}
// If the copy was canceled before Process() was even called, then
// sourceCondition and sinkCondition should be set to error results to
// ensure we don't call Finish() on a canceled nsISafeOutputStream.
MOZ_ASSERT(NS_FAILED(cancelStatus) == canceled, "cancel needs an error");
nsresult sourceCondition = cancelStatus;
nsresult sinkCondition = cancelStatus;
// 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* aSource) override
{
PostContinuationEvent();
return NS_OK;
}
NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream* aSink) override
{
PostContinuationEvent();
return NS_OK;
}
// continuation event handler
NS_IMETHOD Run() override
{
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 Cancel() MOZ_MUST_OVERRIDE override = 0;
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;
// virtual since subclasses call superclass Release()
virtual ~nsAStreamCopier()
{
}
};
NS_IMPL_ISUPPORTS_INHERITED(nsAStreamCopier,
CancelableRunnable,
nsIInputStreamCallback,
nsIOutputStreamCallback)
class nsStreamCopierIB final : public nsAStreamCopier
{
public:
nsStreamCopierIB() : nsAStreamCopier()
{
}
virtual ~nsStreamCopierIB()
{
}
struct MOZ_STACK_CLASS ReadSegmentsState
{
// the nsIOutputStream will outlive the ReadSegmentsState on the stack
nsIOutputStream* MOZ_NON_OWNING_REF mSink;
nsresult mSinkCondition;
};
static nsresult ConsumeInputBuffer(nsIInputStream* aInStr,
void* aClosure,
const char* aBuffer,
uint32_t aOffset,
uint32_t aCount,
uint32_t* aCountWritten)
{
ReadSegmentsState* state = (ReadSegmentsState*)aClosure;
nsresult rv = state->mSink->Write(aBuffer, aCount, aCountWritten);
if (NS_FAILED(rv)) {
state->mSinkCondition = rv;
} else if (*aCountWritten == 0) {
state->mSinkCondition = NS_BASE_STREAM_CLOSED;
}
return state->mSinkCondition;
}
uint32_t DoCopy(nsresult* aSourceCondition,
nsresult* aSinkCondition) override
{
ReadSegmentsState state;
state.mSink = mSink;
state.mSinkCondition = NS_OK;
uint32_t n;
*aSourceCondition =
mSource->ReadSegments(ConsumeInputBuffer, &state, mChunkSize, &n);
*aSinkCondition = state.mSinkCondition;
return n;
}
nsresult Cancel() override
{
return NS_OK;
}
};
class nsStreamCopierOB final : public nsAStreamCopier
{
public:
nsStreamCopierOB() : nsAStreamCopier()
{
}
virtual ~nsStreamCopierOB()
{
}
struct MOZ_STACK_CLASS WriteSegmentsState
{
// the nsIInputStream will outlive the WriteSegmentsState on the stack
nsIInputStream* MOZ_NON_OWNING_REF mSource;
nsresult mSourceCondition;
};
static nsresult FillOutputBuffer(nsIOutputStream* aOutStr,
void* aClosure,
char* aBuffer,
uint32_t aOffset,
uint32_t aCount,
uint32_t* aCountRead)
{
WriteSegmentsState* state = (WriteSegmentsState*)aClosure;
nsresult rv = state->mSource->Read(aBuffer, aCount, aCountRead);
if (NS_FAILED(rv)) {
state->mSourceCondition = rv;
} else if (*aCountRead == 0) {
state->mSourceCondition = NS_BASE_STREAM_CLOSED;
}
return state->mSourceCondition;
}
uint32_t DoCopy(nsresult* aSourceCondition,
nsresult* aSinkCondition) override
{
WriteSegmentsState state;
state.mSource = mSource;
state.mSourceCondition = NS_OK;
uint32_t n;
*aSinkCondition =
mSink->WriteSegments(FillOutputBuffer, &state, mChunkSize, &n);
*aSourceCondition = state.mSourceCondition;
return n;
}
nsresult Cancel() override
{
return NS_OK;
}
};
//-----------------------------------------------------------------------------
nsresult
NS_AsyncCopy(nsIInputStream* aSource,
nsIOutputStream* aSink,
nsIEventTarget* aTarget,
nsAsyncCopyMode aMode,
uint32_t aChunkSize,
nsAsyncCopyCallbackFun aCallback,
void* aClosure,
bool aCloseSource,
bool aCloseSink,
nsISupports** aCopierCtx,
nsAsyncCopyProgressFun aProgressCallback)
{
NS_ASSERTION(aTarget, "non-null target required");
nsresult rv;
nsAStreamCopier* copier;
if (aMode == NS_ASYNCCOPY_VIA_READSEGMENTS) {
copier = new nsStreamCopierIB();
} else {
copier = new nsStreamCopierOB();
}
// Start() takes an owning ref to the copier...
NS_ADDREF(copier);
rv = copier->Start(aSource, aSink, aTarget, aCallback, aClosure, aChunkSize,
aCloseSource, aCloseSink, aProgressCallback);
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* aStream, uint32_t aMaxCount,
nsACString& aResult)
{
nsresult rv = NS_OK;
aResult.Truncate();
while (aMaxCount) {
uint64_t avail64;
rv = aStream->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, aMaxCount);
// resize aResult buffer
uint32_t length = aResult.Length();
if (avail > UINT32_MAX - length) {
return NS_ERROR_FILE_TOO_BIG;
}
aResult.SetLength(length + avail);
if (aResult.Length() != (length + avail)) {
return NS_ERROR_OUT_OF_MEMORY;
}
char* buf = aResult.BeginWriting() + length;
uint32_t n;
rv = aStream->Read(buf, avail, &n);
if (NS_FAILED(rv)) {
break;
}
if (n != avail) {
aResult.SetLength(length + n);
}
if (n == 0) {
break;
}
aMaxCount -= n;
}
return rv;
}
//-----------------------------------------------------------------------------
static nsresult
TestInputStream(nsIInputStream* aInStr,
void* aClosure,
const char* aBuffer,
uint32_t aOffset,
uint32_t aCount,
uint32_t* aCountWritten)
{
bool* result = static_cast<bool*>(aClosure);
*result = true;
return NS_ERROR_ABORT; // don't call me anymore
}
bool
NS_InputStreamIsBuffered(nsIInputStream* aStream)
{
nsCOMPtr<nsIBufferedInputStream> bufferedIn = do_QueryInterface(aStream);
if (bufferedIn) {
return true;
}
bool result = false;
uint32_t n;
nsresult rv = aStream->ReadSegments(TestInputStream, &result, 1, &n);
return result || NS_SUCCEEDED(rv);
}
static nsresult
TestOutputStream(nsIOutputStream* aOutStr,
void* aClosure,
char* aBuffer,
uint32_t aOffset,
uint32_t aCount,
uint32_t* aCountRead)
{
bool* result = static_cast<bool*>(aClosure);
*result = true;
return NS_ERROR_ABORT; // don't call me anymore
}
bool
NS_OutputStreamIsBuffered(nsIOutputStream* aStream)
{
nsCOMPtr<nsIBufferedOutputStream> bufferedOut = do_QueryInterface(aStream);
if (bufferedOut) {
return true;
}
bool result = false;
uint32_t n;
aStream->WriteSegments(TestOutputStream, &result, 1, &n);
return result;
}
//-----------------------------------------------------------------------------
nsresult
NS_CopySegmentToStream(nsIInputStream* aInStr,
void* aClosure,
const char* aBuffer,
uint32_t aOffset,
uint32_t aCount,
uint32_t* aCountWritten)
{
nsIOutputStream* outStr = static_cast<nsIOutputStream*>(aClosure);
*aCountWritten = 0;
while (aCount) {
uint32_t n;
nsresult rv = outStr->Write(aBuffer, aCount, &n);
if (NS_FAILED(rv)) {
return rv;
}
aBuffer += n;
aCount -= n;
*aCountWritten += n;
}
return NS_OK;
}
nsresult
NS_CopySegmentToBuffer(nsIInputStream* aInStr,
void* aClosure,
const char* aBuffer,
uint32_t aOffset,
uint32_t aCount,
uint32_t* aCountWritten)
{
char* toBuf = static_cast<char*>(aClosure);
memcpy(&toBuf[aOffset], aBuffer, aCount);
*aCountWritten = aCount;
return NS_OK;
}
nsresult
NS_CopySegmentToBuffer(nsIOutputStream* aOutStr,
void* aClosure,
char* aBuffer,
uint32_t aOffset,
uint32_t aCount,
uint32_t* aCountRead)
{
const char* fromBuf = static_cast<const char*>(aClosure);
memcpy(aBuffer, &fromBuf[aOffset], aCount);
*aCountRead = aCount;
return NS_OK;
}
nsresult
NS_DiscardSegment(nsIInputStream* aInStr,
void* aClosure,
const char* aBuffer,
uint32_t aOffset,
uint32_t aCount,
uint32_t* aCountWritten)
{
*aCountWritten = aCount;
return NS_OK;
}
//-----------------------------------------------------------------------------
nsresult
NS_WriteSegmentThunk(nsIInputStream* aInStr,
void* aClosure,
const char* aBuffer,
uint32_t aOffset,
uint32_t aCount,
uint32_t* aCountWritten)
{
nsWriteSegmentThunk* thunk = static_cast<nsWriteSegmentThunk*>(aClosure);
return thunk->mFun(thunk->mStream, thunk->mClosure, aBuffer, aOffset, aCount,
aCountWritten);
}
nsresult
NS_FillArray(FallibleTArray<char>& aDest, nsIInputStream* aInput,
uint32_t aKeep, uint32_t* aNewBytes)
{
MOZ_ASSERT(aInput, "null stream");
MOZ_ASSERT(aKeep <= aDest.Length(), "illegal keep count");
char* aBuffer = aDest.Elements();
int64_t keepOffset = int64_t(aDest.Length()) - aKeep;
if (aKeep != 0 && keepOffset > 0) {
memmove(aBuffer, aBuffer + keepOffset, aKeep);
}
nsresult rv =
aInput->Read(aBuffer + aKeep, aDest.Capacity() - aKeep, aNewBytes);
if (NS_FAILED(rv)) {
*aNewBytes = 0;
}
// NOTE: we rely on the fact that the new slots are NOT initialized by
// SetLengthAndRetainStorage here, see nsTArrayElementTraits::Construct()
// in nsTArray.h:
aDest.SetLengthAndRetainStorage(aKeep + *aNewBytes);
MOZ_ASSERT(aDest.Length() <= aDest.Capacity(), "buffer overflow");
return rv;
}
bool
NS_InputStreamIsCloneable(nsIInputStream* aSource)
{
if (!aSource) {
return false;
}
nsCOMPtr<nsICloneableInputStream> cloneable = do_QueryInterface(aSource);
return cloneable && cloneable->GetCloneable();
}
nsresult
NS_CloneInputStream(nsIInputStream* aSource, nsIInputStream** aCloneOut,
nsIInputStream** aReplacementOut)
{
if (NS_WARN_IF(!aSource)) {
return NS_ERROR_FAILURE;
}
// Attempt to perform the clone directly on the source stream
nsCOMPtr<nsICloneableInputStream> cloneable = do_QueryInterface(aSource);
if (cloneable && cloneable->GetCloneable()) {
if (aReplacementOut) {
*aReplacementOut = nullptr;
}
return cloneable->Clone(aCloneOut);
}
// If we failed the clone and the caller does not want to replace their
// original stream, then we are done. Return error.
if (!aReplacementOut) {
return NS_ERROR_FAILURE;
}
// The caller has opted-in to the fallback clone support that replaces
// the original stream. Copy the data to a pipe and return two cloned
// input streams.
nsCOMPtr<nsIInputStream> reader;
nsCOMPtr<nsIInputStream> readerClone;
nsCOMPtr<nsIOutputStream> writer;
nsresult rv = NS_NewPipe(getter_AddRefs(reader), getter_AddRefs(writer),
0, 0, // default segment size and max size
true, true); // non-blocking
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
cloneable = do_QueryInterface(reader);
MOZ_ASSERT(cloneable && cloneable->GetCloneable());
rv = cloneable->Clone(getter_AddRefs(readerClone));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsCOMPtr<nsIEventTarget> target =
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = NS_AsyncCopy(aSource, writer, target, NS_ASYNCCOPY_VIA_WRITESEGMENTS);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
readerClone.forget(aCloneOut);
reader.forget(aReplacementOut);
return NS_OK;
}

295
xpcom/io/nsStreamUtils.h Normal file
View file

@ -0,0 +1,295 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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"
#include "nsTArray.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
* 1/ check whether the input stream implements nsIBufferedInputStream;
* 2/ if not, 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.
*
* PERFORMANCE NOTE: If the stream does not implement nsIBufferedInputStream,
* calling readSegments may cause I/O. Therefore, you should avoid calling
* this function from the main thread.
*
* @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:
* 1/ check whether the output stream implements nsIBufferedOutputStream;
* 2/ if not, 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.
*
* PERFORMANCE NOTE: If the stream does not implement nsIBufferedOutputStream,
* calling writeSegments may cause I/O. Therefore, you should avoid calling
* this function from the main thread.
*
* @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 nsresult
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 nsresult
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 nsresult
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 nsresult
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 nsresult
NS_WriteSegmentThunk(nsIInputStream* aInputStream, void* aClosure,
const char* aFromSegment, uint32_t aToOffset,
uint32_t aCount, uint32_t* aWriteCount);
struct MOZ_STACK_CLASS nsWriteSegmentThunk
{
nsCOMPtr<nsIInputStream> mStream;
nsWriteSegmentFun mFun;
void* mClosure;
};
/**
* Read data from aInput and store in aDest. A non-zero aKeep will keep that
* many bytes from aDest (from the end). New data is appended after the kept
* bytes (if any). aDest's new length on returning from this function is
* aKeep + aNewBytes and is guaranteed to be less than or equal to aDest's
* current capacity.
* @param aDest the array to fill
* @param aInput the stream to read from
* @param aKeep number of bytes to keep (0 <= aKeep <= aDest.Length())
* @param aNewBytes (out) number of bytes read from aInput or zero if Read()
* failed
* @return the result from aInput->Read(...)
*/
extern nsresult
NS_FillArray(FallibleTArray<char>& aDest, nsIInputStream* aInput,
uint32_t aKeep, uint32_t* aNewBytes);
/**
* Return true if the given stream can be directly cloned.
*/
extern bool
NS_InputStreamIsCloneable(nsIInputStream* aSource);
/**
* Clone the provided source stream in the most efficient way possible. This
* first attempts to QI to nsICloneableInputStream to use Clone(). If that is
* not supported or its cloneable attribute is false, then a fallback clone is
* provided by copying the source to a pipe. In this case the caller must
* replace the source stream with the resulting replacement stream. The clone
* and the replacement stream are then cloneable using nsICloneableInputStream
* without duplicating memory. This fallback clone using the pipe is only
* performed if a replacement stream parameter is also passed in.
* @param aSource The input stream to clone.
* @param aCloneOut Required out parameter to hold resulting clone.
* @param aReplacementOut Optional out parameter to hold stream to replace
* original source stream after clone. If not
* provided then the fallback clone process is not
* supported and a non-cloneable source will result
* in failure. Replacement streams are non-blocking.
* @return NS_OK on successful clone. Error otherwise.
*/
extern nsresult
NS_CloneInputStream(nsIInputStream* aSource, nsIInputStream** aCloneOut,
nsIInputStream** aReplacementOut = nullptr);
#endif // !nsStreamUtils_h__

452
xpcom/io/nsStringStream.cpp Normal file
View file

@ -0,0 +1,452 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "nsICloneableInputStream.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;
using mozilla::Maybe;
using mozilla::Some;
//-----------------------------------------------------------------------------
// nsIStringInputStream implementation
//-----------------------------------------------------------------------------
class nsStringInputStream final
: public nsIStringInputStream
, public nsISeekableStream
, public nsISupportsCString
, public nsIIPCSerializableInputStream
, public nsICloneableInputStream
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
NS_DECL_NSISTRINGINPUTSTREAM
NS_DECL_NSISEEKABLESTREAM
NS_DECL_NSISUPPORTSPRIMITIVE
NS_DECL_NSISUPPORTSCSTRING
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
NS_DECL_NSICLONEABLEINPUTSTREAM
nsStringInputStream()
{
Clear();
}
explicit nsStringInputStream(const nsStringInputStream& aOther)
: mOffset(aOther.mOffset)
{
// Use Assign() here because we don't want the life of the clone to be
// dependent on the life of the original stream.
mData.Assign(aOther.mData);
}
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_ADDREF(nsStringInputStream)
NS_IMPL_RELEASE(nsStringInputStream)
NS_IMPL_CLASSINFO(nsStringInputStream, nullptr, nsIClassInfo::THREADSAFE,
NS_STRINGINPUTSTREAM_CID)
NS_IMPL_QUERY_INTERFACE_CI(nsStringInputStream,
nsIStringInputStream,
nsIInputStream,
nsISupportsCString,
nsISeekableStream,
nsIIPCSerializableInputStream,
nsICloneableInputStream)
NS_IMPL_CI_INTERFACE_GETTER(nsStringInputStream,
nsIStringInputStream,
nsIInputStream,
nsISupportsCString,
nsISeekableStream,
nsICloneableInputStream)
/////////
// nsISupportsCString implementation
/////////
NS_IMETHODIMP
nsStringInputStream::GetType(uint16_t* aType)
{
*aType = 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.
if (NS_WARN_IF(Closed())) {
return NS_BASE_STREAM_CLOSED;
}
data.Assign(mData);
return NS_OK;
}
NS_IMETHODIMP
nsStringInputStream::SetData(const nsACString& aData)
{
mData.Assign(aData);
mOffset = 0;
return NS_OK;
}
NS_IMETHODIMP
nsStringInputStream::ToString(char** aResult)
{
// 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* aData, int32_t aDataLen)
{
if (NS_WARN_IF(!aData)) {
return NS_ERROR_INVALID_ARG;
}
mData.Assign(aData, aDataLen);
mOffset = 0;
return NS_OK;
}
NS_IMETHODIMP
nsStringInputStream::AdoptData(char* aData, int32_t aDataLen)
{
if (NS_WARN_IF(!aData)) {
return NS_ERROR_INVALID_ARG;
}
mData.Adopt(aData, aDataLen);
mOffset = 0;
return NS_OK;
}
NS_IMETHODIMP
nsStringInputStream::ShareData(const char* aData, int32_t aDataLen)
{
if (NS_WARN_IF(!aData)) {
return NS_ERROR_INVALID_ARG;
}
if (aDataLen < 0) {
aDataLen = strlen(aData);
}
mData.Rebind(aData, aDataLen);
mOffset = 0;
return NS_OK;
}
NS_IMETHODIMP_(size_t)
nsStringInputStream::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
{
size_t n = aMallocSizeOf(this);
n += mData.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
return n;
}
/////////
// 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 aWriter, void* aClosure,
uint32_t aCount, uint32_t* aResult)
{
NS_ASSERTION(aResult, "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) {
*aResult = 0;
return NS_OK;
}
if (aCount > maxCount) {
aCount = maxCount;
}
nsresult rv = aWriter(this, aClosure, mData.BeginReading() + mOffset, 0,
aCount, aResult);
if (NS_SUCCEEDED(rv)) {
NS_ASSERTION(*aResult <= aCount,
"writer should not write more than we asked it to write");
mOffset += *aResult;
}
// 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 aWhence, int64_t aOffset)
{
if (Closed()) {
return NS_BASE_STREAM_CLOSED;
}
// Compute new stream position. The given offset may be a negative value.
int64_t newPos = aOffset;
switch (aWhence) {
case NS_SEEK_SET:
break;
case NS_SEEK_CUR:
newPos += mOffset;
break;
case NS_SEEK_END:
newPos += Length();
break;
default:
NS_ERROR("invalid aWhence");
return NS_ERROR_INVALID_ARG;
}
if (NS_WARN_IF(newPos < 0) || NS_WARN_IF(newPos > Length())) {
return NS_ERROR_INVALID_ARG;
}
mOffset = (uint32_t)newPos;
return NS_OK;
}
NS_IMETHODIMP
nsStringInputStream::Tell(int64_t* aOutWhere)
{
if (Closed()) {
return NS_BASE_STREAM_CLOSED;
}
*aOutWhere = mOffset;
return NS_OK;
}
NS_IMETHODIMP
nsStringInputStream::SetEOF()
{
if (Closed()) {
return NS_BASE_STREAM_CLOSED;
}
mOffset = Length();
return NS_OK;
}
/////////
// nsIIPCSerializableInputStream implementation
/////////
void
nsStringInputStream::Serialize(InputStreamParams& aParams,
FileDescriptorArray& /* aFDs */)
{
StringInputStreamParams params;
params.data() = PromiseFlatCString(mData);
aParams = params;
}
bool
nsStringInputStream::Deserialize(const InputStreamParams& aParams,
const FileDescriptorArray& /* aFDs */)
{
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;
}
Maybe<uint64_t>
nsStringInputStream::ExpectedSerializedLength()
{
return Some(static_cast<uint64_t>(Length()));
}
/////////
// nsICloneableInputStream implementation
/////////
NS_IMETHODIMP
nsStringInputStream::GetCloneable(bool* aCloneableOut)
{
*aCloneableOut = true;
return NS_OK;
}
NS_IMETHODIMP
nsStringInputStream::Clone(nsIInputStream** aCloneOut)
{
RefPtr<nsIInputStream> ref = new nsStringInputStream(*this);
ref.forget(aCloneOut);
return NS_OK;
}
nsresult
NS_NewByteInputStream(nsIInputStream** aStreamResult,
const char* aStringToRead, int32_t aLength,
nsAssignmentType aAssignment)
{
NS_PRECONDITION(aStreamResult, "null out ptr");
RefPtr<nsStringInputStream> stream = new nsStringInputStream();
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)) {
return rv;
}
stream.forget(aStreamResult);
return NS_OK;
}
nsresult
NS_NewCStringInputStream(nsIInputStream** aStreamResult,
const nsACString& aStringToRead)
{
NS_PRECONDITION(aStreamResult, "null out ptr");
RefPtr<nsStringInputStream> stream = new nsStringInputStream();
stream->SetData(aStringToRead);
stream.forget(aStreamResult);
return NS_OK;
}
// factory method for constructing a nsStringInputStream object
nsresult
nsStringInputStreamConstructor(nsISupports* aOuter, REFNSIID aIID,
void** aResult)
{
*aResult = nullptr;
if (NS_WARN_IF(aOuter)) {
return NS_ERROR_NO_AGGREGATION;
}
RefPtr<nsStringInputStream> inst = new nsStringInputStream();
return inst->QueryInterface(aIID, aResult);
}

63
xpcom/io/nsStringStream.h Normal file
View file

@ -0,0 +1,63 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 nsACString. Result will
* implement nsIStringInputStream and nsISeekableStream.
*/
extern nsresult
NS_NewCStringInputStream(nsIInputStream** aStreamResult,
const nsACString& aStringToRead);
#endif // nsStringStream_h__

View file

@ -0,0 +1,398 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "nsIServiceManager.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsAutoPtr.h"
#include "nsCRT.h"
#include "nsStreamUtils.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 final : public nsIUnicharInputStream
{
public:
explicit 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(char16_t* aBuf,
uint32_t aCount,
uint32_t* aReadCount)
{
if (mPos >= mLen) {
*aReadCount = 0;
return NS_OK;
}
nsAString::const_iterator iter;
mString.BeginReading(iter);
const char16_t* us = iter.get();
uint32_t amount = mLen - mPos;
if (amount > aCount) {
amount = aCount;
}
memcpy(aBuf, us + mPos, sizeof(char16_t) * 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_ISUPPORTS(StringUnicharInputStream, nsIUnicharInputStream)
//----------------------------------------------------------------------
class UTF8InputStream 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;
FallibleTArray<char> mByteData;
FallibleTArray<char16_t> mUnicharData;
uint32_t mByteDataOffset;
uint32_t mUnicharDataOffset;
uint32_t mUnicharDataLength;
};
UTF8InputStream::UTF8InputStream() :
mByteDataOffset(0),
mUnicharDataOffset(0),
mUnicharDataLength(0)
{
}
nsresult
UTF8InputStream::Init(nsIInputStream* aStream)
{
if (!mByteData.SetCapacity(STRING_BUFFER_SIZE, mozilla::fallible) ||
!mUnicharData.SetCapacity(STRING_BUFFER_SIZE, mozilla::fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
mInput = aStream;
return NS_OK;
}
NS_IMPL_ISUPPORTS(UTF8InputStream, nsIUnicharInputStream)
UTF8InputStream::~UTF8InputStream()
{
Close();
}
nsresult
UTF8InputStream::Close()
{
mInput = nullptr;
mByteData.Clear();
mUnicharData.Clear();
return NS_OK;
}
nsresult
UTF8InputStream::Read(char16_t* 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.Elements() + mUnicharDataOffset,
readCount * sizeof(char16_t));
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.Elements() + 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 char16_t* buf = mUnicharData.Elements() + mUnicharDataOffset;
aString.Assign(buf, readCount);
mUnicharDataOffset += readCount;
*aReadCount = readCount;
return NS_OK;
}
int32_t
UTF8InputStream::Fill(nsresult* aErrorCode)
{
if (!mInput) {
// We already closed the stream!
*aErrorCode = NS_BASE_STREAM_CLOSED;
return -1;
}
NS_ASSERTION(mByteData.Length() >= mByteDataOffset, "unsigned madness");
uint32_t remainder = mByteData.Length() - mByteDataOffset;
mByteDataOffset = remainder;
uint32_t nb;
*aErrorCode = NS_FillArray(mByteData, mInput, remainder, &nb);
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.Length(), "bad nb");
// Now convert as much of the byte buffer to unicode as possible
uint32_t srcLen, dstLen;
CountValidUTF8Bytes(mByteData.Elements(), 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(dstLen <= mUnicharData.Capacity(),
"Ouch. I would overflow my buffer if I wasn't so careful.");
if (dstLen > mUnicharData.Capacity()) {
return 0;
}
ConvertUTF8toUTF16 converter(mUnicharData.Elements());
nsASingleFragmentCString::const_char_iterator start = mByteData.Elements();
nsASingleFragmentCString::const_char_iterator end = mByteData.Elements() + 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;
}
nsresult
NS_NewUnicharInputStream(nsIInputStream* aStreamToWrap,
nsIUnicharInputStream** aResult)
{
*aResult = nullptr;
// Create converter input stream
RefPtr<UTF8InputStream> it = new UTF8InputStream();
nsresult rv = it->Init(aStreamToWrap);
if (NS_FAILED(rv)) {
return rv;
}
it.forget(aResult);
return NS_OK;
}

View file

@ -0,0 +1,15 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "nsIUnicharInputStream.h"
nsresult NS_NewUnicharInputStream(nsIInputStream* aStreamToWrap,
nsIUnicharInputStream** aResult);
#endif // nsUnicharInputStream_h__

481
xpcom/io/nsWildCard.cpp Normal file
View file

@ -0,0 +1,481 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 aChar)
{
return ('a' <= aChar && aChar <= 'z') ||
('A' <= aChar && aChar <= 'Z');
}
template<class T>
static int
alphanumeric(T aChar)
{
return ('0' <= aChar && aChar <= '9') || ::alpha(aChar);
}
template<class T>
static int
lower(T aChar)
{
return ('A' <= aChar && aChar <= 'Z') ? aChar + ('a' - 'A') : aChar;
}
template<class T>
static int
upper(T aChar)
{
return ('a' <= aChar && aChar <= 'z') ? aChar - ('a' - 'A') : aChar;
}
/* ----------------------------- _valid_subexp ---------------------------- */
template<class T>
static int
_valid_subexp(const T* aExpr, T aStop1, T aStop2)
{
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; aExpr[x] && (aExpr[x] != aStop1) && (aExpr[x] != aStop2); ++x) {
switch (aExpr[x]) {
case '~':
if (tld) { /* at most one exclusion */
return INVALID_SXP;
}
if (aStop1) { /* no exclusions within unions */
return INVALID_SXP;
}
if (!aExpr[x + 1]) { /* exclusion cannot be last character */
return INVALID_SXP;
}
if (!x) { /* exclusion cannot be first character */
return INVALID_SXP;
}
++tld;
MOZ_FALLTHROUGH;
case '*':
case '?':
case '$':
++nsc;
break;
case '[':
++nsc;
if ((!aExpr[++x]) || (aExpr[x] == ']')) {
return INVALID_SXP;
}
for (; aExpr[x] && (aExpr[x] != ']'); ++x) {
if (aExpr[x] == '\\' && !aExpr[++x]) {
return INVALID_SXP;
}
}
if (!aExpr[x]) {
return INVALID_SXP;
}
break;
case '(':
++nsc;
if (aStop1) { /* no nested unions */
return INVALID_SXP;
}
np = -1;
do {
int t = ::_valid_subexp(&aExpr[++x], T(')'), T('|'));
if (t == 0 || t == INVALID_SXP) {
return INVALID_SXP;
}
x += t;
if (!aExpr[x]) {
return INVALID_SXP;
}
++np;
} while (aExpr[x] == '|');
if (np < 1) { /* must be at least one pipe */
return INVALID_SXP;
}
break;
case ')':
case ']':
case '|':
return INVALID_SXP;
case '\\':
++nsc;
if (!aExpr[++x]) {
return INVALID_SXP;
}
break;
default:
break;
}
}
if (!aStop1 && !nsc) { /* must be at least one special character */
return NON_SXP;
}
return ((aExpr[x] == aStop1 || aExpr[x] == aStop2) ? x : INVALID_SXP);
}
template<class T>
int
NS_WildCardValid_(const T* aExpr)
{
int x = ::_valid_subexp(aExpr, T('\0'), T('\0'));
return (x < 0 ? x : VALID_SXP);
}
int
NS_WildCardValid(const char* aExpr)
{
return NS_WildCardValid_(aExpr);
}
int
NS_WildCardValid(const char16_t* aExpr)
{
return NS_WildCardValid_(aExpr);
}
/* ----------------------------- _shexp_match ----------------------------- */
#define MATCH 0
#define NOMATCH 1
#define ABORTED -1
template<class T>
static int
_shexp_match(const T* aStr, const T* aExpr, bool aCaseInsensitive,
unsigned int aLevel);
/**
* 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-nullptr, copy counted characters to it and null terminate.
*/
template<class T>
static int
_scan_and_copy(const T* aExpr, T aStop1, T aStop2, T* aDest)
{
int sx; /* source index */
T cc;
for (sx = 0; (cc = aExpr[sx]) && cc != aStop1 && cc != aStop2; ++sx) {
if (cc == '\\') {
if (!aExpr[++sx]) {
return ABORTED; /* should be impossible */
}
} else if (cc == '[') {
while ((cc = aExpr[++sx]) && cc != ']') {
if (cc == '\\' && !aExpr[++sx]) {
return ABORTED;
}
}
if (!cc) {
return ABORTED; /* should be impossible */
}
}
}
if (aDest && sx) {
/* Copy all but the closing delimiter. */
memcpy(aDest, aExpr, sx * sizeof(T));
aDest[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* aStr, const T* aExpr, bool aCaseInsensitive,
unsigned int aLevel)
{
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(aExpr, T(')'), T('\0'), static_cast<T*>(nullptr));
if (cp == ABORTED || cp < 4) { /* must be at least "(a|b" before ')' */
return ABORTED;
}
++cp; /* now index of char after closing parenthesis */
e2 = (T*)moz_xmalloc((1 + nsCharTraits<T>::length(aExpr)) * sizeof(T));
if (!e2) {
return ABORTED;
}
for (sx = 1; ; ++sx) {
/* Here, aExpr[sx] is one character past the preceding '(' or '|'. */
/* Copy everything up to the next delimiter to e2 */
count = ::_scan_and_copy(aExpr + 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, aExpr + cp,
nsCharTraits<T>::length(aExpr + cp) + 1);
ret = ::_shexp_match(aStr, e2, aCaseInsensitive, aLevel + 1);
if (ret != NOMATCH || !aExpr[sx] || aExpr[sx] == ')') {
break;
}
}
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 aStart, unsigned char aEnd, unsigned char aVal)
{
char map[256];
memset(map, 0, sizeof(map));
while (aStart <= aEnd) {
map[lower(aStart++)] = 1;
}
return map[lower(aVal)];
}
template<class T>
static int
_shexp_match(const T* aStr, const T* aExpr, bool aCaseInsensitive,
unsigned int aLevel)
{
int x; /* input string index */
int y; /* expression index */
int ret, neg;
if (aLevel > 20) { /* Don't let the stack get too deep. */
return ABORTED;
}
for (x = 0, y = 0; aExpr[y]; ++y, ++x) {
if (!aStr[x] && aExpr[y] != '$' && aExpr[y] != '*') {
return NOMATCH;
}
switch (aExpr[y]) {
case '$':
if (aStr[x]) {
return NOMATCH;
}
--x; /* we don't want loop to increment x */
break;
case '*':
while (aExpr[++y] == '*') {
}
if (!aExpr[y]) {
return MATCH;
}
while (aStr[x]) {
ret = ::_shexp_match(&aStr[x++], &aExpr[y], aCaseInsensitive,
aLevel + 1);
switch (ret) {
case NOMATCH:
continue;
case ABORTED:
return ABORTED;
default:
return MATCH;
}
}
if (aExpr[y] == '$' && aExpr[y + 1] == '\0' && !aStr[x]) {
return MATCH;
} else {
return NOMATCH;
}
case '[': {
T start, end = 0;
int i;
++y;
neg = (aExpr[y] == '^' && aExpr[y + 1] != ']');
if (neg) {
++y;
}
i = y;
start = aExpr[i++];
if (start == '\\') {
start = aExpr[i++];
}
if (::alphanumeric(start) && aExpr[i++] == '-') {
end = aExpr[i++];
if (end == '\\') {
end = aExpr[i++];
}
}
if (::alphanumeric(end) && aExpr[i] == ']') {
/* This is a range form: a-b */
T val = aStr[x];
if (end < start) { /* swap them */
T tmp = end;
end = start;
start = tmp;
}
if (aCaseInsensitive && ::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 (; aExpr[y] != ']'; ++y) {
if (aExpr[y] == '\\') {
++y;
}
if (aCaseInsensitive) {
matched |= (::upper(aStr[x]) == ::upper(aExpr[y]));
} else {
matched |= (aStr[x] == aExpr[y]);
}
}
if (neg == matched) {
return NOMATCH;
}
}
}
break;
case '(':
if (!aExpr[y + 1]) {
return ABORTED;
}
return ::_handle_union(&aStr[x], &aExpr[y], aCaseInsensitive,
aLevel + 1);
case '?':
break;
case ')':
case ']':
case '|':
return ABORTED;
case '\\':
++y;
MOZ_FALLTHROUGH;
default:
if (aCaseInsensitive) {
if (::upper(aStr[x]) != ::upper(aExpr[y])) {
return NOMATCH;
}
} else {
if (aStr[x] != aExpr[y]) {
return NOMATCH;
}
}
break;
}
}
return (aStr[x] ? NOMATCH : MATCH);
}
template<class T>
static int
ns_WildCardMatch(const T* aStr, const T* aXp, bool aCaseInsensitive)
{
T* expr = nullptr;
int ret = MATCH;
if (!nsCharTraits<T>::find(aXp, nsCharTraits<T>::length(aXp), T('~'))) {
return ::_shexp_match(aStr, aXp, aCaseInsensitive, 0);
}
expr = (T*)moz_xmalloc((nsCharTraits<T>::length(aXp) + 1) * sizeof(T));
if (!expr) {
return NOMATCH;
}
memcpy(expr, aXp, (nsCharTraits<T>::length(aXp) + 1) * sizeof(T));
int x = ::_scan_and_copy(expr, T('~'), T('\0'), static_cast<T*>(nullptr));
if (x != ABORTED && expr[x] == '~') {
expr[x++] = '\0';
ret = ::_shexp_match(aStr, &expr[x], aCaseInsensitive, 0);
switch (ret) {
case NOMATCH:
ret = MATCH;
break;
case MATCH:
ret = NOMATCH;
break;
default:
break;
}
}
if (ret == MATCH) {
ret = ::_shexp_match(aStr, expr, aCaseInsensitive, 0);
}
free(expr);
return ret;
}
template<class T>
int
NS_WildCardMatch_(const T* aStr, const T* aExpr, bool aCaseInsensitive)
{
int is_valid = NS_WildCardValid(aExpr);
switch (is_valid) {
case INVALID_SXP:
return -1;
default:
return ::ns_WildCardMatch(aStr, aExpr, aCaseInsensitive);
}
}
int
NS_WildCardMatch(const char* aStr, const char* aXp, bool aCaseInsensitive)
{
return NS_WildCardMatch_(aStr, aXp, aCaseInsensitive);
}
int
NS_WildCardMatch(const char16_t* aStr, const char16_t* aXp,
bool aCaseInsensitive)
{
return NS_WildCardMatch_(aStr, aXp, aCaseInsensitive);
}

64
xpcom/io/nsWildCard.h Normal file
View file

@ -0,0 +1,64 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "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* aExpr);
int NS_WildCardValid(const char16_t* aExpr);
/* 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* aStr, const char* aExpr,
bool aCaseInsensitive);
int NS_WildCardMatch(const char16_t* aStr, const char16_t* aExpr,
bool aCaseInsensitive);
#endif /* nsWildCard_h__ */