import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

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

View file

@ -0,0 +1,195 @@
/* -*- 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/IncrementalTokenizer.h"
#include "mozilla/AutoRestore.h"
#include "nsIInputStream.h"
#include "IncrementalTokenizer.h"
#include <algorithm>
namespace mozilla {
IncrementalTokenizer::IncrementalTokenizer(Consumer aConsumer,
const char * aWhitespaces,
const char * aAdditionalWordChars,
uint32_t aRawMinBuffered)
: TokenizerBase(aWhitespaces, aAdditionalWordChars)
#ifdef DEBUG
, mConsuming(false)
#endif
, mNeedMoreInput(false)
, mRollback(false)
, mInputCursor(0)
, mConsumer(aConsumer)
{
mInputFinished = false;
mMinRawDelivery = aRawMinBuffered;
}
nsresult IncrementalTokenizer::FeedInput(const nsACString & aInput)
{
NS_ENSURE_TRUE(mConsumer, NS_ERROR_NOT_INITIALIZED);
MOZ_ASSERT(!mInputFinished);
mInput.Cut(0, mInputCursor);
mInputCursor = 0;
mInput.Append(aInput);
return Process();
}
nsresult IncrementalTokenizer::FeedInput(nsIInputStream * aInput, uint32_t aCount)
{
NS_ENSURE_TRUE(mConsumer, NS_ERROR_NOT_INITIALIZED);
MOZ_ASSERT(!mInputFinished);
MOZ_ASSERT(!mConsuming);
mInput.Cut(0, mInputCursor);
mInputCursor = 0;
nsresult rv = NS_OK;
while (NS_SUCCEEDED(rv) && aCount) {
nsCString::index_type remainder = mInput.Length();
nsCString::index_type load =
std::min<nsCString::index_type>(aCount, PR_UINT32_MAX - remainder);
if (!load) {
// To keep the API simple, we fail if the input data buffer if filled.
// It's highly unlikely there will ever be such amout of data cumulated
// unless a logic fault in the consumer code.
NS_ERROR("IncrementalTokenizer consumer not reading data?");
return NS_ERROR_OUT_OF_MEMORY;
}
if (!mInput.SetLength(remainder + load, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsCString::char_iterator buffer = mInput.BeginWriting() + remainder;
uint32_t read;
rv = aInput->Read(buffer, load, &read);
if (NS_SUCCEEDED(rv)) {
// remainder + load fits the uint32_t size, so must remainder + read.
mInput.SetLength(remainder + read);
aCount -= read;
rv = Process();
}
}
return rv;
}
nsresult IncrementalTokenizer::FinishInput()
{
NS_ENSURE_TRUE(mConsumer, NS_ERROR_NOT_INITIALIZED);
MOZ_ASSERT(!mInputFinished);
MOZ_ASSERT(!mConsuming);
mInput.Cut(0, mInputCursor);
mInputCursor = 0;
mInputFinished = true;
nsresult rv = Process();
mConsumer = nullptr;
return rv;
}
bool IncrementalTokenizer::Next(Token & aToken)
{
// Assert we are called only from the consumer callback
MOZ_ASSERT(mConsuming);
if (mPastEof) {
return false;
}
nsACString::const_char_iterator next = Parse(aToken);
mPastEof = aToken.Type() == TOKEN_EOF;
if (next == mCursor && !mPastEof) {
// Not enough input to make a deterministic decision.
return false;
}
AssignFragment(aToken, mCursor, next);
mCursor = next;
return true;
}
void IncrementalTokenizer::NeedMoreInput()
{
// Assert we are called only from the consumer callback
MOZ_ASSERT(mConsuming);
// When the input has been finished, we can't set the flag to prevent
// indefinite wait for more input (that will never come)
mNeedMoreInput = !mInputFinished;
}
void IncrementalTokenizer::Rollback()
{
// Assert we are called only from the consumer callback
MOZ_ASSERT(mConsuming);
mRollback = true;
}
nsresult IncrementalTokenizer::Process()
{
#ifdef DEBUG
// Assert we are not re-entered
MOZ_ASSERT(!mConsuming);
AutoRestore<bool> consuming(mConsuming);
mConsuming = true;
#endif
MOZ_ASSERT(!mPastEof);
nsresult rv = NS_OK;
mInput.BeginReading(mCursor);
mCursor += mInputCursor;
mInput.EndReading(mEnd);
while (NS_SUCCEEDED(rv) && !mPastEof) {
Token token;
nsACString::const_char_iterator next = Parse(token);
mPastEof = token.Type() == TOKEN_EOF;
if (next == mCursor && !mPastEof) {
// Not enough input to make a deterministic decision.
break;
}
AssignFragment(token, mCursor, next);
nsACString::const_char_iterator rollback = mCursor;
mCursor = next;
mNeedMoreInput = mRollback = false;
rv = mConsumer(token, *this);
if (NS_FAILED(rv)) {
break;
}
if (mNeedMoreInput || mRollback) {
mCursor = rollback;
mPastEof = false;
if (mNeedMoreInput) {
break;
}
}
}
mInputCursor = mCursor - mInput.BeginReading();
return rv;
}
} // mozilla

View file

@ -0,0 +1,122 @@
/* -*- 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 INCREMENTAL_TOKENIZER_H__
#define INCREMENTAL_TOKENIZER_H__
#include "mozilla/Tokenizer.h"
#include "nsError.h"
#include <functional>
class nsIInputStream;
namespace mozilla {
class IncrementalTokenizer : public TokenizerBase
{
public:
/**
* The consumer callback. The function is called for every single token
* as found in the input. Failure result returned by this callback stops
* the tokenization immediately and bubbles to result of Feed/FinishInput.
*
* Fragment()s of consumed tokens are ensured to remain valid until next call to
* Feed/FinishInput and are pointing to a single linear buffer. Hence, those can
* be safely used to accumulate the data for processing after Feed/FinishInput
* returned.
*/
typedef std::function<nsresult(Token const&, IncrementalTokenizer& i)> Consumer;
/**
* For aWhitespaces and aAdditionalWordChars arguments see TokenizerBase.
*
* @param aConsumer
* A mandatory non-null argument, a function that consumes the tokens as they
* come when the tokenizer is fed.
* @param aRawMinBuffered
* When we have buffered at least aRawMinBuffered data, but there was no custom
* token found so far because of too small incremental feed chunks, deliver
* the raw data to preserve streaming and to save memory. This only has effect
* in OnlyCustomTokenizing mode.
*/
explicit IncrementalTokenizer(Consumer aConsumer,
const char* aWhitespaces = nullptr,
const char* aAdditionalWordChars = nullptr,
uint32_t aRawMinBuffered = 1024);
/**
* Pushes the input to be tokenized. These directly call the Consumer callback
* on every found token. Result of the Consumer callback is returned here.
*
* The tokenizer must be initialized with a valid consumer prior call to these
* methods. It's not allowed to call Feed/FinishInput from inside the Consumer
* callback.
*/
nsresult FeedInput(const nsACString& aInput);
nsresult FeedInput(nsIInputStream* aInput, uint32_t aCount);
nsresult FinishInput();
/**
* Can only be called from inside the consumer callback.
*
* When there is still anything to read from the input, tokenize it, store
* the token type and value to aToken result and shift the cursor past this
* just parsed token. Each call to Next() reads another token from
* the input and shifts the cursor.
*
* Returns false if there is not enough data to deterministically recognize
* tokens or when the last returned token was EOF.
*/
MOZ_MUST_USE
bool Next(Token& aToken);
/**
* Can only be called from inside the consumer callback.
*
* Tells the tokenizer to revert the cursor and stop the async parsing until
* next feed of the input. This is useful when more than one token is needed
* to decide on the syntax but there is not enough input to get a next token
* (Next() returned false.)
*/
void NeedMoreInput();
/**
* Can only be called from inside the consumer callback.
*
* This makes the consumer callback be called again while parsing
* the input at the previous cursor position again. This is useful when
* the tokenizer state (custom tokens, tokenization mode) has changed and
* we want to re-parse the input again.
*/
void Rollback();
private:
// Loops over the input with TokenizerBase::Parse and calls the Consumer callback.
nsresult Process();
#ifdef DEBUG
// True when inside the consumer callback, used only for assertions.
bool mConsuming;
#endif // DEBUG
// Modifyable only from the Consumer callback, tells the parser to break, rollback
// and wait for more input.
bool mNeedMoreInput;
// Modifyable only from the Consumer callback, tells the parser to rollback and
// parse the input again, with (if modified) new settings of the tokenizer.
bool mRollback;
// The input buffer. Updated with each call to Feed/FinishInput.
nsCString mInput;
// Numerical index pointing at the current cursor position. We don't keep direct
// reference to the string buffer since the buffer gets often reallocated.
nsCString::index_type mInputCursor;
// Refernce to the consumer function.
Consumer mConsumer;
};
} // mozilla
#endif

View file

@ -0,0 +1,267 @@
/* -*- 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_StickyTimeDuration_h
#define mozilla_StickyTimeDuration_h
#include "mozilla/TimeStamp.h"
#include "mozilla/FloatingPoint.h"
namespace mozilla {
/**
* A ValueCalculator class that performs additional checks before performing
* arithmetic operations such that if either operand is Forever (or the
* negative equivalent) the result remains Forever (or the negative equivalent
* as appropriate).
*
* Currently this only checks if either argument to each operation is
* Forever/-Forever. However, it is possible that, for example,
* aA + aB > INT64_MAX (or < INT64_MIN).
*
* We currently don't check for that case since we don't expect that to
* happen often except under test conditions in which case the wrapping
* behavior is probably acceptable.
*/
class StickyTimeDurationValueCalculator
{
public:
static int64_t
Add(int64_t aA, int64_t aB)
{
MOZ_ASSERT((aA != INT64_MAX || aB != INT64_MIN) &&
(aA != INT64_MIN || aB != INT64_MAX),
"'Infinity + -Infinity' and '-Infinity + Infinity'"
" are undefined");
// Forever + x = Forever
// x + Forever = Forever
if (aA == INT64_MAX || aB == INT64_MAX) {
return INT64_MAX;
}
// -Forever + x = -Forever
// x + -Forever = -Forever
if (aA == INT64_MIN || aB == INT64_MIN) {
return INT64_MIN;
}
return aA + aB;
}
// Note that we can't just define Add and have BaseTimeDuration call Add with
// negative arguments since INT64_MAX != -INT64_MIN so the saturating logic
// won't work.
static int64_t
Subtract(int64_t aA, int64_t aB)
{
MOZ_ASSERT((aA != INT64_MAX && aA != INT64_MIN) || aA != aB,
"'Infinity - Infinity' and '-Infinity - -Infinity'"
" are undefined");
// Forever - x = Forever
// x - -Forever = Forever
if (aA == INT64_MAX || aB == INT64_MIN) {
return INT64_MAX;
}
// -Forever - x = -Forever
// x - Forever = -Forever
if (aA == INT64_MIN || aB == INT64_MAX) {
return INT64_MIN;
}
return aA - aB;
}
template <typename T>
static int64_t
Multiply(int64_t aA, T aB) {
// Specializations for double, float, and int64_t are provided following.
return Multiply(aA, static_cast<int64_t>(aB));
}
static int64_t
Divide(int64_t aA, int64_t aB) {
MOZ_ASSERT(aB != 0, "Division by zero");
MOZ_ASSERT((aA != INT64_MAX && aA != INT64_MIN) ||
(aB != INT64_MAX && aB != INT64_MIN),
"Dividing +/-Infinity by +/-Infinity is undefined");
// Forever / +x = Forever
// Forever / -x = -Forever
// -Forever / +x = -Forever
// -Forever / -x = Forever
if (aA == INT64_MAX || aA == INT64_MIN) {
return (aA >= 0) ^ (aB >= 0) ? INT64_MIN : INT64_MAX;
}
// x / Forever = 0
// x / -Forever = 0
if (aB == INT64_MAX || aB == INT64_MIN) {
return 0;
}
return aA / aB;
}
static double
DivideDouble(int64_t aA, int64_t aB)
{
MOZ_ASSERT(aB != 0, "Division by zero");
MOZ_ASSERT((aA != INT64_MAX && aA != INT64_MIN) ||
(aB != INT64_MAX && aB != INT64_MIN),
"Dividing +/-Infinity by +/-Infinity is undefined");
// Forever / +x = Forever
// Forever / -x = -Forever
// -Forever / +x = -Forever
// -Forever / -x = Forever
if (aA == INT64_MAX || aA == INT64_MIN) {
return (aA >= 0) ^ (aB >= 0)
? NegativeInfinity<double>()
: PositiveInfinity<double>();
}
// x / Forever = 0
// x / -Forever = 0
if (aB == INT64_MAX || aB == INT64_MIN) {
return 0.0;
}
return static_cast<double>(aA) / aB;
}
static int64_t
Modulo(int64_t aA, int64_t aB)
{
MOZ_ASSERT(aA != INT64_MAX && aA != INT64_MIN,
"Infinity modulo x is undefined");
return aA % aB;
}
};
template <>
inline int64_t
StickyTimeDurationValueCalculator::Multiply<int64_t>(int64_t aA,
int64_t aB)
{
MOZ_ASSERT((aA != 0 || (aB != INT64_MIN && aB != INT64_MAX)) &&
((aA != INT64_MIN && aA != INT64_MAX) || aB != 0),
"Multiplication of infinity by zero");
// Forever * +x = Forever
// Forever * -x = -Forever
// -Forever * +x = -Forever
// -Forever * -x = Forever
//
// i.e. If one or more of the arguments is +/-Forever, then
// return -Forever if the signs differ, or +Forever otherwise.
if (aA == INT64_MAX || aA == INT64_MIN ||
aB == INT64_MAX || aB == INT64_MIN) {
return (aA >= 0) ^ (aB >= 0) ? INT64_MIN : INT64_MAX;
}
return aA * aB;
}
template <>
inline int64_t
StickyTimeDurationValueCalculator::Multiply<double>(int64_t aA, double aB)
{
MOZ_ASSERT((aA != 0 || (!IsInfinite(aB))) &&
((aA != INT64_MIN && aA != INT64_MAX) || aB != 0.0),
"Multiplication of infinity by zero");
// As with Multiply<int64_t>, if one or more of the arguments is
// +/-Forever or +/-Infinity, then return -Forever if the signs differ,
// or +Forever otherwise.
if (aA == INT64_MAX || aA == INT64_MIN || IsInfinite(aB)) {
return (aA >= 0) ^ (aB >= 0.0) ? INT64_MIN : INT64_MAX;
}
return aA * aB;
}
template <>
inline int64_t
StickyTimeDurationValueCalculator::Multiply<float>(int64_t aA, float aB)
{
MOZ_ASSERT(IsInfinite(aB) == IsInfinite(static_cast<double>(aB)),
"Casting to float loses infinite-ness");
return Multiply(aA, static_cast<double>(aB));
}
/**
* Specialization of BaseTimeDuration that uses
* StickyTimeDurationValueCalculator for arithmetic on the mValue member.
*
* Use this class when you need a time duration that is expected to hold values
* of Forever (or the negative equivalent) *and* when you expect that
* time duration to be used in arithmetic operations (and not just value
* comparisons).
*/
typedef BaseTimeDuration<StickyTimeDurationValueCalculator>
StickyTimeDuration;
// Template specializations to allow arithmetic between StickyTimeDuration
// and TimeDuration objects by falling back to the safe behavior.
inline StickyTimeDuration
operator+(const TimeDuration& aA, const StickyTimeDuration& aB)
{
return StickyTimeDuration(aA) + aB;
}
inline StickyTimeDuration
operator+(const StickyTimeDuration& aA, const TimeDuration& aB)
{
return aA + StickyTimeDuration(aB);
}
inline StickyTimeDuration
operator-(const TimeDuration& aA, const StickyTimeDuration& aB)
{
return StickyTimeDuration(aA) - aB;
}
inline StickyTimeDuration
operator-(const StickyTimeDuration& aA, const TimeDuration& aB)
{
return aA - StickyTimeDuration(aB);
}
inline StickyTimeDuration&
operator+=(StickyTimeDuration &aA, const TimeDuration& aB)
{
return aA += StickyTimeDuration(aB);
}
inline StickyTimeDuration&
operator-=(StickyTimeDuration &aA, const TimeDuration& aB)
{
return aA -= StickyTimeDuration(aB);
}
inline double
operator/(const TimeDuration& aA, const StickyTimeDuration& aB)
{
return StickyTimeDuration(aA) / aB;
}
inline double
operator/(const StickyTimeDuration& aA, const TimeDuration& aB)
{
return aA / StickyTimeDuration(aB);
}
inline StickyTimeDuration
operator%(const TimeDuration& aA, const StickyTimeDuration& aB)
{
return StickyTimeDuration(aA) % aB;
}
inline StickyTimeDuration
operator%(const StickyTimeDuration& aA, const TimeDuration& aB)
{
return aA % StickyTimeDuration(aB);
}
} // namespace mozilla
#endif /* mozilla_StickyTimeDuration_h */

738
xpcom/ds/Tokenizer.cpp Normal file
View file

@ -0,0 +1,738 @@
/* -*- 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 "Tokenizer.h"
#include "nsUnicharUtils.h"
#include <algorithm>
namespace mozilla {
static const char sWhitespaces[] = " \t";
Tokenizer::Tokenizer(const nsACString& aSource,
const char* aWhitespaces,
const char* aAdditionalWordChars)
: TokenizerBase(aWhitespaces, aAdditionalWordChars)
{
mInputFinished = true;
aSource.BeginReading(mCursor);
mRecord = mRollback = mCursor;
aSource.EndReading(mEnd);
}
Tokenizer::Tokenizer(const char* aSource,
const char* aWhitespaces,
const char* aAdditionalWordChars)
: Tokenizer(nsDependentCString(aSource), aWhitespaces, aAdditionalWordChars)
{
}
bool
Tokenizer::Next(Token& aToken)
{
if (!HasInput()) {
mHasFailed = true;
return false;
}
mRollback = mCursor;
mCursor = Parse(aToken);
AssignFragment(aToken, mRollback, mCursor);
mPastEof = aToken.Type() == TOKEN_EOF;
mHasFailed = false;
return true;
}
bool
Tokenizer::Check(const TokenType aTokenType, Token& aResult)
{
if (!HasInput()) {
mHasFailed = true;
return false;
}
nsACString::const_char_iterator next = Parse(aResult);
if (aTokenType != aResult.Type()) {
mHasFailed = true;
return false;
}
mRollback = mCursor;
mCursor = next;
AssignFragment(aResult, mRollback, mCursor);
mPastEof = aResult.Type() == TOKEN_EOF;
mHasFailed = false;
return true;
}
bool
Tokenizer::Check(const Token& aToken)
{
if (!HasInput()) {
mHasFailed = true;
return false;
}
Token parsed;
nsACString::const_char_iterator next = Parse(parsed);
if (!aToken.Equals(parsed)) {
mHasFailed = true;
return false;
}
mRollback = mCursor;
mCursor = next;
mPastEof = parsed.Type() == TOKEN_EOF;
mHasFailed = false;
return true;
}
void
Tokenizer::SkipWhites(WhiteSkipping aIncludeNewLines)
{
if (!CheckWhite() && (aIncludeNewLines == DONT_INCLUDE_NEW_LINE || !CheckEOL())) {
return;
}
nsACString::const_char_iterator rollback = mRollback;
while (CheckWhite() || (aIncludeNewLines == INCLUDE_NEW_LINE && CheckEOL())) {
}
mHasFailed = false;
mRollback = rollback;
}
void
Tokenizer::SkipUntil(Token const& aToken)
{
nsACString::const_char_iterator rollback = mCursor;
const Token eof = Token::EndOfFile();
Token t;
while (Next(t)) {
if (aToken.Equals(t) || eof.Equals(t)) {
Rollback();
break;
}
}
mRollback = rollback;
}
bool
Tokenizer::CheckChar(bool (*aClassifier)(const char aChar))
{
if (!aClassifier) {
MOZ_ASSERT(false);
return false;
}
if (!HasInput() || mCursor == mEnd) {
mHasFailed = true;
return false;
}
if (!aClassifier(*mCursor)) {
mHasFailed = true;
return false;
}
mRollback = mCursor;
++mCursor;
mHasFailed = false;
return true;
}
bool
Tokenizer::ReadChar(char* aValue)
{
MOZ_RELEASE_ASSERT(aValue);
Token t;
if (!Check(TOKEN_CHAR, t)) {
return false;
}
*aValue = t.AsChar();
return true;
}
bool
Tokenizer::ReadChar(bool (*aClassifier)(const char aChar), char* aValue)
{
MOZ_RELEASE_ASSERT(aValue);
if (!CheckChar(aClassifier)) {
return false;
}
*aValue = *mRollback;
return true;
}
bool
Tokenizer::ReadWord(nsACString& aValue)
{
Token t;
if (!Check(TOKEN_WORD, t)) {
return false;
}
aValue.Assign(t.AsString());
return true;
}
bool
Tokenizer::ReadWord(nsDependentCSubstring& aValue)
{
Token t;
if (!Check(TOKEN_WORD, t)) {
return false;
}
aValue.Rebind(t.AsString().BeginReading(), t.AsString().Length());
return true;
}
bool
Tokenizer::ReadUntil(Token const& aToken, nsACString& aResult, ClaimInclusion aInclude)
{
nsDependentCSubstring substring;
bool rv = ReadUntil(aToken, substring, aInclude);
aResult.Assign(substring);
return rv;
}
bool
Tokenizer::ReadUntil(Token const& aToken, nsDependentCSubstring& aResult, ClaimInclusion aInclude)
{
Record();
nsACString::const_char_iterator rollback = mCursor;
bool found = false;
Token t;
while (Next(t)) {
if (aToken.Equals(t)) {
found = true;
break;
}
}
Claim(aResult, aInclude);
mRollback = rollback;
return found;
}
void
Tokenizer::Rollback()
{
MOZ_ASSERT(mCursor > mRollback || mPastEof,
"Tokenizer::Rollback() cannot use twice or before any parsing");
mPastEof = false;
mHasFailed = false;
mCursor = mRollback;
}
void
Tokenizer::Record(ClaimInclusion aInclude)
{
mRecord = aInclude == INCLUDE_LAST
? mRollback
: mCursor;
}
void
Tokenizer::Claim(nsACString& aResult, ClaimInclusion aInclusion)
{
nsACString::const_char_iterator close = aInclusion == EXCLUDE_LAST
? mRollback
: mCursor;
aResult.Assign(Substring(mRecord, close));
}
void
Tokenizer::Claim(nsDependentCSubstring& aResult, ClaimInclusion aInclusion)
{
nsACString::const_char_iterator close = aInclusion == EXCLUDE_LAST
? mRollback
: mCursor;
aResult.Rebind(mRecord, close - mRecord);
}
// TokenizerBase
TokenizerBase::TokenizerBase(const char* aWhitespaces,
const char* aAdditionalWordChars)
: mPastEof(false)
, mHasFailed(false)
, mInputFinished(true)
, mMode(Mode::FULL)
, mMinRawDelivery(1024)
, mWhitespaces(aWhitespaces ? aWhitespaces : sWhitespaces)
, mAdditionalWordChars(aAdditionalWordChars)
, mCursor(nullptr)
, mEnd(nullptr)
, mNextCustomTokenID(TOKEN_CUSTOM0)
{
}
TokenizerBase::Token
TokenizerBase::AddCustomToken(const nsACString & aValue,
ECaseSensitivity aCaseInsensitivity, bool aEnabled)
{
MOZ_ASSERT(!aValue.IsEmpty());
UniquePtr<Token>& t = *mCustomTokens.AppendElement();
t = MakeUnique<Token>();
t->mType = static_cast<TokenType>(++mNextCustomTokenID);
t->mCustomCaseInsensitivity = aCaseInsensitivity;
t->mCustomEnabled = aEnabled;
t->mCustom.Assign(aValue);
return *t;
}
void
TokenizerBase::RemoveCustomToken(Token& aToken)
{
if (aToken.mType == TOKEN_UNKNOWN) {
// Already removed
return;
}
for (UniquePtr<Token> const& custom : mCustomTokens) {
if (custom->mType == aToken.mType) {
mCustomTokens.RemoveElement(custom);
aToken.mType = TOKEN_UNKNOWN;
return;
}
}
MOZ_ASSERT(false, "Token to remove not found");
}
void
TokenizerBase::EnableCustomToken(Token const& aToken, bool aEnabled)
{
if (aToken.mType == TOKEN_UNKNOWN) {
// Already removed
return;
}
for (UniquePtr<Token> const& custom : mCustomTokens) {
if (custom->Type() == aToken.Type()) {
// This effectively destroys the token instance.
custom->mCustomEnabled = aEnabled;
return;
}
}
MOZ_ASSERT(false, "Token to change not found");
}
void
TokenizerBase::SetTokenizingMode(Mode aMode)
{
mMode = aMode;
}
bool
TokenizerBase::HasFailed() const
{
return mHasFailed;
}
bool
TokenizerBase::HasInput() const
{
return !mPastEof;
}
nsACString::const_char_iterator
TokenizerBase::Parse(Token& aToken) const
{
if (mCursor == mEnd) {
if (!mInputFinished) {
return mCursor;
}
aToken = Token::EndOfFile();
return mEnd;
}
nsACString::size_type available = mEnd - mCursor;
uint32_t longestCustom = 0;
for (UniquePtr<Token> const& custom : mCustomTokens) {
if (IsCustom(mCursor, *custom, &longestCustom)) {
aToken = *custom;
return mCursor + custom->mCustom.Length();
}
}
if (!mInputFinished && available < longestCustom) {
// Not enough data to deterministically decide.
return mCursor;
}
nsACString::const_char_iterator next = mCursor;
if (mMode == Mode::CUSTOM_ONLY) {
// We have to do a brute-force search for all of the enabled custom
// tokens.
while (next < mEnd) {
++next;
for (UniquePtr<Token> const& custom : mCustomTokens) {
if (IsCustom(next, *custom)) {
aToken = Token::Raw();
return next;
}
}
}
if (mInputFinished) {
// End of the data reached.
aToken = Token::Raw();
return next;
}
if (longestCustom < available && available > mMinRawDelivery) {
// We can return some data w/o waiting for either a custom token
// or call to FinishData() when we leave the tail where all the
// custom tokens potentially fit, so we can't lose only partially
// delivered tokens. This preserves reasonable granularity.
aToken = Token::Raw();
return mEnd - longestCustom + 1;
}
// Not enough data to deterministically decide.
return mCursor;
}
enum State {
PARSE_INTEGER,
PARSE_WORD,
PARSE_CRLF,
PARSE_LF,
PARSE_WS,
PARSE_CHAR,
} state;
if (IsWordFirst(*next)) {
state = PARSE_WORD;
} else if (IsNumber(*next)) {
state = PARSE_INTEGER;
} else if (strchr(mWhitespaces, *next)) { // not UTF-8 friendly?
state = PARSE_WS;
} else if (*next == '\r') {
state = PARSE_CRLF;
} else if (*next == '\n') {
state = PARSE_LF;
} else {
state = PARSE_CHAR;
}
mozilla::CheckedUint64 resultingNumber = 0;
while (next < mEnd) {
switch (state) {
case PARSE_INTEGER:
// Keep it simple for now
resultingNumber *= 10;
resultingNumber += static_cast<uint64_t>(*next - '0');
++next;
if (IsPending(next)) {
break;
}
if (IsEnd(next) || !IsNumber(*next)) {
if (!resultingNumber.isValid()) {
aToken = Token::Error();
} else {
aToken = Token::Number(resultingNumber.value());
}
return next;
}
break;
case PARSE_WORD:
++next;
if (IsPending(next)) {
break;
}
if (IsEnd(next) || !IsWord(*next)) {
aToken = Token::Word(Substring(mCursor, next));
return next;
}
break;
case PARSE_CRLF:
++next;
if (IsPending(next)) {
break;
}
if (!IsEnd(next) && *next == '\n') { // LF is optional
++next;
}
aToken = Token::NewLine();
return next;
case PARSE_LF:
++next;
aToken = Token::NewLine();
return next;
case PARSE_WS:
++next;
aToken = Token::Whitespace();
return next;
case PARSE_CHAR:
++next;
aToken = Token::Char(*mCursor);
return next;
} // switch (state)
} // while (next < end)
MOZ_ASSERT(!mInputFinished);
return mCursor;
}
bool
TokenizerBase::IsEnd(const nsACString::const_char_iterator& caret) const
{
return caret == mEnd;
}
bool
TokenizerBase::IsPending(const nsACString::const_char_iterator& caret) const
{
return IsEnd(caret) && !mInputFinished;
}
bool
TokenizerBase::IsWordFirst(const char aInput) const
{
// TODO: make this fully work with unicode
return (ToLowerCase(static_cast<uint32_t>(aInput)) !=
ToUpperCase(static_cast<uint32_t>(aInput))) ||
'_' == aInput ||
(mAdditionalWordChars ? !!strchr(mAdditionalWordChars, aInput) : false);
}
bool
TokenizerBase::IsWord(const char aInput) const
{
return IsWordFirst(aInput) || IsNumber(aInput);
}
bool
TokenizerBase::IsNumber(const char aInput) const
{
// TODO: are there unicode numbers?
return aInput >= '0' && aInput <= '9';
}
bool
TokenizerBase::IsCustom(const nsACString::const_char_iterator & caret,
const Token & aCustomToken,
uint32_t * aLongest) const
{
MOZ_ASSERT(aCustomToken.mType > TOKEN_CUSTOM0);
if (!aCustomToken.mCustomEnabled) {
return false;
}
if (aLongest) {
*aLongest = std::max(*aLongest, aCustomToken.mCustom.Length());
}
uint32_t inputLength = mEnd - caret;
if (aCustomToken.mCustom.Length() > inputLength) {
return false;
}
nsDependentCSubstring inputFragment(caret, aCustomToken.mCustom.Length());
if (aCustomToken.mCustomCaseInsensitivity == CASE_INSENSITIVE) {
return inputFragment.Equals(aCustomToken.mCustom, nsCaseInsensitiveUTF8StringComparator());
}
return inputFragment.Equals(aCustomToken.mCustom);
}
void TokenizerBase::AssignFragment(Token& aToken,
nsACString::const_char_iterator begin,
nsACString::const_char_iterator end)
{
aToken.AssignFragment(begin, end);
}
// TokenizerBase::Token
TokenizerBase::Token::Token()
: mType(TOKEN_UNKNOWN)
, mChar(0)
, mInteger(0)
, mCustomCaseInsensitivity(CASE_SENSITIVE)
, mCustomEnabled(false)
{
}
TokenizerBase::Token::Token(const Token& aOther)
: mType(aOther.mType)
, mCustom(aOther.mCustom)
, mChar(aOther.mChar)
, mInteger(aOther.mInteger)
, mCustomCaseInsensitivity(aOther.mCustomCaseInsensitivity)
, mCustomEnabled(aOther.mCustomEnabled)
{
if (mType == TOKEN_WORD || mType > TOKEN_CUSTOM0) {
mWord.Rebind(aOther.mWord.BeginReading(), aOther.mWord.Length());
}
}
TokenizerBase::Token&
TokenizerBase::Token::operator=(const Token& aOther)
{
mType = aOther.mType;
mCustom = aOther.mCustom;
mChar = aOther.mChar;
mWord.Rebind(aOther.mWord.BeginReading(), aOther.mWord.Length());
mInteger = aOther.mInteger;
mCustomCaseInsensitivity = aOther.mCustomCaseInsensitivity;
mCustomEnabled = aOther.mCustomEnabled;
return *this;
}
void
TokenizerBase::Token::AssignFragment(nsACString::const_char_iterator begin,
nsACString::const_char_iterator end)
{
mFragment.Rebind(begin, end - begin);
}
// static
TokenizerBase::Token
TokenizerBase::Token::Raw()
{
Token t;
t.mType = TOKEN_RAW;
return t;
}
// static
TokenizerBase::Token
TokenizerBase::Token::Word(const nsACString& aValue)
{
Token t;
t.mType = TOKEN_WORD;
t.mWord.Rebind(aValue.BeginReading(), aValue.Length());
return t;
}
// static
TokenizerBase::Token
TokenizerBase::Token::Char(const char aValue)
{
Token t;
t.mType = TOKEN_CHAR;
t.mChar = aValue;
return t;
}
// static
TokenizerBase::Token
TokenizerBase::Token::Number(const uint64_t aValue)
{
Token t;
t.mType = TOKEN_INTEGER;
t.mInteger = aValue;
return t;
}
// static
TokenizerBase::Token
TokenizerBase::Token::Whitespace()
{
Token t;
t.mType = TOKEN_WS;
t.mChar = '\0';
return t;
}
// static
TokenizerBase::Token
TokenizerBase::Token::NewLine()
{
Token t;
t.mType = TOKEN_EOL;
return t;
}
// static
TokenizerBase::Token
TokenizerBase::Token::EndOfFile()
{
Token t;
t.mType = TOKEN_EOF;
return t;
}
// static
TokenizerBase::Token
TokenizerBase::Token::Error()
{
Token t;
t.mType = TOKEN_ERROR;
return t;
}
bool
TokenizerBase::Token::Equals(const Token& aOther) const
{
if (mType != aOther.mType) {
return false;
}
switch (mType) {
case TOKEN_INTEGER:
return AsInteger() == aOther.AsInteger();
case TOKEN_WORD:
return AsString() == aOther.AsString();
case TOKEN_CHAR:
return AsChar() == aOther.AsChar();
default:
return true;
}
}
char
TokenizerBase::Token::AsChar() const
{
MOZ_ASSERT(mType == TOKEN_CHAR || mType == TOKEN_WS);
return mChar;
}
nsDependentCSubstring
TokenizerBase::Token::AsString() const
{
MOZ_ASSERT(mType == TOKEN_WORD);
return mWord;
}
uint64_t
TokenizerBase::Token::AsInteger() const
{
MOZ_ASSERT(mType == TOKEN_INTEGER);
return mInteger;
}
} // mozilla

446
xpcom/ds/Tokenizer.h Normal file
View file

@ -0,0 +1,446 @@
/* -*- 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 Tokenizer_h__
#define Tokenizer_h__
#include "nsString.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/UniquePtr.h"
#include "nsTArray.h"
namespace mozilla {
class TokenizerBase
{
public:
/**
* The analyzer works with elements in the input cut to a sequence of token
* where each token has an elementary type
*/
enum TokenType : uint32_t
{
TOKEN_UNKNOWN,
TOKEN_RAW,
TOKEN_ERROR,
TOKEN_INTEGER,
TOKEN_WORD,
TOKEN_CHAR,
TOKEN_WS,
TOKEN_EOL,
TOKEN_EOF,
TOKEN_CUSTOM0 = 1000
};
enum ECaseSensitivity
{
CASE_SENSITIVE,
CASE_INSENSITIVE
};
/**
* Class holding the type and the value of a token. It can be manually created
* to allow checks against it via methods of Tokenizer or are results of some of
* the Tokenizer's methods.
*/
class Token
{
TokenType mType;
nsDependentCSubstring mWord;
nsCString mCustom;
char mChar;
uint64_t mInteger;
ECaseSensitivity mCustomCaseInsensitivity;
bool mCustomEnabled;
// If this token is a result of the parsing process, this member is referencing
// a sub-string in the input buffer. If this is externally created Token this
// member is left an empty string.
nsDependentCSubstring mFragment;
friend class TokenizerBase;
void AssignFragment(nsACString::const_char_iterator begin,
nsACString::const_char_iterator end);
static Token Raw();
public:
Token();
Token(const Token& aOther);
Token& operator=(const Token& aOther);
// Static constructors of tokens by type and value
static Token Word(const nsACString& aWord);
static Token Char(const char aChar);
static Token Number(const uint64_t aNumber);
static Token Whitespace();
static Token NewLine();
static Token EndOfFile();
static Token Error();
// Compares the two tokens, type must be identical and value
// of one of the tokens must be 'any' or equal.
bool Equals(const Token& aOther) const;
TokenType Type() const { return mType; }
char AsChar() const;
nsDependentCSubstring AsString() const;
uint64_t AsInteger() const;
nsDependentCSubstring Fragment() const { return mFragment; }
};
/**
* Consumers may register a custom string that, when found in the input, is considered
* a token and returned by Next*() and accepted by Check*() methods.
* AddCustomToken() returns a reference to a token that can then be comapred using
* Token::Equals() againts the output from Next*() or be passed to Check*().
*/
Token AddCustomToken(const nsACString& aValue, ECaseSensitivity aCaseInsensitivity, bool aEnabled = true);
template <uint32_t N>
Token AddCustomToken(const char(&aValue)[N], ECaseSensitivity aCaseInsensitivity, bool aEnabled = true)
{
return AddCustomToken(nsDependentCSubstring(aValue, N - 1), aCaseInsensitivity, aEnabled);
}
void RemoveCustomToken(Token& aToken);
/**
* Only applies to a custom type of a Token (see AddCustomToken above.)
* This turns on and off token recognition. When a custom token is disabled,
* it's ignored as never added as a custom token.
*/
void EnableCustomToken(Token const& aToken, bool aEnable);
/**
* Mode of tokenization.
* FULL tokenization, the default, recognizes built-in tokens and any custom tokens,
* if added.
* CUSTOM_ONLY will only recognize custom tokens, the rest is seen as 'raw'.
* This mode can be understood as a 'binary' mode.
*/
enum class Mode
{
FULL,
CUSTOM_ONLY
};
void SetTokenizingMode(Mode aMode);
/**
* Return false iff the last Check*() call has returned false or when we've read past
* the end of the input string.
*/
MOZ_MUST_USE bool HasFailed() const;
protected:
explicit TokenizerBase(const char* aWhitespaces = nullptr,
const char* aAdditionalWordChars = nullptr);
// false if we have already read the EOF token.
bool HasInput() const;
// Main parsing function, it doesn't shift the read cursor, just returns the next
// token position.
nsACString::const_char_iterator Parse(Token& aToken) const;
// Is read cursor at the end?
bool IsEnd(const nsACString::const_char_iterator& caret) const;
// True, when we are at the end of the input data, but it has not been marked
// as complete yet. In that case we cannot proceed with providing a multi-char token.
bool IsPending(const nsACString::const_char_iterator & caret) const;
// Is read cursor on a character that is a word start?
bool IsWordFirst(const char aInput) const;
// Is read cursor on a character that is an in-word letter?
bool IsWord(const char aInput) const;
// Is read cursor on a character that is a valid number?
// TODO - support multiple radix
bool IsNumber(const char aInput) const;
// Is equal to the given custom token?
bool IsCustom(const nsACString::const_char_iterator& caret,
const Token& aCustomToken, uint32_t* aLongest = nullptr) const;
// Friendly helper to assign a fragment on a Token
static void AssignFragment(Token& aToken,
nsACString::const_char_iterator begin,
nsACString::const_char_iterator end);
// true iff we have already read the EOF token
bool mPastEof;
// true iff the last Check*() call has returned false, reverts to true on Rollback() call
bool mHasFailed;
// true if the input string is final (finished), false when we expect more data
// yet to be fed to the tokenizer (see IncrementalTokenizer derived class).
bool mInputFinished;
// custom only vs full tokenizing mode, see the Parse() method
Mode mMode;
// minimal raw data chunked delivery during incremental feed
uint32_t mMinRawDelivery;
// Customizable list of whitespaces
const char* mWhitespaces;
// Additinal custom word characters
const char* mAdditionalWordChars;
// All these point to the original buffer passed to the constructor or to the incremental
// buffer after FeedInput.
nsACString::const_char_iterator mCursor; // Position of the current (actually next to read) token start
nsACString::const_char_iterator mEnd; // End of the input position
// This is the list of tokens user has registered with AddCustomToken()
nsTArray<UniquePtr<Token>> mCustomTokens;
uint32_t mNextCustomTokenID;
private:
TokenizerBase() = delete;
TokenizerBase(const TokenizerBase&) = delete;
TokenizerBase(TokenizerBase&&) = delete;
TokenizerBase(const TokenizerBase&&) = delete;
TokenizerBase &operator=(const TokenizerBase&) = delete;
};
/**
* This is a simple implementation of a lexical analyzer or maybe better
* called a tokenizer. It doesn't allow any user dictionaries or
* user define token types.
*
* It is limited only to ASCII input for now. UTF-8 or any other input
* encoding must yet be implemented.
*/
class Tokenizer : public TokenizerBase
{
public:
/**
* @param aSource
* The string to parse.
* IMPORTANT NOTE: Tokenizer doesn't ensure the input string buffer lifetime.
* It's up to the consumer to make sure the string's buffer outlives the Tokenizer!
* @param aWhitespaces
* If non-null Tokenizer will use this custom set of whitespaces for CheckWhite()
* and SkipWhites() calls.
* By default the list consists of space and tab.
* @param aAdditionalWordChars
* If non-null it will be added to the list of characters that consist a word.
* This is useful when you want to accept e.g. '-' in HTTP headers.
* By default a word character is consider any character for which upper case
* is different from lower case.
*
* If there is an overlap between aWhitespaces and aAdditionalWordChars, the check for
* word characters is made first.
*/
explicit Tokenizer(const nsACString& aSource,
const char* aWhitespaces = nullptr,
const char* aAdditionalWordChars = nullptr);
explicit Tokenizer(const char* aSource,
const char* aWhitespaces = nullptr,
const char* aAdditionalWordChars = nullptr);
/**
* When there is still anything to read from the input, tokenize it, store the token type
* and value to aToken result and shift the cursor past this just parsed token. Each call
* to Next() reads another token from the input and shifts the cursor.
* Returns false if we have passed the end of the input.
*/
MOZ_MUST_USE
bool Next(Token& aToken);
/**
* Parse the token on the input read cursor position, check its type is equal to aTokenType
* and if so, put it into aResult, shift the cursor and return true. Otherwise, leave
* the input read cursor position intact and return false.
*/
MOZ_MUST_USE
bool Check(const TokenType aTokenType, Token& aResult);
/**
* Same as above method, just compares both token type and token value passed in aToken.
* When both the type and the value equals, shift the cursor and return true. Otherwise
* return false.
*/
MOZ_MUST_USE
bool Check(const Token& aToken);
/**
* SkipWhites method (below) may also skip new line characters automatically.
*/
enum WhiteSkipping {
/**
* SkipWhites will only skip what is defined as a white space (default).
*/
DONT_INCLUDE_NEW_LINE = 0,
/**
* SkipWhites will skip definited white spaces as well as new lines
* automatically.
*/
INCLUDE_NEW_LINE = 1
};
/**
* Skips any occurence of whitespaces specified in mWhitespaces member,
* optionally skip also new lines.
*/
void SkipWhites(WhiteSkipping aIncludeNewLines = DONT_INCLUDE_NEW_LINE);
/**
* Skips all tokens until the given one is found or EOF is hit. The token
* or EOF are next to read.
*/
void SkipUntil(Token const& aToken);
// These are mostly shortcuts for the Check() methods above.
/**
* Check whitespace character is present.
*/
MOZ_MUST_USE
bool CheckWhite() { return Check(Token::Whitespace()); }
/**
* Check there is a single character on the read cursor position. If so, shift the read
* cursor position and return true. Otherwise false.
*/
MOZ_MUST_USE
bool CheckChar(const char aChar) { return Check(Token::Char(aChar)); }
/**
* This is a customizable version of CheckChar. aClassifier is a function called with
* value of the character on the current input read position. If this user function
* returns true, read cursor is shifted and true returned. Otherwise false.
* The user classifiction function is not called when we are at or past the end and
* false is immediately returned.
*/
MOZ_MUST_USE
bool CheckChar(bool (*aClassifier)(const char aChar));
/**
* Check for a whole expected word.
*/
MOZ_MUST_USE
bool CheckWord(const nsACString& aWord) { return Check(Token::Word(aWord)); }
/**
* Shortcut for literal const word check with compile time length calculation.
*/
template <uint32_t N>
MOZ_MUST_USE
bool CheckWord(const char (&aWord)[N]) { return Check(Token::Word(nsDependentCString(aWord, N - 1))); }
/**
* Checks \r, \n or \r\n.
*/
MOZ_MUST_USE
bool CheckEOL() { return Check(Token::NewLine()); }
/**
* Checks we are at the end of the input string reading. If so, shift past the end
* and returns true. Otherwise does nothing and returns false.
*/
MOZ_MUST_USE
bool CheckEOF() { return Check(Token::EndOfFile()); }
/**
* These are shortcuts to obtain the value immediately when the token type matches.
*/
MOZ_MUST_USE bool ReadChar(char* aValue);
MOZ_MUST_USE bool ReadChar(bool (*aClassifier)(const char aChar),
char* aValue);
MOZ_MUST_USE bool ReadWord(nsACString& aValue);
MOZ_MUST_USE bool ReadWord(nsDependentCSubstring& aValue);
/**
* This is an integer read helper. It returns false and doesn't move the read
* cursor when any of the following happens:
* - the token at the read cursor is not an integer
* - the final number doesn't fit the T type
* Otherwise true is returned, aValue is filled with the integral number
* and the cursor is moved forward.
*/
template <typename T>
MOZ_MUST_USE bool ReadInteger(T* aValue)
{
MOZ_RELEASE_ASSERT(aValue);
nsACString::const_char_iterator rollback = mRollback;
nsACString::const_char_iterator cursor = mCursor;
Token t;
if (!Check(TOKEN_INTEGER, t)) {
return false;
}
mozilla::CheckedInt<T> checked(t.AsInteger());
if (!checked.isValid()) {
// Move to a state as if Check() call has failed
mRollback = rollback;
mCursor = cursor;
mHasFailed = true;
return false;
}
*aValue = checked.value();
return true;
}
/**
* Returns the read cursor position back as it was before the last call of any parsing
* method of Tokenizer (Next, Check*, Skip*, Read*) so that the last operation
* can be repeated.
* Rollback cannot be used multiple times, it only reverts the last successfull parse
* operation. It also cannot be used before any parsing operation has been called
* on the Tokenizer.
*/
void Rollback();
/**
* Record() and Claim() are collecting the input as it is being parsed to obtain
* a substring between particular syntax bounderies defined by any recursive
* descent parser or simple parser the Tokenizer is used to read the input for.
* Inlucsion of a token that has just been parsed can be controlled using an arguemnt.
*/
enum ClaimInclusion {
/**
* Include resulting (or passed) token of the last lexical analyzer operation in the result.
*/
INCLUDE_LAST,
/**
* Do not include it.
*/
EXCLUDE_LAST
};
/**
* Start the process of recording. Based on aInclude value the begining of the recorded
* sub-string is at the current position (EXCLUDE_LAST) or at the position before the last
* parsed token (INCLUDE_LAST).
*/
void Record(ClaimInclusion aInclude = EXCLUDE_LAST);
/**
* Claim result of the record started with Record() call before. Depending on aInclude
* the ending of the sub-string result includes or excludes the last parsed or checked
* token.
*/
void Claim(nsACString& aResult, ClaimInclusion aInclude = EXCLUDE_LAST);
void Claim(nsDependentCSubstring& aResult, ClaimInclusion aInclude = EXCLUDE_LAST);
/**
* If aToken is found, aResult is set to the substring between the current
* position and the position of aToken, potentially including aToken depending
* on aInclude.
* If aToken isn't found aResult is set to the substring between the current
* position and the end of the string.
* If aToken is found, the method returns true. Otherwise it returns false.
*
* Calling Rollback() after ReadUntil() will return the read cursor to the
* position it had before ReadUntil was called.
*/
MOZ_MUST_USE bool ReadUntil(Token const& aToken, nsDependentCSubstring& aResult,
ClaimInclusion aInclude = EXCLUDE_LAST);
MOZ_MUST_USE bool ReadUntil(Token const& aToken, nsACString& aResult,
ClaimInclusion aInclude = EXCLUDE_LAST);
protected:
// All these point to the original buffer passed to the Tokenizer's constructor
nsACString::const_char_iterator mRecord; // Position where the recorded sub-string for Claim() is
nsACString::const_char_iterator mRollback; // Position of the previous token start
private:
Tokenizer() = delete;
Tokenizer(const Tokenizer&) = delete;
Tokenizer(Tokenizer&&) = delete;
Tokenizer(const Tokenizer&&) = delete;
Tokenizer &operator=(const Tokenizer&) = delete;
};
} // mozilla
#endif // Tokenizer_h__

105
xpcom/ds/moz.build Normal file
View file

@ -0,0 +1,105 @@
# -*- 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 += [
'nsIArray.idl',
'nsIArrayExtensions.idl',
'nsIAtom.idl',
'nsIAtomService.idl',
'nsICollection.idl',
'nsIEnumerator.idl',
'nsIHashable.idl',
'nsIINIParser.idl',
'nsIMutableArray.idl',
'nsIObserver.idl',
'nsIObserverService.idl',
'nsIPersistentProperties2.idl',
'nsIProperties.idl',
'nsIProperty.idl',
'nsIPropertyBag.idl',
'nsIPropertyBag2.idl',
'nsISerializable.idl',
'nsISimpleEnumerator.idl',
'nsIStringEnumerator.idl',
'nsISupportsArray.idl',
'nsISupportsIterators.idl',
'nsISupportsPrimitives.idl',
'nsIVariant.idl',
'nsIWritablePropertyBag.idl',
'nsIWritablePropertyBag2.idl',
]
if CONFIG['OS_ARCH'] == 'WINNT':
XPIDL_SOURCES += [
'nsIWindowsRegKey.idl',
]
EXPORTS += ['nsWindowsRegKey.h']
SOURCES += [
'nsWindowsRegKey.cpp'
]
XPIDL_MODULE = 'xpcom_ds'
EXPORTS += [
'nsArray.h',
'nsAtomService.h',
'nsCharSeparatedTokenizer.h',
'nsCheapSets.h',
'nsCRT.h',
'nsExpirationTracker.h',
'nsHashPropertyBag.h',
'nsMathUtils.h',
'nsStaticAtom.h',
'nsStaticNameTable.h',
'nsStringEnumerator.h',
'nsSupportsArray.h',
'nsSupportsPrimitives.h',
'nsVariant.h',
'nsWhitespaceTokenizer.h',
]
EXPORTS.mozilla += [
'IncrementalTokenizer.h',
'StickyTimeDuration.h',
'Tokenizer.h',
]
UNIFIED_SOURCES += [
'IncrementalTokenizer.cpp',
'nsArray.cpp',
'nsAtomService.cpp',
'nsAtomTable.cpp',
'nsCRT.cpp',
'nsHashPropertyBag.cpp',
'nsINIParserImpl.cpp',
'nsObserverList.cpp',
'nsObserverService.cpp',
'nsProperties.cpp',
'nsStringEnumerator.cpp',
'nsSupportsArray.cpp',
'nsSupportsArrayEnumerator.cpp',
'nsSupportsPrimitives.cpp',
'nsVariant.cpp',
'Tokenizer.cpp',
]
# These two files cannot be built in unified mode because they use the
# PL_ARENA_CONST_ALIGN_MASK macro with plarena.h.
SOURCES += [
'nsPersistentProperties.cpp',
'nsStaticNameTable.cpp',
]
EXTRA_COMPONENTS += [
'nsINIProcessor.js',
'nsINIProcessor.manifest',
]
LOCAL_INCLUDES += [
'../io',
]
FINAL_LIBRARY = 'xul'

242
xpcom/ds/nsArray.cpp Normal file
View file

@ -0,0 +1,242 @@
/* -*- 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 "nsArray.h"
#include "nsArrayEnumerator.h"
#include "nsIWeakReference.h"
#include "nsIWeakReferenceUtils.h"
#include "nsThreadUtils.h"
// used by IndexOf()
struct MOZ_STACK_CLASS findIndexOfClosure
{
// This is only used for pointer comparison, so we can just use a void*.
void* targetElement;
uint32_t startIndex;
uint32_t resultIndex;
};
static bool FindElementCallback(void* aElement, void* aClosure);
NS_INTERFACE_MAP_BEGIN(nsArray)
NS_INTERFACE_MAP_ENTRY(nsIArray)
NS_INTERFACE_MAP_ENTRY(nsIArrayExtensions)
NS_INTERFACE_MAP_ENTRY(nsIMutableArray)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMutableArray)
NS_INTERFACE_MAP_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsArrayCC)
NS_INTERFACE_MAP_ENTRY(nsIArray)
NS_INTERFACE_MAP_ENTRY(nsIArrayExtensions)
NS_INTERFACE_MAP_ENTRY(nsIMutableArray)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMutableArray)
NS_INTERFACE_MAP_END
nsArrayBase::~nsArrayBase()
{
Clear();
}
NS_IMPL_ADDREF(nsArray)
NS_IMPL_RELEASE(nsArray)
NS_IMPL_CYCLE_COLLECTION_CLASS(nsArrayCC)
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsArrayCC)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsArrayCC)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsArrayCC)
tmp->Clear();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsArrayCC)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArray)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMETHODIMP
nsArrayBase::GetLength(uint32_t* aLength)
{
*aLength = mArray.Count();
return NS_OK;
}
NS_IMETHODIMP
nsArrayBase::QueryElementAt(uint32_t aIndex,
const nsIID& aIID,
void** aResult)
{
nsISupports* obj = mArray.SafeObjectAt(aIndex);
if (!obj) {
return NS_ERROR_ILLEGAL_VALUE;
}
// no need to worry about a leak here, because SafeObjectAt()
// doesn't addref its result
return obj->QueryInterface(aIID, aResult);
}
NS_IMETHODIMP
nsArrayBase::IndexOf(uint32_t aStartIndex, nsISupports* aElement,
uint32_t* aResult)
{
// optimize for the common case by forwarding to mArray
if (aStartIndex == 0) {
uint32_t idx = mArray.IndexOf(aElement);
if (idx == UINT32_MAX) {
return NS_ERROR_FAILURE;
}
*aResult = idx;
return NS_OK;
}
findIndexOfClosure closure = { aElement, aStartIndex, 0 };
bool notFound = mArray.EnumerateForwards(FindElementCallback, &closure);
if (notFound) {
return NS_ERROR_FAILURE;
}
*aResult = closure.resultIndex;
return NS_OK;
}
NS_IMETHODIMP
nsArrayBase::Enumerate(nsISimpleEnumerator** aResult)
{
return NS_NewArrayEnumerator(aResult, static_cast<nsIArray*>(this));
}
// nsIMutableArray implementation
NS_IMETHODIMP
nsArrayBase::AppendElement(nsISupports* aElement, bool aWeak)
{
bool result;
if (aWeak) {
nsCOMPtr<nsIWeakReference> elementRef = do_GetWeakReference(aElement);
NS_ASSERTION(elementRef,
"AppendElement: Trying to use weak references on an object that doesn't support it");
if (!elementRef) {
return NS_ERROR_FAILURE;
}
result = mArray.AppendObject(elementRef);
}
else {
// add the object directly
result = mArray.AppendObject(aElement);
}
return result ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsArrayBase::RemoveElementAt(uint32_t aIndex)
{
bool result = mArray.RemoveObjectAt(aIndex);
return result ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsArrayBase::InsertElementAt(nsISupports* aElement, uint32_t aIndex, bool aWeak)
{
nsCOMPtr<nsISupports> elementRef;
if (aWeak) {
elementRef = do_GetWeakReference(aElement);
NS_ASSERTION(elementRef,
"InsertElementAt: Trying to use weak references on an object that doesn't support it");
if (!elementRef) {
return NS_ERROR_FAILURE;
}
} else {
elementRef = aElement;
}
bool result = mArray.InsertObjectAt(elementRef, aIndex);
return result ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsArrayBase::ReplaceElementAt(nsISupports* aElement, uint32_t aIndex, bool aWeak)
{
nsCOMPtr<nsISupports> elementRef;
if (aWeak) {
elementRef = do_GetWeakReference(aElement);
NS_ASSERTION(elementRef,
"ReplaceElementAt: Trying to use weak references on an object that doesn't support it");
if (!elementRef) {
return NS_ERROR_FAILURE;
}
} else {
elementRef = aElement;
}
mArray.ReplaceObjectAt(elementRef, aIndex);
return NS_OK;
}
NS_IMETHODIMP
nsArrayBase::Clear()
{
mArray.Clear();
return NS_OK;
}
// nsIArrayExtensions implementation.
NS_IMETHODIMP
nsArrayBase::Count(uint32_t* aResult)
{
return GetLength(aResult);
}
NS_IMETHODIMP
nsArrayBase::GetElementAt(uint32_t aIndex, nsISupports** aResult)
{
nsCOMPtr<nsISupports> obj = mArray.SafeObjectAt(aIndex);
obj.forget(aResult);
return NS_OK;
}
//
// static helper routines
//
bool
FindElementCallback(void* aElement, void* aClosure)
{
findIndexOfClosure* closure = static_cast<findIndexOfClosure*>(aClosure);
nsISupports* element = static_cast<nsISupports*>(aElement);
// don't start searching until we're past the startIndex
if (closure->resultIndex >= closure->startIndex &&
element == closure->targetElement) {
return false; // stop! We found it
}
closure->resultIndex++;
return true;
}
nsresult
nsArrayBase::XPCOMConstructor(nsISupports* aOuter, const nsIID& aIID,
void** aResult)
{
if (aOuter) {
return NS_ERROR_NO_AGGREGATION;
}
nsCOMPtr<nsIMutableArray> inst = Create();
return inst->QueryInterface(aIID, aResult);
}
already_AddRefed<nsIMutableArray>
nsArrayBase::Create()
{
nsCOMPtr<nsIMutableArray> inst;
if (NS_IsMainThread()) {
inst = new nsArrayCC;
} else {
inst = new nsArray;
}
return inst.forget();
}

76
xpcom/ds/nsArray.h Normal file
View file

@ -0,0 +1,76 @@
/* -*- 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 nsArray_h__
#define nsArray_h__
#include "nsIArrayExtensions.h"
#include "nsIMutableArray.h"
#include "nsCOMArray.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/Attributes.h"
// {35C66FD1-95E9-4e0a-80C5-C3BD2B375481}
#define NS_ARRAY_CID \
{ 0x35c66fd1, 0x95e9, 0x4e0a, \
{ 0x80, 0xc5, 0xc3, 0xbd, 0x2b, 0x37, 0x54, 0x81 } }
// nsArray without any refcounting declarations
class nsArrayBase : public nsIMutableArray
{
public:
NS_DECL_NSIARRAY
NS_DECL_NSIARRAYEXTENSIONS
NS_DECL_NSIMUTABLEARRAY
/* Both of these factory functions create a cycle-collectable array
on the main thread and a non-cycle-collectable array on other
threads. */
static already_AddRefed<nsIMutableArray> Create();
/* Only for the benefit of the XPCOM module system, use Create()
instead. */
static nsresult XPCOMConstructor(nsISupports* aOuter, const nsIID& aIID,
void** aResult);
protected:
nsArrayBase() {}
nsArrayBase(const nsArrayBase& aOther);
explicit nsArrayBase(const nsCOMArray_base& aBaseArray) : mArray(aBaseArray) {}
virtual ~nsArrayBase();
nsCOMArray_base mArray;
};
class nsArray final : public nsArrayBase
{
friend class nsArrayBase;
public:
NS_DECL_ISUPPORTS
private:
nsArray() : nsArrayBase() {}
nsArray(const nsArray& aOther);
explicit nsArray(const nsCOMArray_base& aBaseArray) : nsArrayBase(aBaseArray) {}
~nsArray() {}
};
class nsArrayCC final : public nsArrayBase
{
friend class nsArrayBase;
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsArrayCC, nsIMutableArray)
private:
nsArrayCC() : nsArrayBase() {}
nsArrayCC(const nsArrayCC& aOther);
explicit nsArrayCC(const nsCOMArray_base& aBaseArray) : nsArrayBase(aBaseArray) {}
~nsArrayCC() {}
};
#endif

View file

@ -0,0 +1,25 @@
/* -*- 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 "nsAtomService.h"
#include "nsIAtom.h"
NS_IMPL_ISUPPORTS(nsAtomService, nsIAtomService)
nsAtomService::nsAtomService()
{
}
nsresult
nsAtomService::GetAtom(const nsAString& aString, nsIAtom** aResult)
{
*aResult = NS_Atomize(aString).take();
if (!*aResult) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}

25
xpcom/ds/nsAtomService.h Normal file
View file

@ -0,0 +1,25 @@
/* -*- 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 __nsAtomService_h
#define __nsAtomService_h
#include "nsIAtomService.h"
#include "mozilla/Attributes.h"
class nsAtomService final : public nsIAtomService
{
public:
nsAtomService();
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIATOMSERVICE
private:
~nsAtomService() {}
};
#endif

736
xpcom/ds/nsAtomTable.cpp Normal file
View file

@ -0,0 +1,736 @@
/* -*- 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/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Mutex.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Sprintf.h"
#include "mozilla/Unused.h"
#include "nsAtomTable.h"
#include "nsStaticAtom.h"
#include "nsString.h"
#include "nsCRT.h"
#include "PLDHashTable.h"
#include "prenv.h"
#include "nsThreadUtils.h"
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "nsAutoPtr.h"
#include "nsUnicharUtils.h"
#include "nsPrintfCString.h"
// There are two kinds of atoms handled by this module.
//
// - DynamicAtom: the atom itself is heap allocated, as is the nsStringBuffer it
// points to. |gAtomTable| holds weak references to them DynamicAtoms. When
// the refcount of a DynamicAtom drops to zero, we increment a static counter.
// When that counter reaches a certain threshold, we iterate over the atom
// table, removing and deleting DynamicAtoms with refcount zero. This allows
// us to avoid acquiring the atom table lock during normal refcounting.
//
// - StaticAtom: the atom itself is heap allocated, but it points to a static
// nsStringBuffer. |gAtomTable| effectively owns StaticAtoms, because such
// atoms ignore all AddRef/Release calls, which ensures they stay alive until
// |gAtomTable| itself is destroyed whereupon they are explicitly deleted.
//
// Note that gAtomTable is used on multiple threads, and callers must
// acquire gAtomTableLock before touching it.
using namespace mozilla;
//----------------------------------------------------------------------
class CheckStaticAtomSizes
{
CheckStaticAtomSizes()
{
static_assert((sizeof(nsFakeStringBuffer<1>().mRefCnt) ==
sizeof(nsStringBuffer().mRefCount)) &&
(sizeof(nsFakeStringBuffer<1>().mSize) ==
sizeof(nsStringBuffer().mStorageSize)) &&
(offsetof(nsFakeStringBuffer<1>, mRefCnt) ==
offsetof(nsStringBuffer, mRefCount)) &&
(offsetof(nsFakeStringBuffer<1>, mSize) ==
offsetof(nsStringBuffer, mStorageSize)) &&
(offsetof(nsFakeStringBuffer<1>, mStringData) ==
sizeof(nsStringBuffer)),
"mocked-up strings' representations should be compatible");
}
};
//----------------------------------------------------------------------
static Atomic<uint32_t, ReleaseAcquire> gUnusedAtomCount(0);
class DynamicAtom final : public nsIAtom
{
public:
static already_AddRefed<DynamicAtom> Create(const nsAString& aString, uint32_t aHash)
{
// The refcount is appropriately initialized in the constructor.
return dont_AddRef(new DynamicAtom(aString, aHash));
}
static void GCAtomTable();
enum class GCKind {
RegularOperation,
Shutdown,
};
static void GCAtomTableLocked(const MutexAutoLock& aProofOfLock,
GCKind aKind);
private:
DynamicAtom(const nsAString& aString, uint32_t aHash)
: mRefCnt(1)
{
mLength = aString.Length();
mIsStatic = false;
RefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aString);
if (buf) {
mString = static_cast<char16_t*>(buf->Data());
} else {
const size_t size = (mLength + 1) * sizeof(char16_t);
buf = nsStringBuffer::Alloc(size);
if (MOZ_UNLIKELY(!buf)) {
// We OOM because atom allocations should be small and it's hard to
// handle them more gracefully in a constructor.
NS_ABORT_OOM(size);
}
mString = static_cast<char16_t*>(buf->Data());
CopyUnicodeTo(aString, 0, mString, mLength);
mString[mLength] = char16_t(0);
}
mHash = aHash;
MOZ_ASSERT(mHash == HashString(mString, mLength));
NS_ASSERTION(mString[mLength] == char16_t(0), "null terminated");
NS_ASSERTION(buf && buf->StorageSize() >= (mLength + 1) * sizeof(char16_t),
"enough storage");
NS_ASSERTION(Equals(aString), "correct data");
// Take ownership of buffer
mozilla::Unused << buf.forget();
}
private:
// We don't need a virtual destructor because we always delete via a
// DynamicAtom* pointer (in GCAtomTable()), not an nsIAtom* pointer.
~DynamicAtom();
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIATOM
};
class StaticAtom final : public nsIAtom
{
public:
StaticAtom(nsStringBuffer* aStringBuffer, uint32_t aLength, uint32_t aHash)
{
mLength = aLength;
mIsStatic = true;
mString = static_cast<char16_t*>(aStringBuffer->Data());
// Technically we could currently avoid doing this addref by instead making
// the static atom buffers have an initial refcount of 2.
aStringBuffer->AddRef();
mHash = aHash;
MOZ_ASSERT(mHash == HashString(mString, mLength));
MOZ_ASSERT(mString[mLength] == char16_t(0), "null terminated");
MOZ_ASSERT(aStringBuffer &&
aStringBuffer->StorageSize() == (mLength + 1) * sizeof(char16_t),
"correct storage");
}
// We don't need a virtual destructor because we always delete via a
// StaticAtom* pointer (in AtomTableClearEntry()), not an nsIAtom* pointer.
~StaticAtom() {}
NS_DECL_ISUPPORTS
NS_DECL_NSIATOM
};
NS_IMPL_QUERY_INTERFACE(StaticAtom, nsIAtom)
NS_IMETHODIMP_(MozExternalRefCountType)
StaticAtom::AddRef()
{
return 2;
}
NS_IMETHODIMP_(MozExternalRefCountType)
StaticAtom::Release()
{
return 1;
}
NS_IMETHODIMP
DynamicAtom::ScriptableToString(nsAString& aBuf)
{
nsStringBuffer::FromData(mString)->ToString(mLength, aBuf);
return NS_OK;
}
NS_IMETHODIMP
StaticAtom::ScriptableToString(nsAString& aBuf)
{
nsStringBuffer::FromData(mString)->ToString(mLength, aBuf);
return NS_OK;
}
NS_IMETHODIMP
DynamicAtom::ToUTF8String(nsACString& aBuf)
{
CopyUTF16toUTF8(nsDependentString(mString, mLength), aBuf);
return NS_OK;
}
NS_IMETHODIMP
StaticAtom::ToUTF8String(nsACString& aBuf)
{
CopyUTF16toUTF8(nsDependentString(mString, mLength), aBuf);
return NS_OK;
}
NS_IMETHODIMP
DynamicAtom::ScriptableEquals(const nsAString& aString, bool* aResult)
{
*aResult = aString.Equals(nsDependentString(mString, mLength));
return NS_OK;
}
NS_IMETHODIMP
StaticAtom::ScriptableEquals(const nsAString& aString, bool* aResult)
{
*aResult = aString.Equals(nsDependentString(mString, mLength));
return NS_OK;
}
NS_IMETHODIMP_(size_t)
DynamicAtom::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
{
size_t n = aMallocSizeOf(this);
n += nsStringBuffer::FromData(mString)->SizeOfIncludingThisIfUnshared(
aMallocSizeOf);
return n;
}
NS_IMETHODIMP_(size_t)
StaticAtom::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
{
size_t n = aMallocSizeOf(this);
// Don't measure the string buffer pointed to by the StaticAtom because it's
// in static memory.
return n;
}
//----------------------------------------------------------------------
/**
* The shared hash table for atom lookups.
*
* Callers must hold gAtomTableLock before manipulating the table.
*/
static PLDHashTable* gAtomTable;
static Mutex* gAtomTableLock;
struct AtomTableKey
{
AtomTableKey(const char16_t* aUTF16String, uint32_t aLength, uint32_t aHash)
: mUTF16String(aUTF16String)
, mUTF8String(nullptr)
, mLength(aLength)
, mHash(aHash)
{
MOZ_ASSERT(mHash == HashString(mUTF16String, mLength));
}
AtomTableKey(const char* aUTF8String, uint32_t aLength, uint32_t aHash)
: mUTF16String(nullptr)
, mUTF8String(aUTF8String)
, mLength(aLength)
, mHash(aHash)
{
mozilla::DebugOnly<bool> err;
MOZ_ASSERT(aHash == HashUTF8AsUTF16(mUTF8String, mLength, &err));
}
AtomTableKey(const char16_t* aUTF16String, uint32_t aLength,
uint32_t* aHashOut)
: mUTF16String(aUTF16String)
, mUTF8String(nullptr)
, mLength(aLength)
{
mHash = HashString(mUTF16String, mLength);
*aHashOut = mHash;
}
AtomTableKey(const char* aUTF8String, uint32_t aLength, uint32_t* aHashOut)
: mUTF16String(nullptr)
, mUTF8String(aUTF8String)
, mLength(aLength)
{
bool err;
mHash = HashUTF8AsUTF16(mUTF8String, mLength, &err);
if (err) {
mUTF8String = nullptr;
mLength = 0;
mHash = 0;
}
*aHashOut = mHash;
}
const char16_t* mUTF16String;
const char* mUTF8String;
uint32_t mLength;
uint32_t mHash;
};
struct AtomTableEntry : public PLDHashEntryHdr
{
// These references are either to DynamicAtoms, in which case they are
// non-owning, or they are to StaticAtoms, which aren't really refcounted.
// See the comment at the top of this file for more details.
nsIAtom* MOZ_NON_OWNING_REF mAtom;
};
static PLDHashNumber
AtomTableGetHash(const void* aKey)
{
const AtomTableKey* k = static_cast<const AtomTableKey*>(aKey);
return k->mHash;
}
static bool
AtomTableMatchKey(const PLDHashEntryHdr* aEntry, const void* aKey)
{
const AtomTableEntry* he = static_cast<const AtomTableEntry*>(aEntry);
const AtomTableKey* k = static_cast<const AtomTableKey*>(aKey);
if (k->mUTF8String) {
return
CompareUTF8toUTF16(nsDependentCSubstring(k->mUTF8String,
k->mUTF8String + k->mLength),
nsDependentAtomString(he->mAtom)) == 0;
}
uint32_t length = he->mAtom->GetLength();
if (length != k->mLength) {
return false;
}
return memcmp(he->mAtom->GetUTF16String(),
k->mUTF16String, length * sizeof(char16_t)) == 0;
}
static void
AtomTableClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
{
auto entry = static_cast<AtomTableEntry*>(aEntry);
nsIAtom* atom = entry->mAtom;
if (atom->IsStaticAtom()) {
// This case -- when the entry being cleared holds a StaticAtom -- only
// occurs when gAtomTable is destroyed, whereupon all StaticAtoms within it
// must be explicitly deleted. The cast is required because StaticAtom
// doesn't have a virtual destructor.
delete static_cast<StaticAtom*>(atom);
}
}
static void
AtomTableInitEntry(PLDHashEntryHdr* aEntry, const void* aKey)
{
static_cast<AtomTableEntry*>(aEntry)->mAtom = nullptr;
}
static const PLDHashTableOps AtomTableOps = {
AtomTableGetHash,
AtomTableMatchKey,
PLDHashTable::MoveEntryStub,
AtomTableClearEntry,
AtomTableInitEntry
};
//----------------------------------------------------------------------
void
DynamicAtom::GCAtomTable()
{
MutexAutoLock lock(*gAtomTableLock);
GCAtomTableLocked(lock, GCKind::RegularOperation);
}
void
DynamicAtom::GCAtomTableLocked(const MutexAutoLock& aProofOfLock,
GCKind aKind)
{
uint32_t removedCount = 0; // Use a non-atomic temporary for cheaper increments.
nsAutoCString nonZeroRefcountAtoms;
uint32_t nonZeroRefcountAtomsCount = 0;
for (auto i = gAtomTable->Iter(); !i.Done(); i.Next()) {
auto entry = static_cast<AtomTableEntry*>(i.Get());
if (entry->mAtom->IsStaticAtom()) {
continue;
}
auto atom = static_cast<DynamicAtom*>(entry->mAtom);
if (atom->mRefCnt == 0) {
i.Remove();
delete atom;
++removedCount;
}
#ifdef NS_FREE_PERMANENT_DATA
else if (aKind == GCKind::Shutdown && PR_GetEnv("XPCOM_MEM_BLOAT_LOG")) {
// Only report leaking atoms in leak-checking builds in a run
// where we are checking for leaks, during shutdown. If
// something is anomalous, then we'll assert later in this
// function.
nsAutoCString name;
atom->ToUTF8String(name);
if (nonZeroRefcountAtomsCount == 0) {
nonZeroRefcountAtoms = name;
} else if (nonZeroRefcountAtomsCount < 20) {
nonZeroRefcountAtoms += NS_LITERAL_CSTRING(",") + name;
} else if (nonZeroRefcountAtomsCount == 20) {
nonZeroRefcountAtoms += NS_LITERAL_CSTRING(",...");
}
nonZeroRefcountAtomsCount++;
}
#endif
}
if (nonZeroRefcountAtomsCount) {
nsPrintfCString msg("%d dynamic atom(s) with non-zero refcount: %s",
nonZeroRefcountAtomsCount, nonZeroRefcountAtoms.get());
NS_ASSERTION(nonZeroRefcountAtomsCount == 0, msg.get());
}
// During the course of this function, the atom table is locked. This means
// that, barring refcounting bugs in consumers, an atom can never go from
// refcount == 0 to refcount != 0 during a GC. However, an atom _can_ go from
// refcount != 0 to refcount == 0 if a Release() occurs in parallel with GC.
// This means that we cannot assert that gUnusedAtomCount == removedCount, and
// thus that there are no unused atoms at the end of a GC. We can and do,
// however, assert this after the last GC at shutdown.
if (aKind == GCKind::RegularOperation) {
MOZ_ASSERT(removedCount <= gUnusedAtomCount);
} else {
// Complain if somebody adds new GCKind enums.
MOZ_ASSERT(aKind == GCKind::Shutdown);
// Our unused atom count should be accurate.
MOZ_ASSERT(removedCount == gUnusedAtomCount);
}
gUnusedAtomCount -= removedCount;
}
NS_IMPL_QUERY_INTERFACE(DynamicAtom, nsIAtom)
NS_IMETHODIMP_(MozExternalRefCountType)
DynamicAtom::AddRef(void)
{
nsrefcnt count = ++mRefCnt;
if (count == 1) {
MOZ_ASSERT(gUnusedAtomCount > 0);
gUnusedAtomCount--;
}
return count;
}
#ifdef DEBUG
// We set a lower GC threshold for atoms in debug builds so that we exercise
// the GC machinery more often.
static const uint32_t kAtomGCThreshold = 20;
#else
static const uint32_t kAtomGCThreshold = 10000;
#endif
NS_IMETHODIMP_(MozExternalRefCountType)
DynamicAtom::Release(void)
{
MOZ_ASSERT(mRefCnt > 0);
nsrefcnt count = --mRefCnt;
if (count == 0) {
if (++gUnusedAtomCount >= kAtomGCThreshold) {
GCAtomTable();
}
}
return count;
}
DynamicAtom::~DynamicAtom()
{
nsStringBuffer::FromData(mString)->Release();
}
//----------------------------------------------------------------------
class StaticAtomEntry : public PLDHashEntryHdr
{
public:
typedef const nsAString& KeyType;
typedef const nsAString* KeyTypePointer;
explicit StaticAtomEntry(KeyTypePointer aKey) {}
StaticAtomEntry(const StaticAtomEntry& aOther) : mAtom(aOther.mAtom) {}
// We do not delete the atom because that's done when gAtomTable is
// destroyed -- which happens immediately after gStaticAtomTable is destroyed
// -- in NS_PurgeAtomTable().
~StaticAtomEntry() {}
bool KeyEquals(KeyTypePointer aKey) const
{
return mAtom->Equals(*aKey);
}
static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
static PLDHashNumber HashKey(KeyTypePointer aKey)
{
return HashString(*aKey);
}
enum { ALLOW_MEMMOVE = true };
// StaticAtoms aren't really refcounted. Because these entries live in a
// global hashtable, this reference is essentially owning.
StaticAtom* MOZ_OWNING_REF mAtom;
};
/**
* A hashtable of static atoms that existed at app startup. This hashtable
* helps nsHtml5AtomTable.
*/
typedef nsTHashtable<StaticAtomEntry> StaticAtomTable;
static StaticAtomTable* gStaticAtomTable = nullptr;
/**
* Whether it is still OK to add atoms to gStaticAtomTable.
*/
static bool gStaticAtomTableSealed = false;
// The atom table very quickly gets 10,000+ entries in it (or even 100,000+).
// But choosing the best initial length has some subtleties: we add ~2700
// static atoms to the table at start-up, and then we start adding and removing
// dynamic atoms. If we make the table too big to start with, when the first
// dynamic atom gets removed the load factor will be < 25% and so we will
// shrink it to 4096 entries.
//
// By choosing an initial length of 4096, we get an initial capacity of 8192.
// That's the biggest initial capacity that will let us be > 25% full when the
// first dynamic atom is removed (when the count is ~2700), thus avoiding any
// shrinking.
#define ATOM_HASHTABLE_INITIAL_LENGTH 4096
void
NS_InitAtomTable()
{
MOZ_ASSERT(!gAtomTable);
gAtomTable = new PLDHashTable(&AtomTableOps, sizeof(AtomTableEntry),
ATOM_HASHTABLE_INITIAL_LENGTH);
gAtomTableLock = new Mutex("Atom Table Lock");
}
void
NS_ShutdownAtomTable()
{
delete gStaticAtomTable;
gStaticAtomTable = nullptr;
#ifdef NS_FREE_PERMANENT_DATA
// Do a final GC to satisfy leak checking. We skip this step in release
// builds.
{
MutexAutoLock lock(*gAtomTableLock);
DynamicAtom::GCAtomTableLocked(lock, DynamicAtom::GCKind::Shutdown);
}
#endif
delete gAtomTable;
gAtomTable = nullptr;
delete gAtomTableLock;
gAtomTableLock = nullptr;
}
void
NS_SizeOfAtomTablesIncludingThis(MallocSizeOf aMallocSizeOf,
size_t* aMain, size_t* aStatic)
{
MutexAutoLock lock(*gAtomTableLock);
*aMain = gAtomTable->ShallowSizeOfIncludingThis(aMallocSizeOf);
for (auto iter = gAtomTable->Iter(); !iter.Done(); iter.Next()) {
auto entry = static_cast<AtomTableEntry*>(iter.Get());
*aMain += entry->mAtom->SizeOfIncludingThis(aMallocSizeOf);
}
// The atoms pointed to by gStaticAtomTable are also pointed to by gAtomTable,
// and they're measured by the loop above. So no need to measure them here.
*aStatic = gStaticAtomTable
? gStaticAtomTable->ShallowSizeOfIncludingThis(aMallocSizeOf)
: 0;
}
static inline AtomTableEntry*
GetAtomHashEntry(const char* aString, uint32_t aLength, uint32_t* aHashOut)
{
gAtomTableLock->AssertCurrentThreadOwns();
AtomTableKey key(aString, aLength, aHashOut);
// This is an infallible add.
return static_cast<AtomTableEntry*>(gAtomTable->Add(&key));
}
static inline AtomTableEntry*
GetAtomHashEntry(const char16_t* aString, uint32_t aLength, uint32_t* aHashOut)
{
gAtomTableLock->AssertCurrentThreadOwns();
AtomTableKey key(aString, aLength, aHashOut);
// This is an infallible add.
return static_cast<AtomTableEntry*>(gAtomTable->Add(&key));
}
void
RegisterStaticAtoms(const nsStaticAtom* aAtoms, uint32_t aAtomCount)
{
MutexAutoLock lock(*gAtomTableLock);
MOZ_RELEASE_ASSERT(!gStaticAtomTableSealed,
"Atom table has already been sealed!");
if (!gStaticAtomTable) {
gStaticAtomTable = new StaticAtomTable();
}
for (uint32_t i = 0; i < aAtomCount; ++i) {
nsStringBuffer* stringBuffer = aAtoms[i].mStringBuffer;
nsIAtom** atomp = aAtoms[i].mAtom;
MOZ_ASSERT(nsCRT::IsAscii(static_cast<char16_t*>(stringBuffer->Data())));
uint32_t stringLen = stringBuffer->StorageSize() / sizeof(char16_t) - 1;
uint32_t hash;
AtomTableEntry* he =
GetAtomHashEntry(static_cast<char16_t*>(stringBuffer->Data()),
stringLen, &hash);
nsIAtom* atom = he->mAtom;
if (atom) {
// Disallow creating a dynamic atom, and then later, while the
// dynamic atom is still alive, registering that same atom as a
// static atom. It causes subtle bugs, and we're programming in
// C++ here, not Smalltalk.
if (!atom->IsStaticAtom()) {
nsAutoCString name;
atom->ToUTF8String(name);
MOZ_CRASH_UNSAFE_PRINTF(
"Static atom registration for %s should be pushed back", name.get());
}
} else {
atom = new StaticAtom(stringBuffer, stringLen, hash);
he->mAtom = atom;
}
*atomp = atom;
if (!gStaticAtomTableSealed) {
StaticAtomEntry* entry =
gStaticAtomTable->PutEntry(nsDependentAtomString(atom));
MOZ_ASSERT(atom->IsStaticAtom());
entry->mAtom = static_cast<StaticAtom*>(atom);
}
}
}
already_AddRefed<nsIAtom>
NS_Atomize(const char* aUTF8String)
{
return NS_Atomize(nsDependentCString(aUTF8String));
}
already_AddRefed<nsIAtom>
NS_Atomize(const nsACString& aUTF8String)
{
MutexAutoLock lock(*gAtomTableLock);
uint32_t hash;
AtomTableEntry* he = GetAtomHashEntry(aUTF8String.Data(),
aUTF8String.Length(),
&hash);
if (he->mAtom) {
nsCOMPtr<nsIAtom> atom = he->mAtom;
return atom.forget();
}
// This results in an extra addref/release of the nsStringBuffer.
// Unfortunately there doesn't seem to be any APIs to avoid that.
// Actually, now there is, sort of: ForgetSharedBuffer.
nsString str;
CopyUTF8toUTF16(aUTF8String, str);
RefPtr<DynamicAtom> atom = DynamicAtom::Create(str, hash);
he->mAtom = atom;
return atom.forget();
}
already_AddRefed<nsIAtom>
NS_Atomize(const char16_t* aUTF16String)
{
return NS_Atomize(nsDependentString(aUTF16String));
}
already_AddRefed<nsIAtom>
NS_Atomize(const nsAString& aUTF16String)
{
MutexAutoLock lock(*gAtomTableLock);
uint32_t hash;
AtomTableEntry* he = GetAtomHashEntry(aUTF16String.Data(),
aUTF16String.Length(),
&hash);
if (he->mAtom) {
nsCOMPtr<nsIAtom> atom = he->mAtom;
return atom.forget();
}
RefPtr<DynamicAtom> atom = DynamicAtom::Create(aUTF16String, hash);
he->mAtom = atom;
return atom.forget();
}
nsrefcnt
NS_GetNumberOfAtoms(void)
{
DynamicAtom::GCAtomTable(); // Trigger a GC so that we return a deterministic result.
MutexAutoLock lock(*gAtomTableLock);
return gAtomTable->EntryCount();
}
nsIAtom*
NS_GetStaticAtom(const nsAString& aUTF16String)
{
NS_PRECONDITION(gStaticAtomTable, "Static atom table not created yet.");
NS_PRECONDITION(gStaticAtomTableSealed, "Static atom table not sealed yet.");
StaticAtomEntry* entry = gStaticAtomTable->GetEntry(aUTF16String);
return entry ? entry->mAtom : nullptr;
}
void
NS_SealStaticAtomTable()
{
gStaticAtomTableSealed = true;
}

19
xpcom/ds/nsAtomTable.h Normal file
View file

@ -0,0 +1,19 @@
/* -*- 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 nsAtomTable_h__
#define nsAtomTable_h__
#include "mozilla/MemoryReporting.h"
#include <stddef.h>
void NS_InitAtomTable();
void NS_ShutdownAtomTable();
void NS_SizeOfAtomTablesIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
size_t* aMain, size_t* aStatic);
#endif // nsAtomTable_h__

187
xpcom/ds/nsCRT.cpp Normal file
View file

@ -0,0 +1,187 @@
/* -*- 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/. */
/**
* MODULE NOTES:
* @update gess7/30/98
*
* Much as I hate to do it, we were using string compares wrong.
* Often, programmers call functions like strcmp(s1,s2), and pass
* one or more null strings. Rather than blow up on these, I've
* added quick checks to ensure that cases like this don't cause
* us to fail.
*
* In general, if you pass a null into any of these string compare
* routines, we simply return 0.
*/
#include "nsCRT.h"
#include "nsDebug.h"
//----------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
// My lovely strtok routine
#define IS_DELIM(m, c) ((m)[(c) >> 3] & (1 << ((c) & 7)))
#define SET_DELIM(m, c) ((m)[(c) >> 3] |= (1 << ((c) & 7)))
#define DELIM_TABLE_SIZE 32
char*
nsCRT::strtok(char* aString, const char* aDelims, char** aNewStr)
{
NS_ASSERTION(aString,
"Unlike regular strtok, the first argument cannot be null.");
char delimTable[DELIM_TABLE_SIZE];
uint32_t i;
char* result;
char* str = aString;
for (i = 0; i < DELIM_TABLE_SIZE; ++i) {
delimTable[i] = '\0';
}
for (i = 0; aDelims[i]; i++) {
SET_DELIM(delimTable, static_cast<uint8_t>(aDelims[i]));
}
NS_ASSERTION(aDelims[i] == '\0', "too many delimiters");
// skip to beginning
while (*str && IS_DELIM(delimTable, static_cast<uint8_t>(*str))) {
str++;
}
result = str;
// fix up the end of the token
while (*str) {
if (IS_DELIM(delimTable, static_cast<uint8_t>(*str))) {
*str++ = '\0';
break;
}
str++;
}
*aNewStr = str;
return str == result ? nullptr : result;
}
////////////////////////////////////////////////////////////////////////////////
/**
* Compare unichar string ptrs, stopping at the 1st null
* NOTE: If both are null, we return 0.
* NOTE: We terminate the search upon encountering a nullptr
*
* @update gess 11/10/99
* @param s1 and s2 both point to unichar strings
* @return 0 if they match, -1 if s1<s2; 1 if s1>s2
*/
int32_t
nsCRT::strcmp(const char16_t* aStr1, const char16_t* aStr2)
{
if (aStr1 && aStr2) {
for (;;) {
char16_t c1 = *aStr1++;
char16_t c2 = *aStr2++;
if (c1 != c2) {
if (c1 < c2) {
return -1;
}
return 1;
}
if (c1 == 0 || c2 == 0) {
break;
}
}
} else {
if (aStr1) { // aStr2 must have been null
return -1;
}
if (aStr2) { // aStr1 must have been null
return 1;
}
}
return 0;
}
/**
* Compare unichar string ptrs, stopping at the 1st null or nth char.
* NOTE: If either is null, we return 0.
* NOTE: We DO NOT terminate the search upon encountering nullptr's before N
*
* @update gess 11/10/99
* @param s1 and s2 both point to unichar strings
* @return 0 if they match, -1 if s1<s2; 1 if s1>s2
*/
int32_t
nsCRT::strncmp(const char16_t* aStr1, const char16_t* aStr2, uint32_t aNum)
{
if (aStr1 && aStr2) {
if (aNum != 0) {
do {
char16_t c1 = *aStr1++;
char16_t c2 = *aStr2++;
if (c1 != c2) {
if (c1 < c2) {
return -1;
}
return 1;
}
} while (--aNum != 0);
}
}
return 0;
}
const char*
nsCRT::memmem(const char* aHaystack, uint32_t aHaystackLen,
const char* aNeedle, uint32_t aNeedleLen)
{
// Sanity checking
if (!(aHaystack && aNeedle && aHaystackLen && aNeedleLen &&
aNeedleLen <= aHaystackLen)) {
return nullptr;
}
#ifdef HAVE_MEMMEM
return (const char*)::memmem(aHaystack, aHaystackLen, aNeedle, aNeedleLen);
#else
// No memmem means we need to roll our own. This isn't really optimized
// for performance ... if that becomes an issue we can take some inspiration
// from the js string compare code in jsstr.cpp
for (uint32_t i = 0; i < aHaystackLen - aNeedleLen; i++) {
if (!memcmp(aHaystack + i, aNeedle, aNeedleLen)) {
return aHaystack + i;
}
}
#endif
return nullptr;
}
// This should use NSPR but NSPR isn't exporting its PR_strtoll function
// Until then...
int64_t
nsCRT::atoll(const char* aStr)
{
if (!aStr) {
return 0;
}
int64_t ll = 0;
while (*aStr && *aStr >= '0' && *aStr <= '9') {
ll *= 10;
ll += *aStr - '0';
aStr++;
}
return ll;
}

186
xpcom/ds/nsCRT.h Normal file
View file

@ -0,0 +1,186 @@
/* -*- 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 nsCRT_h___
#define nsCRT_h___
#include <stdlib.h>
#include <ctype.h>
#include "plstr.h"
#include "nscore.h"
#include "nsCRTGlue.h"
#if defined(XP_WIN)
# define NS_LINEBREAK "\015\012"
# define NS_LINEBREAK_LEN 2
#else
# ifdef XP_UNIX
# define NS_LINEBREAK "\012"
# define NS_LINEBREAK_LEN 1
# endif /* XP_UNIX */
#endif /* XP_WIN */
extern const char16_t kIsoLatin1ToUCS2[256];
// This macro can be used in a class declaration for classes that want
// to ensure that their instance memory is zeroed.
#define NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW \
void* operator new(size_t sz) CPP_THROW_NEW { \
void* rv = ::operator new(sz); \
if (rv) { \
memset(rv, 0, sz); \
} \
return rv; \
} \
void operator delete(void* ptr) { \
::operator delete(ptr); \
}
// This macro works with the next macro to declare a non-inlined
// version of the above.
#define NS_DECL_ZEROING_OPERATOR_NEW \
void* operator new(size_t sz) CPP_THROW_NEW; \
void operator delete(void* ptr);
#define NS_IMPL_ZEROING_OPERATOR_NEW(_class) \
void* _class::operator new(size_t sz) CPP_THROW_NEW { \
void* rv = ::operator new(sz); \
if (rv) { \
memset(rv, 0, sz); \
} \
return rv; \
} \
void _class::operator delete(void* ptr) { \
::operator delete(ptr); \
}
// Freeing helper
#define CRTFREEIF(x) if (x) { nsCRT::free(x); x = 0; }
/// This is a wrapper class around all the C runtime functions.
class nsCRT
{
public:
enum
{
LF = '\n' /* Line Feed */,
VTAB = '\v' /* Vertical Tab */,
CR = '\r' /* Carriage Return */
};
/// String comparison.
static int32_t strcmp(const char* aStr1, const char* aStr2)
{
return int32_t(PL_strcmp(aStr1, aStr2));
}
static int32_t strncmp(const char* aStr1, const char* aStr2,
uint32_t aMaxLen)
{
return int32_t(PL_strncmp(aStr1, aStr2, aMaxLen));
}
/// Case-insensitive string comparison.
static int32_t strcasecmp(const char* aStr1, const char* aStr2)
{
return int32_t(PL_strcasecmp(aStr1, aStr2));
}
/// Case-insensitive string comparison with length
static int32_t strncasecmp(const char* aStr1, const char* aStr2,
uint32_t aMaxLen)
{
int32_t result = int32_t(PL_strncasecmp(aStr1, aStr2, aMaxLen));
//Egads. PL_strncasecmp is returning *very* negative numbers.
//Some folks expect -1,0,1, so let's temper its enthusiasm.
if (result < 0) {
result = -1;
}
return result;
}
static int32_t strncmp(const char* aStr1, const char* aStr2, int32_t aMaxLen)
{
// inline the first test (assumes strings are not null):
int32_t diff =
((const unsigned char*)aStr1)[0] - ((const unsigned char*)aStr2)[0];
if (diff != 0) {
return diff;
}
return int32_t(PL_strncmp(aStr1, aStr2, unsigned(aMaxLen)));
}
/**
How to use this fancy (thread-safe) version of strtok:
void main(void) {
printf("%s\n\nTokens:\n", string);
// Establish string and get the first token:
char* newStr;
token = nsCRT::strtok(string, seps, &newStr);
while (token != nullptr) {
// While there are tokens in "string"
printf(" %s\n", token);
// Get next token:
token = nsCRT::strtok(newStr, seps, &newStr);
}
}
* WARNING - STRTOK WHACKS str THE FIRST TIME IT IS CALLED *
* MAKE A COPY OF str IF YOU NEED TO USE IT AFTER strtok() *
*/
static char* strtok(char* aStr, const char* aDelims, char** aNewStr);
/// Like strcmp except for ucs2 strings
static int32_t strcmp(const char16_t* aStr1, const char16_t* aStr2);
/// Like strcmp except for ucs2 strings
static int32_t strncmp(const char16_t* aStr1, const char16_t* aStr2,
uint32_t aMaxLen);
// The GNU libc has memmem, which is strstr except for binary data
// This is our own implementation that uses memmem on platforms
// where it's available.
static const char* memmem(const char* aHaystack, uint32_t aHaystackLen,
const char* aNeedle, uint32_t aNeedleLen);
// String to longlong
static int64_t atoll(const char* aStr);
static char ToUpper(char aChar) { return NS_ToUpper(aChar); }
static char ToLower(char aChar) { return NS_ToLower(aChar); }
static bool IsUpper(char aChar) { return NS_IsUpper(aChar); }
static bool IsLower(char aChar) { return NS_IsLower(aChar); }
static bool IsAscii(char16_t aChar) { return NS_IsAscii(aChar); }
static bool IsAscii(const char16_t* aString) { return NS_IsAscii(aString); }
static bool IsAsciiAlpha(char16_t aChar) { return NS_IsAsciiAlpha(aChar); }
static bool IsAsciiDigit(char16_t aChar) { return NS_IsAsciiDigit(aChar); }
static bool IsAsciiSpace(char16_t aChar) { return NS_IsAsciiWhitespace(aChar); }
static bool IsAscii(const char* aString) { return NS_IsAscii(aString); }
static bool IsAscii(const char* aString, uint32_t aLength)
{
return NS_IsAscii(aString, aLength);
}
};
inline bool
NS_IS_SPACE(char16_t aChar)
{
return ((int(aChar) & 0x7f) == int(aChar)) && isspace(int(aChar));
}
#define NS_IS_CNTRL(i) ((((unsigned int) (i)) > 0x7f) ? (int) 0 : iscntrl(i))
#define NS_IS_DIGIT(i) ((((unsigned int) (i)) > 0x7f) ? (int) 0 : isdigit(i))
#if defined(XP_WIN)
#define NS_IS_ALPHA(VAL) (isascii((int)(VAL)) && isalpha((int)(VAL)))
#else
#define NS_IS_ALPHA(VAL) ((((unsigned int) (VAL)) > 0x7f) ? (int) 0 : isalpha((int)(VAL)))
#endif
#endif /* nsCRT_h___ */

View file

@ -0,0 +1,200 @@
/* -*- 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 __nsCharSeparatedTokenizer_h
#define __nsCharSeparatedTokenizer_h
#include "mozilla/RangedPtr.h"
#include "nsDependentSubstring.h"
#include "nsCRT.h"
/**
* This parses a SeparatorChar-separated string into tokens.
* Whitespace surrounding tokens is not treated as part of tokens, however
* whitespace inside a token is. If the final token is the empty string, it is
* not returned.
*
* Some examples, with SeparatorChar = ',':
*
* "foo, bar, baz" -> "foo" "bar" "baz"
* "foo,bar,baz" -> "foo" "bar" "baz"
* "foo , bar hi , baz" -> "foo" "bar hi" "baz"
* "foo, ,bar,baz" -> "foo" "" "bar" "baz"
* "foo,,bar,baz" -> "foo" "" "bar" "baz"
* "foo,bar,baz," -> "foo" "bar" "baz"
*
* The function used for whitespace detection is a template argument.
* By default, it is NS_IsAsciiWhitespace.
*/
template<typename DependentSubstringType, bool IsWhitespace(char16_t)>
class nsTCharSeparatedTokenizer
{
typedef typename DependentSubstringType::char_type CharType;
typedef typename DependentSubstringType::substring_type SubstringType;
public:
// Flags -- only one for now. If we need more, they should be defined to
// be 1 << 1, 1 << 2, etc. (They're masks, and aFlags is a bitfield.)
enum
{
SEPARATOR_OPTIONAL = 1
};
nsTCharSeparatedTokenizer(const SubstringType& aSource,
CharType aSeparatorChar,
uint32_t aFlags = 0)
: mIter(aSource.Data(), aSource.Length())
, mEnd(aSource.Data() + aSource.Length(), aSource.Data(),
aSource.Length())
, mSeparatorChar(aSeparatorChar)
, mWhitespaceBeforeFirstToken(false)
, mWhitespaceAfterCurrentToken(false)
, mSeparatorAfterCurrentToken(false)
, mSeparatorOptional(aFlags & SEPARATOR_OPTIONAL)
{
// Skip initial whitespace
while (mIter < mEnd && IsWhitespace(*mIter)) {
mWhitespaceBeforeFirstToken = true;
++mIter;
}
}
/**
* Checks if any more tokens are available.
*/
bool hasMoreTokens() const
{
MOZ_ASSERT(mIter == mEnd || !IsWhitespace(*mIter),
"Should be at beginning of token if there is one");
return mIter < mEnd;
}
/*
* Returns true if there is whitespace prior to the first token.
*/
bool whitespaceBeforeFirstToken() const
{
return mWhitespaceBeforeFirstToken;
}
/*
* Returns true if there is a separator after the current token.
* Useful if you want to check whether the last token has a separator
* after it which may not be valid.
*/
bool separatorAfterCurrentToken() const
{
return mSeparatorAfterCurrentToken;
}
/*
* Returns true if there is any whitespace after the current token.
*/
bool whitespaceAfterCurrentToken() const
{
return mWhitespaceAfterCurrentToken;
}
/**
* Returns the next token.
*/
const DependentSubstringType nextToken()
{
mozilla::RangedPtr<const CharType> tokenStart = mIter;
mozilla::RangedPtr<const CharType> tokenEnd = mIter;
MOZ_ASSERT(mIter == mEnd || !IsWhitespace(*mIter),
"Should be at beginning of token if there is one");
// Search until we hit separator or end (or whitespace, if a separator
// isn't required -- see clause with 'break' below).
while (mIter < mEnd && *mIter != mSeparatorChar) {
// Skip to end of the current word.
while (mIter < mEnd &&
!IsWhitespace(*mIter) && *mIter != mSeparatorChar) {
++mIter;
}
tokenEnd = mIter;
// Skip whitespace after the current word.
mWhitespaceAfterCurrentToken = false;
while (mIter < mEnd && IsWhitespace(*mIter)) {
mWhitespaceAfterCurrentToken = true;
++mIter;
}
if (mSeparatorOptional) {
// We've hit (and skipped) whitespace, and that's sufficient to end
// our token, regardless of whether we've reached a SeparatorChar.
break;
} // (else, we'll keep looping until we hit mEnd or SeparatorChar)
}
mSeparatorAfterCurrentToken = (mIter != mEnd &&
*mIter == mSeparatorChar);
MOZ_ASSERT(mSeparatorOptional ||
(mSeparatorAfterCurrentToken == (mIter < mEnd)),
"If we require a separator and haven't hit the end of "
"our string, then we shouldn't have left the loop "
"unless we hit a separator");
// Skip separator (and any whitespace after it), if we're at one.
if (mSeparatorAfterCurrentToken) {
++mIter;
while (mIter < mEnd && IsWhitespace(*mIter)) {
mWhitespaceAfterCurrentToken = true;
++mIter;
}
}
return Substring(tokenStart.get(), tokenEnd.get());
}
private:
mozilla::RangedPtr<const CharType> mIter;
const mozilla::RangedPtr<const CharType> mEnd;
CharType mSeparatorChar;
bool mWhitespaceBeforeFirstToken;
bool mWhitespaceAfterCurrentToken;
bool mSeparatorAfterCurrentToken;
bool mSeparatorOptional;
};
template<bool IsWhitespace(char16_t) = NS_IsAsciiWhitespace>
class nsCharSeparatedTokenizerTemplate
: public nsTCharSeparatedTokenizer<nsDependentSubstring, IsWhitespace>
{
public:
nsCharSeparatedTokenizerTemplate(const nsSubstring& aSource,
char16_t aSeparatorChar,
uint32_t aFlags = 0)
: nsTCharSeparatedTokenizer<nsDependentSubstring,
IsWhitespace>(aSource, aSeparatorChar, aFlags)
{
}
};
typedef nsCharSeparatedTokenizerTemplate<> nsCharSeparatedTokenizer;
template<bool IsWhitespace(char16_t) = NS_IsAsciiWhitespace>
class nsCCharSeparatedTokenizerTemplate
: public nsTCharSeparatedTokenizer<nsDependentCSubstring, IsWhitespace>
{
public:
nsCCharSeparatedTokenizerTemplate(const nsCSubstring& aSource,
char aSeparatorChar,
uint32_t aFlags = 0)
: nsTCharSeparatedTokenizer<nsDependentCSubstring,
IsWhitespace>(aSource, aSeparatorChar, aFlags)
{
}
};
typedef nsCCharSeparatedTokenizerTemplate<> nsCCharSeparatedTokenizer;
#endif /* __nsCharSeparatedTokenizer_h */

171
xpcom/ds/nsCheapSets.h Normal file
View file

@ -0,0 +1,171 @@
/* -*- 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 __nsCheapSets_h__
#define __nsCheapSets_h__
#include "nsTHashtable.h"
#include <stdint.h>
enum nsCheapSetOperator
{
OpNext = 0, // enumerator says continue
OpRemove = 1, // enumerator says remove and continue
};
/**
* A set that takes up minimal size when there are 0 or 1 entries in the set.
* Use for cases where sizes of 0 and 1 are even slightly common.
*/
template<typename EntryType>
class nsCheapSet
{
public:
typedef typename EntryType::KeyType KeyType;
typedef nsCheapSetOperator (*Enumerator)(EntryType* aEntry, void* userArg);
nsCheapSet() : mState(ZERO) {}
~nsCheapSet() { Clear(); }
/**
* Remove all entries.
*/
void Clear()
{
switch (mState) {
case ZERO:
break;
case ONE:
GetSingleEntry()->~EntryType();
break;
case MANY:
delete mUnion.table;
break;
default:
NS_NOTREACHED("bogus state");
break;
}
mState = ZERO;
}
void Put(const KeyType aVal);
void Remove(const KeyType aVal);
bool Contains(const KeyType aVal)
{
switch (mState) {
case ZERO:
return false;
case ONE:
return GetSingleEntry()->KeyEquals(EntryType::KeyToPointer(aVal));
case MANY:
return !!mUnion.table->GetEntry(aVal);
default:
NS_NOTREACHED("bogus state");
return false;
}
}
uint32_t EnumerateEntries(Enumerator aEnumFunc, void* aUserArg)
{
switch (mState) {
case ZERO:
return 0;
case ONE:
if (aEnumFunc(GetSingleEntry(), aUserArg) == OpRemove) {
GetSingleEntry()->~EntryType();
mState = ZERO;
}
return 1;
case MANY: {
uint32_t n = mUnion.table->Count();
for (auto iter = mUnion.table->Iter(); !iter.Done(); iter.Next()) {
auto entry = static_cast<EntryType*>(iter.Get());
if (aEnumFunc(entry, aUserArg) == OpRemove) {
iter.Remove();
}
}
return n;
}
default:
NS_NOTREACHED("bogus state");
return 0;
}
}
private:
EntryType* GetSingleEntry()
{
return reinterpret_cast<EntryType*>(&mUnion.singleEntry[0]);
}
enum SetState
{
ZERO,
ONE,
MANY
};
union
{
nsTHashtable<EntryType>* table;
char singleEntry[sizeof(EntryType)];
} mUnion;
enum SetState mState;
};
template<typename EntryType>
void
nsCheapSet<EntryType>::Put(const KeyType aVal)
{
switch (mState) {
case ZERO:
new (GetSingleEntry()) EntryType(EntryType::KeyToPointer(aVal));
mState = ONE;
return;
case ONE: {
nsTHashtable<EntryType>* table = new nsTHashtable<EntryType>();
EntryType* entry = GetSingleEntry();
table->PutEntry(entry->GetKey());
entry->~EntryType();
mUnion.table = table;
mState = MANY;
}
MOZ_FALLTHROUGH;
case MANY:
mUnion.table->PutEntry(aVal);
return;
default:
NS_NOTREACHED("bogus state");
return;
}
}
template<typename EntryType>
void
nsCheapSet<EntryType>::Remove(const KeyType aVal)
{
switch (mState) {
case ZERO:
break;
case ONE:
if (Contains(aVal)) {
GetSingleEntry()->~EntryType();
mState = ZERO;
}
break;
case MANY:
mUnion.table->RemoveEntry(aVal);
break;
default:
NS_NOTREACHED("bogus state");
break;
}
}
#endif

View file

@ -0,0 +1,570 @@
/* -*- 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 NSEXPIRATIONTRACKER_H_
#define NSEXPIRATIONTRACKER_H_
#include "mozilla/Logging.h"
#include "nsTArray.h"
#include "nsITimer.h"
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "nsComponentManagerUtils.h"
#include "nsIEventTarget.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsThreadUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/Services.h"
/**
* Data used to track the expiration state of an object. We promise that this
* is 32 bits so that objects that includes this as a field can pad and align
* efficiently.
*/
struct nsExpirationState
{
enum
{
NOT_TRACKED = (1U << 4) - 1,
MAX_INDEX_IN_GENERATION = (1U << 28) - 1
};
nsExpirationState() : mGeneration(NOT_TRACKED) {}
bool IsTracked() { return mGeneration != NOT_TRACKED; }
/**
* The generation that this object belongs to, or NOT_TRACKED.
*/
uint32_t mGeneration:4;
uint32_t mIndexInGeneration:28;
};
/**
* ExpirationTracker classes:
* - ExpirationTrackerImpl (Thread-safe class)
* - nsExpirationTracker (Main-thread only class)
*
* These classes can track the lifetimes and usage of a large number of
* objects, and send a notification some window of time after a live object was
* last used. This is very useful when you manage a large number of objects
* and want to flush some after they haven't been used for a while.
* nsExpirationTracker is designed to be very space and time efficient.
*
* The type parameter T is the object type that we will track pointers to. T
* must include an accessible method GetExpirationState() that returns a
* pointer to an nsExpirationState associated with the object (preferably,
* stored in a field of the object).
*
* The parameter K is the number of generations that will be used. Increasing
* the number of generations narrows the window within which we promise
* to fire notifications, at a slight increase in space cost for the tracker.
* We require 2 <= K <= nsExpirationState::NOT_TRACKED (currently 15).
*
* To use this class, you need to inherit from it and override the
* NotifyExpired() method.
*
* The approach is to track objects in K generations. When an object is accessed
* it moves from its current generation to the newest generation. Generations
* are stored in a cyclic array; when a timer interrupt fires, we advance
* the current generation pointer to effectively age all objects very efficiently.
* By storing information in each object about its generation and index within its
* generation array, we make removal of objects from a generation very cheap.
*
* Future work:
* -- Add a method to change the timer period?
*/
/**
* Base class for ExiprationTracker implementations.
*
* nsExpirationTracker class below is a specialized class to be inherited by the
* instances to be accessed only on main-thread.
*
* For creating a thread-safe tracker, you can define a subclass inheriting this
* base class and specialize the Mutex and AutoLock to be used.
*
*/
template<typename T, uint32_t K, typename Mutex, typename AutoLock>
class ExpirationTrackerImpl
{
public:
/**
* Initialize the tracker.
* @param aTimerPeriod the timer period in milliseconds. The guarantees
* provided by the tracker are defined in terms of this period. If the
* period is zero, then we don't use a timer and rely on someone calling
* AgeOneGenerationLocked explicitly.
*/
ExpirationTrackerImpl(uint32_t aTimerPeriod, const char* aName)
: mTimerPeriod(aTimerPeriod)
, mNewestGeneration(0)
, mInAgeOneGeneration(false)
, mName(aName)
{
static_assert(K >= 2 && K <= nsExpirationState::NOT_TRACKED,
"Unsupported number of generations (must be 2 <= K <= 15)");
MOZ_ASSERT(NS_IsMainThread());
mObserver = new ExpirationTrackerObserver();
mObserver->Init(this);
}
virtual ~ExpirationTrackerImpl()
{
MOZ_ASSERT(NS_IsMainThread());
if (mTimer) {
mTimer->Cancel();
}
mObserver->Destroy();
}
/**
* Add an object to be tracked. It must not already be tracked. It will
* be added to the newest generation, i.e., as if it was just used.
* @return an error on out-of-memory
*/
nsresult AddObjectLocked(T* aObj, const AutoLock& aAutoLock)
{
nsExpirationState* state = aObj->GetExpirationState();
NS_ASSERTION(!state->IsTracked(),
"Tried to add an object that's already tracked");
nsTArray<T*>& generation = mGenerations[mNewestGeneration];
uint32_t index = generation.Length();
if (index > nsExpirationState::MAX_INDEX_IN_GENERATION) {
NS_WARNING("More than 256M elements tracked, this is probably a problem");
return NS_ERROR_OUT_OF_MEMORY;
}
if (index == 0) {
// We might need to start the timer
nsresult rv = CheckStartTimerLocked(aAutoLock);
if (NS_FAILED(rv)) {
return rv;
}
}
if (!generation.AppendElement(aObj)) {
return NS_ERROR_OUT_OF_MEMORY;
}
state->mGeneration = mNewestGeneration;
state->mIndexInGeneration = index;
return NS_OK;
}
/**
* Remove an object from the tracker. It must currently be tracked.
*/
void RemoveObjectLocked(T* aObj, const AutoLock& aAutoLock)
{
nsExpirationState* state = aObj->GetExpirationState();
NS_ASSERTION(state->IsTracked(), "Tried to remove an object that's not tracked");
nsTArray<T*>& generation = mGenerations[state->mGeneration];
uint32_t index = state->mIndexInGeneration;
NS_ASSERTION(generation.Length() > index &&
generation[index] == aObj, "Object is lying about its index");
// Move the last object to fill the hole created by removing aObj
uint32_t last = generation.Length() - 1;
T* lastObj = generation[last];
generation[index] = lastObj;
lastObj->GetExpirationState()->mIndexInGeneration = index;
generation.RemoveElementAt(last);
state->mGeneration = nsExpirationState::NOT_TRACKED;
// We do not check whether we need to stop the timer here. The timer
// will check that itself next time it fires. Checking here would not
// be efficient since we'd need to track all generations. Also we could
// thrash by incessantly creating and destroying timers if someone
// kept adding and removing an object from the tracker.
}
/**
* Notify that an object has been used.
* @return an error if we lost the object from the tracker...
*/
nsresult MarkUsedLocked(T* aObj, const AutoLock& aAutoLock)
{
nsExpirationState* state = aObj->GetExpirationState();
if (mNewestGeneration == state->mGeneration) {
return NS_OK;
}
RemoveObjectLocked(aObj, aAutoLock);
return AddObjectLocked(aObj, aAutoLock);
}
/**
* The timer calls this, but it can also be manually called if you want
* to age objects "artifically". This can result in calls to NotifyExpiredLocked.
*/
void AgeOneGenerationLocked(const AutoLock& aAutoLock)
{
if (mInAgeOneGeneration) {
NS_WARNING("Can't reenter AgeOneGeneration from NotifyExpired");
return;
}
mInAgeOneGeneration = true;
uint32_t reapGeneration =
mNewestGeneration > 0 ? mNewestGeneration - 1 : K - 1;
nsTArray<T*>& generation = mGenerations[reapGeneration];
// The following is rather tricky. We have to cope with objects being
// removed from this generation either because of a call to RemoveObject
// (or indirectly via MarkUsedLocked) inside NotifyExpiredLocked. Fortunately
// no objects can be added to this generation because it's not the newest
// generation. We depend on the fact that RemoveObject can only cause
// the indexes of objects in this generation to *decrease*, not increase.
// So if we start from the end and work our way backwards we are guaranteed
// to see each object at least once.
size_t index = generation.Length();
for (;;) {
// Objects could have been removed so index could be outside
// the array
index = XPCOM_MIN(index, generation.Length());
if (index == 0) {
break;
}
--index;
NotifyExpiredLocked(generation[index], aAutoLock);
}
// Any leftover objects from reapGeneration just end up in the new
// newest-generation. This is bad form, though, so warn if there are any.
if (!generation.IsEmpty()) {
NS_WARNING("Expired objects were not removed or marked used");
}
// Free excess memory used by the generation array, since we probably
// just removed most or all of its elements.
generation.Compact();
mNewestGeneration = reapGeneration;
mInAgeOneGeneration = false;
}
/**
* This just calls AgeOneGenerationLocked K times. Under normal circumstances
* this will result in all objects getting NotifyExpiredLocked called on them,
* but if NotifyExpiredLocked itself marks some objects as used, then those
* objects might not expire. This would be a good thing to call if we get into
* a critically-low memory situation.
*/
void AgeAllGenerationsLocked(const AutoLock& aAutoLock)
{
uint32_t i;
for (i = 0; i < K; ++i) {
AgeOneGenerationLocked(aAutoLock);
}
}
class Iterator
{
private:
ExpirationTrackerImpl<T, K, Mutex, AutoLock>* mTracker;
uint32_t mGeneration;
uint32_t mIndex;
public:
Iterator(ExpirationTrackerImpl<T, K, Mutex, AutoLock>* aTracker,
AutoLock& aAutoLock)
: mTracker(aTracker)
, mGeneration(0)
, mIndex(0)
{
}
T* Next()
{
while (mGeneration < K) {
nsTArray<T*>* generation = &mTracker->mGenerations[mGeneration];
if (mIndex < generation->Length()) {
++mIndex;
return (*generation)[mIndex - 1];
}
++mGeneration;
mIndex = 0;
}
return nullptr;
}
};
friend class Iterator;
bool IsEmptyLocked(const AutoLock& aAutoLock)
{
for (uint32_t i = 0; i < K; ++i) {
if (!mGenerations[i].IsEmpty()) {
return false;
}
}
return true;
}
protected:
/**
* This must be overridden to catch notifications. It is called whenever
* we detect that an object has not been used for at least (K-1)*mTimerPeriod
* milliseconds. If timer events are not delayed, it will be called within
* roughly K*mTimerPeriod milliseconds after the last use.
* (Unless AgeOneGenerationLocked or AgeAllGenerationsLocked have been called
* to accelerate the aging process.)
*
* NOTE: These bounds ignore delays in timer firings due to actual work being
* performed by the browser. We use a slack timer so there is always at least
* mTimerPeriod milliseconds between firings, which gives us (K-1)*mTimerPeriod
* as a pretty solid lower bound. The upper bound is rather loose, however.
* If the maximum amount by which any given timer firing is delayed is D, then
* the upper bound before NotifyExpiredLocked is called is K*(mTimerPeriod + D).
*
* The NotifyExpiredLocked call is expected to remove the object from the tracker,
* but it need not. The object (or other objects) could be "resurrected"
* by calling MarkUsedLocked() on them, or they might just not be removed.
* Any objects left over that have not been resurrected or removed
* are placed in the new newest-generation, but this is considered "bad form"
* and should be avoided (we'll issue a warning). (This recycling counts
* as "a use" for the purposes of the expiry guarantee above...)
*
* For robustness and simplicity, we allow objects to be notified more than
* once here in the same timer tick.
*/
virtual void NotifyExpiredLocked(T*, const AutoLock&) = 0;
virtual Mutex& GetMutex() = 0;
private:
class ExpirationTrackerObserver;
RefPtr<ExpirationTrackerObserver> mObserver;
nsTArray<T*> mGenerations[K];
nsCOMPtr<nsITimer> mTimer;
uint32_t mTimerPeriod;
uint32_t mNewestGeneration;
bool mInAgeOneGeneration;
const char* const mName; // Used for timer firing profiling.
/**
* Whenever "memory-pressure" is observed, it calls AgeAllGenerationsLocked()
* to minimize memory usage.
*/
class ExpirationTrackerObserver final : public nsIObserver
{
public:
void Init(ExpirationTrackerImpl<T, K, Mutex, AutoLock>* aObj)
{
mOwner = aObj;
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->AddObserver(this, "memory-pressure", false);
}
}
void Destroy()
{
mOwner = nullptr;
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->RemoveObserver(this, "memory-pressure");
}
}
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
private:
ExpirationTrackerImpl<T, K, Mutex, AutoLock>* mOwner;
};
void HandleLowMemory() {
AutoLock lock(GetMutex());
AgeAllGenerationsLocked(lock);
}
void HandleTimeout() {
AutoLock lock(GetMutex());
AgeOneGenerationLocked(lock);
// Cancel the timer if we have no objects to track
if (IsEmptyLocked(lock)) {
mTimer->Cancel();
mTimer = nullptr;
}
}
static void TimerCallback(nsITimer* aTimer, void* aThis)
{
ExpirationTrackerImpl* tracker = static_cast<ExpirationTrackerImpl*>(aThis);
tracker->HandleTimeout();
}
nsresult CheckStartTimerLocked(const AutoLock& aAutoLock)
{
if (mTimer || !mTimerPeriod) {
return NS_OK;
}
mTimer = do_CreateInstance("@mozilla.org/timer;1");
if (!mTimer) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (!NS_IsMainThread()) {
// TimerCallback should always be run on the main thread to prevent races
// to the destruction of the tracker.
nsCOMPtr<nsIEventTarget> target = do_GetMainThread();
NS_ENSURE_STATE(target);
mTimer->SetTarget(target);
}
mTimer->InitWithNamedFuncCallback(TimerCallback, this, mTimerPeriod,
nsITimer::TYPE_REPEATING_SLACK, mName);
return NS_OK;
}
};
namespace detail {
class PlaceholderLock {
public:
void Lock() {}
void Unlock() {}
};
class PlaceholderAutoLock {
public:
explicit PlaceholderAutoLock(PlaceholderLock&) { }
~PlaceholderAutoLock() = default;
};
template<typename T, uint32_t K>
using SingleThreadedExpirationTracker =
ExpirationTrackerImpl<T, K, PlaceholderLock, PlaceholderAutoLock>;
} // namespace detail
template<typename T, uint32_t K>
class nsExpirationTracker : protected ::detail::SingleThreadedExpirationTracker<T, K>
{
typedef ::detail::PlaceholderLock Lock;
typedef ::detail::PlaceholderAutoLock AutoLock;
Lock mLock;
AutoLock FakeLock() {
return AutoLock(mLock);
}
Lock& GetMutex() override
{
return mLock;
}
void NotifyExpiredLocked(T* aObject, const AutoLock&) override
{
NotifyExpired(aObject);
}
protected:
virtual void NotifyExpired(T* aObj) = 0;
public:
nsExpirationTracker(uint32_t aTimerPeriod, const char* aName)
: ::detail::SingleThreadedExpirationTracker<T, K>(aTimerPeriod, aName)
{ }
virtual ~nsExpirationTracker()
{ }
nsresult AddObject(T* aObj)
{
return this->AddObjectLocked(aObj, FakeLock());
}
void RemoveObject(T* aObj)
{
this->RemoveObjectLocked(aObj, FakeLock());
}
nsresult MarkUsed(T* aObj)
{
return this->MarkUsedLocked(aObj, FakeLock());
}
void AgeOneGeneration()
{
this->AgeOneGenerationLocked(FakeLock());
}
void AgeAllGenerations()
{
this->AgeAllGenerationsLocked(FakeLock());
}
class Iterator
{
private:
AutoLock mAutoLock;
typename ExpirationTrackerImpl<T, K, Lock, AutoLock>::Iterator mIterator;
public:
explicit Iterator(nsExpirationTracker<T, K>* aTracker)
: mAutoLock(aTracker->GetMutex())
, mIterator(aTracker, mAutoLock)
{
}
T* Next()
{
return mIterator.Next();
}
};
friend class Iterator;
bool IsEmpty()
{
return this->IsEmptyLocked(FakeLock());
}
};
template<typename T, uint32_t K, typename Mutex, typename AutoLock>
NS_IMETHODIMP
ExpirationTrackerImpl<T, K, Mutex, AutoLock>::
ExpirationTrackerObserver::Observe(
nsISupports* aSubject, const char* aTopic, const char16_t* aData)
{
if (!strcmp(aTopic, "memory-pressure") && mOwner) {
mOwner->HandleLowMemory();
}
return NS_OK;
}
template<class T, uint32_t K, typename Mutex, typename AutoLock>
NS_IMETHODIMP_(MozExternalRefCountType)
ExpirationTrackerImpl<T, K, Mutex, AutoLock>::
ExpirationTrackerObserver::AddRef(void)
{
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
NS_ASSERT_OWNINGTHREAD(ExpirationTrackerObserver);
++mRefCnt;
NS_LOG_ADDREF(this, mRefCnt, "ExpirationTrackerObserver", sizeof(*this));
return mRefCnt;
}
template<class T, uint32_t K, typename Mutex, typename AutoLock>
NS_IMETHODIMP_(MozExternalRefCountType)
ExpirationTrackerImpl<T, K, Mutex, AutoLock>::
ExpirationTrackerObserver::Release(void)
{
MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
NS_ASSERT_OWNINGTHREAD(ExpirationTrackerObserver);
--mRefCnt;
NS_LOG_RELEASE(this, mRefCnt, "ExpirationTrackerObserver");
if (mRefCnt == 0) {
NS_ASSERT_OWNINGTHREAD(ExpirationTrackerObserver);
mRefCnt = 1; /* stabilize */
delete (this);
return 0;
}
return mRefCnt;
}
template<class T, uint32_t K, typename Mutex, typename AutoLock>
NS_IMETHODIMP
ExpirationTrackerImpl<T, K, Mutex, AutoLock>::
ExpirationTrackerObserver::QueryInterface(
REFNSIID aIID, void** aInstancePtr)
{
NS_ASSERTION(aInstancePtr,
"QueryInterface requires a non-NULL destination!");
nsresult rv = NS_ERROR_FAILURE;
NS_INTERFACE_TABLE(ExpirationTrackerObserver, nsIObserver)
return rv;
}
#endif /*NSEXPIRATIONTRACKER_H_*/

View file

@ -0,0 +1,284 @@
/* -*- 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 "nsHashPropertyBag.h"
#include "nsArray.h"
#include "nsArrayEnumerator.h"
#include "nsIVariant.h"
#include "nsIProperty.h"
#include "nsVariant.h"
#include "mozilla/Attributes.h"
/*
* nsHashPropertyBagBase implementation.
*/
NS_IMETHODIMP
nsHashPropertyBagBase::HasKey(const nsAString& aName, bool* aResult)
{
*aResult = mPropertyHash.Get(aName, nullptr);
return NS_OK;
}
NS_IMETHODIMP
nsHashPropertyBagBase::Get(const nsAString& aName, nsIVariant** aResult)
{
if (!mPropertyHash.Get(aName, aResult)) {
*aResult = nullptr;
}
return NS_OK;
}
NS_IMETHODIMP
nsHashPropertyBagBase::GetProperty(const nsAString& aName, nsIVariant** aResult)
{
bool isFound = mPropertyHash.Get(aName, aResult);
if (!isFound) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
nsHashPropertyBagBase::SetProperty(const nsAString& aName, nsIVariant* aValue)
{
if (NS_WARN_IF(!aValue)) {
return NS_ERROR_INVALID_ARG;
}
mPropertyHash.Put(aName, aValue);
return NS_OK;
}
NS_IMETHODIMP
nsHashPropertyBagBase::DeleteProperty(const nsAString& aName)
{
// is it too much to ask for ns*Hashtable to return
// a boolean indicating whether RemoveEntry succeeded
// or not?!?!
bool isFound = mPropertyHash.Get(aName, nullptr);
if (!isFound) {
return NS_ERROR_FAILURE;
}
// then from the hash
mPropertyHash.Remove(aName);
return NS_OK;
}
//
// nsSimpleProperty class and impl; used for GetEnumerator
//
class nsSimpleProperty final : public nsIProperty
{
~nsSimpleProperty() {}
public:
nsSimpleProperty(const nsAString& aName, nsIVariant* aValue)
: mName(aName)
, mValue(aValue)
{
}
NS_DECL_ISUPPORTS
NS_DECL_NSIPROPERTY
protected:
nsString mName;
nsCOMPtr<nsIVariant> mValue;
};
NS_IMPL_ISUPPORTS(nsSimpleProperty, nsIProperty)
NS_IMETHODIMP
nsSimpleProperty::GetName(nsAString& aName)
{
aName.Assign(mName);
return NS_OK;
}
NS_IMETHODIMP
nsSimpleProperty::GetValue(nsIVariant** aValue)
{
NS_IF_ADDREF(*aValue = mValue);
return NS_OK;
}
// end nsSimpleProperty
NS_IMETHODIMP
nsHashPropertyBagBase::GetEnumerator(nsISimpleEnumerator** aResult)
{
nsCOMPtr<nsIMutableArray> propertyArray = nsArray::Create();
if (!propertyArray) {
return NS_ERROR_OUT_OF_MEMORY;
}
for (auto iter = mPropertyHash.Iter(); !iter.Done(); iter.Next()) {
const nsAString& key = iter.Key();
nsIVariant* data = iter.UserData();
nsSimpleProperty* sprop = new nsSimpleProperty(key, data);
propertyArray->AppendElement(sprop, false);
}
return NS_NewArrayEnumerator(aResult, propertyArray);
}
#define IMPL_GETSETPROPERTY_AS(Name, Type) \
NS_IMETHODIMP \
nsHashPropertyBagBase::GetPropertyAs ## Name (const nsAString & prop, Type *_retval) \
{ \
nsIVariant* v = mPropertyHash.GetWeak(prop); \
if (!v) \
return NS_ERROR_NOT_AVAILABLE; \
return v->GetAs ## Name(_retval); \
} \
\
NS_IMETHODIMP \
nsHashPropertyBagBase::SetPropertyAs ## Name (const nsAString & prop, Type value) \
{ \
nsCOMPtr<nsIWritableVariant> var = new nsVariant(); \
var->SetAs ## Name(value); \
return SetProperty(prop, var); \
}
IMPL_GETSETPROPERTY_AS(Int32, int32_t)
IMPL_GETSETPROPERTY_AS(Uint32, uint32_t)
IMPL_GETSETPROPERTY_AS(Int64, int64_t)
IMPL_GETSETPROPERTY_AS(Uint64, uint64_t)
IMPL_GETSETPROPERTY_AS(Double, double)
IMPL_GETSETPROPERTY_AS(Bool, bool)
NS_IMETHODIMP
nsHashPropertyBagBase::GetPropertyAsAString(const nsAString& aProp,
nsAString& aResult)
{
nsIVariant* v = mPropertyHash.GetWeak(aProp);
if (!v) {
return NS_ERROR_NOT_AVAILABLE;
}
return v->GetAsAString(aResult);
}
NS_IMETHODIMP
nsHashPropertyBagBase::GetPropertyAsACString(const nsAString& aProp,
nsACString& aResult)
{
nsIVariant* v = mPropertyHash.GetWeak(aProp);
if (!v) {
return NS_ERROR_NOT_AVAILABLE;
}
return v->GetAsACString(aResult);
}
NS_IMETHODIMP
nsHashPropertyBagBase::GetPropertyAsAUTF8String(const nsAString& aProp,
nsACString& aResult)
{
nsIVariant* v = mPropertyHash.GetWeak(aProp);
if (!v) {
return NS_ERROR_NOT_AVAILABLE;
}
return v->GetAsAUTF8String(aResult);
}
NS_IMETHODIMP
nsHashPropertyBagBase::GetPropertyAsInterface(const nsAString& aProp,
const nsIID& aIID,
void** aResult)
{
nsIVariant* v = mPropertyHash.GetWeak(aProp);
if (!v) {
return NS_ERROR_NOT_AVAILABLE;
}
nsCOMPtr<nsISupports> val;
nsresult rv = v->GetAsISupports(getter_AddRefs(val));
if (NS_FAILED(rv)) {
return rv;
}
if (!val) {
// We have a value, but it's null
*aResult = nullptr;
return NS_OK;
}
return val->QueryInterface(aIID, aResult);
}
NS_IMETHODIMP
nsHashPropertyBagBase::SetPropertyAsAString(const nsAString& aProp,
const nsAString& aValue)
{
nsCOMPtr<nsIWritableVariant> var = new nsVariant();
var->SetAsAString(aValue);
return SetProperty(aProp, var);
}
NS_IMETHODIMP
nsHashPropertyBagBase::SetPropertyAsACString(const nsAString& aProp,
const nsACString& aValue)
{
nsCOMPtr<nsIWritableVariant> var = new nsVariant();
var->SetAsACString(aValue);
return SetProperty(aProp, var);
}
NS_IMETHODIMP
nsHashPropertyBagBase::SetPropertyAsAUTF8String(const nsAString& aProp,
const nsACString& aValue)
{
nsCOMPtr<nsIWritableVariant> var = new nsVariant();
var->SetAsAUTF8String(aValue);
return SetProperty(aProp, var);
}
NS_IMETHODIMP
nsHashPropertyBagBase::SetPropertyAsInterface(const nsAString& aProp,
nsISupports* aValue)
{
nsCOMPtr<nsIWritableVariant> var = new nsVariant();
var->SetAsISupports(aValue);
return SetProperty(aProp, var);
}
/*
* nsHashPropertyBag implementation.
*/
NS_IMPL_ADDREF(nsHashPropertyBag)
NS_IMPL_RELEASE(nsHashPropertyBag)
NS_INTERFACE_MAP_BEGIN(nsHashPropertyBag)
NS_INTERFACE_MAP_ENTRY(nsIWritablePropertyBag)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIPropertyBag, nsIWritablePropertyBag)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWritablePropertyBag)
NS_INTERFACE_MAP_ENTRY(nsIPropertyBag2)
NS_INTERFACE_MAP_ENTRY(nsIWritablePropertyBag2)
NS_INTERFACE_MAP_END
/*
* nsHashPropertyBagCC implementation.
*/
NS_IMPL_CYCLE_COLLECTION(nsHashPropertyBagCC, mPropertyHash)
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHashPropertyBagCC)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHashPropertyBagCC)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsHashPropertyBagCC)
NS_INTERFACE_MAP_ENTRY(nsIWritablePropertyBag)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIPropertyBag, nsIWritablePropertyBag)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWritablePropertyBag)
NS_INTERFACE_MAP_ENTRY(nsIPropertyBag2)
NS_INTERFACE_MAP_ENTRY(nsIWritablePropertyBag2)
NS_INTERFACE_MAP_END

View file

@ -0,0 +1,57 @@
/* -*- 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 nsHashPropertyBag_h___
#define nsHashPropertyBag_h___
#include "nsIVariant.h"
#include "nsIWritablePropertyBag.h"
#include "nsIWritablePropertyBag2.h"
#include "nsCycleCollectionParticipant.h"
#include "nsInterfaceHashtable.h"
class nsHashPropertyBagBase
: public nsIWritablePropertyBag
, public nsIWritablePropertyBag2
{
public:
nsHashPropertyBagBase() {}
NS_DECL_NSIPROPERTYBAG
NS_DECL_NSIPROPERTYBAG2
NS_DECL_NSIWRITABLEPROPERTYBAG
NS_DECL_NSIWRITABLEPROPERTYBAG2
protected:
// a hash table of string -> nsIVariant
nsInterfaceHashtable<nsStringHashKey, nsIVariant> mPropertyHash;
};
class nsHashPropertyBag : public nsHashPropertyBagBase
{
public:
nsHashPropertyBag() {}
NS_DECL_THREADSAFE_ISUPPORTS
protected:
virtual ~nsHashPropertyBag() {}
};
/* A cycle collected nsHashPropertyBag for main-thread-only use. */
class nsHashPropertyBagCC final : public nsHashPropertyBagBase
{
public:
nsHashPropertyBagCC() {}
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsHashPropertyBagCC,
nsIWritablePropertyBag)
protected:
virtual ~nsHashPropertyBagCC() {}
};
#endif /* nsHashPropertyBag_h___ */

91
xpcom/ds/nsIArray.idl Normal file
View file

@ -0,0 +1,91 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface nsISimpleEnumerator;
/**
* nsIArray
*
* An indexed collection of elements. Provides basic functionality for
* retrieving elements at a specific position, searching for
* elements. Indexes are zero-based, such that the last element in the
* array is stored at the index length-1.
*
* For an array which can be modified, see nsIMutableArray below.
*
* Neither interface makes any attempt to protect the individual
* elements from modification. The convention is that the elements of
* the array should not be modified. Documentation within a specific
* interface should describe variations from this convention.
*
* It is also convention that if an interface provides access to an
* nsIArray, that the array should not be QueryInterfaced to an
* nsIMutableArray for modification. If the interface in question had
* intended the array to be modified, it would have returned an
* nsIMutableArray!
*
* null is a valid entry in the array, and as such any nsISupports
* parameters may be null, except where noted.
*/
[scriptable, uuid(114744d9-c369-456e-b55a-52fe52880d2d)]
interface nsIArray : nsISupports
{
/**
* length
*
* number of elements in the array.
*/
readonly attribute unsigned long length;
/**
* queryElementAt()
*
* Retrieve a specific element of the array, and QueryInterface it
* to the specified interface. null is a valid result for
* this method, but exceptions are thrown in other circumstances
*
* @param index position of element
* @param uuid the IID of the requested interface
* @param result the object, QI'd to the requested interface
*
* @throws NS_ERROR_NO_INTERFACE when an entry exists at the
* specified index, but the requested interface is not
* available.
* @throws NS_ERROR_ILLEGAL_VALUE when index > length-1
*
*/
void queryElementAt(in unsigned long index,
in nsIIDRef uuid,
[iid_is(uuid), retval] out nsQIResult result);
/**
* indexOf()
*
* Get the position of a specific element. Note that since null is
* a valid input, exceptions are used to indicate that an element
* is not found.
*
* @param startIndex The initial element to search in the array
* To start at the beginning, use 0 as the
* startIndex
* @param element The element you are looking for
* @returns a number >= startIndex which is the position of the
* element in the array.
* @throws NS_ERROR_FAILURE if the element was not in the array.
*/
unsigned long indexOf(in unsigned long startIndex,
in nsISupports element);
/**
* enumerate the array
*
* @returns a new enumerator positioned at the start of the array
* @throws NS_ERROR_FAILURE if the array is empty (to make it easy
* to detect errors), or NS_ERROR_OUT_OF_MEMORY if out of memory.
*/
nsISimpleEnumerator enumerate();
};

View file

@ -0,0 +1,51 @@
/* -*- 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 "nsIArray.idl"
/**
* Helper interface for allowing scripts to treat nsIArray instances as if
* they were nsISupportsArray instances while iterating.
*
* nsISupportsArray is convenient to iterate over in JavaScript:
*
* for (let i = 0; i < array.Count(); ++i) {
* let elem = array.GetElementAt(i);
* ...
* }
*
* but doing the same with nsIArray is somewhat less convenient, since
* queryElementAt is not nearly so nice to use from JavaScript. So we provide
* this extension interface so interfaces that currently return
* nsISupportsArray can start returning nsIArrayExtensions and all JavaScript
* should Just Work. Eventually we'll roll this interface into nsIArray
* itself, possibly getting rid of the Count() method, as it duplicates
* nsIArray functionality.
*/
[scriptable, uuid(261d442e-050c-453d-8aaa-b3f23bcc528b)]
interface nsIArrayExtensions : nsIArray
{
/**
* Count()
*
* Retrieves the length of the array. This is an alias for the
* |nsIArray.length| attribute.
*/
uint32_t Count();
/**
* GetElementAt()
*
* Retrieve a specific element of the array. null is a valid result for
* this method.
*
* Note: If the index is out of bounds null will be returned.
* This differs from the behavior of nsIArray.queryElementAt() which
* will throw if an invalid index is specified.
*
* @param index position of element
*/
nsISupports GetElementAt(in uint32_t index);
};

166
xpcom/ds/nsIAtom.idl Normal file
View file

@ -0,0 +1,166 @@
/* -*- 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"
%{C++
#include "nsStringGlue.h"
#include "nsCOMPtr.h"
#include "nsStringBuffer.h"
%}
native MallocSizeOf(mozilla::MallocSizeOf);
/*
* Should this really be scriptable? Using atoms from script or proxies
* could be dangerous since double-wrapping could lead to loss of
* pointer identity.
*/
[scriptable, builtinclass, uuid(8b8c11d4-3ed5-4079-8974-73c7576cdb34)]
interface nsIAtom : nsISupports
{
/**
* Get the Unicode or UTF8 value for the string
*/
[binaryname(ScriptableToString)] AString toString();
[noscript] AUTF8String toUTF8String();
/**
* Compare the atom to a specific string value
* Note that this will NEVER return/throw an error condition.
*/
[binaryname(ScriptableEquals)] boolean equals(in AString aString);
[noscript, notxpcom]
size_t SizeOfIncludingThis(in MallocSizeOf aMallocSizeOf);
%{C++
// note this is NOT virtual so this won't muck with the vtable!
inline bool Equals(const nsAString& aString) const {
return aString.Equals(nsDependentString(mString, mLength));
}
inline bool IsStaticAtom() const {
return mIsStatic;
}
inline char16ptr_t GetUTF16String() const {
return mString;
}
inline uint32_t GetLength() const {
return mLength;
}
inline void ToString(nsAString& aBuf) {
// See the comment on |mString|'s declaration.
nsStringBuffer::FromData(mString)->ToString(mLength, aBuf);
}
inline nsStringBuffer* GetStringBuffer() const {
// See the comment on |mString|'s declaration.
return nsStringBuffer::FromData(mString);
}
/**
* A hashcode that is better distributed than the actual atom
* pointer, for use in situations that need a well-distributed
* hashcode.
*/
inline uint32_t hash() const {
return mHash;
}
protected:
uint32_t mLength:31;
uint32_t mIsStatic:1;
uint32_t mHash;
/**
* WARNING! There is an invisible constraint on |mString|: the chars it
* points to must belong to an nsStringBuffer. This is so that the
* nsStringBuffer::FromData() calls above are valid.
*/
char16_t* mString;
%}
};
%{C++
/*
* The four forms of NS_Atomize (for use with |nsCOMPtr<nsIAtom>|) return the
* atom for the string given. At any given time there will always be one atom
* representing a given string. Atoms are intended to make string comparison
* cheaper by simplifying it to pointer equality. A pointer to the atom that
* does not own a reference is not guaranteed to be valid.
*/
/**
* Find an atom that matches the given UTF-8 string.
* The string is assumed to be zero terminated. Never returns null.
*/
extern already_AddRefed<nsIAtom> NS_Atomize(const char* aUTF8String);
/**
* Find an atom that matches the given UTF-8 string. Never returns null.
*/
extern already_AddRefed<nsIAtom> NS_Atomize(const nsACString& aUTF8String);
/**
* Find an atom that matches the given UTF-16 string.
* The string is assumed to be zero terminated. Never returns null.
*/
extern already_AddRefed<nsIAtom> NS_Atomize(const char16_t* aUTF16String);
/**
* Find an atom that matches the given UTF-16 string. Never returns null.
*/
extern already_AddRefed<nsIAtom> NS_Atomize(const nsAString& aUTF16String);
/**
* Return a count of the total number of atoms currently
* alive in the system.
*/
extern nsrefcnt NS_GetNumberOfAtoms(void);
/**
* Return a pointer for a static atom for the string or null if there's
* no static atom for this string.
*/
extern nsIAtom* NS_GetStaticAtom(const nsAString& aUTF16String);
/**
* Seal the static atom table
*/
extern void NS_SealStaticAtomTable();
class nsAtomString : public nsString
{
public:
explicit nsAtomString(nsIAtom* aAtom)
{
aAtom->ToString(*this);
}
};
class nsAtomCString : public nsCString
{
public:
explicit nsAtomCString(nsIAtom* aAtom)
{
aAtom->ToUTF8String(*this);
}
};
class nsDependentAtomString : public nsDependentString
{
public:
explicit nsDependentAtomString(nsIAtom* aAtom)
: nsDependentString(aAtom->GetUTF16String(), aAtom->GetLength())
{
}
};
%}

View file

@ -0,0 +1,35 @@
/* -*- 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 nsIAtom;
%{C++
#define NS_ATOMSERVICE_CID \
{ /* ed3db3fc-0168-4cab-8818-98f5475a490c */ \
0xed3db3fc, \
0x0168, \
0x4cab, \
{0x88, 0x18, 0x98, 0xf5, 0x47, 0x5a, 0x49, 0x0c} }
#define NS_ATOMSERVICE_CONTRACTID "@mozilla.org/atom-service;1"
%}
/*
* Should this really be scriptable? Using atoms from script or proxies
* could be dangerous since double-wrapping could lead to loss of
* pointer identity.
*/
[scriptable, uuid(9c1f50b9-f9eb-42d4-a8cb-2c7600aeb241)]
interface nsIAtomService : nsISupports {
/**
* Version of NS_Atomize that doesn't require linking against the
* XPCOM library. See nsIAtom.idl.
*/
nsIAtom getAtom(in AString value);
};

View file

@ -0,0 +1,67 @@
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISerializable.idl"
interface nsIEnumerator;
[deprecated, scriptable, uuid(83b6019c-cbc4-11d2-8cca-0060b0fc14a3)]
interface nsICollection : nsISerializable
{
uint32_t Count();
nsISupports GetElementAt(in uint32_t index);
void QueryElementAt(in uint32_t index, in nsIIDRef uuid,
[iid_is(uuid),retval] out nsQIResult result);
void SetElementAt(in uint32_t index, in nsISupports item);
void AppendElement(in nsISupports item);
void RemoveElement(in nsISupports item);
/**
* This clashes with |nsISimpleEnumerator nsIArray.enumerate()| (only on the
* binary side), so it is renamed with a 'Deprecated' prefix in favor of the
* non-deprecated |nsIArray.enumerate|.
*/
[binaryname(DeprecatedEnumerate)]
nsIEnumerator Enumerate();
void Clear();
};
%{C++
#ifndef nsCOMPtr_h__
#include "nsCOMPtr.h"
#endif
class MOZ_STACK_CLASS nsQueryElementAt : public nsCOMPtr_helper
{
public:
nsQueryElementAt( nsICollection* aCollection, uint32_t aIndex, nsresult* aErrorPtr )
: mCollection(aCollection),
mIndex(aIndex),
mErrorPtr(aErrorPtr)
{
// nothing else to do here
}
virtual nsresult NS_FASTCALL operator()( const nsIID& aIID, void** )
const override;
private:
nsICollection* MOZ_NON_OWNING_REF mCollection;
uint32_t mIndex;
nsresult* mErrorPtr;
};
inline
const nsQueryElementAt
do_QueryElementAt( nsICollection* aCollection, uint32_t aIndex, nsresult* aErrorPtr = 0 )
{
return nsQueryElementAt(aCollection, aIndex, aErrorPtr);
}
%}

View file

@ -0,0 +1,50 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
%{C++
#define NS_ENUMERATOR_FALSE 1
%}
/*
* DO NOT USE THIS INTERFACE. IT IS HORRIBLY BROKEN, USES NS_COMFALSE
* AND IS BASICALLY IMPOSSIBLE TO USE CORRECTLY THROUGH PROXIES OR
* XPCONNECT. IF YOU SEE NEW USES OF THIS INTERFACE IN CODE YOU ARE
* REVIEWING, YOU SHOULD INSIST ON nsISimpleEnumerator.
*
* DON'T MAKE ME COME OVER THERE.
*/
[deprecated, scriptable, uuid(ad385286-cbc4-11d2-8cca-0060b0fc14a3)]
interface nsIEnumerator : nsISupports {
/** First will reset the list. will return NS_FAILED if no items
*/
void first();
/** Next will advance the list. will return failed if already at end
*/
void next();
/** CurrentItem will return the CurrentItem item it will fail if the
* list is empty
*/
nsISupports currentItem();
/** return if the collection is at the end. that is the beginning following
* a call to Prev and it is the end of the list following a call to next
*/
void isDone();
};
[deprecated, uuid(75f158a0-cadd-11d2-8cca-0060b0fc14a3)]
interface nsIBidirectionalEnumerator : nsIEnumerator {
/** Last will reset the list to the end. will return NS_FAILED if no items
*/
void last();
/** Prev will decrement the list. will return failed if already at beginning
*/
void prev();
};

24
xpcom/ds/nsIHashable.idl Normal file
View file

@ -0,0 +1,24 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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"
/**
* Represents an object that can be stored in a hashtable.
*/
[scriptable, uuid(17e595fa-b57a-4933-bd0f-b1812e8ab188)]
interface nsIHashable : nsISupports
{
/**
* Is this object the equivalent of the other object?
*/
boolean equals(in nsIHashable aOther);
/**
* A generated hashcode for this object. Objects that are equivalent
* must have the same hash code. Getting this property should never
* throw an exception!
*/
readonly attribute unsigned long hashCode;
};

58
xpcom/ds/nsIINIParser.idl Normal file
View file

@ -0,0 +1,58 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.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 nsIUTF8StringEnumerator;
interface nsIFile;
[scriptable, uuid(7eb955f6-3e78-4d39-b72f-c1bf12a94bce)]
interface nsIINIParser : nsISupports
{
/**
* Enumerates the [section]s available in the INI file.
*/
nsIUTF8StringEnumerator getSections();
/**
* Enumerates the keys available within a section.
*/
nsIUTF8StringEnumerator getKeys(in AUTF8String aSection);
/**
* Get the value of a string for a particular section and key.
*/
AUTF8String getString(in AUTF8String aSection, in AUTF8String aKey);
};
[scriptable, uuid(b67bb24b-31a3-4a6a-a5d9-0485c9af5a04)]
interface nsIINIParserWriter : nsISupports
{
/**
* Windows and the NSIS installer code sometimes expect INI files to be in
* UTF-16 encoding. On Windows only, this flag to writeFile can be used to
* change the encoding from its default UTF-8.
*/
const unsigned long WRITE_UTF16 = 0x1;
/**
* Set the value of a string for a particular section and key.
*/
void setString(in AUTF8String aSection, in AUTF8String aKey, in AUTF8String aValue);
/**
* Write to the INI file.
*/
void writeFile([optional] in nsIFile aINIFile,
[optional] in unsigned long aFlags);
};
[scriptable, uuid(ccae7ea5-1218-4b51-aecb-c2d8ecd46af9)]
interface nsIINIParserFactory : nsISupports
{
/**
* Create an iniparser instance from a local file.
*/
nsIINIParser createINIParser(in nsIFile aINIFile);
};

View file

@ -0,0 +1,110 @@
/* -*- 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 "nsIArrayExtensions.idl"
/**
* nsIMutableArray
* A separate set of methods that will act on the array. Consumers of
* nsIArray should not QueryInterface to nsIMutableArray unless they
* own the array.
*
* As above, it is legal to add null elements to the array. Note also
* that null elements can be created as a side effect of
* insertElementAt(). Conversely, if insertElementAt() is never used,
* and null elements are never explicitly added to the array, then it
* is guaranteed that queryElementAt() will never return a null value.
*
* Any of these methods may throw NS_ERROR_OUT_OF_MEMORY when the
* array must grow to complete the call, but the allocation fails.
*/
[scriptable, uuid(af059da0-c85b-40ec-af07-ae4bfdc192cc)]
interface nsIMutableArray : nsIArrayExtensions
{
/**
* appendElement()
*
* Append an element at the end of the array.
*
* @param element The element to append.
* @param weak Whether or not to store the element using a weak
* reference.
* @throws NS_ERROR_FAILURE when a weak reference is requested,
* but the element does not support
* nsIWeakReference.
*/
void appendElement(in nsISupports element, in boolean weak);
/**
* removeElementAt()
*
* Remove an element at a specific position, moving all elements
* stored at a higher position down one.
* To remove a specific element, use indexOf() to find the index
* first, then call removeElementAt().
*
* @param index the position of the item
*
*/
void removeElementAt(in unsigned long index);
/**
* insertElementAt()
*
* Insert an element at the given position, moving the element
* currently located in that position, and all elements in higher
* position, up by one.
*
* @param element The element to insert
* @param index The position in the array:
* If the position is lower than the current length
* of the array, the elements at that position and
* onwards are bumped one position up.
* If the position is equal to the current length
* of the array, the new element is appended.
* An index lower than 0 or higher than the current
* length of the array is invalid and will be ignored.
*
* @throws NS_ERROR_FAILURE when a weak reference is requested,
* but the element does not support
* nsIWeakReference.
*/
void insertElementAt(in nsISupports element, in unsigned long index,
in boolean weak);
/**
* replaceElementAt()
*
* Replace the element at the given position.
*
* @param element The new element to insert
* @param index The position in the array
* If the position is lower than the current length
* of the array, an existing element will be replaced.
* If the position is equal to the current length
* of the array, the new element is appended.
* If the position is higher than the current length
* of the array, empty elements are appended followed
* by the new element at the specified position.
* An index lower than 0 is invalid and will be ignored.
*
* @param weak Whether or not to store the new element using a weak
* reference.
*
* @throws NS_ERROR_FAILURE when a weak reference is requested,
* but the element does not support
* nsIWeakReference.
*/
void replaceElementAt(in nsISupports element, in unsigned long index,
in boolean weak);
/**
* clear()
*
* clear the entire array, releasing all stored objects
*/
void clear();
};

View file

@ -0,0 +1,142 @@
/* -*- 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 "nsINIParserImpl.h"
#include "nsINIParser.h"
#include "nsStringEnumerator.h"
#include "nsTArray.h"
#include "mozilla/Attributes.h"
class nsINIParserImpl final
: public nsIINIParser
{
~nsINIParserImpl() {}
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIINIPARSER
nsresult Init(nsIFile* aINIFile) { return mParser.Init(aINIFile); }
private:
nsINIParser mParser;
};
NS_IMPL_ISUPPORTS(nsINIParserFactory,
nsIINIParserFactory,
nsIFactory)
NS_IMETHODIMP
nsINIParserFactory::CreateINIParser(nsIFile* aINIFile,
nsIINIParser** aResult)
{
*aResult = nullptr;
RefPtr<nsINIParserImpl> p(new nsINIParserImpl());
if (!p) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsresult rv = p->Init(aINIFile);
if (NS_SUCCEEDED(rv)) {
NS_ADDREF(*aResult = p);
}
return rv;
}
NS_IMETHODIMP
nsINIParserFactory::CreateInstance(nsISupports* aOuter,
REFNSIID aIID,
void** aResult)
{
if (NS_WARN_IF(aOuter)) {
return NS_ERROR_NO_AGGREGATION;
}
// We are our own singleton.
return QueryInterface(aIID, aResult);
}
NS_IMETHODIMP
nsINIParserFactory::LockFactory(bool aLock)
{
return NS_OK;
}
NS_IMPL_ISUPPORTS(nsINIParserImpl,
nsIINIParser)
static bool
SectionCB(const char* aSection, void* aClosure)
{
nsTArray<nsCString>* strings = static_cast<nsTArray<nsCString>*>(aClosure);
strings->AppendElement()->Assign(aSection);
return true;
}
NS_IMETHODIMP
nsINIParserImpl::GetSections(nsIUTF8StringEnumerator** aResult)
{
nsTArray<nsCString>* strings = new nsTArray<nsCString>;
if (!strings) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsresult rv = mParser.GetSections(SectionCB, strings);
if (NS_SUCCEEDED(rv)) {
rv = NS_NewAdoptingUTF8StringEnumerator(aResult, strings);
}
if (NS_FAILED(rv)) {
delete strings;
}
return rv;
}
static bool
KeyCB(const char* aKey, const char* aValue, void* aClosure)
{
nsTArray<nsCString>* strings = static_cast<nsTArray<nsCString>*>(aClosure);
strings->AppendElement()->Assign(aKey);
return true;
}
NS_IMETHODIMP
nsINIParserImpl::GetKeys(const nsACString& aSection,
nsIUTF8StringEnumerator** aResult)
{
nsTArray<nsCString>* strings = new nsTArray<nsCString>;
if (!strings) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsresult rv = mParser.GetStrings(PromiseFlatCString(aSection).get(),
KeyCB, strings);
if (NS_SUCCEEDED(rv)) {
rv = NS_NewAdoptingUTF8StringEnumerator(aResult, strings);
}
if (NS_FAILED(rv)) {
delete strings;
}
return rv;
}
NS_IMETHODIMP
nsINIParserImpl::GetString(const nsACString& aSection,
const nsACString& aKey,
nsACString& aResult)
{
return mParser.GetString(PromiseFlatCString(aSection).get(),
PromiseFlatCString(aKey).get(),
aResult);
}

View file

@ -0,0 +1,33 @@
/* -*- 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 nsINIParserImpl_h__
#define nsINIParserImpl_h__
#include "nsIINIParser.h"
#include "nsIFactory.h"
#include "mozilla/Attributes.h"
#define NS_INIPARSERFACTORY_CID \
{ 0xdfac10a9, 0xdd24, 0x43cf, \
{ 0xa0, 0x95, 0x6f, 0xfa, 0x2e, 0x4b, 0x6a, 0x6c } }
#define NS_INIPARSERFACTORY_CONTRACTID \
"@mozilla.org/xpcom/ini-parser-factory;1"
class nsINIParserFactory final
: public nsIINIParserFactory
, public nsIFactory
{
~nsINIParserFactory() {}
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIINIPARSERFACTORY
NS_DECL_NSIFACTORY
};
#endif // nsINIParserImpl_h__

192
xpcom/ds/nsINIProcessor.js Normal file
View file

@ -0,0 +1,192 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
function INIProcessorFactory() {
}
INIProcessorFactory.prototype = {
classID: Components.ID("{6ec5f479-8e13-4403-b6ca-fe4c2dca14fd}"),
QueryInterface : XPCOMUtils.generateQI([Ci.nsIINIParserFactory]),
createINIParser : function (aINIFile) {
return new INIProcessor(aINIFile);
}
}; // end of INIProcessorFactory implementation
const MODE_WRONLY = 0x02;
const MODE_CREATE = 0x08;
const MODE_TRUNCATE = 0x20;
// nsIINIParser implementation
function INIProcessor(aFile) {
this._iniFile = aFile;
this._iniData = {};
this._readFile();
}
INIProcessor.prototype = {
QueryInterface : XPCOMUtils.generateQI([Ci.nsIINIParser, Ci.nsIINIParserWriter]),
__utf8Converter : null, // UCS2 <--> UTF8 string conversion
get _utf8Converter() {
if (!this.__utf8Converter) {
this.__utf8Converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Ci.nsIScriptableUnicodeConverter);
this.__utf8Converter.charset = "UTF-8";
}
return this.__utf8Converter;
},
__utf16leConverter : null, // UCS2 <--> UTF16LE string conversion
get _utf16leConverter() {
if (!this.__utf16leConverter) {
this.__utf16leConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Ci.nsIScriptableUnicodeConverter);
this.__utf16leConverter.charset = "UTF-16LE";
}
return this.__utf16leConverter;
},
_utfConverterReset : function() {
this.__utf8Converter = null;
this.__utf16leConverter = null;
},
_iniFile : null,
_iniData : null,
/*
* Reads the INI file and stores the data internally.
*/
_readFile : function() {
// If file doesn't exist, there's nothing to do.
if (!this._iniFile.exists() || 0 == this._iniFile.fileSize)
return;
let iniParser = Cc["@mozilla.org/xpcom/ini-parser-factory;1"]
.getService(Ci.nsIINIParserFactory).createINIParser(this._iniFile);
for (let section of XPCOMUtils.IterStringEnumerator(iniParser.getSections())) {
this._iniData[section] = {};
for (let key of XPCOMUtils.IterStringEnumerator(iniParser.getKeys(section))) {
this._iniData[section][key] = iniParser.getString(section, key);
}
}
},
// nsIINIParser
getSections : function() {
let sections = [];
for (let section in this._iniData)
sections.push(section);
return new stringEnumerator(sections);
},
getKeys : function(aSection) {
let keys = [];
if (aSection in this._iniData)
for (let key in this._iniData[aSection])
keys.push(key);
return new stringEnumerator(keys);
},
getString : function(aSection, aKey) {
if (!(aSection in this._iniData))
throw Cr.NS_ERROR_FAILURE;
if (!(aKey in this._iniData[aSection]))
throw Cr.NS_ERROR_FAILURE;
return this._iniData[aSection][aKey];
},
// nsIINIParserWriter
setString : function(aSection, aKey, aValue) {
const isSectionIllegal = /[\0\r\n\[\]]/;
const isKeyValIllegal = /[\0\r\n=]/;
if (isSectionIllegal.test(aSection))
throw Components.Exception("bad character in section name",
Cr.ERROR_ILLEGAL_VALUE);
if (isKeyValIllegal.test(aKey) || isKeyValIllegal.test(aValue))
throw Components.Exception("bad character in key/value",
Cr.ERROR_ILLEGAL_VALUE);
if (!(aSection in this._iniData))
this._iniData[aSection] = {};
this._iniData[aSection][aKey] = aValue;
},
writeFile : function(aFile, aFlags) {
let converter;
function writeLine(data) {
data += "\n";
data = converter.ConvertFromUnicode(data);
data += converter.Finish();
outputStream.write(data, data.length);
}
if (!aFile)
aFile = this._iniFile;
let safeStream = Cc["@mozilla.org/network/safe-file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
safeStream.init(aFile, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE,
0o600, null);
var outputStream = Cc["@mozilla.org/network/buffered-output-stream;1"].
createInstance(Ci.nsIBufferedOutputStream);
outputStream.init(safeStream, 8192);
outputStream.QueryInterface(Ci.nsISafeOutputStream); // for .finish()
if (Ci.nsIINIParserWriter.WRITE_UTF16 == aFlags
&& 'nsIWindowsRegKey' in Ci) {
outputStream.write("\xFF\xFE", 2);
converter = this._utf16leConverter;
} else {
converter = this._utf8Converter;
}
for (let section in this._iniData) {
writeLine("[" + section + "]");
for (let key in this._iniData[section]) {
writeLine(key + "=" + this._iniData[section][key]);
}
}
outputStream.finish();
}
};
function stringEnumerator(stringArray) {
this._strings = stringArray;
}
stringEnumerator.prototype = {
QueryInterface : XPCOMUtils.generateQI([Ci.nsIUTF8StringEnumerator]),
_strings : null,
_enumIndex: 0,
hasMore : function() {
return (this._enumIndex < this._strings.length);
},
getNext : function() {
return this._strings[this._enumIndex++];
}
};
var component = [INIProcessorFactory];
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component);

View file

@ -0,0 +1,2 @@
component {6ec5f479-8e13-4403-b6ca-fe4c2dca14fd} nsINIProcessor.js
contract @mozilla.org/xpcom/ini-processor-factory;1 {6ec5f479-8e13-4403-b6ca-fe4c2dca14fd}

38
xpcom/ds/nsIObserver.idl Normal file
View file

@ -0,0 +1,38 @@
/* -*- 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"
/**
* This interface is implemented by an object that wants
* to observe an event corresponding to a topic.
*/
[scriptable, function, uuid(DB242E01-E4D9-11d2-9DDE-000064657374)]
interface nsIObserver : nsISupports {
/**
* Observe will be called when there is a notification for the
* topic |aTopic|. This assumes that the object implementing
* this interface has been registered with an observer service
* such as the nsIObserverService.
*
* If you expect multiple topics/subjects, the impl is
* responsible for filtering.
*
* You should not modify, add, remove, or enumerate
* notifications in the implemention of observe.
*
* @param aSubject : Notification specific interface pointer.
* @param aTopic : The notification topic or subject.
* @param aData : Notification specific wide string.
* subject event.
*/
void observe( in nsISupports aSubject,
in string aTopic,
in wstring aData );
};

View file

@ -0,0 +1,77 @@
/* -*- 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 nsIObserver;
interface nsISimpleEnumerator;
/**
* nsIObserverService
*
* Service allows a client listener (nsIObserver) to register and unregister for
* notifications of specific string referenced topic. Service also provides a
* way to notify registered listeners and a way to enumerate registered client
* listeners.
*/
[scriptable, uuid(D07F5192-E3D1-11d2-8ACD-00105A1B8860)]
interface nsIObserverService : nsISupports
{
/**
* AddObserver
*
* Registers a given listener for a notifications regarding the specified
* topic.
*
* @param anObserve : The interface pointer which will receive notifications.
* @param aTopic : The notification topic or subject.
* @param ownsWeak : If set to false, the nsIObserverService will hold a
* strong reference to |anObserver|. If set to true and
* |anObserver| supports the nsIWeakReference interface,
* a weak reference will be held. Otherwise an error will be
* returned.
*/
void addObserver( in nsIObserver anObserver, in string aTopic, in boolean ownsWeak);
/**
* removeObserver
*
* Unregisters a given listener from notifications regarding the specified
* topic.
*
* @param anObserver : The interface pointer which will stop recieving
* notifications.
* @param aTopic : The notification topic or subject.
*/
void removeObserver( in nsIObserver anObserver, in string aTopic );
/**
* notifyObservers
*
* Notifies all registered listeners of the given topic.
*
* @param aSubject : Notification specific interface pointer.
* @param aTopic : The notification topic or subject.
* @param someData : Notification specific wide string.
*/
void notifyObservers( in nsISupports aSubject,
in string aTopic,
in wstring someData );
/**
* enumerateObservers
*
* Returns an enumeration of all registered listeners.
*
* @param aTopic : The notification topic or subject.
*/
nsISimpleEnumerator enumerateObservers( in string aTopic );
};

View file

@ -0,0 +1,14 @@
/* -*- 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 __gen_nsIPersistentProperties_h__
#define __gen_nsIPersistentProperties_h__
// "soft" switch over to an IDL generated header file
#include "nsIPersistentProperties2.h"
#endif /* __gen_nsIPersistentProperties_h__ */

View file

@ -0,0 +1,63 @@
/* -*- 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 "nsIProperties.idl"
interface nsIInputStream;
interface nsIOutputStream;
interface nsISimpleEnumerator;
[scriptable, uuid(283EE646-1AEF-11D4-98B3-00C04fA0CE9A)]
interface nsIPropertyElement : nsISupports {
attribute AUTF8String key;
attribute AString value;
};
[scriptable, uuid(706867af-0400-4faa-beb1-0dae87308784)]
interface nsIPersistentProperties : nsIProperties
{
/**
* load a set of name/value pairs from the input stream
* names and values should be in UTF8
*/
void load(in nsIInputStream input);
/**
* output the values to the stream - results will be in UTF8
*/
void save(in nsIOutputStream output, in AUTF8String header);
/**
* get an enumeration of nsIPropertyElement objects,
* which are read-only (i.e. setting properties on the element will
* not make changes back into the source nsIPersistentProperties
*/
nsISimpleEnumerator enumerate();
/**
* shortcut to nsIProperty's get() which retrieves a string value
* directly (and thus faster)
*/
AString getStringProperty(in AUTF8String key);
/**
* shortcut to nsIProperty's set() which sets a string value
* directly (and thus faster). If the given property already exists,
* then the old value will be returned
*/
AString setStringProperty(in AUTF8String key, in AString value);
};
%{C++
#define NS_IPERSISTENTPROPERTIES_CID \
{ 0x2245e573, 0x9464, 0x11d2, \
{ 0x9b, 0x8b, 0x0, 0x80, 0x5f, 0x8a, 0x16, 0xd9 } }
#define NS_PERSISTENTPROPERTIES_CONTRACTID "@mozilla.org/persistent-properties;1"
%}

View file

@ -0,0 +1,46 @@
/* -*- Mode: IDL; 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"
/*
* Simple mapping service interface.
*/
[scriptable, uuid(78650582-4e93-4b60-8e85-26ebd3eb14ca)]
interface nsIProperties : nsISupports
{
/**
* Gets a property with a given name.
*
* @throws NS_ERROR_FAILURE if a property with that name doesn't exist.
* @throws NS_ERROR_NO_INTERFACE if the found property fails to QI to the
* given iid.
*/
void get(in string prop, in nsIIDRef iid,
[iid_is(iid),retval] out nsQIResult result);
/**
* Sets a property with a given name to a given value.
*/
void set(in string prop, in nsISupports value);
/**
* Returns true if the property with the given name exists.
*/
boolean has(in string prop);
/**
* Undefines a property.
* @throws NS_ERROR_FAILURE if a property with that name doesn't
* already exist.
*/
void undefine(in string prop);
/**
* Returns an array of the keys.
*/
void getKeys(out uint32_t count, [array, size_is(count), retval] out string keys);
};

25
xpcom/ds/nsIProperty.idl Normal file
View file

@ -0,0 +1,25 @@
/* -*- 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/. */
/* nsIVariant based Property support. */
#include "nsISupports.idl"
interface nsIVariant;
[scriptable, uuid(6dcf9030-a49f-11d5-910d-0010a4e73d9a)]
interface nsIProperty : nsISupports
{
/**
* Get the name of the property.
*/
readonly attribute AString name;
/**
* Get the value of the property.
*/
readonly attribute nsIVariant value;
};

View file

@ -0,0 +1,30 @@
/* -*- 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/. */
/* nsIVariant based Property Bag support. */
#include "nsISupports.idl"
interface nsIVariant;
interface nsISimpleEnumerator;
[scriptable, uuid(bfcd37b0-a49f-11d5-910d-0010a4e73d9a)]
interface nsIPropertyBag : nsISupports
{
/**
* Get a nsISimpleEnumerator whose elements are nsIProperty objects.
*/
readonly attribute nsISimpleEnumerator enumerator;
/**
* Get a property value for the given name.
* @throws NS_ERROR_FAILURE if a property with that name doesn't
* exist.
*/
nsIVariant getProperty(in AString name);
};

View file

@ -0,0 +1,42 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* nsIVariant based Property Bag support. */
#include "nsIPropertyBag.idl"
[scriptable, uuid(625cfd1e-da1e-4417-9ee9-dbc8e0b3fd79)]
interface nsIPropertyBag2 : nsIPropertyBag
{
// Accessing a property as a different type may attempt conversion to the
// requested value
int32_t getPropertyAsInt32 (in AString prop);
uint32_t getPropertyAsUint32 (in AString prop);
int64_t getPropertyAsInt64 (in AString prop);
uint64_t getPropertyAsUint64 (in AString prop);
double getPropertyAsDouble (in AString prop);
AString getPropertyAsAString (in AString prop);
ACString getPropertyAsACString (in AString prop);
AUTF8String getPropertyAsAUTF8String (in AString prop);
boolean getPropertyAsBool (in AString prop);
/**
* This method returns null if the value exists, but is null.
*/
void getPropertyAsInterface (in AString prop,
in nsIIDRef iid,
[iid_is(iid), retval] out nsQIResult result);
/**
* This method returns null if the value does not exist,
* or exists but is null.
*/
nsIVariant get (in AString prop);
/**
* Check for the existence of a key.
*/
boolean hasKey (in AString prop);
};

View file

@ -0,0 +1,32 @@
/* -*- 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 nsIObjectInputStream;
interface nsIObjectOutputStream;
[scriptable, uuid(91cca981-c26d-44a8-bebe-d9ed4891503a)]
interface nsISerializable : nsISupports
{
/**
* Initialize the object implementing nsISerializable, which must have
* been freshly constructed via CreateInstance. All data members that
* can't be set to default values must have been serialized by write,
* and should be read from aInputStream in the same order by this method.
*/
void read(in nsIObjectInputStream aInputStream);
/**
* Serialize the object implementing nsISerializable to aOutputStream, by
* writing each data member that must be recovered later to reconstitute
* a working replica of this object, in a canonical member and byte order,
* to aOutputStream.
*
* NB: a class that implements nsISerializable *must* also implement
* nsIClassInfo, in particular nsIClassInfo::GetClassID.
*/
void write(in nsIObjectOutputStream aOutputStream);
};

View file

@ -0,0 +1,46 @@
/* -*- 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"
/**
* Used to enumerate over elements defined by its implementor.
* Although hasMoreElements() can be called independently of getNext(),
* getNext() must be pre-ceeded by a call to hasMoreElements(). There is
* no way to "reset" an enumerator, once you obtain one.
*
* @version 1.0
*/
[scriptable, uuid(D1899240-F9D2-11D2-BDD6-000064657374)]
interface nsISimpleEnumerator : nsISupports {
/**
* Called to determine whether or not the enumerator has
* any elements that can be returned via getNext(). This method
* is generally used to determine whether or not to initiate or
* continue iteration over the enumerator, though it can be
* called without subsequent getNext() calls. Does not affect
* internal state of enumerator.
*
* @see getNext()
* @return true if there are remaining elements in the enumerator.
* false if there are no more elements in the enumerator.
*/
boolean hasMoreElements();
/**
* Called to retrieve the next element in the enumerator. The "next"
* element is the first element upon the first call. Must be
* pre-ceeded by a call to hasMoreElements() which returns PR_TRUE.
* This method is generally called within a loop to iterate over
* the elements in the enumerator.
*
* @see hasMoreElements()
* @throws NS_ERROR_FAILURE if there are no more elements
* to enumerate.
* @return the next element in the enumeration.
*/
nsISupports getNext();
};

View file

@ -0,0 +1,25 @@
/* -*- 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"
/**
* Used to enumerate over an ordered list of strings.
*/
[scriptable, uuid(50d3ef6c-9380-4f06-9fb2-95488f7d141c)]
interface nsIStringEnumerator : nsISupports
{
boolean hasMore();
AString getNext();
};
[scriptable, uuid(9bdf1010-3695-4907-95ed-83d0410ec307)]
interface nsIUTF8StringEnumerator : nsISupports
{
boolean hasMore();
AUTF8String getNext();
};

View file

@ -0,0 +1,60 @@
/* -*- 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 "nsICollection.idl"
/*
* This entire interface is deprecated and should not be used.
* See nsIArray and nsIMutableArray for the new implementations.
*
* http://groups.google.com/groups?q=nsisupportsarray+group:netscape.public.mozilla.xpcom&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=3D779491.3050506%40netscape.com&rnum=2
* http://groups.google.com/groups?q=nsisupportsarray+group:netscape.public.mozilla.xpcom&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=al8412%245ab2%40ripley.netscape.com&rnum=8
*/
%{C++
class nsIBidirectionalEnumerator;
class nsISupportsArray;
#define NS_SUPPORTSARRAY_CID \
{ /* bda17d50-0d6b-11d3-9331-00104ba0fd40 */ \
0xbda17d50, \
0x0d6b, \
0x11d3, \
{0x93, 0x31, 0x00, 0x10, 0x4b, 0xa0, 0xfd, 0x40} \
}
#define NS_SUPPORTSARRAY_CONTRACTID "@mozilla.org/supports-array;1"
%}
[deprecated, scriptable, uuid(241addc8-3608-4e73-8083-2fd6fa09eba2)]
interface nsISupportsArray : nsICollection {
[notxpcom] long IndexOf([const] in nsISupports aPossibleElement);
// xpcom-compatible versions
long GetIndexOf(in nsISupports aPossibleElement);
[notxpcom] boolean InsertElementAt(in nsISupports aElement,
in unsigned long aIndex);
[notxpcom] boolean ReplaceElementAt(in nsISupports aElement,
in unsigned long aIndex);
[notxpcom] boolean RemoveElementAt(in unsigned long aIndex);
// xpcom-compatible versions
void DeleteElementAt(in unsigned long aIndex);
nsISupportsArray clone();
};
%{C++
// Construct and return a default implementation of nsISupportsArray:
extern MOZ_MUST_USE nsresult
NS_NewISupportsArray(nsISupportsArray** aInstancePtrResult);
%}

View file

@ -0,0 +1,292 @@
/* -*- 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/. */
/* nsISupportsIterators.idl --- IDL defining general purpose iterators */
#include "nsISupports.idl"
/*
...
*/
/**
* ...
*/
[scriptable, uuid(7330650e-1dd2-11b2-a0c2-9ff86ee97bed)]
interface nsIOutputIterator : nsISupports
{
/**
* Put |anElementToPut| into the underlying container or sequence at the position currently pointed to by this iterator.
* The iterator and the underlying container or sequence cooperate to |Release()|
* the replaced element, if any and if necessary, and to |AddRef()| the new element.
*
* The result is undefined if this iterator currently points outside the
* useful range of the underlying container or sequence.
*
* @param anElementToPut the element to place into the underlying container or sequence
*/
void putElement( in nsISupports anElementToPut );
/**
* Advance this iterator to the next position in the underlying container or sequence.
*/
void stepForward();
};
/**
* ...
*/
[scriptable, uuid(85585e12-1dd2-11b2-a930-f6929058269a)]
interface nsIInputIterator : nsISupports
{
/**
* Retrieve (and |AddRef()|) the element this iterator currently points to.
*
* The result is undefined if this iterator currently points outside the
* useful range of the underlying container or sequence.
*
* @result a new reference to the element this iterator currently points to (if any)
*/
nsISupports getElement();
/**
* Advance this iterator to the next position in the underlying container or sequence.
*/
void stepForward();
/**
* Test if |anotherIterator| points to the same position in the underlying container or sequence.
*
* The result is undefined if |anotherIterator| was not created by or for the same underlying container or sequence.
*
* @param anotherIterator another iterator to compare against, created by or for the same underlying container or sequence
* @result true if |anotherIterator| points to the same position in the underlying container or sequence
*/
boolean isEqualTo( in nsISupports anotherIterator );
/**
* Create a new iterator pointing to the same position in the underlying container or sequence to which this iterator currently points.
* The returned iterator is suitable for use in a subsequent call to |isEqualTo()| against this iterator.
*
* @result a new iterator pointing at the same position in the same underlying container or sequence as this iterator
*/
nsISupports clone();
};
/**
* ...
*/
[scriptable, uuid(8da01646-1dd2-11b2-98a7-c7009045be7e)]
interface nsIForwardIterator : nsISupports
{
/**
* Retrieve (and |AddRef()|) the element this iterator currently points to.
*
* The result is undefined if this iterator currently points outside the
* useful range of the underlying container or sequence.
*
* @result a new reference to the element this iterator currently points to (if any)
*/
nsISupports getElement();
/**
* Put |anElementToPut| into the underlying container or sequence at the position currently pointed to by this iterator.
* The iterator and the underlying container or sequence cooperate to |Release()|
* the replaced element, if any and if necessary, and to |AddRef()| the new element.
*
* The result is undefined if this iterator currently points outside the
* useful range of the underlying container or sequence.
*
* @param anElementToPut the element to place into the underlying container or sequence
*/
void putElement( in nsISupports anElementToPut );
/**
* Advance this iterator to the next position in the underlying container or sequence.
*/
void stepForward();
/**
* Test if |anotherIterator| points to the same position in the underlying container or sequence.
*
* The result is undefined if |anotherIterator| was not created by or for the same underlying container or sequence.
*
* @param anotherIterator another iterator to compare against, created by or for the same underlying container or sequence
* @result true if |anotherIterator| points to the same position in the underlying container or sequence
*/
boolean isEqualTo( in nsISupports anotherIterator );
/**
* Create a new iterator pointing to the same position in the underlying container or sequence to which this iterator currently points.
* The returned iterator is suitable for use in a subsequent call to |isEqualTo()| against this iterator.
*
* @result a new iterator pointing at the same position in the same underlying container or sequence as this iterator
*/
nsISupports clone();
};
/**
* ...
*/
[scriptable, uuid(948defaa-1dd1-11b2-89f6-8ce81f5ebda9)]
interface nsIBidirectionalIterator : nsISupports
{
/**
* Retrieve (and |AddRef()|) the element this iterator currently points to.
*
* The result is undefined if this iterator currently points outside the
* useful range of the underlying container or sequence.
*
* @result a new reference to the element this iterator currently points to (if any)
*/
nsISupports getElement();
/**
* Put |anElementToPut| into the underlying container or sequence at the position currently pointed to by this iterator.
* The iterator and the underlying container or sequence cooperate to |Release()|
* the replaced element, if any and if necessary, and to |AddRef()| the new element.
*
* The result is undefined if this iterator currently points outside the
* useful range of the underlying container or sequence.
*
* @param anElementToPut the element to place into the underlying container or sequence
*/
void putElement( in nsISupports anElementToPut );
/**
* Advance this iterator to the next position in the underlying container or sequence.
*/
void stepForward();
/**
* Move this iterator to the previous position in the underlying container or sequence.
*/
void stepBackward();
/**
* Test if |anotherIterator| points to the same position in the underlying container or sequence.
*
* The result is undefined if |anotherIterator| was not created by or for the same underlying container or sequence.
*
* @param anotherIterator another iterator to compare against, created by or for the same underlying container or sequence
* @result true if |anotherIterator| points to the same position in the underlying container or sequence
*/
boolean isEqualTo( in nsISupports anotherIterator );
/**
* Create a new iterator pointing to the same position in the underlying container or sequence to which this iterator currently points.
* The returned iterator is suitable for use in a subsequent call to |isEqualTo()| against this iterator.
*
* @result a new iterator pointing at the same position in the same underlying container or sequence as this iterator
*/
nsISupports clone();
};
/**
* ...
*/
[scriptable, uuid(9bd6fdb0-1dd1-11b2-9101-d15375968230)]
interface nsIRandomAccessIterator : nsISupports
{
/**
* Retrieve (and |AddRef()|) the element this iterator currently points to.
*
* The result is undefined if this iterator currently points outside the
* useful range of the underlying container or sequence.
*
* @result a new reference to the element this iterator currently points to (if any)
*/
nsISupports getElement();
/**
* Retrieve (and |AddRef()|) an element at some offset from where this iterator currently points.
* The offset may be negative. |getElementAt(0)| is equivalent to |getElement()|.
*
* The result is undefined if this iterator currently points outside the
* useful range of the underlying container or sequence.
*
* @param anOffset a |0|-based offset from the position to which this iterator currently points
* @result a new reference to the indicated element (if any)
*/
nsISupports getElementAt( in int32_t anOffset );
/**
* Put |anElementToPut| into the underlying container or sequence at the position currently pointed to by this iterator.
* The iterator and the underlying container or sequence cooperate to |Release()|
* the replaced element, if any and if necessary, and to |AddRef()| the new element.
*
* The result is undefined if this iterator currently points outside the
* useful range of the underlying container or sequence.
*
* @param anElementToPut the element to place into the underlying container or sequence
*/
void putElement( in nsISupports anElementToPut );
/**
* Put |anElementToPut| into the underlying container or sequence at the position |anOffset| away from that currently pointed to by this iterator.
* The iterator and the underlying container or sequence cooperate to |Release()|
* the replaced element, if any and if necessary, and to |AddRef()| the new element.
* |putElementAt(0, obj)| is equivalent to |putElement(obj)|.
*
* The result is undefined if this iterator currently points outside the
* useful range of the underlying container or sequence.
*
* @param anOffset a |0|-based offset from the position to which this iterator currently points
* @param anElementToPut the element to place into the underlying container or sequence
*/
void putElementAt( in int32_t anOffset, in nsISupports anElementToPut );
/**
* Advance this iterator to the next position in the underlying container or sequence.
*/
void stepForward();
/**
* Move this iterator by |anOffset| positions in the underlying container or sequence.
* |anOffset| may be negative. |stepForwardBy(1)| is equivalent to |stepForward()|.
* |stepForwardBy(0)| is a no-op.
*
* @param anOffset a |0|-based offset from the position to which this iterator currently points
*/
void stepForwardBy( in int32_t anOffset );
/**
* Move this iterator to the previous position in the underlying container or sequence.
*/
void stepBackward();
/**
* Move this iterator backwards by |anOffset| positions in the underlying container or sequence.
* |anOffset| may be negative. |stepBackwardBy(1)| is equivalent to |stepBackward()|.
* |stepBackwardBy(n)| is equivalent to |stepForwardBy(-n)|. |stepBackwardBy(0)| is a no-op.
*
* @param anOffset a |0|-based offset from the position to which this iterator currently points
*/
void stepBackwardBy( in int32_t anOffset );
/**
* Test if |anotherIterator| points to the same position in the underlying container or sequence.
*
* The result is undefined if |anotherIterator| was not created by or for the same underlying container or sequence.
*
* @param anotherIterator another iterator to compare against, created by or for the same underlying container or sequence
* @result true if |anotherIterator| points to the same position in the underlying container or sequence
*/
boolean isEqualTo( in nsISupports anotherIterator );
/**
* Create a new iterator pointing to the same position in the underlying container or sequence to which this iterator currently points.
* The returned iterator is suitable for use in a subsequent call to |isEqualTo()| against this iterator.
*
* @result a new iterator pointing at the same position in the same underlying container or sequence as this iterator
*/
nsISupports clone();
};
%{C++
%}

View file

@ -0,0 +1,235 @@
/* -*- 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/. */
/* nsISupports wrappers for single primitive pieces of data. */
#include "nsISupports.idl"
/**
* Primitive base interface.
*
* These first three are pointer types and do data copying
* using the nsIMemory. Be careful!
*/
[scriptable, uuid(d0d4b136-1dd1-11b2-9371-f0727ef827c0)]
interface nsISupportsPrimitive : nsISupports
{
const unsigned short TYPE_ID = 1;
const unsigned short TYPE_CSTRING = 2;
const unsigned short TYPE_STRING = 3;
const unsigned short TYPE_PRBOOL = 4;
const unsigned short TYPE_PRUINT8 = 5;
const unsigned short TYPE_PRUINT16 = 6;
const unsigned short TYPE_PRUINT32 = 7;
const unsigned short TYPE_PRUINT64 = 8;
const unsigned short TYPE_PRTIME = 9;
const unsigned short TYPE_CHAR = 10;
const unsigned short TYPE_PRINT16 = 11;
const unsigned short TYPE_PRINT32 = 12;
const unsigned short TYPE_PRINT64 = 13;
const unsigned short TYPE_FLOAT = 14;
const unsigned short TYPE_DOUBLE = 15;
const unsigned short TYPE_VOID = 16;
const unsigned short TYPE_INTERFACE_POINTER = 17;
readonly attribute unsigned short type;
};
/**
* Scriptable storage for nsID structures
*/
[scriptable, uuid(d18290a0-4a1c-11d3-9890-006008962422)]
interface nsISupportsID : nsISupportsPrimitive
{
attribute nsIDPtr data;
string toString();
};
/**
* Scriptable storage for ASCII strings
*/
[scriptable, uuid(d65ff270-4a1c-11d3-9890-006008962422)]
interface nsISupportsCString : nsISupportsPrimitive
{
attribute ACString data;
string toString();
};
/**
* Scriptable storage for Unicode strings
*/
[scriptable, uuid(d79dc970-4a1c-11d3-9890-006008962422)]
interface nsISupportsString : nsISupportsPrimitive
{
attribute AString data;
wstring toString();
};
/**
* The rest are truly primitive and are passed by value
*/
/**
* Scriptable storage for booleans
*/
[scriptable, uuid(ddc3b490-4a1c-11d3-9890-006008962422)]
interface nsISupportsPRBool : nsISupportsPrimitive
{
attribute boolean data;
string toString();
};
/**
* Scriptable storage for 8-bit integers
*/
[scriptable, uuid(dec2e4e0-4a1c-11d3-9890-006008962422)]
interface nsISupportsPRUint8 : nsISupportsPrimitive
{
attribute uint8_t data;
string toString();
};
/**
* Scriptable storage for unsigned 16-bit integers
*/
[scriptable, uuid(dfacb090-4a1c-11d3-9890-006008962422)]
interface nsISupportsPRUint16 : nsISupportsPrimitive
{
attribute uint16_t data;
string toString();
};
/**
* Scriptable storage for unsigned 32-bit integers
*/
[scriptable, uuid(e01dc470-4a1c-11d3-9890-006008962422)]
interface nsISupportsPRUint32 : nsISupportsPrimitive
{
attribute uint32_t data;
string toString();
};
/**
* Scriptable storage for 64-bit integers
*/
[scriptable, uuid(e13567c0-4a1c-11d3-9890-006008962422)]
interface nsISupportsPRUint64 : nsISupportsPrimitive
{
attribute uint64_t data;
string toString();
};
/**
* Scriptable storage for NSPR date/time values
*/
[scriptable, uuid(e2563630-4a1c-11d3-9890-006008962422)]
interface nsISupportsPRTime : nsISupportsPrimitive
{
attribute PRTime data;
string toString();
};
/**
* Scriptable storage for single character values
* (often used to store an ASCII character)
*/
[scriptable, uuid(e2b05e40-4a1c-11d3-9890-006008962422)]
interface nsISupportsChar : nsISupportsPrimitive
{
attribute char data;
string toString();
};
/**
* Scriptable storage for 16-bit integers
*/
[scriptable, uuid(e30d94b0-4a1c-11d3-9890-006008962422)]
interface nsISupportsPRInt16 : nsISupportsPrimitive
{
attribute int16_t data;
string toString();
};
/**
* Scriptable storage for 32-bit integers
*/
[scriptable, uuid(e36c5250-4a1c-11d3-9890-006008962422)]
interface nsISupportsPRInt32 : nsISupportsPrimitive
{
attribute int32_t data;
string toString();
};
/**
* Scriptable storage for 64-bit integers
*/
[scriptable, uuid(e3cb0ff0-4a1c-11d3-9890-006008962422)]
interface nsISupportsPRInt64 : nsISupportsPrimitive
{
attribute int64_t data;
string toString();
};
/**
* Scriptable storage for floating point numbers
*/
[scriptable, uuid(abeaa390-4ac0-11d3-baea-00805f8a5dd7)]
interface nsISupportsFloat : nsISupportsPrimitive
{
attribute float data;
string toString();
};
/**
* Scriptable storage for doubles
*/
[scriptable, uuid(b32523a0-4ac0-11d3-baea-00805f8a5dd7)]
interface nsISupportsDouble : nsISupportsPrimitive
{
attribute double data;
string toString();
};
/**
* Scriptable storage for generic pointers
*/
[scriptable, uuid(464484f0-568d-11d3-baf8-00805f8a5dd7)]
interface nsISupportsVoid : nsISupportsPrimitive
{
[noscript] attribute voidPtr data;
string toString();
};
/**
* Scriptable storage for other XPCOM objects
*/
[scriptable, uuid(995ea724-1dd1-11b2-9211-c21bdd3e7ed0)]
interface nsISupportsInterfacePointer : nsISupportsPrimitive
{
attribute nsISupports data;
attribute nsIDPtr dataIID;
string toString();
};

155
xpcom/ds/nsIVariant.idl Normal file
View file

@ -0,0 +1,155 @@
/* -*- 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/. */
/* The long avoided variant support for xpcom. */
#include "nsISupports.idl"
[scriptable,uuid(4d12e540-83d7-11d5-90ed-0010a4e73d9a)]
interface nsIDataType : nsISupports
{
// These MUST match the declarations in xpt_struct.h.
// Otherwise the world is likely to explode.
// From xpt_struct.h ...
const uint16_t VTYPE_INT8 = 0; // TD_INT8 = 0,
const uint16_t VTYPE_INT16 = 1; // TD_INT16 = 1,
const uint16_t VTYPE_INT32 = 2; // TD_INT32 = 2,
const uint16_t VTYPE_INT64 = 3; // TD_INT64 = 3,
const uint16_t VTYPE_UINT8 = 4; // TD_UINT8 = 4,
const uint16_t VTYPE_UINT16 = 5; // TD_UINT16 = 5,
const uint16_t VTYPE_UINT32 = 6; // TD_UINT32 = 6,
const uint16_t VTYPE_UINT64 = 7; // TD_UINT64 = 7,
const uint16_t VTYPE_FLOAT = 8; // TD_FLOAT = 8,
const uint16_t VTYPE_DOUBLE = 9; // TD_DOUBLE = 9,
const uint16_t VTYPE_BOOL = 10; // TD_BOOL = 10,
const uint16_t VTYPE_CHAR = 11; // TD_CHAR = 11,
const uint16_t VTYPE_WCHAR = 12; // TD_WCHAR = 12,
const uint16_t VTYPE_VOID = 13; // TD_VOID = 13,
const uint16_t VTYPE_ID = 14; // TD_PNSIID = 14,
const uint16_t VTYPE_DOMSTRING = 15; // TD_DOMSTRING = 15,
const uint16_t VTYPE_CHAR_STR = 16; // TD_PSTRING = 16,
const uint16_t VTYPE_WCHAR_STR = 17; // TD_PWSTRING = 17,
const uint16_t VTYPE_INTERFACE = 18; // TD_INTERFACE_TYPE = 18,
const uint16_t VTYPE_INTERFACE_IS = 19; // TD_INTERFACE_IS_TYPE = 19,
const uint16_t VTYPE_ARRAY = 20; // TD_ARRAY = 20,
const uint16_t VTYPE_STRING_SIZE_IS = 21; // TD_PSTRING_SIZE_IS = 21,
const uint16_t VTYPE_WSTRING_SIZE_IS = 22; // TD_PWSTRING_SIZE_IS = 22,
const uint16_t VTYPE_UTF8STRING = 23; // TD_UTF8STRING = 23,
const uint16_t VTYPE_CSTRING = 24; // TD_CSTRING = 24,
const uint16_t VTYPE_ASTRING = 25; // TD_ASTRING = 25,
const uint16_t VTYPE_EMPTY_ARRAY = 254;
const uint16_t VTYPE_EMPTY = 255;
};
/**
* XPConnect has magic to transparently convert between nsIVariant and JS types.
* We mark the interface [scriptable] so that JS can use methods
* that refer to this interface. But we mark all the methods and attributes
* [noscript] since any nsIVariant object will be automatically converted to a
* JS type anyway.
*/
[scriptable, uuid(81e4c2de-acac-4ad6-901a-b5fb1b851a0d)]
interface nsIVariant : nsISupports
{
[noscript] readonly attribute uint16_t dataType;
[noscript] uint8_t getAsInt8();
[noscript] int16_t getAsInt16();
[noscript] int32_t getAsInt32();
[noscript] int64_t getAsInt64();
[noscript] uint8_t getAsUint8();
[noscript] uint16_t getAsUint16();
[noscript] uint32_t getAsUint32();
[noscript] uint64_t getAsUint64();
[noscript] float getAsFloat();
[noscript] double getAsDouble();
[noscript] boolean getAsBool();
[noscript] char getAsChar();
[noscript] wchar getAsWChar();
[notxpcom] nsresult getAsID(out nsID retval);
[noscript] AString getAsAString();
[noscript] DOMString getAsDOMString();
[noscript] ACString getAsACString();
[noscript] AUTF8String getAsAUTF8String();
[noscript] string getAsString();
[noscript] wstring getAsWString();
[noscript] nsISupports getAsISupports();
[noscript] jsval getAsJSVal();
[noscript] void getAsInterface(out nsIIDPtr iid,
[iid_is(iid), retval] out nsQIResult iface);
[notxpcom] nsresult getAsArray(out uint16_t type, out nsIID iid,
out uint32_t count, out voidPtr ptr);
[noscript] void getAsStringWithSize(out uint32_t size,
[size_is(size), retval] out string str);
[noscript] void getAsWStringWithSize(out uint32_t size,
[size_is(size), retval] out wstring str);
};
/**
* An object that implements nsIVariant may or may NOT also implement this
* nsIWritableVariant.
*
* If the 'writable' attribute is false then attempts to call any of the 'set'
* methods can be expected to fail. Setting the 'writable' attribute may or
* may not succeed.
*
*/
[scriptable, uuid(5586a590-8c82-11d5-90f3-0010a4e73d9a)]
interface nsIWritableVariant : nsIVariant
{
attribute boolean writable;
void setAsInt8(in uint8_t aValue);
void setAsInt16(in int16_t aValue);
void setAsInt32(in int32_t aValue);
void setAsInt64(in int64_t aValue);
void setAsUint8(in uint8_t aValue);
void setAsUint16(in uint16_t aValue);
void setAsUint32(in uint32_t aValue);
void setAsUint64(in uint64_t aValue);
void setAsFloat(in float aValue);
void setAsDouble(in double aValue);
void setAsBool(in boolean aValue);
void setAsChar(in char aValue);
void setAsWChar(in wchar aValue);
void setAsID(in nsIDRef aValue);
void setAsAString(in AString aValue);
void setAsDOMString(in DOMString aValue);
void setAsACString(in ACString aValue);
void setAsAUTF8String(in AUTF8String aValue);
void setAsString(in string aValue);
void setAsWString(in wstring aValue);
void setAsISupports(in nsISupports aValue);
void setAsInterface(in nsIIDRef iid,
[iid_is(iid)] in nsQIResult iface);
[noscript] void setAsArray(in uint16_t type, in nsIIDPtr iid,
in uint32_t count, in voidPtr ptr);
void setAsStringWithSize(in uint32_t size,
[size_is(size)] in string str);
void setAsWStringWithSize(in uint32_t size,
[size_is(size)] in wstring str);
void setAsVoid();
void setAsEmpty();
void setAsEmptyArray();
void setFromVariant(in nsIVariant aValue);
};
%{C++
// The contractID for the generic implementation built in to xpcom.
#define NS_VARIANT_CONTRACTID "@mozilla.org/variant;1"
%}

View file

@ -0,0 +1,332 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
native HKEY(HKEY);
/**
* This interface is designed to provide scriptable access to the Windows
* registry system ("With Great Power Comes Great Responsibility"). The
* interface represents a single key in the registry.
*
* This interface is highly Win32 specific.
*/
[scriptable, uuid(2555b930-d64f-437e-9be7-0a2cb252c1f4)]
interface nsIWindowsRegKey : nsISupports
{
/**
* Root keys. The values for these keys correspond to the values from
* WinReg.h in the MS Platform SDK. The ROOT_KEY_ prefix corresponds to the
* HKEY_ prefix in the MS Platform SDK.
*
* This interface is not restricted to using only these root keys.
*/
const unsigned long ROOT_KEY_CLASSES_ROOT = 0x80000000;
const unsigned long ROOT_KEY_CURRENT_USER = 0x80000001;
const unsigned long ROOT_KEY_LOCAL_MACHINE = 0x80000002;
/**
* Values for the mode parameter passed to the open and create methods.
* The values defined here correspond to the REGSAM values defined in
* WinNT.h in the MS Platform SDK, where ACCESS_ is replaced with KEY_.
*
* This interface is not restricted to using only these access types.
*/
const unsigned long ACCESS_BASIC = 0x00020000;
const unsigned long ACCESS_QUERY_VALUE = 0x00000001;
const unsigned long ACCESS_SET_VALUE = 0x00000002;
const unsigned long ACCESS_CREATE_SUB_KEY = 0x00000004;
const unsigned long ACCESS_ENUMERATE_SUB_KEYS = 0x00000008;
const unsigned long ACCESS_NOTIFY = 0x00000010;
const unsigned long ACCESS_READ = ACCESS_BASIC |
ACCESS_QUERY_VALUE |
ACCESS_ENUMERATE_SUB_KEYS |
ACCESS_NOTIFY;
const unsigned long ACCESS_WRITE = ACCESS_BASIC |
ACCESS_SET_VALUE |
ACCESS_CREATE_SUB_KEY;
const unsigned long ACCESS_ALL = ACCESS_READ |
ACCESS_WRITE;
const unsigned long WOW64_32 = 0x00000200;
const unsigned long WOW64_64 = 0x00000100;
/**
* Values for the type of a registry value. The numeric values of these
* constants are taken directly from WinNT.h in the MS Platform SDK.
* The Microsoft documentation should be consulted for the exact meaning of
* these value types.
*
* This interface is somewhat restricted to using only these value types.
* There is no method that is directly equivalent to RegQueryValueEx or
* RegSetValueEx. In particular, this interface does not support the
* REG_MULTI_SZ and REG_EXPAND_SZ value types. It is still possible to
* enumerate values that have other types (i.e., getValueType may return a
* type not defined below).
*/
const unsigned long TYPE_NONE = 0; // REG_NONE
const unsigned long TYPE_STRING = 1; // REG_SZ
const unsigned long TYPE_BINARY = 3; // REG_BINARY
const unsigned long TYPE_INT = 4; // REG_DWORD
const unsigned long TYPE_INT64 = 11; // REG_QWORD
/**
* This attribute exposes the native HKEY and is available to provide C++
* consumers with the flexibility of making other Windows registry API calls
* that are not exposed via this interface.
*
* It is possible to initialize this object by setting an HKEY on it. In
* that case, it is the responsibility of the consumer setting the HKEY to
* ensure that it is a valid HKEY.
*
* WARNING: Setting the key does not close the old key.
*/
[noscript] attribute HKEY key;
/**
* This method closes the key. If the key is already closed, then this
* method does nothing.
*/
void close();
/**
* This method opens an existing key. This method fails if the key
* does not exist.
*
* NOTE: On 32-bit Windows, it is valid to pass any HKEY as the rootKey
* parameter of this function. However, for compatibility with 64-bit
* Windows, that usage should probably be avoided in favor of openChild.
*
* @param rootKey
* A root key defined above or any valid HKEY on 32-bit Windows.
* @param relPath
* A relative path from the given root key.
* @param mode
* Access mode, which is a bit-wise OR of the ACCESS_ values defined
* above.
*/
void open(in unsigned long rootKey, in AString relPath, in unsigned long mode);
/**
* This method opens an existing key or creates a new key.
*
* NOTE: On 32-bit Windows, it is valid to pass any HKEY as the rootKey
* parameter of this function. However, for compatibility with 64-bit
* Windows, that usage should probably be avoided in favor of createChild.
*
* @param rootKey
* A root key defined above or any valid HKEY on 32-bit Windows.
* @param relPath
* A relative path from the given root key.
* @param mode
* Access mode, which is a bit-wise OR of the ACCESS_ values defined
* above.
*/
void create(in unsigned long rootKey, in AString relPath, in unsigned long mode);
/**
* This method opens a subkey relative to this key. This method fails if the
* key does not exist.
*
* @return nsIWindowsRegKey for the newly opened subkey.
*/
nsIWindowsRegKey openChild(in AString relPath, in unsigned long mode);
/**
* This method opens or creates a subkey relative to this key.
*
* @return nsIWindowsRegKey for the newly opened or created subkey.
*/
nsIWindowsRegKey createChild(in AString relPath, in unsigned long mode);
/**
* This attribute returns the number of child keys.
*/
readonly attribute unsigned long childCount;
/**
* This method returns the name of the n'th child key.
*
* @param index
* The index of the requested child key.
*/
AString getChildName(in unsigned long index);
/**
* This method checks to see if the key has a child by the given name.
*
* @param name
* The name of the requested child key.
*/
boolean hasChild(in AString name);
/**
* This attribute returns the number of values under this key.
*/
readonly attribute unsigned long valueCount;
/**
* This method returns the name of the n'th value under this key.
*
* @param index
* The index of the requested value.
*/
AString getValueName(in unsigned long index);
/**
* This method checks to see if the key has a value by the given name.
*
* @param name
* The name of the requested value.
*/
boolean hasValue(in AString name);
/**
* This method removes a child key and all of its values. This method will
* fail if the key has any children of its own.
*
* @param relPath
* The relative path from this key to the key to be removed.
*/
void removeChild(in AString relPath);
/**
* This method removes the value with the given name.
*
* @param name
* The name of the value to be removed.
*/
void removeValue(in AString name);
/**
* This method returns the type of the value with the given name. The return
* value is one of the "TYPE_" constants defined above.
*
* @param name
* The name of the value to query.
*/
unsigned long getValueType(in AString name);
/**
* This method reads the string contents of the named value as a Unicode
* string.
*
* @param name
* The name of the value to query. This parameter can be the empty
* string to request the key's default value.
*/
AString readStringValue(in AString name);
/**
* This method reads the integer contents of the named value.
*
* @param name
* The name of the value to query.
*/
unsigned long readIntValue(in AString name);
/**
* This method reads the 64-bit integer contents of the named value.
*
* @param name
* The name of the value to query.
*/
unsigned long long readInt64Value(in AString name);
/**
* This method reads the binary contents of the named value under this key.
*
* JavaScript callers should take care with the result of this method since
* it will be byte-expanded to form a JS string. (The binary data will be
* treated as an ISO-Latin-1 character string, which it is not).
*
* @param name
* The name of the value to query.
*/
ACString readBinaryValue(in AString name);
/**
* This method writes the unicode string contents of the named value. The
* value will be created if it does not already exist.
*
* @param name
* The name of the value to modify. This parameter can be the empty
* string to modify the key's default value.
* @param data
* The data for the value to modify.
*/
void writeStringValue(in AString name, in AString data);
/**
* This method writes the integer contents of the named value. The value
* will be created if it does not already exist.
*
* @param name
* The name of the value to modify.
* @param data
* The data for the value to modify.
*/
void writeIntValue(in AString name, in unsigned long data);
/**
* This method writes the 64-bit integer contents of the named value. The
* value will be created if it does not already exist.
*
* @param name
* The name of the value to modify.
* @param data
* The data for the value to modify.
*/
void writeInt64Value(in AString name, in unsigned long long data);
/**
* This method writes the binary contents of the named value. The value will
* be created if it does not already exist.
*
* JavaScript callers should take care with the value passed to this method
* since it will be truncated from a JS string (unicode) to a ISO-Latin-1
* string. (The binary data will be treated as an ISO-Latin-1 character
* string, which it is not). So, JavaScript callers should only pass
* character values in the range \u0000 to \u00FF, or else data loss will
* occur.
*
* @param name
* The name of the value to modify.
* @param data
* The data for the value to modify.
*/
void writeBinaryValue(in AString name, in ACString data);
/**
* This method starts watching the key to see if any of its values have
* changed. The key must have been opened with mode including ACCESS_NOTIFY.
* If recurse is true, then this key and any of its descendant keys are
* watched. Otherwise, only this key is watched.
*
* @param recurse
* Indicates whether or not to also watch child keys.
*/
void startWatching(in boolean recurse);
/**
* This method stops any watching of the key initiated by a call to
* startWatching. This method does nothing if the key is not being watched.
*/
void stopWatching();
/**
* This method returns true if the key is being watched for changes (i.e.,
* if startWatching() was called).
*/
boolean isWatching();
/**
* This method returns true if the key has changed and false otherwise.
* This method will always return false if startWatching was not called.
*/
boolean hasChanged();
};

View file

@ -0,0 +1,27 @@
/* -*- 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/. */
/* nsIVariant based writable Property Bag support. */
#include "nsIPropertyBag.idl"
[scriptable, uuid(96fc4671-eeb4-4823-9421-e50fb70ad353)]
interface nsIWritablePropertyBag : nsIPropertyBag
{
/**
* Set a property with the given name to the given value. If
* a property already exists with the given name, it is
* overwritten.
*/
void setProperty(in AString name, in nsIVariant value);
/**
* Delete a property with the given name.
* @throws NS_ERROR_FAILURE if a property with that name doesn't
* exist.
*/
void deleteProperty(in AString name);
};

View file

@ -0,0 +1,22 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* nsIVariant based Property Bag support. */
#include "nsIPropertyBag2.idl"
[scriptable, uuid(9cfd1587-360e-4957-a58f-4c2b1c5e7ed9)]
interface nsIWritablePropertyBag2 : nsIPropertyBag2
{
void setPropertyAsInt32 (in AString prop, in int32_t value);
void setPropertyAsUint32 (in AString prop, in uint32_t value);
void setPropertyAsInt64 (in AString prop, in int64_t value);
void setPropertyAsUint64 (in AString prop, in uint64_t value);
void setPropertyAsDouble (in AString prop, in double value);
void setPropertyAsAString (in AString prop, in AString value);
void setPropertyAsACString (in AString prop, in ACString value);
void setPropertyAsAUTF8String (in AString prop, in AUTF8String value);
void setPropertyAsBool (in AString prop, in boolean value);
void setPropertyAsInterface (in AString prop, in nsISupports value);
};

127
xpcom/ds/nsMathUtils.h Normal file
View file

@ -0,0 +1,127 @@
/* -*- 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 nsMathUtils_h__
#define nsMathUtils_h__
#include "nscore.h"
#include <cmath>
#include <float.h>
#ifdef SOLARIS
#include <ieeefp.h>
#endif
/*
* round
*/
inline double
NS_round(double aNum)
{
return aNum >= 0.0 ? floor(aNum + 0.5) : ceil(aNum - 0.5);
}
inline float
NS_roundf(float aNum)
{
return aNum >= 0.0f ? floorf(aNum + 0.5f) : ceilf(aNum - 0.5f);
}
inline int32_t
NS_lround(double aNum)
{
return aNum >= 0.0 ? int32_t(aNum + 0.5) : int32_t(aNum - 0.5);
}
/* NS_roundup30 rounds towards infinity for positive and */
/* negative numbers. */
#if defined(XP_WIN32) && defined(_M_IX86) && !defined(__GNUC__) && !defined(__clang__)
inline int32_t NS_lroundup30(float x)
{
/* Code derived from Laurent de Soras' paper at */
/* http://ldesoras.free.fr/doc/articles/rounding_en.pdf */
/* Rounding up on Windows is expensive using the float to */
/* int conversion and the floor function. A faster */
/* approach is to use f87 rounding while assuming the */
/* default rounding mode of rounding to the nearest */
/* integer. This rounding mode, however, actually rounds */
/* to the nearest integer so we add the floating point */
/* number to itself and add our rounding factor before */
/* doing the conversion to an integer. We then do a right */
/* shift of one bit on the integer to divide by two. */
/* This routine doesn't handle numbers larger in magnitude */
/* than 2^30 but this is fine for NSToCoordRound because */
/* Coords are limited to 2^30 in magnitude. */
static const double round_to_nearest = 0.5f;
int i;
__asm {
fld x ; load fp argument
fadd st, st(0) ; double it
fadd round_to_nearest ; add the rounding factor
fistp dword ptr i ; convert the result to int
}
return i >> 1; /* divide by 2 */
}
#endif /* XP_WIN32 && _M_IX86 && !__GNUC__ */
inline int32_t
NS_lroundf(float aNum)
{
return aNum >= 0.0f ? int32_t(aNum + 0.5f) : int32_t(aNum - 0.5f);
}
/*
* hypot. We don't need a super accurate version of this, if a platform
* turns up with none of the possibilities below it would be okay to fall
* back to sqrt(x*x + y*y).
*/
inline double
NS_hypot(double aNum1, double aNum2)
{
#ifdef __GNUC__
return __builtin_hypot(aNum1, aNum2);
#elif defined _WIN32
return _hypot(aNum1, aNum2);
#else
return hypot(aNum1, aNum2);
#endif
}
/**
* Check whether a floating point number is finite (not +/-infinity and not a
* NaN value).
*/
inline bool
NS_finite(double aNum)
{
#ifdef WIN32
// NOTE: '!!' casts an int to bool without spamming MSVC warning C4800.
return !!_finite(aNum);
#elif defined(XP_DARWIN)
// Darwin has deprecated |finite| and recommends |isfinite|. The former is
// not present in the iOS SDK.
return std::isfinite(aNum);
#else
return finite(aNum);
#endif
}
/**
* Returns the result of the modulo of x by y using a floored division.
* fmod(x, y) is using a truncated division.
* The main difference is that the result of this method will have the sign of
* y while the result of fmod(x, y) will have the sign of x.
*/
inline double
NS_floorModulo(double aNum1, double aNum2)
{
return (aNum1 - aNum2 * floor(aNum1 / aNum2));
}
#endif

142
xpcom/ds/nsObserverList.cpp Normal file
View file

@ -0,0 +1,142 @@
/* -*- 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 "nsObserverList.h"
#include "nsAutoPtr.h"
#include "nsCOMArray.h"
#include "nsISimpleEnumerator.h"
#include "xpcpublic.h"
nsresult
nsObserverList::AddObserver(nsIObserver* anObserver, bool ownsWeak)
{
NS_ASSERTION(anObserver, "Null input");
if (!ownsWeak) {
ObserverRef* o = mObservers.AppendElement(anObserver);
if (!o) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
nsCOMPtr<nsIWeakReference> weak = do_GetWeakReference(anObserver);
if (!weak) {
return NS_NOINTERFACE;
}
ObserverRef* o = mObservers.AppendElement(weak);
if (!o) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
nsresult
nsObserverList::RemoveObserver(nsIObserver* anObserver)
{
NS_ASSERTION(anObserver, "Null input");
if (mObservers.RemoveElement(static_cast<nsISupports*>(anObserver))) {
return NS_OK;
}
nsCOMPtr<nsIWeakReference> observerRef = do_GetWeakReference(anObserver);
if (!observerRef) {
return NS_ERROR_FAILURE;
}
if (!mObservers.RemoveElement(observerRef)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
void
nsObserverList::GetObserverList(nsISimpleEnumerator** anEnumerator)
{
RefPtr<nsObserverEnumerator> e(new nsObserverEnumerator(this));
e.forget(anEnumerator);
}
void
nsObserverList::FillObserverArray(nsCOMArray<nsIObserver>& aArray)
{
aArray.SetCapacity(mObservers.Length());
nsTArray<ObserverRef> observers(mObservers);
for (int32_t i = observers.Length() - 1; i >= 0; --i) {
if (observers[i].isWeakRef) {
nsCOMPtr<nsIObserver> o(do_QueryReferent(observers[i].asWeak()));
if (o) {
aArray.AppendObject(o);
} else {
// the object has gone away, remove the weakref
mObservers.RemoveElement(observers[i].asWeak());
}
} else {
aArray.AppendObject(observers[i].asObserver());
}
}
}
void
nsObserverList::AppendStrongObservers(nsCOMArray<nsIObserver>& aArray)
{
aArray.SetCapacity(aArray.Length() + mObservers.Length());
for (int32_t i = mObservers.Length() - 1; i >= 0; --i) {
if (!mObservers[i].isWeakRef) {
aArray.AppendObject(mObservers[i].asObserver());
}
}
}
void
nsObserverList::NotifyObservers(nsISupports* aSubject,
const char* aTopic,
const char16_t* someData)
{
nsCOMArray<nsIObserver> observers;
FillObserverArray(observers);
for (int32_t i = 0; i < observers.Count(); ++i) {
observers[i]->Observe(aSubject, aTopic, someData);
}
}
NS_IMPL_ISUPPORTS(nsObserverEnumerator, nsISimpleEnumerator)
nsObserverEnumerator::nsObserverEnumerator(nsObserverList* aObserverList)
: mIndex(0)
{
aObserverList->FillObserverArray(mObservers);
}
NS_IMETHODIMP
nsObserverEnumerator::HasMoreElements(bool* aResult)
{
*aResult = (mIndex < mObservers.Count());
return NS_OK;
}
NS_IMETHODIMP
nsObserverEnumerator::GetNext(nsISupports** aResult)
{
if (mIndex == mObservers.Count()) {
NS_ERROR("Enumerating after HasMoreElements returned false.");
return NS_ERROR_UNEXPECTED;
}
NS_ADDREF(*aResult = mObservers[mIndex]);
++mIndex;
return NS_OK;
}

93
xpcom/ds/nsObserverList.h Normal file
View file

@ -0,0 +1,93 @@
/* -*- 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 nsObserverList_h___
#define nsObserverList_h___
#include "nsISupports.h"
#include "nsTArray.h"
#include "nsCOMPtr.h"
#include "nsCOMArray.h"
#include "nsIObserver.h"
#include "nsIWeakReference.h"
#include "nsHashKeys.h"
#include "nsISimpleEnumerator.h"
#include "mozilla/Attributes.h"
struct ObserverRef
{
ObserverRef(const ObserverRef& aO) : isWeakRef(aO.isWeakRef), ref(aO.ref) {}
explicit ObserverRef(nsIObserver* aObserver) : isWeakRef(false), ref(aObserver) {}
explicit ObserverRef(nsIWeakReference* aWeak) : isWeakRef(true), ref(aWeak) {}
bool isWeakRef;
nsCOMPtr<nsISupports> ref;
nsIObserver* asObserver()
{
NS_ASSERTION(!isWeakRef, "Isn't a strong ref.");
return static_cast<nsIObserver*>((nsISupports*)ref);
}
nsIWeakReference* asWeak()
{
NS_ASSERTION(isWeakRef, "Isn't a weak ref.");
return static_cast<nsIWeakReference*>((nsISupports*)ref);
}
bool operator==(nsISupports* aRhs) const { return ref == aRhs; }
};
class nsObserverList : public nsCharPtrHashKey
{
friend class nsObserverService;
public:
explicit nsObserverList(const char* aKey) : nsCharPtrHashKey(aKey)
{
MOZ_COUNT_CTOR(nsObserverList);
}
~nsObserverList()
{
MOZ_COUNT_DTOR(nsObserverList);
}
MOZ_MUST_USE nsresult AddObserver(nsIObserver* aObserver, bool aOwnsWeak);
MOZ_MUST_USE nsresult RemoveObserver(nsIObserver* aObserver);
void NotifyObservers(nsISupports* aSubject,
const char* aTopic,
const char16_t* aSomeData);
void GetObserverList(nsISimpleEnumerator** aEnumerator);
// Fill an array with the observers of this category.
// The array is filled in last-added-first order.
void FillObserverArray(nsCOMArray<nsIObserver>& aArray);
// Like FillObserverArray(), but only for strongly held observers.
void AppendStrongObservers(nsCOMArray<nsIObserver>& aArray);
private:
nsTArray<ObserverRef> mObservers;
};
class nsObserverEnumerator final : public nsISimpleEnumerator
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISIMPLEENUMERATOR
explicit nsObserverEnumerator(nsObserverList* aObserverList);
private:
~nsObserverEnumerator() {}
int32_t mIndex; // Counts up from 0
nsCOMArray<nsIObserver> mObservers;
};
#endif /* nsObserverList_h___ */

View file

@ -0,0 +1,312 @@
/* -*- 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/Logging.h"
#include "nsAutoPtr.h"
#include "nsIConsoleService.h"
#include "nsIObserverService.h"
#include "nsIObserver.h"
#include "nsIScriptError.h"
#include "nsObserverService.h"
#include "nsObserverList.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "nsEnumeratorUtils.h"
#include "xpcpublic.h"
#include "mozilla/net/NeckoCommon.h"
#include "mozilla/Services.h"
#define NOTIFY_GLOBAL_OBSERVERS
// Log module for nsObserverService logging...
//
// To enable logging (see prlog.h for full details):
//
// set MOZ_LOG=ObserverService:5
// set MOZ_LOG_FILE=service.log
//
// This enables LogLevel::Debug level information and places all output in
// the file service.log.
static mozilla::LazyLogModule sObserverServiceLog("ObserverService");
#define LOG(x) MOZ_LOG(sObserverServiceLog, mozilla::LogLevel::Debug, x)
using namespace mozilla;
NS_IMETHODIMP
nsObserverService::CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize)
{
struct SuspectObserver
{
SuspectObserver(const char* aTopic, size_t aReferentCount)
: mTopic(aTopic)
, mReferentCount(aReferentCount)
{}
const char* mTopic;
size_t mReferentCount;
};
size_t totalNumStrong = 0;
size_t totalNumWeakAlive = 0;
size_t totalNumWeakDead = 0;
nsTArray<SuspectObserver> suspectObservers;
for (auto iter = mObserverTopicTable.Iter(); !iter.Done(); iter.Next()) {
nsObserverList* observerList = iter.Get();
if (!observerList) {
continue;
}
size_t topicNumStrong = 0;
size_t topicNumWeakAlive = 0;
size_t topicNumWeakDead = 0;
nsTArray<ObserverRef>& observers = observerList->mObservers;
for (uint32_t i = 0; i < observers.Length(); i++) {
if (observers[i].isWeakRef) {
nsCOMPtr<nsIObserver> observerRef(
do_QueryReferent(observers[i].asWeak()));
if (observerRef) {
topicNumWeakAlive++;
} else {
topicNumWeakDead++;
}
} else {
topicNumStrong++;
}
}
totalNumStrong += topicNumStrong;
totalNumWeakAlive += topicNumWeakAlive;
totalNumWeakDead += topicNumWeakDead;
// Keep track of topics that have a suspiciously large number
// of referents (symptom of leaks).
size_t topicTotal = topicNumStrong + topicNumWeakAlive + topicNumWeakDead;
if (topicTotal > kSuspectReferentCount) {
SuspectObserver suspect(observerList->GetKey(), topicTotal);
suspectObservers.AppendElement(suspect);
}
}
// These aren't privacy-sensitive and so don't need anonymizing.
for (uint32_t i = 0; i < suspectObservers.Length(); i++) {
SuspectObserver& suspect = suspectObservers[i];
nsPrintfCString suspectPath("observer-service-suspect/referent(topic=%s)",
suspect.mTopic);
aHandleReport->Callback(
/* process */ EmptyCString(),
suspectPath, KIND_OTHER, UNITS_COUNT, suspect.mReferentCount,
NS_LITERAL_CSTRING("A topic with a suspiciously large number of "
"referents. This may be symptomatic of a leak "
"if the number of referents is high with "
"respect to the number of windows."),
aData);
}
MOZ_COLLECT_REPORT(
"observer-service/referent/strong", KIND_OTHER, UNITS_COUNT,
totalNumStrong,
"The number of strong references held by the observer service.");
MOZ_COLLECT_REPORT(
"observer-service/referent/weak/alive", KIND_OTHER, UNITS_COUNT,
totalNumWeakAlive,
"The number of weak references held by the observer service that are "
"still alive.");
MOZ_COLLECT_REPORT(
"observer-service/referent/weak/dead", KIND_OTHER, UNITS_COUNT,
totalNumWeakDead,
"The number of weak references held by the observer service that are "
"dead.");
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsObserverService Implementation
NS_IMPL_ISUPPORTS(nsObserverService,
nsIObserverService,
nsObserverService,
nsIMemoryReporter)
nsObserverService::nsObserverService()
: mShuttingDown(false)
{
}
nsObserverService::~nsObserverService(void)
{
Shutdown();
}
void
nsObserverService::RegisterReporter()
{
RegisterWeakMemoryReporter(this);
}
void
nsObserverService::Shutdown()
{
UnregisterWeakMemoryReporter(this);
mShuttingDown = true;
mObserverTopicTable.Clear();
}
nsresult
nsObserverService::Create(nsISupports* aOuter, const nsIID& aIID,
void** aInstancePtr)
{
LOG(("nsObserverService::Create()"));
RefPtr<nsObserverService> os = new nsObserverService();
if (!os) {
return NS_ERROR_OUT_OF_MEMORY;
}
// The memory reporter can not be immediately registered here because
// the nsMemoryReporterManager may attempt to get the nsObserverService
// during initialization, causing a recursive GetService.
NS_DispatchToCurrentThread(NewRunnableMethod(os, &nsObserverService::RegisterReporter));
return os->QueryInterface(aIID, aInstancePtr);
}
#define NS_ENSURE_VALIDCALL \
if (!NS_IsMainThread()) { \
MOZ_CRASH("Using observer service off the main thread!"); \
return NS_ERROR_UNEXPECTED; \
} \
if (mShuttingDown) { \
NS_ERROR("Using observer service after XPCOM shutdown!"); \
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; \
}
NS_IMETHODIMP
nsObserverService::AddObserver(nsIObserver* aObserver, const char* aTopic,
bool aOwnsWeak)
{
LOG(("nsObserverService::AddObserver(%p: %s)",
(void*)aObserver, aTopic));
NS_ENSURE_VALIDCALL
if (NS_WARN_IF(!aObserver) || NS_WARN_IF(!aTopic)) {
return NS_ERROR_INVALID_ARG;
}
// Specifically allow http-on-opening-request in the child process;
// see bug 1269765.
if (mozilla::net::IsNeckoChild() && !strncmp(aTopic, "http-on-", 8) &&
strcmp(aTopic, "http-on-opening-request")) {
nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
error->Init(NS_LITERAL_STRING("http-on-* observers only work in the parent process"),
EmptyString(), EmptyString(), 0, 0,
nsIScriptError::warningFlag, "chrome javascript");
console->LogMessage(error);
return NS_ERROR_NOT_IMPLEMENTED;
}
nsObserverList* observerList = mObserverTopicTable.PutEntry(aTopic);
if (!observerList) {
return NS_ERROR_OUT_OF_MEMORY;
}
return observerList->AddObserver(aObserver, aOwnsWeak);
}
NS_IMETHODIMP
nsObserverService::RemoveObserver(nsIObserver* aObserver, const char* aTopic)
{
LOG(("nsObserverService::RemoveObserver(%p: %s)",
(void*)aObserver, aTopic));
NS_ENSURE_VALIDCALL
if (NS_WARN_IF(!aObserver) || NS_WARN_IF(!aTopic)) {
return NS_ERROR_INVALID_ARG;
}
nsObserverList* observerList = mObserverTopicTable.GetEntry(aTopic);
if (!observerList) {
return NS_ERROR_FAILURE;
}
/* This death grip is to protect against stupid consumers who call
RemoveObserver from their Destructor, see bug 485834/bug 325392. */
nsCOMPtr<nsIObserver> kungFuDeathGrip(aObserver);
return observerList->RemoveObserver(aObserver);
}
NS_IMETHODIMP
nsObserverService::EnumerateObservers(const char* aTopic,
nsISimpleEnumerator** anEnumerator)
{
NS_ENSURE_VALIDCALL
if (NS_WARN_IF(!anEnumerator) || NS_WARN_IF(!aTopic)) {
return NS_ERROR_INVALID_ARG;
}
nsObserverList* observerList = mObserverTopicTable.GetEntry(aTopic);
if (!observerList) {
return NS_NewEmptyEnumerator(anEnumerator);
}
observerList->GetObserverList(anEnumerator);
return NS_OK;
}
// Enumerate observers of aTopic and call Observe on each.
NS_IMETHODIMP nsObserverService::NotifyObservers(nsISupports* aSubject,
const char* aTopic,
const char16_t* aSomeData)
{
LOG(("nsObserverService::NotifyObservers(%s)", aTopic));
NS_ENSURE_VALIDCALL
if (NS_WARN_IF(!aTopic)) {
return NS_ERROR_INVALID_ARG;
}
nsObserverList* observerList = mObserverTopicTable.GetEntry(aTopic);
if (observerList) {
observerList->NotifyObservers(aSubject, aTopic, aSomeData);
}
#ifdef NOTIFY_GLOBAL_OBSERVERS
observerList = mObserverTopicTable.GetEntry("*");
if (observerList) {
observerList->NotifyObservers(aSubject, aTopic, aSomeData);
}
#endif
return NS_OK;
}
NS_IMETHODIMP
nsObserverService::UnmarkGrayStrongObservers()
{
NS_ENSURE_VALIDCALL
nsCOMArray<nsIObserver> strongObservers;
for (auto iter = mObserverTopicTable.Iter(); !iter.Done(); iter.Next()) {
nsObserverList* aObserverList = iter.Get();
if (aObserverList) {
aObserverList->AppendStrongObservers(strongObservers);
}
}
for (uint32_t i = 0; i < strongObservers.Length(); ++i) {
xpc_TryUnmarkWrappedGrayObject(strongObservers[i]);
}
return NS_OK;
}

View file

@ -0,0 +1,55 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsObserverService_h___
#define nsObserverService_h___
#include "nsIObserverService.h"
#include "nsObserverList.h"
#include "nsIMemoryReporter.h"
#include "nsTHashtable.h"
#include "mozilla/Attributes.h"
// {D07F5195-E3D1-11d2-8ACD-00105A1B8860}
#define NS_OBSERVERSERVICE_CID \
{ 0xd07f5195, 0xe3d1, 0x11d2, { 0x8a, 0xcd, 0x0, 0x10, 0x5a, 0x1b, 0x88, 0x60 } }
class nsIMemoryReporter;
class nsObserverService final
: public nsIObserverService
, public nsIMemoryReporter
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_OBSERVERSERVICE_CID)
nsObserverService();
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVERSERVICE
NS_DECL_NSIMEMORYREPORTER
void Shutdown();
static MOZ_MUST_USE nsresult Create(nsISupports* aOuter, const nsIID& aIID,
void** aInstancePtr);
// Unmark any strongly held observers implemented in JS so the cycle
// collector will not traverse them.
NS_IMETHOD UnmarkGrayStrongObservers();
private:
~nsObserverService(void);
void RegisterReporter();
static const size_t kSuspectReferentCount = 100;
bool mShuttingDown;
nsTHashtable<nsObserverList> mObserverTopicTable;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsObserverService, NS_OBSERVERSERVICE_CID)
#endif /* nsObserverService_h___ */

View file

@ -0,0 +1,666 @@
/* -*- 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 "nsArrayEnumerator.h"
#include "nsID.h"
#include "nsCOMArray.h"
#include "nsUnicharInputStream.h"
#include "nsPrintfCString.h"
#include "nsAutoPtr.h"
#define PL_ARENA_CONST_ALIGN_MASK 3
#include "nsPersistentProperties.h"
#include "nsIProperties.h"
struct PropertyTableEntry : public PLDHashEntryHdr
{
// both of these are arena-allocated
const char* mKey;
const char16_t* mValue;
};
static char16_t*
ArenaStrdup(const nsAFlatString& aString, PLArenaPool* aArena)
{
void* mem;
// add one to include the null terminator
int32_t len = (aString.Length() + 1) * sizeof(char16_t);
PL_ARENA_ALLOCATE(mem, aArena, len);
NS_ASSERTION(mem, "Couldn't allocate space!\n");
if (mem) {
memcpy(mem, aString.get(), len);
}
return static_cast<char16_t*>(mem);
}
static char*
ArenaStrdup(const nsAFlatCString& aString, PLArenaPool* aArena)
{
void* mem;
// add one to include the null terminator
int32_t len = (aString.Length() + 1) * sizeof(char);
PL_ARENA_ALLOCATE(mem, aArena, len);
NS_ASSERTION(mem, "Couldn't allocate space!\n");
if (mem) {
memcpy(mem, aString.get(), len);
}
return static_cast<char*>(mem);
}
static const struct PLDHashTableOps property_HashTableOps = {
PLDHashTable::HashStringKey,
PLDHashTable::MatchStringKey,
PLDHashTable::MoveEntryStub,
PLDHashTable::ClearEntryStub,
nullptr,
};
//
// parser stuff
//
enum EParserState
{
eParserState_AwaitingKey,
eParserState_Key,
eParserState_AwaitingValue,
eParserState_Value,
eParserState_Comment
};
enum EParserSpecial
{
eParserSpecial_None, // not parsing a special character
eParserSpecial_Escaped, // awaiting a special character
eParserSpecial_Unicode // parsing a \Uxxx value
};
class MOZ_STACK_CLASS nsPropertiesParser
{
public:
explicit nsPropertiesParser(nsIPersistentProperties* aProps)
: mHaveMultiLine(false)
, mState(eParserState_AwaitingKey)
, mProps(aProps)
{
}
void FinishValueState(nsAString& aOldValue)
{
static const char trimThese[] = " \t";
mKey.Trim(trimThese, false, true);
// This is really ugly hack but it should be fast
char16_t backup_char;
uint32_t minLength = mMinLength;
if (minLength) {
backup_char = mValue[minLength - 1];
mValue.SetCharAt('x', minLength - 1);
}
mValue.Trim(trimThese, false, true);
if (minLength) {
mValue.SetCharAt(backup_char, minLength - 1);
}
mProps->SetStringProperty(NS_ConvertUTF16toUTF8(mKey), mValue, aOldValue);
mSpecialState = eParserSpecial_None;
WaitForKey();
}
EParserState GetState() { return mState; }
static nsresult SegmentWriter(nsIUnicharInputStream* aStream,
void* aClosure,
const char16_t* aFromSegment,
uint32_t aToOffset,
uint32_t aCount,
uint32_t* aWriteCount);
nsresult ParseBuffer(const char16_t* aBuffer, uint32_t aBufferLength);
private:
bool ParseValueCharacter(
char16_t aChar, // character that is just being parsed
const char16_t* aCur, // pointer to character aChar in the buffer
const char16_t*& aTokenStart, // string copying is done in blocks as big as
// possible, aTokenStart points to the beginning
// of this block
nsAString& aOldValue); // when duplicate property is found, new value
// is stored into hashtable and the old one is
// placed in this variable
void WaitForKey()
{
mState = eParserState_AwaitingKey;
}
void EnterKeyState()
{
mKey.Truncate();
mState = eParserState_Key;
}
void WaitForValue()
{
mState = eParserState_AwaitingValue;
}
void EnterValueState()
{
mValue.Truncate();
mMinLength = 0;
mState = eParserState_Value;
mSpecialState = eParserSpecial_None;
}
void EnterCommentState()
{
mState = eParserState_Comment;
}
nsAutoString mKey;
nsAutoString mValue;
uint32_t mUnicodeValuesRead; // should be 4!
char16_t mUnicodeValue; // currently parsed unicode value
bool mHaveMultiLine; // is TRUE when last processed characters form
// any of following sequences:
// - "\\\r"
// - "\\\n"
// - "\\\r\n"
// - any sequence above followed by any
// combination of ' ' and '\t'
bool mMultiLineCanSkipN; // TRUE if "\\\r" was detected
uint32_t mMinLength; // limit right trimming at the end to not trim
// escaped whitespaces
EParserState mState;
// if we see a '\' then we enter this special state
EParserSpecial mSpecialState;
nsCOMPtr<nsIPersistentProperties> mProps;
};
inline bool
IsWhiteSpace(char16_t aChar)
{
return (aChar == ' ') || (aChar == '\t') ||
(aChar == '\r') || (aChar == '\n');
}
inline bool
IsEOL(char16_t aChar)
{
return (aChar == '\r') || (aChar == '\n');
}
bool
nsPropertiesParser::ParseValueCharacter(char16_t aChar, const char16_t* aCur,
const char16_t*& aTokenStart,
nsAString& aOldValue)
{
switch (mSpecialState) {
// the normal state - look for special characters
case eParserSpecial_None:
switch (aChar) {
case '\\':
if (mHaveMultiLine) {
// there is nothing to append to mValue yet
mHaveMultiLine = false;
} else {
mValue += Substring(aTokenStart, aCur);
}
mSpecialState = eParserSpecial_Escaped;
break;
case '\n':
// if we detected multiline and got only "\\\r" ignore next "\n" if any
if (mHaveMultiLine && mMultiLineCanSkipN) {
// but don't allow another '\n' to be skipped
mMultiLineCanSkipN = false;
// Now there is nothing to append to the mValue since we are skipping
// whitespaces at the beginning of the new line of the multiline
// property. Set aTokenStart properly to ensure that nothing is appended
// if we find regular line-end or the end of the buffer.
aTokenStart = aCur + 1;
break;
}
MOZ_FALLTHROUGH;
case '\r':
// we're done! We have a key and value
mValue += Substring(aTokenStart, aCur);
FinishValueState(aOldValue);
mHaveMultiLine = false;
break;
default:
// there is nothing to do with normal characters,
// but handle multilines correctly
if (mHaveMultiLine) {
if (aChar == ' ' || aChar == '\t') {
// don't allow another '\n' to be skipped
mMultiLineCanSkipN = false;
// Now there is nothing to append to the mValue since we are skipping
// whitespaces at the beginning of the new line of the multiline
// property. Set aTokenStart properly to ensure that nothing is appended
// if we find regular line-end or the end of the buffer.
aTokenStart = aCur + 1;
break;
}
mHaveMultiLine = false;
aTokenStart = aCur;
}
break; // from switch on (aChar)
}
break; // from switch on (mSpecialState)
// saw a \ character, so parse the character after that
case eParserSpecial_Escaped:
// probably want to start parsing at the next token
// other characters, like 'u' might override this
aTokenStart = aCur + 1;
mSpecialState = eParserSpecial_None;
switch (aChar) {
// the easy characters - \t, \n, and so forth
case 't':
mValue += char16_t('\t');
mMinLength = mValue.Length();
break;
case 'n':
mValue += char16_t('\n');
mMinLength = mValue.Length();
break;
case 'r':
mValue += char16_t('\r');
mMinLength = mValue.Length();
break;
case '\\':
mValue += char16_t('\\');
break;
// switch to unicode mode!
case 'u':
case 'U':
mSpecialState = eParserSpecial_Unicode;
mUnicodeValuesRead = 0;
mUnicodeValue = 0;
break;
// a \ immediately followed by a newline means we're going multiline
case '\r':
case '\n':
mHaveMultiLine = true;
mMultiLineCanSkipN = (aChar == '\r');
mSpecialState = eParserSpecial_None;
break;
default:
// don't recognize the character, so just append it
mValue += aChar;
break;
}
break;
// we're in the middle of parsing a 4-character unicode value
// like \u5f39
case eParserSpecial_Unicode:
if ('0' <= aChar && aChar <= '9') {
mUnicodeValue =
(mUnicodeValue << 4) | (aChar - '0');
} else if ('a' <= aChar && aChar <= 'f') {
mUnicodeValue =
(mUnicodeValue << 4) | (aChar - 'a' + 0x0a);
} else if ('A' <= aChar && aChar <= 'F') {
mUnicodeValue =
(mUnicodeValue << 4) | (aChar - 'A' + 0x0a);
} else {
// non-hex character. Append what we have, and move on.
mValue += mUnicodeValue;
mMinLength = mValue.Length();
mSpecialState = eParserSpecial_None;
// leave aTokenStart at this unknown character, so it gets appended
aTokenStart = aCur;
// ensure parsing this non-hex character again
return false;
}
if (++mUnicodeValuesRead >= 4) {
aTokenStart = aCur + 1;
mSpecialState = eParserSpecial_None;
mValue += mUnicodeValue;
mMinLength = mValue.Length();
}
break;
}
return true;
}
nsresult
nsPropertiesParser::SegmentWriter(nsIUnicharInputStream* aStream,
void* aClosure,
const char16_t* aFromSegment,
uint32_t aToOffset,
uint32_t aCount,
uint32_t* aWriteCount)
{
nsPropertiesParser* parser = static_cast<nsPropertiesParser*>(aClosure);
parser->ParseBuffer(aFromSegment, aCount);
*aWriteCount = aCount;
return NS_OK;
}
nsresult
nsPropertiesParser::ParseBuffer(const char16_t* aBuffer,
uint32_t aBufferLength)
{
const char16_t* cur = aBuffer;
const char16_t* end = aBuffer + aBufferLength;
// points to the start/end of the current key or value
const char16_t* tokenStart = nullptr;
// if we're in the middle of parsing a key or value, make sure
// the current token points to the beginning of the current buffer
if (mState == eParserState_Key ||
mState == eParserState_Value) {
tokenStart = aBuffer;
}
nsAutoString oldValue;
while (cur != end) {
char16_t c = *cur;
switch (mState) {
case eParserState_AwaitingKey:
if (c == '#' || c == '!') {
EnterCommentState();
}
else if (!IsWhiteSpace(c)) {
// not a comment, not whitespace, we must have found a key!
EnterKeyState();
tokenStart = cur;
}
break;
case eParserState_Key:
if (c == '=' || c == ':') {
mKey += Substring(tokenStart, cur);
WaitForValue();
}
break;
case eParserState_AwaitingValue:
if (IsEOL(c)) {
// no value at all! mimic the normal value-ending
EnterValueState();
FinishValueState(oldValue);
}
// ignore white space leading up to the value
else if (!IsWhiteSpace(c)) {
tokenStart = cur;
EnterValueState();
// make sure to handle this first character
if (ParseValueCharacter(c, cur, tokenStart, oldValue)) {
cur++;
}
// If the character isn't consumed, don't do cur++ and parse
// the character again. This can happen f.e. for char 'X' in sequence
// "\u00X". This character can be control character and must be
// processed again.
continue;
}
break;
case eParserState_Value:
if (ParseValueCharacter(c, cur, tokenStart, oldValue)) {
cur++;
}
// See few lines above for reason of doing this
continue;
case eParserState_Comment:
// stay in this state till we hit EOL
if (c == '\r' || c == '\n') {
WaitForKey();
}
break;
}
// finally, advance to the next character
cur++;
}
// if we're still parsing the value and are in eParserSpecial_None, then
// append whatever we have..
if (mState == eParserState_Value && tokenStart &&
mSpecialState == eParserSpecial_None) {
mValue += Substring(tokenStart, cur);
}
// if we're still parsing the key, then append whatever we have..
else if (mState == eParserState_Key && tokenStart) {
mKey += Substring(tokenStart, cur);
}
return NS_OK;
}
nsPersistentProperties::nsPersistentProperties()
: mIn(nullptr)
, mTable(&property_HashTableOps, sizeof(PropertyTableEntry), 16)
{
PL_INIT_ARENA_POOL(&mArena, "PersistentPropertyArena", 2048);
}
nsPersistentProperties::~nsPersistentProperties()
{
PL_FinishArenaPool(&mArena);
}
nsresult
nsPersistentProperties::Create(nsISupports* aOuter, REFNSIID aIID,
void** aResult)
{
if (aOuter) {
return NS_ERROR_NO_AGGREGATION;
}
RefPtr<nsPersistentProperties> props = new nsPersistentProperties();
return props->QueryInterface(aIID, aResult);
}
NS_IMPL_ISUPPORTS(nsPersistentProperties, nsIPersistentProperties, nsIProperties)
NS_IMETHODIMP
nsPersistentProperties::Load(nsIInputStream* aIn)
{
nsresult rv = NS_NewUnicharInputStream(aIn, getter_AddRefs(mIn));
if (rv != NS_OK) {
NS_WARNING("Error creating UnicharInputStream");
return NS_ERROR_FAILURE;
}
nsPropertiesParser parser(this);
uint32_t nProcessed;
// If this 4096 is changed to some other value, make sure to adjust
// the bug121341.properties test file accordingly.
while (NS_SUCCEEDED(rv = mIn->ReadSegments(nsPropertiesParser::SegmentWriter,
&parser, 4096, &nProcessed)) &&
nProcessed != 0);
mIn = nullptr;
if (NS_FAILED(rv)) {
return rv;
}
// We may have an unprocessed value at this point
// if the last line did not have a proper line ending.
if (parser.GetState() == eParserState_Value) {
nsAutoString oldValue;
parser.FinishValueState(oldValue);
}
return NS_OK;
}
NS_IMETHODIMP
nsPersistentProperties::SetStringProperty(const nsACString& aKey,
const nsAString& aNewValue,
nsAString& aOldValue)
{
const nsAFlatCString& flatKey = PromiseFlatCString(aKey);
auto entry = static_cast<PropertyTableEntry*>
(mTable.Add(flatKey.get()));
if (entry->mKey) {
aOldValue = entry->mValue;
NS_WARNING(nsPrintfCString("the property %s already exists",
flatKey.get()).get());
} else {
aOldValue.Truncate();
}
entry->mKey = ArenaStrdup(flatKey, &mArena);
entry->mValue = ArenaStrdup(PromiseFlatString(aNewValue), &mArena);
return NS_OK;
}
NS_IMETHODIMP
nsPersistentProperties::Save(nsIOutputStream* aOut, const nsACString& aHeader)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsPersistentProperties::GetStringProperty(const nsACString& aKey,
nsAString& aValue)
{
const nsAFlatCString& flatKey = PromiseFlatCString(aKey);
auto entry = static_cast<PropertyTableEntry*>(mTable.Search(flatKey.get()));
if (!entry) {
return NS_ERROR_FAILURE;
}
aValue = entry->mValue;
return NS_OK;
}
NS_IMETHODIMP
nsPersistentProperties::Enumerate(nsISimpleEnumerator** aResult)
{
nsCOMArray<nsIPropertyElement> props;
// We know the necessary size; we can avoid growing it while adding elements
props.SetCapacity(mTable.EntryCount());
// Step through hash entries populating a transient array
for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) {
auto entry = static_cast<PropertyTableEntry*>(iter.Get());
RefPtr<nsPropertyElement> element =
new nsPropertyElement(nsDependentCString(entry->mKey),
nsDependentString(entry->mValue));
if (!props.AppendObject(element)) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
return NS_NewArrayEnumerator(aResult, props);
}
////////////////////////////////////////////////////////////////////////////////
// XXX Some day we'll unify the nsIPersistentProperties interface with
// nsIProperties, but until now...
NS_IMETHODIMP
nsPersistentProperties::Get(const char* aProp, const nsIID& aUUID,
void** aResult)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsPersistentProperties::Set(const char* aProp, nsISupports* value)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsPersistentProperties::Undefine(const char* aProp)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsPersistentProperties::Has(const char* aProp, bool* aResult)
{
*aResult = !!mTable.Search(aProp);
return NS_OK;
}
NS_IMETHODIMP
nsPersistentProperties::GetKeys(uint32_t* aCount, char*** aKeys)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
////////////////////////////////////////////////////////////////////////////////
// PropertyElement
////////////////////////////////////////////////////////////////////////////////
nsresult
nsPropertyElement::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
{
if (aOuter) {
return NS_ERROR_NO_AGGREGATION;
}
RefPtr<nsPropertyElement> propElem = new nsPropertyElement();
return propElem->QueryInterface(aIID, aResult);
}
NS_IMPL_ISUPPORTS(nsPropertyElement, nsIPropertyElement)
NS_IMETHODIMP
nsPropertyElement::GetKey(nsACString& aReturnKey)
{
aReturnKey = mKey;
return NS_OK;
}
NS_IMETHODIMP
nsPropertyElement::GetValue(nsAString& aReturnValue)
{
aReturnValue = mValue;
return NS_OK;
}
NS_IMETHODIMP
nsPropertyElement::SetKey(const nsACString& aKey)
{
mKey = aKey;
return NS_OK;
}
NS_IMETHODIMP
nsPropertyElement::SetValue(const nsAString& aValue)
{
mValue = aValue;
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,67 @@
/* -*- 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 nsPersistentProperties_h___
#define nsPersistentProperties_h___
#include "nsIPersistentProperties2.h"
#include "PLDHashTable.h"
#include "plarena.h"
#include "nsString.h"
#include "nsCOMPtr.h"
#include "mozilla/Attributes.h"
class nsIUnicharInputStream;
class nsPersistentProperties final : public nsIPersistentProperties
{
public:
nsPersistentProperties();
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIPROPERTIES
NS_DECL_NSIPERSISTENTPROPERTIES
static MOZ_MUST_USE nsresult
Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
private:
~nsPersistentProperties();
protected:
nsCOMPtr<nsIUnicharInputStream> mIn;
PLDHashTable mTable;
PLArenaPool mArena;
};
class nsPropertyElement final : public nsIPropertyElement
{
public:
nsPropertyElement()
{
}
nsPropertyElement(const nsACString& aKey, const nsAString& aValue)
: mKey(aKey)
, mValue(aValue)
{
}
NS_DECL_ISUPPORTS
NS_DECL_NSIPROPERTYELEMENT
static nsresult Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
private:
~nsPropertyElement() {}
protected:
nsCString mKey;
nsString mValue;
};
#endif /* nsPersistentProperties_h___ */

99
xpcom/ds/nsProperties.cpp Normal file
View file

@ -0,0 +1,99 @@
/* -*- 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 "nsProperties.h"
////////////////////////////////////////////////////////////////////////////////
NS_IMPL_AGGREGATED(nsProperties)
NS_INTERFACE_MAP_BEGIN_AGGREGATED(nsProperties)
NS_INTERFACE_MAP_ENTRY(nsIProperties)
NS_INTERFACE_MAP_END
NS_IMETHODIMP
nsProperties::Get(const char* prop, const nsIID& uuid, void** result)
{
if (NS_WARN_IF(!prop)) {
return NS_ERROR_INVALID_ARG;
}
nsCOMPtr<nsISupports> value;
if (!nsProperties_HashBase::Get(prop, getter_AddRefs(value))) {
return NS_ERROR_FAILURE;
}
return (value) ? value->QueryInterface(uuid, result) : NS_ERROR_NO_INTERFACE;
}
NS_IMETHODIMP
nsProperties::Set(const char* prop, nsISupports* value)
{
if (NS_WARN_IF(!prop)) {
return NS_ERROR_INVALID_ARG;
}
Put(prop, value);
return NS_OK;
}
NS_IMETHODIMP
nsProperties::Undefine(const char* prop)
{
if (NS_WARN_IF(!prop)) {
return NS_ERROR_INVALID_ARG;
}
nsCOMPtr<nsISupports> value;
if (!nsProperties_HashBase::Get(prop, getter_AddRefs(value))) {
return NS_ERROR_FAILURE;
}
Remove(prop);
return NS_OK;
}
NS_IMETHODIMP
nsProperties::Has(const char* prop, bool* result)
{
if (NS_WARN_IF(!prop)) {
return NS_ERROR_INVALID_ARG;
}
nsCOMPtr<nsISupports> value;
*result = nsProperties_HashBase::Get(prop, getter_AddRefs(value));
return NS_OK;
}
NS_IMETHODIMP
nsProperties::GetKeys(uint32_t* aCount, char*** aKeys)
{
if (NS_WARN_IF(!aCount) || NS_WARN_IF(!aKeys)) {
return NS_ERROR_INVALID_ARG;
}
uint32_t count = Count();
char** keys = (char**)moz_xmalloc(count * sizeof(char*));
uint32_t j = 0;
for (auto iter = this->Iter(); !iter.Done(); iter.Next()) {
const char* key = iter.Key();
keys[j] = strdup(key);
if (!keys[j]) {
// Free 'em all
for (uint32_t i = 0; i < j; i++) {
free(keys[i]);
}
free(keys);
return NS_ERROR_OUT_OF_MEMORY;
}
j++;
}
*aCount = count;
*aKeys = keys;
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////

39
xpcom/ds/nsProperties.h Normal file
View file

@ -0,0 +1,39 @@
/* -*- 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 nsProperties_h___
#define nsProperties_h___
#include "nsIProperties.h"
#include "nsInterfaceHashtable.h"
#include "nsHashKeys.h"
#include "nsAgg.h"
#include "mozilla/Attributes.h"
#define NS_PROPERTIES_CID \
{ /* 4de2bc90-b1bf-11d3-93b6-00104ba0fd40 */ \
0x4de2bc90, \
0xb1bf, \
0x11d3, \
{0x93, 0xb6, 0x00, 0x10, 0x4b, 0xa0, 0xfd, 0x40} \
}
typedef nsInterfaceHashtable<nsCharPtrHashKey,
nsISupports> nsProperties_HashBase;
class nsProperties final
: public nsIProperties
, public nsProperties_HashBase
{
public:
NS_DECL_AGGREGATED
NS_DECL_NSIPROPERTIES
explicit nsProperties(nsISupports *aOuter) { NS_INIT_AGGREGATED(aOuter); }
~nsProperties() {}
};
#endif /* nsProperties_h___ */

53
xpcom/ds/nsStaticAtom.h Normal file
View file

@ -0,0 +1,53 @@
/* -*- 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 nsStaticAtom_h__
#define nsStaticAtom_h__
#include "nsIAtom.h"
#include "nsStringBuffer.h"
#define NS_STATIC_ATOM(buffer_name, atom_ptr) \
{ (nsStringBuffer*) &buffer_name, atom_ptr }
#define NS_STATIC_ATOM_BUFFER(buffer_name, str_data) \
static nsFakeStringBuffer<sizeof(str_data)> buffer_name = \
{ 1, sizeof(str_data) * sizeof(char16_t), (u"" str_data) };
/**
* Holds data used to initialize large number of atoms during startup. Use
* the above macros to initialize these structs. They should never be accessed
* directly other than from AtomTable.cpp
*/
struct nsStaticAtom
{
// mStringBuffer points to the string buffer for a permanent atom, and is
// therefore safe as a non-owning reference.
nsStringBuffer* MOZ_NON_OWNING_REF mStringBuffer;
nsIAtom** mAtom;
};
/**
* This is a struct with the same binary layout as a nsStringBuffer.
*/
template<uint32_t size>
struct nsFakeStringBuffer
{
int32_t mRefCnt;
uint32_t mSize;
char16_t mStringData[size];
};
// Register an array of static atoms with the atom table
template<uint32_t N>
void
NS_RegisterStaticAtoms(const nsStaticAtom (&aAtoms)[N])
{
extern void RegisterStaticAtoms(const nsStaticAtom*, uint32_t aAtomCount);
RegisterStaticAtoms(aAtoms, N);
}
#endif

View file

@ -0,0 +1,201 @@
/* -*- 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/. */
/* Class to manage lookup of static names in a table. */
#include "nsCRT.h"
#include "nscore.h"
#include "mozilla/HashFunctions.h"
#include "nsISupportsImpl.h"
#define PL_ARENA_CONST_ALIGN_MASK 3
#include "nsStaticNameTable.h"
using namespace mozilla;
struct NameTableKey
{
NameTableKey(const nsDependentCString aNameArray[],
const nsAFlatCString* aKeyStr)
: mNameArray(aNameArray)
, mIsUnichar(false)
{
mKeyStr.m1b = aKeyStr;
}
NameTableKey(const nsDependentCString aNameArray[],
const nsAFlatString* aKeyStr)
: mNameArray(aNameArray)
, mIsUnichar(true)
{
mKeyStr.m2b = aKeyStr;
}
const nsDependentCString* mNameArray;
union
{
const nsAFlatCString* m1b;
const nsAFlatString* m2b;
} mKeyStr;
bool mIsUnichar;
};
struct NameTableEntry : public PLDHashEntryHdr
{
int32_t mIndex;
};
static bool
matchNameKeysCaseInsensitive(const PLDHashEntryHdr* aHdr, const void* aVoidKey)
{
auto entry = static_cast<const NameTableEntry*>(aHdr);
auto key = static_cast<const NameTableKey*>(aVoidKey);
const nsDependentCString* name = &key->mNameArray[entry->mIndex];
return key->mIsUnichar
? key->mKeyStr.m2b->LowerCaseEqualsASCII(name->get(), name->Length())
: key->mKeyStr.m1b->LowerCaseEqualsASCII(name->get(), name->Length());
}
/*
* caseInsensitiveHashKey is just like PLDHashTable::HashStringKey except it
* uses (*s & ~0x20) instead of simply *s. This means that "aFOO" and
* "afoo" and "aFoo" will all hash to the same thing. It also means
* that some strings that aren't case-insensensitively equal will hash
* to the same value, but it's just a hash function so it doesn't
* matter.
*/
static PLDHashNumber
caseInsensitiveStringHashKey(const void* aKey)
{
PLDHashNumber h = 0;
const NameTableKey* tableKey = static_cast<const NameTableKey*>(aKey);
if (tableKey->mIsUnichar) {
for (const char16_t* s = tableKey->mKeyStr.m2b->get();
*s != '\0';
s++) {
h = AddToHash(h, *s & ~0x20);
}
} else {
for (const unsigned char* s = reinterpret_cast<const unsigned char*>(
tableKey->mKeyStr.m1b->get());
*s != '\0';
s++) {
h = AddToHash(h, *s & ~0x20);
}
}
return h;
}
static const struct PLDHashTableOps nametable_CaseInsensitiveHashTableOps = {
caseInsensitiveStringHashKey,
matchNameKeysCaseInsensitive,
PLDHashTable::MoveEntryStub,
PLDHashTable::ClearEntryStub,
nullptr,
};
nsStaticCaseInsensitiveNameTable::nsStaticCaseInsensitiveNameTable(
const char* const aNames[], int32_t aLength)
: mNameArray(nullptr)
, mNameTable(&nametable_CaseInsensitiveHashTableOps,
sizeof(NameTableEntry), aLength)
, mNullStr("")
{
MOZ_COUNT_CTOR(nsStaticCaseInsensitiveNameTable);
MOZ_ASSERT(aNames, "null name table");
MOZ_ASSERT(aLength, "0 length");
mNameArray = (nsDependentCString*)
moz_xmalloc(aLength * sizeof(nsDependentCString));
for (int32_t index = 0; index < aLength; ++index) {
const char* raw = aNames[index];
#ifdef DEBUG
{
// verify invariants of contents
nsAutoCString temp1(raw);
nsDependentCString temp2(raw);
ToLowerCase(temp1);
MOZ_ASSERT(temp1.Equals(temp2), "upper case char in table");
MOZ_ASSERT(nsCRT::IsAscii(raw),
"non-ascii string in table -- "
"case-insensitive matching won't work right");
}
#endif
// use placement-new to initialize the string object
nsDependentCString* strPtr = &mNameArray[index];
new (strPtr) nsDependentCString(raw);
NameTableKey key(mNameArray, strPtr);
auto entry = static_cast<NameTableEntry*>(mNameTable.Add(&key, fallible));
if (!entry) {
continue;
}
// If the entry already exists it's unlikely but possible that its index is
// zero, in which case this assertion won't fail. But if the index is
// non-zero (highly likely) then it will fail. In other words, this
// assertion is likely but not guaranteed to detect if an entry is already
// used.
MOZ_ASSERT(entry->mIndex == 0, "Entry already exists!");
entry->mIndex = index;
}
#ifdef DEBUG
mNameTable.MarkImmutable();
#endif
}
nsStaticCaseInsensitiveNameTable::~nsStaticCaseInsensitiveNameTable()
{
// manually call the destructor on placement-new'ed objects
for (uint32_t index = 0; index < mNameTable.EntryCount(); index++) {
mNameArray[index].~nsDependentCString();
}
free((void*)mNameArray);
MOZ_COUNT_DTOR(nsStaticCaseInsensitiveNameTable);
}
int32_t
nsStaticCaseInsensitiveNameTable::Lookup(const nsACString& aName)
{
NS_ASSERTION(mNameArray, "not inited");
const nsAFlatCString& str = PromiseFlatCString(aName);
NameTableKey key(mNameArray, &str);
auto entry = static_cast<NameTableEntry*>(mNameTable.Search(&key));
return entry ? entry->mIndex : nsStaticCaseInsensitiveNameTable::NOT_FOUND;
}
int32_t
nsStaticCaseInsensitiveNameTable::Lookup(const nsAString& aName)
{
NS_ASSERTION(mNameArray, "not inited");
const nsAFlatString& str = PromiseFlatString(aName);
NameTableKey key(mNameArray, &str);
auto entry = static_cast<NameTableEntry*>(mNameTable.Search(&key));
return entry ? entry->mIndex : nsStaticCaseInsensitiveNameTable::NOT_FOUND;
}
const nsAFlatCString&
nsStaticCaseInsensitiveNameTable::GetStringValue(int32_t aIndex)
{
NS_ASSERTION(mNameArray, "not inited");
if ((NOT_FOUND < aIndex) && ((uint32_t)aIndex < mNameTable.EntryCount())) {
return mNameArray[aIndex];
}
return mNullStr;
}

View file

@ -0,0 +1,49 @@
/* -*- 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/. */
/* Classes to manage lookup of static names in a table. */
#ifndef nsStaticNameTable_h___
#define nsStaticNameTable_h___
#include "PLDHashTable.h"
#include "nsString.h"
/* This class supports case insensitive lookup.
*
* It differs from atom tables:
* - It supports case insensitive lookup.
* - It has minimal footprint by not copying the string table.
* - It does no locking.
* - It returns zero based indexes and const nsCString& as required by its
* callers in the parser.
* - It is not an xpcom interface - meant for fast lookup in static tables.
*
* ***REQUIREMENTS***
* - It *requires* that all entries in the table be lowercase only.
* - It *requires* that the table of strings be in memory that lives at least
* as long as this table object - typically a static string array.
*/
class nsStaticCaseInsensitiveNameTable
{
public:
enum { NOT_FOUND = -1 };
int32_t Lookup(const nsACString& aName);
int32_t Lookup(const nsAString& aName);
const nsAFlatCString& GetStringValue(int32_t aIndex);
nsStaticCaseInsensitiveNameTable(const char* const aNames[], int32_t aLength);
~nsStaticCaseInsensitiveNameTable();
private:
nsDependentCString* mNameArray;
PLDHashTable mNameTable;
nsDependentCString mNullStr;
};
#endif /* nsStaticNameTable_h___ */

View file

@ -0,0 +1,262 @@
/* -*- 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 "nsStringEnumerator.h"
#include "nsISimpleEnumerator.h"
#include "nsSupportsPrimitives.h"
#include "mozilla/Attributes.h"
#include "nsTArray.h"
//
// nsStringEnumerator
//
class nsStringEnumerator final
: public nsIStringEnumerator
, public nsIUTF8StringEnumerator
, public nsISimpleEnumerator
{
public:
nsStringEnumerator(const nsTArray<nsString>* aArray, bool aOwnsArray)
: mArray(aArray)
, mIndex(0)
, mOwnsArray(aOwnsArray)
, mIsUnicode(true)
{}
nsStringEnumerator(const nsTArray<nsCString>* aArray, bool aOwnsArray)
: mCArray(aArray)
, mIndex(0)
, mOwnsArray(aOwnsArray)
, mIsUnicode(false)
{}
nsStringEnumerator(const nsTArray<nsString>* aArray, nsISupports* aOwner)
: mArray(aArray)
, mIndex(0)
, mOwner(aOwner)
, mOwnsArray(false)
, mIsUnicode(true)
{}
nsStringEnumerator(const nsTArray<nsCString>* aArray, nsISupports* aOwner)
: mCArray(aArray)
, mIndex(0)
, mOwner(aOwner)
, mOwnsArray(false)
, mIsUnicode(false)
{}
NS_DECL_ISUPPORTS
NS_DECL_NSIUTF8STRINGENUMERATOR
// have to declare nsIStringEnumerator manually, because of
// overlapping method names
NS_IMETHOD GetNext(nsAString& aResult) override;
NS_DECL_NSISIMPLEENUMERATOR
private:
~nsStringEnumerator()
{
if (mOwnsArray) {
// const-casting is safe here, because the NS_New*
// constructors make sure mOwnsArray is consistent with
// the constness of the objects
if (mIsUnicode) {
delete const_cast<nsTArray<nsString>*>(mArray);
} else {
delete const_cast<nsTArray<nsCString>*>(mCArray);
}
}
}
union
{
const nsTArray<nsString>* mArray;
const nsTArray<nsCString>* mCArray;
};
inline uint32_t Count()
{
return mIsUnicode ? mArray->Length() : mCArray->Length();
}
uint32_t mIndex;
// the owner allows us to hold a strong reference to the object
// that owns the array. Having a non-null value in mOwner implies
// that mOwnsArray is false, because we rely on the real owner
// to release the array
nsCOMPtr<nsISupports> mOwner;
bool mOwnsArray;
bool mIsUnicode;
};
NS_IMPL_ISUPPORTS(nsStringEnumerator,
nsIStringEnumerator,
nsIUTF8StringEnumerator,
nsISimpleEnumerator)
NS_IMETHODIMP
nsStringEnumerator::HasMore(bool* aResult)
{
*aResult = mIndex < Count();
return NS_OK;
}
NS_IMETHODIMP
nsStringEnumerator::HasMoreElements(bool* aResult)
{
return HasMore(aResult);
}
NS_IMETHODIMP
nsStringEnumerator::GetNext(nsISupports** aResult)
{
if (mIsUnicode) {
nsSupportsString* stringImpl = new nsSupportsString();
if (!stringImpl) {
return NS_ERROR_OUT_OF_MEMORY;
}
stringImpl->SetData(mArray->ElementAt(mIndex++));
*aResult = stringImpl;
} else {
nsSupportsCString* cstringImpl = new nsSupportsCString();
if (!cstringImpl) {
return NS_ERROR_OUT_OF_MEMORY;
}
cstringImpl->SetData(mCArray->ElementAt(mIndex++));
*aResult = cstringImpl;
}
NS_ADDREF(*aResult);
return NS_OK;
}
NS_IMETHODIMP
nsStringEnumerator::GetNext(nsAString& aResult)
{
if (NS_WARN_IF(mIndex >= Count())) {
return NS_ERROR_UNEXPECTED;
}
if (mIsUnicode) {
aResult = mArray->ElementAt(mIndex++);
} else {
CopyUTF8toUTF16(mCArray->ElementAt(mIndex++), aResult);
}
return NS_OK;
}
NS_IMETHODIMP
nsStringEnumerator::GetNext(nsACString& aResult)
{
if (NS_WARN_IF(mIndex >= Count())) {
return NS_ERROR_UNEXPECTED;
}
if (mIsUnicode) {
CopyUTF16toUTF8(mArray->ElementAt(mIndex++), aResult);
} else {
aResult = mCArray->ElementAt(mIndex++);
}
return NS_OK;
}
template<class T>
static inline nsresult
StringEnumeratorTail(T** aResult)
{
if (!*aResult) {
return NS_ERROR_OUT_OF_MEMORY;
}
NS_ADDREF(*aResult);
return NS_OK;
}
//
// constructors
//
nsresult
NS_NewStringEnumerator(nsIStringEnumerator** aResult,
const nsTArray<nsString>* aArray, nsISupports* aOwner)
{
if (NS_WARN_IF(!aResult) || NS_WARN_IF(!aArray)) {
return NS_ERROR_INVALID_ARG;
}
*aResult = new nsStringEnumerator(aArray, aOwner);
return StringEnumeratorTail(aResult);
}
nsresult
NS_NewUTF8StringEnumerator(nsIUTF8StringEnumerator** aResult,
const nsTArray<nsCString>* aArray,
nsISupports* aOwner)
{
if (NS_WARN_IF(!aResult) || NS_WARN_IF(!aArray)) {
return NS_ERROR_INVALID_ARG;
}
*aResult = new nsStringEnumerator(aArray, aOwner);
return StringEnumeratorTail(aResult);
}
nsresult
NS_NewAdoptingStringEnumerator(nsIStringEnumerator** aResult,
nsTArray<nsString>* aArray)
{
if (NS_WARN_IF(!aResult) || NS_WARN_IF(!aArray)) {
return NS_ERROR_INVALID_ARG;
}
*aResult = new nsStringEnumerator(aArray, true);
return StringEnumeratorTail(aResult);
}
nsresult
NS_NewAdoptingUTF8StringEnumerator(nsIUTF8StringEnumerator** aResult,
nsTArray<nsCString>* aArray)
{
if (NS_WARN_IF(!aResult) || NS_WARN_IF(!aArray)) {
return NS_ERROR_INVALID_ARG;
}
*aResult = new nsStringEnumerator(aArray, true);
return StringEnumeratorTail(aResult);
}
// const ones internally just forward to the non-const equivalents
nsresult
NS_NewStringEnumerator(nsIStringEnumerator** aResult,
const nsTArray<nsString>* aArray)
{
if (NS_WARN_IF(!aResult) || NS_WARN_IF(!aArray)) {
return NS_ERROR_INVALID_ARG;
}
*aResult = new nsStringEnumerator(aArray, false);
return StringEnumeratorTail(aResult);
}
nsresult
NS_NewUTF8StringEnumerator(nsIUTF8StringEnumerator** aResult,
const nsTArray<nsCString>* aArray)
{
if (NS_WARN_IF(!aResult) || NS_WARN_IF(!aArray)) {
return NS_ERROR_INVALID_ARG;
}
*aResult = new nsStringEnumerator(aArray, false);
return StringEnumeratorTail(aResult);
}

View file

@ -0,0 +1,91 @@
/* -*- 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 "nsIStringEnumerator.h"
#include "nsStringFwd.h"
#include "nsTArrayForwardDeclare.h"
// nsIStringEnumerator/nsIUTF8StringEnumerator implementations
//
// Currently all implementations support both interfaces. The
// constructors below provide the most common interface for the given
// type (i.e. nsIStringEnumerator for char16_t* strings, and so
// forth) but any resulting enumerators can be queried to the other
// type. Internally, the enumerators will hold onto the type that was
// passed in and do conversion if GetNext() for the other type of
// string is called.
// There are a few different types of enumerators:
//
// These enumerators hold a pointer to the array. Be careful
// because modifying the array may confuse the iterator, especially if
// you insert or remove elements in the middle of the array.
//
// The non-adopting enumerator requires that the array sticks around
// at least as long as the enumerator does. These are for constant
// string arrays that the enumerator does not own, this could be used
// in VERY specialized cases such as when the provider KNOWS that the
// string enumerator will be consumed immediately, or will at least
// outlast the array.
// For example:
//
// nsTArray<nsCString> array;
// array.AppendCString("abc");
// array.AppendCString("def");
// NS_NewStringEnumerator(&enumerator, &array, true);
//
// // call some internal method which iterates the enumerator
// InternalMethod(enumerator);
// NS_RELEASE(enumerator);
//
MOZ_MUST_USE nsresult
NS_NewStringEnumerator(nsIStringEnumerator** aResult,
const nsTArray<nsString>* aArray,
nsISupports* aOwner);
MOZ_MUST_USE nsresult
NS_NewUTF8StringEnumerator(nsIUTF8StringEnumerator** aResult,
const nsTArray<nsCString>* aArray);
MOZ_MUST_USE nsresult
NS_NewStringEnumerator(nsIStringEnumerator** aResult,
const nsTArray<nsString>* aArray);
// Adopting string enumerators assume ownership of the array and will
// call |operator delete| on the array when the enumerator is destroyed
// this is useful when the provider creates an array solely for the
// purpose of creating the enumerator.
// For example:
//
// nsTArray<nsCString>* array = new nsTArray<nsCString>;
// array->AppendString("abcd");
// NS_NewAdoptingStringEnumerator(&result, array);
MOZ_MUST_USE nsresult
NS_NewAdoptingStringEnumerator(nsIStringEnumerator** aResult,
nsTArray<nsString>* aArray);
MOZ_MUST_USE nsresult
NS_NewAdoptingUTF8StringEnumerator(nsIUTF8StringEnumerator** aResult,
nsTArray<nsCString>* aArray);
// these versions take a refcounted "owner" which will be addreffed
// when the enumerator is created, and destroyed when the enumerator
// is released. This allows providers to give non-owning pointers to
// ns*StringArray member variables without worrying about lifetime
// issues
// For example:
//
// nsresult MyClass::Enumerate(nsIUTF8StringEnumerator** aResult) {
// mCategoryList->AppendString("abcd");
// return NS_NewStringEnumerator(aResult, mCategoryList, this);
// }
//
MOZ_MUST_USE nsresult
NS_NewUTF8StringEnumerator(nsIUTF8StringEnumerator** aResult,
const nsTArray<nsCString>* aArray,
nsISupports* aOwner);

View file

@ -0,0 +1,264 @@
/* -*- 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 <stdint.h>
#include <string.h>
#include "nsArrayEnumerator.h"
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
#include "nsSupportsArray.h"
#include "nsSupportsArrayEnumerator.h"
// Disable deprecation warnings generated by nsISupportsArray and associated
// classes.
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#elif defined(_MSC_VER)
#pragma warning (push)
#pragma warning (disable : 4996)
#endif
nsresult
nsQueryElementAt::operator()(const nsIID& aIID, void** aResult) const
{
nsresult status =
mCollection ? mCollection->QueryElementAt(mIndex, aIID, aResult) :
NS_ERROR_NULL_POINTER;
if (mErrorPtr) {
*mErrorPtr = status;
}
return status;
}
nsSupportsArray::nsSupportsArray()
{
}
nsSupportsArray::~nsSupportsArray()
{
Clear();
}
nsresult
nsSupportsArray::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
{
if (aOuter) {
return NS_ERROR_NO_AGGREGATION;
}
nsCOMPtr<nsISupportsArray> it = new nsSupportsArray();
return it->QueryInterface(aIID, aResult);
}
NS_IMPL_ISUPPORTS(nsSupportsArray, nsIArray, nsISupportsArray, nsICollection,
nsISerializable)
NS_IMETHODIMP
nsSupportsArray::Read(nsIObjectInputStream* aStream)
{
nsresult rv;
uint32_t newArraySize;
rv = aStream->Read32(&newArraySize);
if (NS_FAILED(rv)) {
return rv;
}
uint32_t count;
rv = aStream->Read32(&count);
if (NS_FAILED(rv)) {
return rv;
}
NS_ASSERTION(count <= newArraySize, "overlarge mCount!");
if (count > newArraySize) {
count = newArraySize;
}
// Don't clear out our array until we know we have enough space for the new
// one and have successfully copied everything out of the stream.
nsCOMArray<nsISupports> tmp;
tmp.SetCapacity(newArraySize);
tmp.SetCount(count);
auto elems = tmp.Elements();
for (uint32_t i = 0; i < count; i++) {
rv = aStream->ReadObject(true, &elems[i]);
if (NS_FAILED(rv)) {
return rv;
}
}
// Now clear out existing refs and replace with the new array.
mArray.Clear();
mArray.SwapElements(tmp);
return NS_OK;
}
NS_IMETHODIMP
nsSupportsArray::Write(nsIObjectOutputStream* aStream)
{
nsresult rv;
rv = aStream->Write32(mArray.Capacity());
if (NS_FAILED(rv)) {
return rv;
}
rv = aStream->Write32(mArray.Length());
if (NS_FAILED(rv)) {
return rv;
}
for (size_t i = 0; i < mArray.Length(); i++) {
rv = aStream->WriteObject(mArray[i], true);
if (NS_FAILED(rv)) {
return rv;
}
}
return NS_OK;
}
NS_IMETHODIMP
nsSupportsArray::GetElementAt(uint32_t aIndex, nsISupports** aOutPtr)
{
nsCOMPtr<nsISupports> elm = mArray.SafeElementAt(aIndex);
elm.forget(aOutPtr);
return NS_OK;
}
NS_IMETHODIMP_(int32_t)
nsSupportsArray::IndexOf(const nsISupports* aPossibleElement)
{
// nsCOMArray takes a non-const param, but it just passes through to
// nsTArray which takes a const param.
return mArray.IndexOf(const_cast<nsISupports*>(aPossibleElement));
}
NS_IMETHODIMP_(bool)
nsSupportsArray::InsertElementAt(nsISupports* aElement, uint32_t aIndex)
{
return mArray.InsertObjectAt(aElement, aIndex);
}
NS_IMETHODIMP_(bool)
nsSupportsArray::ReplaceElementAt(nsISupports* aElement, uint32_t aIndex)
{
// nsCOMArray::ReplaceObjectAt will grow the array if necessary. Instead
// we do the bounds check and only replace if it's in range.
if (aIndex < mArray.Length()) {
mArray.ReplaceElementAt(aIndex, aElement);
return true;
}
return false;
}
NS_IMETHODIMP_(bool)
nsSupportsArray::RemoveElementAt(uint32_t aIndex)
{
return mArray.RemoveObjectAt(aIndex);
}
NS_IMETHODIMP
nsSupportsArray::RemoveElement(nsISupports* aElement)
{
return mArray.RemoveObject(aElement) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsSupportsArray::Clear(void)
{
mArray.Clear();
return NS_OK;
}
NS_IMETHODIMP
nsSupportsArray::DeprecatedEnumerate(nsIEnumerator** aResult)
{
RefPtr<nsSupportsArrayEnumerator> e = new nsSupportsArrayEnumerator(this);
e.forget(aResult);
return NS_OK;
}
NS_IMETHODIMP
nsSupportsArray::Clone(nsISupportsArray** aResult)
{
nsCOMPtr<nsISupportsArray> newArray;
nsresult rv = NS_NewISupportsArray(getter_AddRefs(newArray));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
for (size_t i = 0; i < mArray.Length(); i++) {
// AppendElement does an odd cast of bool to nsresult, we just cast back
// here.
if (!(bool)newArray->AppendElement(mArray[i])) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
newArray.forget(aResult);
return NS_OK;
}
nsresult
NS_NewISupportsArray(nsISupportsArray** aInstancePtrResult)
{
nsresult rv;
rv = nsSupportsArray::Create(nullptr, NS_GET_IID(nsISupportsArray),
(void**)aInstancePtrResult);
return rv;
}
/**
* nsIArray adapters.
*/
NS_IMETHODIMP
nsSupportsArray::GetLength(uint32_t* aLength) {
return Count(aLength);
}
NS_IMETHODIMP
nsSupportsArray::QueryElementAt(uint32_t aIndex, const nsIID& aIID, void** aResult)
{
nsISupports* element = mArray.SafeElementAt(aIndex);
if (element) {
return element->QueryInterface(aIID, aResult);
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsSupportsArray::IndexOf(uint32_t aStartIndex, nsISupports* aElement, uint32_t* aResult)
{
int32_t idx = mArray.IndexOf(aElement, aStartIndex);
if (idx < 0) {
return NS_ERROR_FAILURE;
}
*aResult = static_cast<uint32_t>(idx);
return NS_OK;
}
NS_IMETHODIMP
nsSupportsArray::Enumerate(nsISimpleEnumerator** aResult)
{
return NS_NewArrayEnumerator(aResult, this);
}
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#elif defined(_MSC_VER)
#pragma warning (pop)
#endif

107
xpcom/ds/nsSupportsArray.h Normal file
View file

@ -0,0 +1,107 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsSupportsArray_h__
#define nsSupportsArray_h__
#include "nsIArray.h"
#include "nsCOMArray.h"
#include "mozilla/Attributes.h"
// Disable deprecation warnings generated by nsISupportsArray and associated
// classes.
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#elif defined(_MSC_VER)
#pragma warning (push)
#pragma warning (disable : 4996)
#endif
#include "nsISupportsArray.h"
class nsSupportsArray final : public nsISupportsArray,
public nsIArray
{
~nsSupportsArray(void); // nonvirtual since we're not subclassed
public:
nsSupportsArray(void);
static MOZ_MUST_USE nsresult
Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSISERIALIZABLE
// nsICollection methods:
NS_IMETHOD Count(uint32_t* aResult) override
{
*aResult = mArray.Length();
return NS_OK;
}
NS_IMETHOD GetElementAt(uint32_t aIndex, nsISupports** aResult) override;
MOZ_MUST_USE NS_IMETHOD
SetElementAt(uint32_t aIndex, nsISupports* aValue) override
{
return ReplaceElementAt(aValue, aIndex) ? NS_OK : NS_ERROR_FAILURE;
}
MOZ_MUST_USE NS_IMETHOD AppendElement(nsISupports* aElement) override
{
// XXX Invalid cast of bool to nsresult (bug 778110)
return (nsresult)InsertElementAt(aElement, mArray.Length())/* ? NS_OK : NS_ERROR_FAILURE*/;
}
// XXX this is badly named - should be RemoveFirstElement
MOZ_MUST_USE NS_IMETHOD RemoveElement(nsISupports* aElement) override;
NS_IMETHOD DeprecatedEnumerate(nsIEnumerator** aResult) override;
NS_IMETHOD Clear(void) override;
// nsISupportsArray methods:
NS_IMETHOD_(int32_t) IndexOf(const nsISupports* aPossibleElement) override;
NS_IMETHOD GetIndexOf(nsISupports* aPossibleElement, int32_t* aResult) override
{
*aResult = IndexOf(aPossibleElement);
return NS_OK;
}
MOZ_MUST_USE NS_IMETHOD_(bool)
InsertElementAt(nsISupports* aElement, uint32_t aIndex) override;
MOZ_MUST_USE NS_IMETHOD_(bool)
ReplaceElementAt(nsISupports* aElement, uint32_t aIndex) override;
MOZ_MUST_USE NS_IMETHOD_(bool)
RemoveElementAt(uint32_t aIndex) override;
MOZ_MUST_USE NS_IMETHOD DeleteElementAt(uint32_t aIndex) override
{
return (RemoveElementAt(aIndex) ? NS_OK : NS_ERROR_FAILURE);
}
MOZ_MUST_USE NS_IMETHOD Clone(nsISupportsArray** aResult) override;
/**
* nsIArray adapters.
*/
NS_DECL_NSIARRAY
private:
// Copy constructors are not allowed
explicit nsSupportsArray(const nsISupportsArray& aOther);
nsCOMArray<nsISupports> mArray;
};
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#elif defined(_MSC_VER)
#pragma warning (pop)
#endif
#endif // nsSupportsArray_h__

View file

@ -0,0 +1,131 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsSupportsArrayEnumerator.h"
// Disable deprecation warnings generated by nsISupportsArray and associated
// classes.
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#elif defined(_MSC_VER)
#pragma warning (push)
#pragma warning (disable : 4996)
#endif
#include "nsISupportsArray.h"
nsSupportsArrayEnumerator::nsSupportsArrayEnumerator(nsISupportsArray* array)
: mArray(array)
, mCursor(0)
{
NS_ASSERTION(array, "null array");
}
nsSupportsArrayEnumerator::~nsSupportsArrayEnumerator()
{
}
NS_IMPL_ISUPPORTS(nsSupportsArrayEnumerator, nsIBidirectionalEnumerator,
nsIEnumerator)
NS_IMETHODIMP
nsSupportsArrayEnumerator::First()
{
mCursor = 0;
uint32_t cnt;
nsresult rv = mArray->Count(&cnt);
if (NS_FAILED(rv)) {
return rv;
}
int32_t end = (int32_t)cnt;
if (mCursor < end) {
return NS_OK;
} else {
return NS_ERROR_FAILURE;
}
}
NS_IMETHODIMP
nsSupportsArrayEnumerator::Next()
{
uint32_t cnt;
nsresult rv = mArray->Count(&cnt);
if (NS_FAILED(rv)) {
return rv;
}
int32_t end = (int32_t)cnt;
if (mCursor < end) { // don't count upward forever
mCursor++;
}
if (mCursor < end) {
return NS_OK;
} else {
return NS_ERROR_FAILURE;
}
}
NS_IMETHODIMP
nsSupportsArrayEnumerator::CurrentItem(nsISupports** aItem)
{
NS_ASSERTION(aItem, "null out parameter");
uint32_t cnt;
nsresult rv = mArray->Count(&cnt);
if (NS_FAILED(rv)) {
return rv;
}
if (mCursor >= 0 && mCursor < (int32_t)cnt) {
return mArray->GetElementAt(mCursor, aItem);
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsSupportsArrayEnumerator::IsDone()
{
uint32_t cnt;
nsresult rv = mArray->Count(&cnt);
if (NS_FAILED(rv)) {
return rv;
}
// XXX This is completely incompatible with the meaning of nsresult.
// NS_ENUMERATOR_FALSE is defined to be 1. (bug 778111)
return (mCursor >= 0 && mCursor < (int32_t)cnt)
? (nsresult)NS_ENUMERATOR_FALSE : NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsSupportsArrayEnumerator::Last()
{
uint32_t cnt;
nsresult rv = mArray->Count(&cnt);
if (NS_FAILED(rv)) {
return rv;
}
mCursor = cnt - 1;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsArrayEnumerator::Prev()
{
if (mCursor >= 0) {
--mCursor;
}
if (mCursor >= 0) {
return NS_OK;
} else {
return NS_ERROR_FAILURE;
}
}
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#elif defined(_MSC_VER)
#pragma warning (pop)
#endif

View file

@ -0,0 +1,56 @@
/* -*- 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 nsSupportsArrayEnumerator_h___
#define nsSupportsArrayEnumerator_h___
#include "nsCOMPtr.h"
#include "mozilla/Attributes.h"
// Disable deprecation warnings generated by nsISupportsArray and associated
// classes.
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#elif defined(_MSC_VER)
#pragma warning (push)
#pragma warning (disable : 4996)
#endif
#include "nsIEnumerator.h"
class nsISupportsArray;
class nsSupportsArrayEnumerator final : public nsIBidirectionalEnumerator
{
public:
NS_DECL_ISUPPORTS
explicit nsSupportsArrayEnumerator(nsISupportsArray* aArray);
// nsIEnumerator methods:
NS_DECL_NSIENUMERATOR
// nsIBidirectionalEnumerator methods:
NS_DECL_NSIBIDIRECTIONALENUMERATOR
private:
~nsSupportsArrayEnumerator();
protected:
nsCOMPtr<nsISupportsArray> mArray;
int32_t mCursor;
};
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#elif defined(_MSC_VER)
#pragma warning (pop)
#endif
#endif // __nsSupportsArrayEnumerator_h

View file

@ -0,0 +1,849 @@
/* -*- 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 "nsSupportsPrimitives.h"
#include "nsMemory.h"
#include "mozilla/Assertions.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Sprintf.h"
#include <algorithm>
template<typename T>
static char*
DataToString(const char* aFormat, T aData)
{
static const int size = 32;
char buf[size];
int len = SprintfLiteral(buf, aFormat, aData);
MOZ_ASSERT(len >= 0);
return static_cast<char*>(nsMemory::Clone(buf, std::min(len + 1, size) *
sizeof(char)));
}
/***************************************************************************/
NS_IMPL_ISUPPORTS(nsSupportsID, nsISupportsID, nsISupportsPrimitive)
nsSupportsID::nsSupportsID()
: mData(nullptr)
{
}
NS_IMETHODIMP
nsSupportsID::GetType(uint16_t* aType)
{
NS_ASSERTION(aType, "Bad pointer");
*aType = TYPE_ID;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsID::GetData(nsID** aData)
{
NS_ASSERTION(aData, "Bad pointer");
if (mData) {
*aData = static_cast<nsID*>(nsMemory::Clone(mData, sizeof(nsID)));
} else {
*aData = nullptr;
}
return NS_OK;
}
NS_IMETHODIMP
nsSupportsID::SetData(const nsID* aData)
{
if (mData) {
free(mData);
}
if (aData) {
mData = static_cast<nsID*>(nsMemory::Clone(aData, sizeof(nsID)));
} else {
mData = nullptr;
}
return NS_OK;
}
NS_IMETHODIMP
nsSupportsID::ToString(char** aResult)
{
NS_ASSERTION(aResult, "Bad pointer");
if (mData) {
*aResult = mData->ToString();
} else {
static const char nullStr[] = "null";
*aResult = static_cast<char*>(nsMemory::Clone(nullStr, sizeof(nullStr)));
}
return NS_OK;
}
/*****************************************************************************
* nsSupportsCString
*****************************************************************************/
NS_IMPL_ISUPPORTS(nsSupportsCString, nsISupportsCString,
nsISupportsPrimitive)
NS_IMETHODIMP
nsSupportsCString::GetType(uint16_t* aType)
{
NS_ASSERTION(aType, "Bad pointer");
*aType = TYPE_CSTRING;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsCString::GetData(nsACString& aData)
{
aData = mData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsCString::ToString(char** aResult)
{
*aResult = ToNewCString(mData);
if (!*aResult) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
NS_IMETHODIMP
nsSupportsCString::SetData(const nsACString& aData)
{
bool ok = mData.Assign(aData, mozilla::fallible);
if (!ok) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
/*****************************************************************************
* nsSupportsString
*****************************************************************************/
NS_IMPL_ISUPPORTS(nsSupportsString, nsISupportsString,
nsISupportsPrimitive)
NS_IMETHODIMP
nsSupportsString::GetType(uint16_t* aType)
{
NS_ASSERTION(aType, "Bad pointer");
*aType = TYPE_STRING;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsString::GetData(nsAString& aData)
{
aData = mData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsString::ToString(char16_t** aResult)
{
*aResult = ToNewUnicode(mData);
if (!*aResult) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
NS_IMETHODIMP
nsSupportsString::SetData(const nsAString& aData)
{
bool ok = mData.Assign(aData, mozilla::fallible);
if (!ok) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
/***************************************************************************/
NS_IMPL_ISUPPORTS(nsSupportsPRBool, nsISupportsPRBool,
nsISupportsPrimitive)
nsSupportsPRBool::nsSupportsPRBool()
: mData(false)
{
}
NS_IMETHODIMP
nsSupportsPRBool::GetType(uint16_t* aType)
{
NS_ASSERTION(aType, "Bad pointer");
*aType = TYPE_PRBOOL;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRBool::GetData(bool* aData)
{
NS_ASSERTION(aData, "Bad pointer");
*aData = mData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRBool::SetData(bool aData)
{
mData = aData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRBool::ToString(char** aResult)
{
NS_ASSERTION(aResult, "Bad pointer");
const char* str = mData ? "true" : "false";
*aResult = static_cast<char*>(nsMemory::Clone(str, (strlen(str) + 1) *
sizeof(char)));
return NS_OK;
}
/***************************************************************************/
NS_IMPL_ISUPPORTS(nsSupportsPRUint8, nsISupportsPRUint8,
nsISupportsPrimitive)
nsSupportsPRUint8::nsSupportsPRUint8()
: mData(0)
{
}
NS_IMETHODIMP
nsSupportsPRUint8::GetType(uint16_t* aType)
{
NS_ASSERTION(aType, "Bad pointer");
*aType = TYPE_PRUINT8;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRUint8::GetData(uint8_t* aData)
{
NS_ASSERTION(aData, "Bad pointer");
*aData = mData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRUint8::SetData(uint8_t aData)
{
mData = aData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRUint8::ToString(char** aResult)
{
NS_ASSERTION(aResult, "Bad pointer");
*aResult = DataToString("%u", static_cast<unsigned int>(mData));
return NS_OK;
}
/***************************************************************************/
NS_IMPL_ISUPPORTS(nsSupportsPRUint16, nsISupportsPRUint16,
nsISupportsPrimitive)
nsSupportsPRUint16::nsSupportsPRUint16()
: mData(0)
{
}
NS_IMETHODIMP
nsSupportsPRUint16::GetType(uint16_t* aType)
{
NS_ASSERTION(aType, "Bad pointer");
*aType = TYPE_PRUINT16;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRUint16::GetData(uint16_t* aData)
{
NS_ASSERTION(aData, "Bad pointer");
*aData = mData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRUint16::SetData(uint16_t aData)
{
mData = aData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRUint16::ToString(char** aResult)
{
NS_ASSERTION(aResult, "Bad pointer");
*aResult = DataToString("%u", static_cast<unsigned int>(mData));
return NS_OK;
}
/***************************************************************************/
NS_IMPL_ISUPPORTS(nsSupportsPRUint32, nsISupportsPRUint32,
nsISupportsPrimitive)
nsSupportsPRUint32::nsSupportsPRUint32()
: mData(0)
{
}
NS_IMETHODIMP
nsSupportsPRUint32::GetType(uint16_t* aType)
{
NS_ASSERTION(aType, "Bad pointer");
*aType = TYPE_PRUINT32;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRUint32::GetData(uint32_t* aData)
{
NS_ASSERTION(aData, "Bad pointer");
*aData = mData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRUint32::SetData(uint32_t aData)
{
mData = aData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRUint32::ToString(char** aResult)
{
NS_ASSERTION(aResult, "Bad pointer");
*aResult = DataToString("%u", mData);
return NS_OK;
}
/***************************************************************************/
NS_IMPL_ISUPPORTS(nsSupportsPRUint64, nsISupportsPRUint64,
nsISupportsPrimitive)
nsSupportsPRUint64::nsSupportsPRUint64()
: mData(0)
{
}
NS_IMETHODIMP
nsSupportsPRUint64::GetType(uint16_t* aType)
{
NS_ASSERTION(aType, "Bad pointer");
*aType = TYPE_PRUINT64;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRUint64::GetData(uint64_t* aData)
{
NS_ASSERTION(aData, "Bad pointer");
*aData = mData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRUint64::SetData(uint64_t aData)
{
mData = aData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRUint64::ToString(char** aResult)
{
NS_ASSERTION(aResult, "Bad pointer");
*aResult = DataToString("%llu", mData);
return NS_OK;
}
/***************************************************************************/
NS_IMPL_ISUPPORTS(nsSupportsPRTime, nsISupportsPRTime,
nsISupportsPrimitive)
nsSupportsPRTime::nsSupportsPRTime()
: mData(0)
{
}
NS_IMETHODIMP
nsSupportsPRTime::GetType(uint16_t* aType)
{
NS_ASSERTION(aType, "Bad pointer");
*aType = TYPE_PRTIME;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRTime::GetData(PRTime* aData)
{
NS_ASSERTION(aData, "Bad pointer");
*aData = mData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRTime::SetData(PRTime aData)
{
mData = aData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRTime::ToString(char** aResult)
{
NS_ASSERTION(aResult, "Bad pointer");
*aResult = DataToString("%" PRIu64, mData);
return NS_OK;
}
/***************************************************************************/
NS_IMPL_ISUPPORTS(nsSupportsChar, nsISupportsChar,
nsISupportsPrimitive)
nsSupportsChar::nsSupportsChar()
: mData(0)
{
}
NS_IMETHODIMP
nsSupportsChar::GetType(uint16_t* aType)
{
NS_ASSERTION(aType, "Bad pointer");
*aType = TYPE_CHAR;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsChar::GetData(char* aData)
{
NS_ASSERTION(aData, "Bad pointer");
*aData = mData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsChar::SetData(char aData)
{
mData = aData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsChar::ToString(char** aResult)
{
NS_ASSERTION(aResult, "Bad pointer");
*aResult = static_cast<char*>(moz_xmalloc(2 * sizeof(char)));
*aResult[0] = mData;
*aResult[1] = '\0';
return NS_OK;
}
/***************************************************************************/
NS_IMPL_ISUPPORTS(nsSupportsPRInt16, nsISupportsPRInt16,
nsISupportsPrimitive)
nsSupportsPRInt16::nsSupportsPRInt16()
: mData(0)
{
}
NS_IMETHODIMP
nsSupportsPRInt16::GetType(uint16_t* aType)
{
NS_ASSERTION(aType, "Bad pointer");
*aType = TYPE_PRINT16;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRInt16::GetData(int16_t* aData)
{
NS_ASSERTION(aData, "Bad pointer");
*aData = mData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRInt16::SetData(int16_t aData)
{
mData = aData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRInt16::ToString(char** aResult)
{
NS_ASSERTION(aResult, "Bad pointer");
*aResult = DataToString("%d", static_cast<int>(mData));
return NS_OK;
}
/***************************************************************************/
NS_IMPL_ISUPPORTS(nsSupportsPRInt32, nsISupportsPRInt32,
nsISupportsPrimitive)
nsSupportsPRInt32::nsSupportsPRInt32()
: mData(0)
{
}
NS_IMETHODIMP
nsSupportsPRInt32::GetType(uint16_t* aType)
{
NS_ASSERTION(aType, "Bad pointer");
*aType = TYPE_PRINT32;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRInt32::GetData(int32_t* aData)
{
NS_ASSERTION(aData, "Bad pointer");
*aData = mData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRInt32::SetData(int32_t aData)
{
mData = aData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRInt32::ToString(char** aResult)
{
NS_ASSERTION(aResult, "Bad pointer");
*aResult = DataToString("%d", mData);
return NS_OK;
}
/***************************************************************************/
NS_IMPL_ISUPPORTS(nsSupportsPRInt64, nsISupportsPRInt64,
nsISupportsPrimitive)
nsSupportsPRInt64::nsSupportsPRInt64()
: mData(0)
{
}
NS_IMETHODIMP
nsSupportsPRInt64::GetType(uint16_t* aType)
{
NS_ASSERTION(aType, "Bad pointer");
*aType = TYPE_PRINT64;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRInt64::GetData(int64_t* aData)
{
NS_ASSERTION(aData, "Bad pointer");
*aData = mData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRInt64::SetData(int64_t aData)
{
mData = aData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsPRInt64::ToString(char** aResult)
{
NS_ASSERTION(aResult, "Bad pointer");
*aResult = DataToString("%" PRId64, mData);
return NS_OK;
}
/***************************************************************************/
NS_IMPL_ISUPPORTS(nsSupportsFloat, nsISupportsFloat,
nsISupportsPrimitive)
nsSupportsFloat::nsSupportsFloat()
: mData(float(0.0))
{
}
NS_IMETHODIMP
nsSupportsFloat::GetType(uint16_t* aType)
{
NS_ASSERTION(aType, "Bad pointer");
*aType = TYPE_FLOAT;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsFloat::GetData(float* aData)
{
NS_ASSERTION(aData, "Bad pointer");
*aData = mData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsFloat::SetData(float aData)
{
mData = aData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsFloat::ToString(char** aResult)
{
NS_ASSERTION(aResult, "Bad pointer");
*aResult = DataToString("%f", static_cast<double>(mData));
return NS_OK;
}
/***************************************************************************/
NS_IMPL_ISUPPORTS(nsSupportsDouble, nsISupportsDouble,
nsISupportsPrimitive)
nsSupportsDouble::nsSupportsDouble()
: mData(double(0.0))
{
}
NS_IMETHODIMP
nsSupportsDouble::GetType(uint16_t* aType)
{
NS_ASSERTION(aType, "Bad pointer");
*aType = TYPE_DOUBLE;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsDouble::GetData(double* aData)
{
NS_ASSERTION(aData, "Bad pointer");
*aData = mData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsDouble::SetData(double aData)
{
mData = aData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsDouble::ToString(char** aResult)
{
NS_ASSERTION(aResult, "Bad pointer");
*aResult = DataToString("%f", mData);
return NS_OK;
}
/***************************************************************************/
NS_IMPL_ISUPPORTS(nsSupportsVoid, nsISupportsVoid,
nsISupportsPrimitive)
nsSupportsVoid::nsSupportsVoid()
: mData(nullptr)
{
}
NS_IMETHODIMP
nsSupportsVoid::GetType(uint16_t* aType)
{
NS_ASSERTION(aType, "Bad pointer");
*aType = TYPE_VOID;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsVoid::GetData(void** aData)
{
NS_ASSERTION(aData, "Bad pointer");
*aData = mData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsVoid::SetData(void* aData)
{
mData = aData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsVoid::ToString(char** aResult)
{
NS_ASSERTION(aResult, "Bad pointer");
static const char str[] = "[raw data]";
*aResult = static_cast<char*>(nsMemory::Clone(str, sizeof(str)));
return NS_OK;
}
/***************************************************************************/
NS_IMPL_ISUPPORTS(nsSupportsInterfacePointer,
nsISupportsInterfacePointer,
nsISupportsPrimitive)
nsSupportsInterfacePointer::nsSupportsInterfacePointer()
: mIID(nullptr)
{
}
nsSupportsInterfacePointer::~nsSupportsInterfacePointer()
{
if (mIID) {
free(mIID);
}
}
NS_IMETHODIMP
nsSupportsInterfacePointer::GetType(uint16_t* aType)
{
NS_ASSERTION(aType, "Bad pointer");
*aType = TYPE_INTERFACE_POINTER;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsInterfacePointer::GetData(nsISupports** aData)
{
NS_ASSERTION(aData, "Bad pointer");
*aData = mData;
NS_IF_ADDREF(*aData);
return NS_OK;
}
NS_IMETHODIMP
nsSupportsInterfacePointer::SetData(nsISupports* aData)
{
mData = aData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsInterfacePointer::GetDataIID(nsID** aIID)
{
NS_ASSERTION(aIID, "Bad pointer");
if (mIID) {
*aIID = static_cast<nsID*>(nsMemory::Clone(mIID, sizeof(nsID)));
} else {
*aIID = nullptr;
}
return NS_OK;
}
NS_IMETHODIMP
nsSupportsInterfacePointer::SetDataIID(const nsID* aIID)
{
if (mIID) {
free(mIID);
}
if (aIID) {
mIID = static_cast<nsID*>(nsMemory::Clone(aIID, sizeof(nsID)));
} else {
mIID = nullptr;
}
return NS_OK;
}
NS_IMETHODIMP
nsSupportsInterfacePointer::ToString(char** aResult)
{
NS_ASSERTION(aResult, "Bad pointer");
static const char str[] = "[interface pointer]";
// jband sez: think about asking nsIInterfaceInfoManager whether
// the interface has a known human-readable name
*aResult = static_cast<char*>(nsMemory::Clone(str, sizeof(str)));
return NS_OK;
}
/***************************************************************************/
NS_IMPL_ISUPPORTS(nsSupportsDependentCString, nsISupportsCString,
nsISupportsPrimitive)
nsSupportsDependentCString::nsSupportsDependentCString(const char* aStr)
: mData(aStr)
{ }
NS_IMETHODIMP
nsSupportsDependentCString::GetType(uint16_t* aType)
{
if (NS_WARN_IF(!aType)) {
return NS_ERROR_INVALID_ARG;
}
*aType = TYPE_CSTRING;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsDependentCString::GetData(nsACString& aData)
{
aData = mData;
return NS_OK;
}
NS_IMETHODIMP
nsSupportsDependentCString::ToString(char** aResult)
{
if (NS_WARN_IF(!aResult)) {
return NS_ERROR_INVALID_ARG;
}
*aResult = ToNewCString(mData);
if (!*aResult) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
NS_IMETHODIMP
nsSupportsDependentCString::SetData(const nsACString& aData)
{
return NS_ERROR_NOT_IMPLEMENTED;
}

View file

@ -0,0 +1,327 @@
/* -*- 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 nsSupportsPrimitives_h__
#define nsSupportsPrimitives_h__
#include "mozilla/Attributes.h"
#include "nsISupportsPrimitives.h"
#include "nsCOMPtr.h"
#include "nsString.h"
class nsSupportsID final : public nsISupportsID
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISUPPORTSPRIMITIVE
NS_DECL_NSISUPPORTSID
nsSupportsID();
private:
~nsSupportsID() {}
nsID* mData;
};
/***************************************************************************/
class nsSupportsCString final : public nsISupportsCString
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISUPPORTSPRIMITIVE
NS_DECL_NSISUPPORTSCSTRING
nsSupportsCString() {}
private:
~nsSupportsCString() {}
nsCString mData;
};
/***************************************************************************/
class nsSupportsString final : public nsISupportsString
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISUPPORTSPRIMITIVE
NS_DECL_NSISUPPORTSSTRING
nsSupportsString() {}
private:
~nsSupportsString() {}
nsString mData;
};
/***************************************************************************/
class nsSupportsPRBool final : public nsISupportsPRBool
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSISUPPORTSPRIMITIVE
NS_DECL_NSISUPPORTSPRBOOL
nsSupportsPRBool();
private:
~nsSupportsPRBool() {}
bool mData;
};
/***************************************************************************/
class nsSupportsPRUint8 final : public nsISupportsPRUint8
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISUPPORTSPRIMITIVE
NS_DECL_NSISUPPORTSPRUINT8
nsSupportsPRUint8();
private:
~nsSupportsPRUint8() {}
uint8_t mData;
};
/***************************************************************************/
class nsSupportsPRUint16 final : public nsISupportsPRUint16
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISUPPORTSPRIMITIVE
NS_DECL_NSISUPPORTSPRUINT16
nsSupportsPRUint16();
private:
~nsSupportsPRUint16() {}
uint16_t mData;
};
/***************************************************************************/
class nsSupportsPRUint32 final : public nsISupportsPRUint32
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISUPPORTSPRIMITIVE
NS_DECL_NSISUPPORTSPRUINT32
nsSupportsPRUint32();
private:
~nsSupportsPRUint32() {}
uint32_t mData;
};
/***************************************************************************/
class nsSupportsPRUint64 final : public nsISupportsPRUint64
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISUPPORTSPRIMITIVE
NS_DECL_NSISUPPORTSPRUINT64
nsSupportsPRUint64();
private:
~nsSupportsPRUint64() {}
uint64_t mData;
};
/***************************************************************************/
class nsSupportsPRTime final : public nsISupportsPRTime
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISUPPORTSPRIMITIVE
NS_DECL_NSISUPPORTSPRTIME
nsSupportsPRTime();
private:
~nsSupportsPRTime() {}
PRTime mData;
};
/***************************************************************************/
class nsSupportsChar final : public nsISupportsChar
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISUPPORTSPRIMITIVE
NS_DECL_NSISUPPORTSCHAR
nsSupportsChar();
private:
~nsSupportsChar() {}
char mData;
};
/***************************************************************************/
class nsSupportsPRInt16 final : public nsISupportsPRInt16
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISUPPORTSPRIMITIVE
NS_DECL_NSISUPPORTSPRINT16
nsSupportsPRInt16();
private:
~nsSupportsPRInt16() {}
int16_t mData;
};
/***************************************************************************/
class nsSupportsPRInt32 final : public nsISupportsPRInt32
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISUPPORTSPRIMITIVE
NS_DECL_NSISUPPORTSPRINT32
nsSupportsPRInt32();
private:
~nsSupportsPRInt32() {}
int32_t mData;
};
/***************************************************************************/
class nsSupportsPRInt64 final : public nsISupportsPRInt64
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISUPPORTSPRIMITIVE
NS_DECL_NSISUPPORTSPRINT64
nsSupportsPRInt64();
private:
~nsSupportsPRInt64() {}
int64_t mData;
};
/***************************************************************************/
class nsSupportsFloat final : public nsISupportsFloat
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISUPPORTSPRIMITIVE
NS_DECL_NSISUPPORTSFLOAT
nsSupportsFloat();
private:
~nsSupportsFloat() {}
float mData;
};
/***************************************************************************/
class nsSupportsDouble final : public nsISupportsDouble
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISUPPORTSPRIMITIVE
NS_DECL_NSISUPPORTSDOUBLE
nsSupportsDouble();
private:
~nsSupportsDouble() {}
double mData;
};
/***************************************************************************/
class nsSupportsVoid final : public nsISupportsVoid
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSISUPPORTSPRIMITIVE
NS_DECL_NSISUPPORTSVOID
nsSupportsVoid();
private:
~nsSupportsVoid() {}
void* mData;
};
/***************************************************************************/
class nsSupportsInterfacePointer final : public nsISupportsInterfacePointer
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSISUPPORTSPRIMITIVE
NS_DECL_NSISUPPORTSINTERFACEPOINTER
nsSupportsInterfacePointer();
private:
~nsSupportsInterfacePointer();
nsCOMPtr<nsISupports> mData;
nsID* mIID;
};
/***************************************************************************/
/**
* Wraps a static const char* buffer for use with nsISupportsCString
*
* Only use this class with static buffers, or arena-allocated buffers of
* permanent lifetime!
*/
class nsSupportsDependentCString final : public nsISupportsCString
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISUPPORTSPRIMITIVE
NS_DECL_NSISUPPORTSCSTRING
explicit nsSupportsDependentCString(const char* aStr);
private:
~nsSupportsDependentCString() {}
nsDependentCString mData;
};
#endif /* nsSupportsPrimitives_h__ */

2220
xpcom/ds/nsVariant.cpp Normal file

File diff suppressed because it is too large Load diff

229
xpcom/ds/nsVariant.h Normal file
View file

@ -0,0 +1,229 @@
/* -*- 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 nsVariant_h
#define nsVariant_h
#include "nsIVariant.h"
#include "nsStringFwd.h"
#include "mozilla/Attributes.h"
#include "nsCycleCollectionParticipant.h"
/**
* Map the nsAUTF8String, nsUTF8String classes to the nsACString and
* nsCString classes respectively for now. These defines need to be removed
* once Jag lands his nsUTF8String implementation.
*/
#define nsAUTF8String nsACString
#define nsUTF8String nsCString
#define PromiseFlatUTF8String PromiseFlatCString
/**
* nsDiscriminatedUnion is a class that nsIVariant implementors can use
* to hold the underlying data.
*/
class nsDiscriminatedUnion
{
public:
nsDiscriminatedUnion() : mType(nsIDataType::VTYPE_EMPTY) {}
nsDiscriminatedUnion(const nsDiscriminatedUnion&) = delete;
nsDiscriminatedUnion(nsDiscriminatedUnion&&) = delete;
~nsDiscriminatedUnion() { Cleanup(); }
nsDiscriminatedUnion& operator=(const nsDiscriminatedUnion&) = delete;
nsDiscriminatedUnion& operator=(nsDiscriminatedUnion&&) = delete;
void Cleanup();
uint16_t GetType() const { return mType; }
MOZ_MUST_USE nsresult ConvertToInt8(uint8_t* aResult) const;
MOZ_MUST_USE nsresult ConvertToInt16(int16_t* aResult) const;
MOZ_MUST_USE nsresult ConvertToInt32(int32_t* aResult) const;
MOZ_MUST_USE nsresult ConvertToInt64(int64_t* aResult) const;
MOZ_MUST_USE nsresult ConvertToUint8(uint8_t* aResult) const;
MOZ_MUST_USE nsresult ConvertToUint16(uint16_t* aResult) const;
MOZ_MUST_USE nsresult ConvertToUint32(uint32_t* aResult) const;
MOZ_MUST_USE nsresult ConvertToUint64(uint64_t* aResult) const;
MOZ_MUST_USE nsresult ConvertToFloat(float* aResult) const;
MOZ_MUST_USE nsresult ConvertToDouble(double* aResult) const;
MOZ_MUST_USE nsresult ConvertToBool(bool* aResult) const;
MOZ_MUST_USE nsresult ConvertToChar(char* aResult) const;
MOZ_MUST_USE nsresult ConvertToWChar(char16_t* aResult) const;
MOZ_MUST_USE nsresult ConvertToID(nsID* aResult) const;
MOZ_MUST_USE nsresult ConvertToAString(nsAString& aResult) const;
MOZ_MUST_USE nsresult ConvertToAUTF8String(nsAUTF8String& aResult) const;
MOZ_MUST_USE nsresult ConvertToACString(nsACString& aResult) const;
MOZ_MUST_USE nsresult ConvertToString(char** aResult) const;
MOZ_MUST_USE nsresult ConvertToWString(char16_t** aResult) const;
MOZ_MUST_USE nsresult ConvertToStringWithSize(uint32_t* aSize, char** aStr) const;
MOZ_MUST_USE nsresult ConvertToWStringWithSize(uint32_t* aSize, char16_t** aStr) const;
MOZ_MUST_USE nsresult ConvertToISupports(nsISupports** aResult) const;
MOZ_MUST_USE nsresult ConvertToInterface(nsIID** aIID, void** aInterface) const;
MOZ_MUST_USE nsresult ConvertToArray(uint16_t* aType, nsIID* aIID,
uint32_t* aCount, void** aPtr) const;
MOZ_MUST_USE nsresult SetFromVariant(nsIVariant* aValue);
void SetFromInt8(uint8_t aValue);
void SetFromInt16(int16_t aValue);
void SetFromInt32(int32_t aValue);
void SetFromInt64(int64_t aValue);
void SetFromUint8(uint8_t aValue);
void SetFromUint16(uint16_t aValue);
void SetFromUint32(uint32_t aValue);
void SetFromUint64(uint64_t aValue);
void SetFromFloat(float aValue);
void SetFromDouble(double aValue);
void SetFromBool(bool aValue);
void SetFromChar(char aValue);
void SetFromWChar(char16_t aValue);
void SetFromID(const nsID& aValue);
void SetFromAString(const nsAString& aValue);
void SetFromDOMString(const nsAString& aValue);
void SetFromAUTF8String(const nsAUTF8String& aValue);
void SetFromACString(const nsACString& aValue);
MOZ_MUST_USE nsresult SetFromString(const char* aValue);
MOZ_MUST_USE nsresult SetFromWString(const char16_t* aValue);
void SetFromISupports(nsISupports* aValue);
void SetFromInterface(const nsIID& aIID, nsISupports* aValue);
MOZ_MUST_USE nsresult SetFromArray(uint16_t aType, const nsIID* aIID,
uint32_t aCount, void* aValue);
MOZ_MUST_USE nsresult SetFromStringWithSize(uint32_t aSize,
const char* aValue);
MOZ_MUST_USE nsresult SetFromWStringWithSize(uint32_t aSize,
const char16_t* aValue);
// Like SetFromWStringWithSize, but leaves the string uninitialized. It does
// does write the null-terminator.
void AllocateWStringWithSize(uint32_t aSize);
void SetToVoid();
void SetToEmpty();
void SetToEmptyArray();
void Traverse(nsCycleCollectionTraversalCallback& aCb) const;
private:
MOZ_MUST_USE nsresult
ToManageableNumber(nsDiscriminatedUnion* aOutData) const;
void FreeArray();
MOZ_MUST_USE bool String2ID(nsID* aPid) const;
MOZ_MUST_USE nsresult ToString(nsACString& aOutString) const;
public:
union
{
int8_t mInt8Value;
int16_t mInt16Value;
int32_t mInt32Value;
int64_t mInt64Value;
uint8_t mUint8Value;
uint16_t mUint16Value;
uint32_t mUint32Value;
uint64_t mUint64Value;
float mFloatValue;
double mDoubleValue;
bool mBoolValue;
char mCharValue;
char16_t mWCharValue;
nsIID mIDValue;
nsAString* mAStringValue;
nsAUTF8String* mUTF8StringValue;
nsACString* mCStringValue;
struct
{
// This is an owning reference that cannot be an nsCOMPtr because
// nsDiscriminatedUnion needs to be POD. AddRef/Release are manually
// called on this.
nsISupports* MOZ_OWNING_REF mInterfaceValue;
nsIID mInterfaceID;
} iface;
struct
{
nsIID mArrayInterfaceID;
void* mArrayValue;
uint32_t mArrayCount;
uint16_t mArrayType;
} array;
struct
{
char* mStringValue;
uint32_t mStringLength;
} str;
struct
{
char16_t* mWStringValue;
uint32_t mWStringLength;
} wstr;
} u;
uint16_t mType;
};
/**
* nsVariant implements the generic variant support. The xpcom module registers
* a factory (see NS_VARIANT_CONTRACTID in nsIVariant.idl) that will create
* these objects. They are created 'empty' and 'writable'.
*
* nsIVariant users won't usually need to see this class.
*/
class nsVariantBase : public nsIWritableVariant
{
public:
NS_DECL_NSIVARIANT
NS_DECL_NSIWRITABLEVARIANT
nsVariantBase();
protected:
~nsVariantBase() {};
nsDiscriminatedUnion mData;
bool mWritable;
};
class nsVariant final : public nsVariantBase
{
public:
NS_DECL_ISUPPORTS
nsVariant() {};
private:
~nsVariant() {};
};
class nsVariantCC final : public nsVariantBase
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(nsVariantCC)
nsVariantCC() {};
private:
~nsVariantCC() {};
};
/**
* Users of nsIVariant should be using the contractID and not this CID.
* - see NS_VARIANT_CONTRACTID in nsIVariant.idl.
*/
#define NS_VARIANT_CID \
{ /* 0D6EA1D0-879C-11d5-90EF-0010A4E73D9A */ \
0xd6ea1d0, \
0x879c, \
0x11d5, \
{0x90, 0xef, 0x0, 0x10, 0xa4, 0xe7, 0x3d, 0x9a}}
#endif // nsVariant_h

View file

@ -0,0 +1,110 @@
/* -*- 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 __nsWhitespaceTokenizer_h
#define __nsWhitespaceTokenizer_h
#include "mozilla/RangedPtr.h"
#include "nsDependentSubstring.h"
#include "nsCRT.h"
template<typename DependentSubstringType, bool IsWhitespace(char16_t)>
class nsTWhitespaceTokenizer
{
typedef typename DependentSubstringType::char_type CharType;
typedef typename DependentSubstringType::substring_type SubstringType;
public:
explicit nsTWhitespaceTokenizer(const SubstringType& aSource)
: mIter(aSource.Data(), aSource.Length())
, mEnd(aSource.Data() + aSource.Length(), aSource.Data(),
aSource.Length())
, mWhitespaceBeforeFirstToken(false)
, mWhitespaceAfterCurrentToken(false)
{
while (mIter < mEnd && IsWhitespace(*mIter)) {
mWhitespaceBeforeFirstToken = true;
++mIter;
}
}
/**
* Checks if any more tokens are available.
*/
bool hasMoreTokens() const
{
return mIter < mEnd;
}
/*
* Returns true if there is whitespace prior to the first token.
*/
bool whitespaceBeforeFirstToken() const
{
return mWhitespaceBeforeFirstToken;
}
/*
* Returns true if there is any whitespace after the current token.
* This is always true unless we're reading the last token.
*/
bool whitespaceAfterCurrentToken() const
{
return mWhitespaceAfterCurrentToken;
}
/**
* Returns the next token.
*/
const DependentSubstringType nextToken()
{
const mozilla::RangedPtr<const CharType> tokenStart = mIter;
while (mIter < mEnd && !IsWhitespace(*mIter)) {
++mIter;
}
const mozilla::RangedPtr<const CharType> tokenEnd = mIter;
mWhitespaceAfterCurrentToken = false;
while (mIter < mEnd && IsWhitespace(*mIter)) {
mWhitespaceAfterCurrentToken = true;
++mIter;
}
return Substring(tokenStart.get(), tokenEnd.get());
}
private:
mozilla::RangedPtr<const CharType> mIter;
const mozilla::RangedPtr<const CharType> mEnd;
bool mWhitespaceBeforeFirstToken;
bool mWhitespaceAfterCurrentToken;
};
template<bool IsWhitespace(char16_t) = NS_IsAsciiWhitespace>
class nsWhitespaceTokenizerTemplate
: public nsTWhitespaceTokenizer<nsDependentSubstring, IsWhitespace>
{
public:
explicit nsWhitespaceTokenizerTemplate(const nsSubstring& aSource)
: nsTWhitespaceTokenizer<nsDependentSubstring, IsWhitespace>(aSource)
{
}
};
typedef nsWhitespaceTokenizerTemplate<> nsWhitespaceTokenizer;
template<bool IsWhitespace(char16_t) = NS_IsAsciiWhitespace>
class nsCWhitespaceTokenizerTemplate
: public nsTWhitespaceTokenizer<nsDependentCSubstring, IsWhitespace>
{
public:
explicit nsCWhitespaceTokenizerTemplate(const nsCSubstring& aSource)
: nsTWhitespaceTokenizer<nsDependentCSubstring, IsWhitespace>(aSource)
{
}
};
typedef nsCWhitespaceTokenizerTemplate<> nsCWhitespaceTokenizer;
#endif /* __nsWhitespaceTokenizer_h */

View file

@ -0,0 +1,579 @@
/* -*- 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 <windows.h>
#include <shlwapi.h>
#include <stdlib.h>
#include "nsWindowsRegKey.h"
#include "nsString.h"
#include "nsCOMPtr.h"
#include "mozilla/Attributes.h"
#include "nsAutoPtr.h"
//-----------------------------------------------------------------------------
// According to MSDN, the following limits apply (in characters excluding room
// for terminating null character):
#define MAX_KEY_NAME_LEN 255
#define MAX_VALUE_NAME_LEN 16383
class nsWindowsRegKey final : public nsIWindowsRegKey
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIWINDOWSREGKEY
nsWindowsRegKey()
: mKey(nullptr)
, mWatchEvent(nullptr)
, mWatchRecursive(FALSE)
{
}
private:
~nsWindowsRegKey()
{
Close();
}
HKEY mKey;
HANDLE mWatchEvent;
BOOL mWatchRecursive;
};
NS_IMPL_ISUPPORTS(nsWindowsRegKey, nsIWindowsRegKey)
NS_IMETHODIMP
nsWindowsRegKey::GetKey(HKEY* aKey)
{
*aKey = mKey;
return NS_OK;
}
NS_IMETHODIMP
nsWindowsRegKey::SetKey(HKEY aKey)
{
// We do not close the older aKey!
StopWatching();
mKey = aKey;
return NS_OK;
}
NS_IMETHODIMP
nsWindowsRegKey::Close()
{
StopWatching();
if (mKey) {
RegCloseKey(mKey);
mKey = nullptr;
}
return NS_OK;
}
NS_IMETHODIMP
nsWindowsRegKey::Open(uint32_t aRootKey, const nsAString& aPath,
uint32_t aMode)
{
Close();
LONG rv = RegOpenKeyExW((HKEY)(intptr_t)aRootKey,
PromiseFlatString(aPath).get(), 0, (REGSAM)aMode,
&mKey);
return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsWindowsRegKey::Create(uint32_t aRootKey, const nsAString& aPath,
uint32_t aMode)
{
Close();
DWORD disposition;
LONG rv = RegCreateKeyExW((HKEY)(intptr_t)aRootKey,
PromiseFlatString(aPath).get(), 0, nullptr,
REG_OPTION_NON_VOLATILE, (REGSAM)aMode, nullptr,
&mKey, &disposition);
return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsWindowsRegKey::OpenChild(const nsAString& aPath, uint32_t aMode,
nsIWindowsRegKey** aResult)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
nsCOMPtr<nsIWindowsRegKey> child = new nsWindowsRegKey();
nsresult rv = child->Open((uintptr_t)mKey, aPath, aMode);
if (NS_FAILED(rv)) {
return rv;
}
child.swap(*aResult);
return NS_OK;
}
NS_IMETHODIMP
nsWindowsRegKey::CreateChild(const nsAString& aPath, uint32_t aMode,
nsIWindowsRegKey** aResult)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
nsCOMPtr<nsIWindowsRegKey> child = new nsWindowsRegKey();
nsresult rv = child->Create((uintptr_t)mKey, aPath, aMode);
if (NS_FAILED(rv)) {
return rv;
}
child.swap(*aResult);
return NS_OK;
}
NS_IMETHODIMP
nsWindowsRegKey::GetChildCount(uint32_t* aResult)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
DWORD numSubKeys;
LONG rv = RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, &numSubKeys,
nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr);
if (rv != ERROR_SUCCESS) {
return NS_ERROR_FAILURE;
}
*aResult = numSubKeys;
return NS_OK;
}
NS_IMETHODIMP
nsWindowsRegKey::GetChildName(uint32_t aIndex, nsAString& aResult)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
FILETIME lastWritten;
wchar_t nameBuf[MAX_KEY_NAME_LEN + 1];
DWORD nameLen = sizeof(nameBuf) / sizeof(nameBuf[0]);
LONG rv = RegEnumKeyExW(mKey, aIndex, nameBuf, &nameLen, nullptr, nullptr,
nullptr, &lastWritten);
if (rv != ERROR_SUCCESS) {
return NS_ERROR_NOT_AVAILABLE; // XXX what's the best error code here?
}
aResult.Assign(nameBuf, nameLen);
return NS_OK;
}
NS_IMETHODIMP
nsWindowsRegKey::HasChild(const nsAString& aName, bool* aResult)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
// Check for the existence of a child key by opening the key with minimal
// rights. Perhaps there is a more efficient way to do this?
HKEY key;
LONG rv = RegOpenKeyExW(mKey, PromiseFlatString(aName).get(), 0,
STANDARD_RIGHTS_READ, &key);
if ((*aResult = (rv == ERROR_SUCCESS && key))) {
RegCloseKey(key);
}
return NS_OK;
}
NS_IMETHODIMP
nsWindowsRegKey::GetValueCount(uint32_t* aResult)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
DWORD numValues;
LONG rv = RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, &numValues, nullptr, nullptr,
nullptr, nullptr);
if (rv != ERROR_SUCCESS) {
return NS_ERROR_FAILURE;
}
*aResult = numValues;
return NS_OK;
}
NS_IMETHODIMP
nsWindowsRegKey::GetValueName(uint32_t aIndex, nsAString& aResult)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
wchar_t nameBuf[MAX_VALUE_NAME_LEN];
DWORD nameLen = sizeof(nameBuf) / sizeof(nameBuf[0]);
LONG rv = RegEnumValueW(mKey, aIndex, nameBuf, &nameLen, nullptr, nullptr,
nullptr, nullptr);
if (rv != ERROR_SUCCESS) {
return NS_ERROR_NOT_AVAILABLE; // XXX what's the best error code here?
}
aResult.Assign(nameBuf, nameLen);
return NS_OK;
}
NS_IMETHODIMP
nsWindowsRegKey::HasValue(const nsAString& aName, bool* aResult)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
nullptr, nullptr);
*aResult = (rv == ERROR_SUCCESS);
return NS_OK;
}
NS_IMETHODIMP
nsWindowsRegKey::RemoveChild(const nsAString& aName)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
LONG rv = RegDeleteKeyW(mKey, PromiseFlatString(aName).get());
return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsWindowsRegKey::RemoveValue(const nsAString& aName)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
LONG rv = RegDeleteValueW(mKey, PromiseFlatString(aName).get());
return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsWindowsRegKey::GetValueType(const nsAString& aName, uint32_t* aResult)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0,
(LPDWORD)aResult, nullptr, nullptr);
return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsWindowsRegKey::ReadStringValue(const nsAString& aName, nsAString& aResult)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
DWORD type, size;
const nsString& flatName = PromiseFlatString(aName);
LONG rv = RegQueryValueExW(mKey, flatName.get(), 0, &type, nullptr, &size);
if (rv != ERROR_SUCCESS) {
return NS_ERROR_FAILURE;
}
// This must be a string type in order to fetch the value as a string.
// We're being a bit forgiving here by allowing types other than REG_SZ.
if (type != REG_SZ && type != REG_EXPAND_SZ && type != REG_MULTI_SZ) {
return NS_ERROR_FAILURE;
}
// The buffer size must be a multiple of 2.
if (size % 2 != 0) {
return NS_ERROR_UNEXPECTED;
}
if (size == 0) {
aResult.Truncate();
return NS_OK;
}
// |size| may or may not include the terminating null character.
DWORD resultLen = size / 2;
if (!aResult.SetLength(resultLen, mozilla::fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsAString::iterator begin;
aResult.BeginWriting(begin);
rv = RegQueryValueExW(mKey, flatName.get(), 0, &type, (LPBYTE)begin.get(),
&size);
if (!aResult.CharAt(resultLen - 1)) {
// The string passed to us had a null terminator in the final position.
aResult.Truncate(resultLen - 1);
}
// Expand the environment variables if needed
if (type == REG_EXPAND_SZ) {
const nsString& flatSource = PromiseFlatString(aResult);
resultLen = ExpandEnvironmentStringsW(flatSource.get(), nullptr, 0);
if (resultLen > 1) {
nsAutoString expandedResult;
// |resultLen| includes the terminating null character
--resultLen;
if (!expandedResult.SetLength(resultLen, mozilla::fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsAString::iterator begin;
expandedResult.BeginWriting(begin);
resultLen = ExpandEnvironmentStringsW(flatSource.get(),
wwc(begin.get()),
resultLen + 1);
if (resultLen <= 0) {
rv = ERROR_UNKNOWN_FEATURE;
aResult.Truncate();
} else {
rv = ERROR_SUCCESS;
aResult = expandedResult;
}
} else if (resultLen == 1) {
// It apparently expands to nothing (just a null terminator).
aResult.Truncate();
}
}
return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsWindowsRegKey::ReadIntValue(const nsAString& aName, uint32_t* aResult)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
DWORD size = sizeof(*aResult);
LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
(LPBYTE)aResult, &size);
return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsWindowsRegKey::ReadInt64Value(const nsAString& aName, uint64_t* aResult)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
DWORD size = sizeof(*aResult);
LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
(LPBYTE)aResult, &size);
return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsWindowsRegKey::ReadBinaryValue(const nsAString& aName, nsACString& aResult)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
DWORD size;
LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0,
nullptr, nullptr, &size);
if (rv != ERROR_SUCCESS) {
return NS_ERROR_FAILURE;
}
if (!size) {
aResult.Truncate();
return NS_OK;
}
if (!aResult.SetLength(size, mozilla::fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsACString::iterator begin;
aResult.BeginWriting(begin);
rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
(LPBYTE)begin.get(), &size);
return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsWindowsRegKey::WriteStringValue(const nsAString& aName,
const nsAString& aValue)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
// Need to indicate complete size of buffer including null terminator.
const nsString& flatValue = PromiseFlatString(aValue);
LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_SZ,
(const BYTE*)flatValue.get(),
(flatValue.Length() + 1) * sizeof(char16_t));
return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsWindowsRegKey::WriteIntValue(const nsAString& aName, uint32_t aValue)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_DWORD,
(const BYTE*)&aValue, sizeof(aValue));
return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsWindowsRegKey::WriteInt64Value(const nsAString& aName, uint64_t aValue)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_QWORD,
(const BYTE*)&aValue, sizeof(aValue));
return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsWindowsRegKey::WriteBinaryValue(const nsAString& aName,
const nsACString& aValue)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
const nsCString& flatValue = PromiseFlatCString(aValue);
LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_BINARY,
(const BYTE*)flatValue.get(), flatValue.Length());
return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsWindowsRegKey::StartWatching(bool aRecurse)
{
if (NS_WARN_IF(!mKey)) {
return NS_ERROR_NOT_INITIALIZED;
}
if (mWatchEvent) {
return NS_OK;
}
mWatchEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
if (!mWatchEvent) {
return NS_ERROR_OUT_OF_MEMORY;
}
DWORD filter = REG_NOTIFY_CHANGE_NAME |
REG_NOTIFY_CHANGE_ATTRIBUTES |
REG_NOTIFY_CHANGE_LAST_SET |
REG_NOTIFY_CHANGE_SECURITY;
LONG rv = RegNotifyChangeKeyValue(mKey, aRecurse, filter, mWatchEvent, TRUE);
if (rv != ERROR_SUCCESS) {
StopWatching();
// On older versions of Windows, this call is not implemented, so simply
// return NS_OK in those cases and pretend that the watching is happening.
return (rv == ERROR_CALL_NOT_IMPLEMENTED) ? NS_OK : NS_ERROR_FAILURE;
}
mWatchRecursive = aRecurse;
return NS_OK;
}
NS_IMETHODIMP
nsWindowsRegKey::StopWatching()
{
if (mWatchEvent) {
CloseHandle(mWatchEvent);
mWatchEvent = nullptr;
}
return NS_OK;
}
NS_IMETHODIMP
nsWindowsRegKey::HasChanged(bool* aResult)
{
if (mWatchEvent && WaitForSingleObject(mWatchEvent, 0) == WAIT_OBJECT_0) {
// An event only gets signaled once, then it's done, so we have to set up
// another event to watch.
StopWatching();
StartWatching(mWatchRecursive);
*aResult = true;
} else {
*aResult = false;
}
return NS_OK;
}
NS_IMETHODIMP
nsWindowsRegKey::IsWatching(bool* aResult)
{
*aResult = (mWatchEvent != nullptr);
return NS_OK;
}
//-----------------------------------------------------------------------------
void
NS_NewWindowsRegKey(nsIWindowsRegKey** aResult)
{
RefPtr<nsWindowsRegKey> key = new nsWindowsRegKey();
key.forget(aResult);
}
//-----------------------------------------------------------------------------
nsresult
nsWindowsRegKeyConstructor(nsISupports* aDelegate, const nsIID& aIID,
void** aResult)
{
if (aDelegate) {
return NS_ERROR_NO_AGGREGATION;
}
nsCOMPtr<nsIWindowsRegKey> key;
NS_NewWindowsRegKey(getter_AddRefs(key));
return key->QueryInterface(aIID, aResult);
}

View file

@ -0,0 +1,43 @@
/* -*- 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 nsWindowsRegKey_h__
#define nsWindowsRegKey_h__
//-----------------------------------------------------------------------------
#include "nsIWindowsRegKey.h"
/**
* This ContractID may be used to instantiate a windows registry key object
* via the XPCOM component manager.
*/
#define NS_WINDOWSREGKEY_CONTRACTID "@mozilla.org/windows-registry-key;1"
/**
* This function may be used to instantiate a windows registry key object prior
* to XPCOM being initialized.
*/
extern "C" void NS_NewWindowsRegKey(nsIWindowsRegKey** aResult);
//-----------------------------------------------------------------------------
#ifdef IMPL_LIBXUL
// a53bc624-d577-4839-b8ec-bb5040a52ff4
#define NS_WINDOWSREGKEY_CID \
{ 0xa53bc624, 0xd577, 0x4839, \
{ 0xb8, 0xec, 0xbb, 0x50, 0x40, 0xa5, 0x2f, 0xf4 } }
extern MOZ_MUST_USE nsresult nsWindowsRegKeyConstructor(nsISupports* aOuter,
const nsIID& aIID,
void** aResult);
#endif // IMPL_LIBXUL
//-----------------------------------------------------------------------------
#endif // nsWindowsRegKey_h__