mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-02 21:37:32 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
645
xpcom/io/Base64.cpp
Normal file
645
xpcom/io/Base64.cpp
Normal 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
73
xpcom/io/Base64.h
Normal 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
35
xpcom/io/CocoaFileUtils.h
Normal 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
267
xpcom/io/CocoaFileUtils.mm
Normal 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
75
xpcom/io/FileUtilsWin.cpp
Normal 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
144
xpcom/io/FileUtilsWin.h
Normal 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
|
||||
209
xpcom/io/SlicedInputStream.cpp
Normal file
209
xpcom/io/SlicedInputStream.cpp
Normal 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);
|
||||
}
|
||||
50
xpcom/io/SlicedInputStream.h
Normal file
50
xpcom/io/SlicedInputStream.h
Normal 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
|
||||
256
xpcom/io/SnappyCompressOutputStream.cpp
Normal file
256
xpcom/io/SnappyCompressOutputStream.cpp
Normal 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
|
||||
69
xpcom/io/SnappyCompressOutputStream.h
Normal file
69
xpcom/io/SnappyCompressOutputStream.h
Normal 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__
|
||||
258
xpcom/io/SnappyFrameUtils.cpp
Normal file
258
xpcom/io/SnappyFrameUtils.cpp
Normal 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
|
||||
85
xpcom/io/SnappyFrameUtils.h
Normal file
85
xpcom/io/SnappyFrameUtils.h
Normal 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__
|
||||
362
xpcom/io/SnappyUncompressInputStream.cpp
Normal file
362
xpcom/io/SnappyUncompressInputStream.cpp
Normal 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
|
||||
90
xpcom/io/SnappyUncompressInputStream.h
Normal file
90
xpcom/io/SnappyUncompressInputStream.h
Normal 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__
|
||||
830
xpcom/io/SpecialSystemDirectory.cpp
Normal file
830
xpcom/io/SpecialSystemDirectory.cpp
Normal 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
|
||||
|
||||
107
xpcom/io/SpecialSystemDirectory.h
Normal file
107
xpcom/io/SpecialSystemDirectory.h
Normal 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
154
xpcom/io/crc32c.c
Normal 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
23
xpcom/io/crc32c.h
Normal 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
140
xpcom/io/moz.build
Normal 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']
|
||||
314
xpcom/io/nsAnonymousTemporaryFile.cpp
Normal file
314
xpcom/io/nsAnonymousTemporaryFile.cpp
Normal 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
|
||||
|
||||
31
xpcom/io/nsAnonymousTemporaryFile.h
Normal file
31
xpcom/io/nsAnonymousTemporaryFile.h
Normal 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);
|
||||
|
||||
118
xpcom/io/nsAppDirectoryServiceDefs.h
Normal file
118
xpcom/io/nsAppDirectoryServiceDefs.h
Normal 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___
|
||||
609
xpcom/io/nsAppFileLocationProvider.cpp
Normal file
609
xpcom/io/nsAppFileLocationProvider.cpp
Normal 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
|
||||
55
xpcom/io/nsAppFileLocationProvider.h
Normal file
55
xpcom/io/nsAppFileLocationProvider.h
Normal 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
1016
xpcom/io/nsBinaryStream.cpp
Normal file
File diff suppressed because it is too large
Load diff
101
xpcom/io/nsBinaryStream.h
Normal file
101
xpcom/io/nsBinaryStream.h
Normal 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___
|
||||
766
xpcom/io/nsDirectoryService.cpp
Normal file
766
xpcom/io/nsDirectoryService.cpp
Normal 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;
|
||||
}
|
||||
66
xpcom/io/nsDirectoryService.h
Normal file
66
xpcom/io/nsDirectoryService.h
Normal 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
|
||||
|
||||
98
xpcom/io/nsDirectoryServiceAtomList.h
Normal file
98
xpcom/io/nsDirectoryServiceAtomList.h
Normal 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
|
||||
168
xpcom/io/nsDirectoryServiceDefs.h
Normal file
168
xpcom/io/nsDirectoryServiceDefs.h
Normal 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
|
||||
31
xpcom/io/nsDirectoryServiceUtils.h
Normal file
31
xpcom/io/nsDirectoryServiceUtils.h
Normal 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
633
xpcom/io/nsEscape.cpp
Normal 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
224
xpcom/io/nsEscape.h
Normal 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_
|
||||
104
xpcom/io/nsIAsyncInputStream.idl
Normal file
104
xpcom/io/nsIAsyncInputStream.idl
Normal 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);
|
||||
};
|
||||
104
xpcom/io/nsIAsyncOutputStream.idl
Normal file
104
xpcom/io/nsIAsyncOutputStream.idl
Normal 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);
|
||||
};
|
||||
119
xpcom/io/nsIBinaryInputStream.idl
Normal file
119
xpcom/io/nsIBinaryInputStream.idl
Normal 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
|
||||
|
||||
%}
|
||||
90
xpcom/io/nsIBinaryOutputStream.idl
Normal file
90
xpcom/io/nsIBinaryOutputStream.idl
Normal 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;
|
||||
}
|
||||
|
||||
%}
|
||||
22
xpcom/io/nsICloneableInputStream.idl
Normal file
22
xpcom/io/nsICloneableInputStream.idl
Normal 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();
|
||||
};
|
||||
40
xpcom/io/nsIConverterInputStream.idl
Normal file
40
xpcom/io/nsIConverterInputStream.idl
Normal 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);
|
||||
};
|
||||
|
||||
44
xpcom/io/nsIConverterOutputStream.idl
Normal file
44
xpcom/io/nsIConverterOutputStream.idl
Normal 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);
|
||||
};
|
||||
34
xpcom/io/nsIDirectoryEnumerator.idl
Normal file
34
xpcom/io/nsIDirectoryEnumerator.idl
Normal 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();
|
||||
};
|
||||
|
||||
103
xpcom/io/nsIDirectoryService.idl
Normal file
103
xpcom/io/nsIDirectoryService.idl
Normal 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
521
xpcom/io/nsIFile.idl
Normal 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
34
xpcom/io/nsIIOUtil.idl
Normal 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
147
xpcom/io/nsIInputStream.idl
Normal 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();
|
||||
};
|
||||
42
xpcom/io/nsIInputStreamTee.idl
Normal file
42
xpcom/io/nsIInputStreamTee.idl
Normal 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);
|
||||
%}
|
||||
26
xpcom/io/nsILineInputStream.idl
Normal file
26
xpcom/io/nsILineInputStream.idl
Normal 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
17
xpcom/io/nsILocalFile.idl
Normal 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
|
||||
{
|
||||
};
|
||||
|
||||
179
xpcom/io/nsILocalFileMac.idl
Normal file
179
xpcom/io/nsILocalFileMac.idl
Normal 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);
|
||||
}
|
||||
%}
|
||||
121
xpcom/io/nsILocalFileWin.idl
Normal file
121
xpcom/io/nsILocalFileWin.idl
Normal 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);
|
||||
};
|
||||
|
||||
55
xpcom/io/nsIMultiplexInputStream.idl
Normal file
55
xpcom/io/nsIMultiplexInputStream.idl
Normal 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
32
xpcom/io/nsIOUtil.cpp
Normal 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
27
xpcom/io/nsIOUtil.h
Normal 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__ */
|
||||
53
xpcom/io/nsIObjectInputStream.idl
Normal file
53
xpcom/io/nsIObjectInputStream.idl
Normal 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;
|
||||
}
|
||||
|
||||
%}
|
||||
97
xpcom/io/nsIObjectOutputStream.idl
Normal file
97
xpcom/io/nsIObjectOutputStream.idl
Normal 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;
|
||||
}
|
||||
|
||||
%}
|
||||
145
xpcom/io/nsIOutputStream.idl
Normal file
145
xpcom/io/nsIOutputStream.idl
Normal 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
171
xpcom/io/nsIPipe.idl
Normal 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);
|
||||
|
||||
%}
|
||||
39
xpcom/io/nsISafeOutputStream.idl
Normal file
39
xpcom/io/nsISafeOutputStream.idl
Normal 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();
|
||||
};
|
||||
32
xpcom/io/nsIScriptableBase64Encoder.idl
Normal file
32
xpcom/io/nsIScriptableBase64Encoder.idl
Normal 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);
|
||||
};
|
||||
67
xpcom/io/nsIScriptableInputStream.idl
Normal file
67
xpcom/io/nsIScriptableInputStream.idl
Normal 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);
|
||||
};
|
||||
74
xpcom/io/nsISeekableStream.idl
Normal file
74
xpcom/io/nsISeekableStream.idl
Normal 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();
|
||||
};
|
||||
69
xpcom/io/nsIStorageStream.idl
Normal file
69
xpcom/io/nsIStorageStream.idl
Normal 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);
|
||||
%}
|
||||
88
xpcom/io/nsIStreamBufferAccess.idl
Normal file
88
xpcom/io/nsIStreamBufferAccess.idl
Normal 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))
|
||||
|
||||
%}
|
||||
66
xpcom/io/nsIStringStream.idl
Normal file
66
xpcom/io/nsIStringStream.idl
Normal 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);
|
||||
};
|
||||
95
xpcom/io/nsIUnicharInputStream.idl
Normal file
95
xpcom/io/nsIUnicharInputStream.idl
Normal 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();
|
||||
};
|
||||
26
xpcom/io/nsIUnicharLineInputStream.idl
Normal file
26
xpcom/io/nsIUnicharLineInputStream.idl
Normal 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);
|
||||
};
|
||||
47
xpcom/io/nsIUnicharOutputStream.idl
Normal file
47
xpcom/io/nsIUnicharOutputStream.idl
Normal 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();
|
||||
};
|
||||
366
xpcom/io/nsInputStreamTee.cpp
Normal file
366
xpcom/io/nsInputStreamTee.cpp
Normal 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
|
||||
488
xpcom/io/nsLinebreakConverter.cpp
Normal file
488
xpcom/io/nsLinebreakConverter.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
131
xpcom/io/nsLinebreakConverter.h
Normal file
131
xpcom/io/nsLinebreakConverter.h
Normal 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
124
xpcom/io/nsLocalFile.h
Normal 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
|
||||
328
xpcom/io/nsLocalFileCommon.cpp
Normal file
328
xpcom/io/nsLocalFileCommon.cpp
Normal 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
2715
xpcom/io/nsLocalFileUnix.cpp
Normal file
File diff suppressed because it is too large
Load diff
138
xpcom/io/nsLocalFileUnix.h
Normal file
138
xpcom/io/nsLocalFileUnix.h
Normal 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
3787
xpcom/io/nsLocalFileWin.cpp
Normal file
File diff suppressed because it is too large
Load diff
123
xpcom/io/nsLocalFileWin.h
Normal file
123
xpcom/io/nsLocalFileWin.h
Normal 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
|
||||
835
xpcom/io/nsMultiplexInputStream.cpp
Normal file
835
xpcom/io/nsMultiplexInputStream.cpp
Normal 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;
|
||||
}
|
||||
30
xpcom/io/nsMultiplexInputStream.h
Normal file
30
xpcom/io/nsMultiplexInputStream.h
Normal 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_
|
||||
1044
xpcom/io/nsNativeCharsetUtils.cpp
Normal file
1044
xpcom/io/nsNativeCharsetUtils.cpp
Normal file
File diff suppressed because it is too large
Load diff
63
xpcom/io/nsNativeCharsetUtils.h
Normal file
63
xpcom/io/nsNativeCharsetUtils.h
Normal 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
24
xpcom/io/nsPipe.h
Normal 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
2007
xpcom/io/nsPipe3.cpp
Normal file
File diff suppressed because it is too large
Load diff
28
xpcom/io/nsScriptableBase64Encoder.cpp
Normal file
28
xpcom/io/nsScriptableBase64Encoder.cpp
Normal 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);
|
||||
}
|
||||
30
xpcom/io/nsScriptableBase64Encoder.h
Normal file
30
xpcom/io/nsScriptableBase64Encoder.h
Normal 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
|
||||
134
xpcom/io/nsScriptableInputStream.cpp
Normal file
134
xpcom/io/nsScriptableInputStream.cpp
Normal 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);
|
||||
}
|
||||
47
xpcom/io/nsScriptableInputStream.h
Normal file
47
xpcom/io/nsScriptableInputStream.h
Normal 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_
|
||||
169
xpcom/io/nsSegmentedBuffer.cpp
Normal file
169
xpcom/io/nsSegmentedBuffer.cpp
Normal 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
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
109
xpcom/io/nsSegmentedBuffer.h
Normal file
109
xpcom/io/nsSegmentedBuffer.h
Normal 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__
|
||||
648
xpcom/io/nsStorageStream.cpp
Normal file
648
xpcom/io/nsStorageStream.cpp
Normal 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
|
||||
73
xpcom/io/nsStorageStream.h
Normal file
73
xpcom/io/nsStorageStream.h
Normal 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
957
xpcom/io/nsStreamUtils.cpp
Normal 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
295
xpcom/io/nsStreamUtils.h
Normal 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
452
xpcom/io/nsStringStream.cpp
Normal 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
63
xpcom/io/nsStringStream.h
Normal 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__
|
||||
398
xpcom/io/nsUnicharInputStream.cpp
Normal file
398
xpcom/io/nsUnicharInputStream.cpp
Normal 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;
|
||||
}
|
||||
15
xpcom/io/nsUnicharInputStream.h
Normal file
15
xpcom/io/nsUnicharInputStream.h
Normal 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
481
xpcom/io/nsWildCard.cpp
Normal 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
64
xpcom/io/nsWildCard.h
Normal 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__ */
|
||||
Loading…
Add table
Add a link
Reference in a new issue