summaryrefslogtreecommitdiffstats
path: root/xpcom/ds
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/ds')
-rw-r--r--xpcom/ds/IncrementalTokenizer.cpp195
-rw-r--r--xpcom/ds/IncrementalTokenizer.h122
-rw-r--r--xpcom/ds/StickyTimeDuration.h267
-rw-r--r--xpcom/ds/Tokenizer.cpp738
-rw-r--r--xpcom/ds/Tokenizer.h446
-rw-r--r--xpcom/ds/moz.build105
-rw-r--r--xpcom/ds/nsArray.cpp242
-rw-r--r--xpcom/ds/nsArray.h76
-rw-r--r--xpcom/ds/nsAtomService.cpp25
-rw-r--r--xpcom/ds/nsAtomService.h25
-rw-r--r--xpcom/ds/nsAtomTable.cpp736
-rw-r--r--xpcom/ds/nsAtomTable.h19
-rw-r--r--xpcom/ds/nsCRT.cpp187
-rw-r--r--xpcom/ds/nsCRT.h186
-rw-r--r--xpcom/ds/nsCharSeparatedTokenizer.h200
-rw-r--r--xpcom/ds/nsCheapSets.h171
-rw-r--r--xpcom/ds/nsExpirationTracker.h570
-rw-r--r--xpcom/ds/nsHashPropertyBag.cpp284
-rw-r--r--xpcom/ds/nsHashPropertyBag.h57
-rw-r--r--xpcom/ds/nsIArray.idl91
-rw-r--r--xpcom/ds/nsIArrayExtensions.idl51
-rw-r--r--xpcom/ds/nsIAtom.idl166
-rw-r--r--xpcom/ds/nsIAtomService.idl35
-rw-r--r--xpcom/ds/nsICollection.idl67
-rw-r--r--xpcom/ds/nsIEnumerator.idl50
-rw-r--r--xpcom/ds/nsIHashable.idl24
-rw-r--r--xpcom/ds/nsIINIParser.idl58
-rw-r--r--xpcom/ds/nsIMutableArray.idl110
-rw-r--r--xpcom/ds/nsINIParserImpl.cpp142
-rw-r--r--xpcom/ds/nsINIParserImpl.h33
-rw-r--r--xpcom/ds/nsINIProcessor.js192
-rw-r--r--xpcom/ds/nsINIProcessor.manifest2
-rw-r--r--xpcom/ds/nsIObserver.idl38
-rw-r--r--xpcom/ds/nsIObserverService.idl77
-rw-r--r--xpcom/ds/nsIPersistentProperties.h14
-rw-r--r--xpcom/ds/nsIPersistentProperties2.idl63
-rw-r--r--xpcom/ds/nsIProperties.idl46
-rw-r--r--xpcom/ds/nsIProperty.idl25
-rw-r--r--xpcom/ds/nsIPropertyBag.idl30
-rw-r--r--xpcom/ds/nsIPropertyBag2.idl42
-rw-r--r--xpcom/ds/nsISerializable.idl32
-rw-r--r--xpcom/ds/nsISimpleEnumerator.idl46
-rw-r--r--xpcom/ds/nsIStringEnumerator.idl25
-rw-r--r--xpcom/ds/nsISupportsArray.idl60
-rw-r--r--xpcom/ds/nsISupportsIterators.idl292
-rw-r--r--xpcom/ds/nsISupportsPrimitives.idl235
-rw-r--r--xpcom/ds/nsIVariant.idl155
-rw-r--r--xpcom/ds/nsIWindowsRegKey.idl332
-rw-r--r--xpcom/ds/nsIWritablePropertyBag.idl27
-rw-r--r--xpcom/ds/nsIWritablePropertyBag2.idl22
-rw-r--r--xpcom/ds/nsMathUtils.h127
-rw-r--r--xpcom/ds/nsObserverList.cpp142
-rw-r--r--xpcom/ds/nsObserverList.h93
-rw-r--r--xpcom/ds/nsObserverService.cpp312
-rw-r--r--xpcom/ds/nsObserverService.h55
-rw-r--r--xpcom/ds/nsPersistentProperties.cpp666
-rw-r--r--xpcom/ds/nsPersistentProperties.h67
-rw-r--r--xpcom/ds/nsProperties.cpp99
-rw-r--r--xpcom/ds/nsProperties.h39
-rw-r--r--xpcom/ds/nsStaticAtom.h53
-rw-r--r--xpcom/ds/nsStaticNameTable.cpp201
-rw-r--r--xpcom/ds/nsStaticNameTable.h49
-rw-r--r--xpcom/ds/nsStringEnumerator.cpp262
-rw-r--r--xpcom/ds/nsStringEnumerator.h91
-rw-r--r--xpcom/ds/nsSupportsArray.cpp264
-rw-r--r--xpcom/ds/nsSupportsArray.h107
-rw-r--r--xpcom/ds/nsSupportsArrayEnumerator.cpp131
-rw-r--r--xpcom/ds/nsSupportsArrayEnumerator.h56
-rw-r--r--xpcom/ds/nsSupportsPrimitives.cpp849
-rw-r--r--xpcom/ds/nsSupportsPrimitives.h327
-rw-r--r--xpcom/ds/nsVariant.cpp2220
-rw-r--r--xpcom/ds/nsVariant.h229
-rw-r--r--xpcom/ds/nsWhitespaceTokenizer.h110
-rw-r--r--xpcom/ds/nsWindowsRegKey.cpp579
-rw-r--r--xpcom/ds/nsWindowsRegKey.h43
75 files changed, 14404 insertions, 0 deletions
diff --git a/xpcom/ds/IncrementalTokenizer.cpp b/xpcom/ds/IncrementalTokenizer.cpp
new file mode 100644
index 000000000..429428516
--- /dev/null
+++ b/xpcom/ds/IncrementalTokenizer.cpp
@@ -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
diff --git a/xpcom/ds/IncrementalTokenizer.h b/xpcom/ds/IncrementalTokenizer.h
new file mode 100644
index 000000000..f93668e63
--- /dev/null
+++ b/xpcom/ds/IncrementalTokenizer.h
@@ -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
diff --git a/xpcom/ds/StickyTimeDuration.h b/xpcom/ds/StickyTimeDuration.h
new file mode 100644
index 000000000..39a887dbc
--- /dev/null
+++ b/xpcom/ds/StickyTimeDuration.h
@@ -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 */
diff --git a/xpcom/ds/Tokenizer.cpp b/xpcom/ds/Tokenizer.cpp
new file mode 100644
index 000000000..66cc1ebb7
--- /dev/null
+++ b/xpcom/ds/Tokenizer.cpp
@@ -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
diff --git a/xpcom/ds/Tokenizer.h b/xpcom/ds/Tokenizer.h
new file mode 100644
index 000000000..b4aad9ed9
--- /dev/null
+++ b/xpcom/ds/Tokenizer.h
@@ -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__
diff --git a/xpcom/ds/moz.build b/xpcom/ds/moz.build
new file mode 100644
index 000000000..e12f1c3dd
--- /dev/null
+++ b/xpcom/ds/moz.build
@@ -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'
diff --git a/xpcom/ds/nsArray.cpp b/xpcom/ds/nsArray.cpp
new file mode 100644
index 000000000..d137a14a5
--- /dev/null
+++ b/xpcom/ds/nsArray.cpp
@@ -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();
+}
diff --git a/xpcom/ds/nsArray.h b/xpcom/ds/nsArray.h
new file mode 100644
index 000000000..0cda23487
--- /dev/null
+++ b/xpcom/ds/nsArray.h
@@ -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
diff --git a/xpcom/ds/nsAtomService.cpp b/xpcom/ds/nsAtomService.cpp
new file mode 100644
index 000000000..7785ca2e4
--- /dev/null
+++ b/xpcom/ds/nsAtomService.cpp
@@ -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;
+}
diff --git a/xpcom/ds/nsAtomService.h b/xpcom/ds/nsAtomService.h
new file mode 100644
index 000000000..7a1bcf2cd
--- /dev/null
+++ b/xpcom/ds/nsAtomService.h
@@ -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
diff --git a/xpcom/ds/nsAtomTable.cpp b/xpcom/ds/nsAtomTable.cpp
new file mode 100644
index 000000000..3dd3bd36c
--- /dev/null
+++ b/xpcom/ds/nsAtomTable.cpp
@@ -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;
+}
diff --git a/xpcom/ds/nsAtomTable.h b/xpcom/ds/nsAtomTable.h
new file mode 100644
index 000000000..89e5792f7
--- /dev/null
+++ b/xpcom/ds/nsAtomTable.h
@@ -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__
diff --git a/xpcom/ds/nsCRT.cpp b/xpcom/ds/nsCRT.cpp
new file mode 100644
index 000000000..0d11a8c26
--- /dev/null
+++ b/xpcom/ds/nsCRT.cpp
@@ -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;
+}
+
diff --git a/xpcom/ds/nsCRT.h b/xpcom/ds/nsCRT.h
new file mode 100644
index 000000000..6fad83d6d
--- /dev/null
+++ b/xpcom/ds/nsCRT.h
@@ -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___ */
diff --git a/xpcom/ds/nsCharSeparatedTokenizer.h b/xpcom/ds/nsCharSeparatedTokenizer.h
new file mode 100644
index 000000000..0e24d9d3e
--- /dev/null
+++ b/xpcom/ds/nsCharSeparatedTokenizer.h
@@ -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 */
diff --git a/xpcom/ds/nsCheapSets.h b/xpcom/ds/nsCheapSets.h
new file mode 100644
index 000000000..d75e60d20
--- /dev/null
+++ b/xpcom/ds/nsCheapSets.h
@@ -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
diff --git a/xpcom/ds/nsExpirationTracker.h b/xpcom/ds/nsExpirationTracker.h
new file mode 100644
index 000000000..2e77895db
--- /dev/null
+++ b/xpcom/ds/nsExpirationTracker.h
@@ -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_*/
diff --git a/xpcom/ds/nsHashPropertyBag.cpp b/xpcom/ds/nsHashPropertyBag.cpp
new file mode 100644
index 000000000..6f9fc8dec
--- /dev/null
+++ b/xpcom/ds/nsHashPropertyBag.cpp
@@ -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
diff --git a/xpcom/ds/nsHashPropertyBag.h b/xpcom/ds/nsHashPropertyBag.h
new file mode 100644
index 000000000..e41c984ba
--- /dev/null
+++ b/xpcom/ds/nsHashPropertyBag.h
@@ -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___ */
diff --git a/xpcom/ds/nsIArray.idl b/xpcom/ds/nsIArray.idl
new file mode 100644
index 000000000..d591253e9
--- /dev/null
+++ b/xpcom/ds/nsIArray.idl
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "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();
+};
diff --git a/xpcom/ds/nsIArrayExtensions.idl b/xpcom/ds/nsIArrayExtensions.idl
new file mode 100644
index 000000000..3682d2ee7
--- /dev/null
+++ b/xpcom/ds/nsIArrayExtensions.idl
@@ -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);
+};
diff --git a/xpcom/ds/nsIAtom.idl b/xpcom/ds/nsIAtom.idl
new file mode 100644
index 000000000..c02540838
--- /dev/null
+++ b/xpcom/ds/nsIAtom.idl
@@ -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())
+ {
+ }
+};
+
+%}
diff --git a/xpcom/ds/nsIAtomService.idl b/xpcom/ds/nsIAtomService.idl
new file mode 100644
index 000000000..a7885a413
--- /dev/null
+++ b/xpcom/ds/nsIAtomService.idl
@@ -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);
+};
diff --git a/xpcom/ds/nsICollection.idl b/xpcom/ds/nsICollection.idl
new file mode 100644
index 000000000..3cd851419
--- /dev/null
+++ b/xpcom/ds/nsICollection.idl
@@ -0,0 +1,67 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "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);
+ }
+
+%}
diff --git a/xpcom/ds/nsIEnumerator.idl b/xpcom/ds/nsIEnumerator.idl
new file mode 100644
index 000000000..9482ef436
--- /dev/null
+++ b/xpcom/ds/nsIEnumerator.idl
@@ -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();
+};
diff --git a/xpcom/ds/nsIHashable.idl b/xpcom/ds/nsIHashable.idl
new file mode 100644
index 000000000..aa223787c
--- /dev/null
+++ b/xpcom/ds/nsIHashable.idl
@@ -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;
+};
diff --git a/xpcom/ds/nsIINIParser.idl b/xpcom/ds/nsIINIParser.idl
new file mode 100644
index 000000000..166828e5d
--- /dev/null
+++ b/xpcom/ds/nsIINIParser.idl
@@ -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);
+};
diff --git a/xpcom/ds/nsIMutableArray.idl b/xpcom/ds/nsIMutableArray.idl
new file mode 100644
index 000000000..67e527a81
--- /dev/null
+++ b/xpcom/ds/nsIMutableArray.idl
@@ -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();
+};
diff --git a/xpcom/ds/nsINIParserImpl.cpp b/xpcom/ds/nsINIParserImpl.cpp
new file mode 100644
index 000000000..24def4e58
--- /dev/null
+++ b/xpcom/ds/nsINIParserImpl.cpp
@@ -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);
+}
diff --git a/xpcom/ds/nsINIParserImpl.h b/xpcom/ds/nsINIParserImpl.h
new file mode 100644
index 000000000..8e63f36d7
--- /dev/null
+++ b/xpcom/ds/nsINIParserImpl.h
@@ -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__
diff --git a/xpcom/ds/nsINIProcessor.js b/xpcom/ds/nsINIProcessor.js
new file mode 100644
index 000000000..832f35a5f
--- /dev/null
+++ b/xpcom/ds/nsINIProcessor.js
@@ -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);
diff --git a/xpcom/ds/nsINIProcessor.manifest b/xpcom/ds/nsINIProcessor.manifest
new file mode 100644
index 000000000..da19d2aed
--- /dev/null
+++ b/xpcom/ds/nsINIProcessor.manifest
@@ -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}
diff --git a/xpcom/ds/nsIObserver.idl b/xpcom/ds/nsIObserver.idl
new file mode 100644
index 000000000..cfb4f912b
--- /dev/null
+++ b/xpcom/ds/nsIObserver.idl
@@ -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 );
+
+};
+
diff --git a/xpcom/ds/nsIObserverService.idl b/xpcom/ds/nsIObserverService.idl
new file mode 100644
index 000000000..d5b40e227
--- /dev/null
+++ b/xpcom/ds/nsIObserverService.idl
@@ -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 );
+
+
+};
+
+
diff --git a/xpcom/ds/nsIPersistentProperties.h b/xpcom/ds/nsIPersistentProperties.h
new file mode 100644
index 000000000..e886076e9
--- /dev/null
+++ b/xpcom/ds/nsIPersistentProperties.h
@@ -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__ */
diff --git a/xpcom/ds/nsIPersistentProperties2.idl b/xpcom/ds/nsIPersistentProperties2.idl
new file mode 100644
index 000000000..3f24bb773
--- /dev/null
+++ b/xpcom/ds/nsIPersistentProperties2.idl
@@ -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"
+
+%}
+
diff --git a/xpcom/ds/nsIProperties.idl b/xpcom/ds/nsIProperties.idl
new file mode 100644
index 000000000..a87e14443
--- /dev/null
+++ b/xpcom/ds/nsIProperties.idl
@@ -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);
+};
diff --git a/xpcom/ds/nsIProperty.idl b/xpcom/ds/nsIProperty.idl
new file mode 100644
index 000000000..9e6e1e487
--- /dev/null
+++ b/xpcom/ds/nsIProperty.idl
@@ -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;
+};
diff --git a/xpcom/ds/nsIPropertyBag.idl b/xpcom/ds/nsIPropertyBag.idl
new file mode 100644
index 000000000..1d1dfced7
--- /dev/null
+++ b/xpcom/ds/nsIPropertyBag.idl
@@ -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);
+};
+
+
diff --git a/xpcom/ds/nsIPropertyBag2.idl b/xpcom/ds/nsIPropertyBag2.idl
new file mode 100644
index 000000000..7b4be7f4a
--- /dev/null
+++ b/xpcom/ds/nsIPropertyBag2.idl
@@ -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);
+};
diff --git a/xpcom/ds/nsISerializable.idl b/xpcom/ds/nsISerializable.idl
new file mode 100644
index 000000000..f525f3a8f
--- /dev/null
+++ b/xpcom/ds/nsISerializable.idl
@@ -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);
+};
diff --git a/xpcom/ds/nsISimpleEnumerator.idl b/xpcom/ds/nsISimpleEnumerator.idl
new file mode 100644
index 000000000..cbb0cd200
--- /dev/null
+++ b/xpcom/ds/nsISimpleEnumerator.idl
@@ -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();
+};
diff --git a/xpcom/ds/nsIStringEnumerator.idl b/xpcom/ds/nsIStringEnumerator.idl
new file mode 100644
index 000000000..d9c4130ee
--- /dev/null
+++ b/xpcom/ds/nsIStringEnumerator.idl
@@ -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();
+};
+
diff --git a/xpcom/ds/nsISupportsArray.idl b/xpcom/ds/nsISupportsArray.idl
new file mode 100644
index 000000000..30e2a5035
--- /dev/null
+++ b/xpcom/ds/nsISupportsArray.idl
@@ -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);
+
+%}
diff --git a/xpcom/ds/nsISupportsIterators.idl b/xpcom/ds/nsISupportsIterators.idl
new file mode 100644
index 000000000..8d47375d5
--- /dev/null
+++ b/xpcom/ds/nsISupportsIterators.idl
@@ -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++
+%}
diff --git a/xpcom/ds/nsISupportsPrimitives.idl b/xpcom/ds/nsISupportsPrimitives.idl
new file mode 100644
index 000000000..71940739c
--- /dev/null
+++ b/xpcom/ds/nsISupportsPrimitives.idl
@@ -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();
+};
+
+
diff --git a/xpcom/ds/nsIVariant.idl b/xpcom/ds/nsIVariant.idl
new file mode 100644
index 000000000..ef5528463
--- /dev/null
+++ b/xpcom/ds/nsIVariant.idl
@@ -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"
+%}
diff --git a/xpcom/ds/nsIWindowsRegKey.idl b/xpcom/ds/nsIWindowsRegKey.idl
new file mode 100644
index 000000000..25be0995b
--- /dev/null
+++ b/xpcom/ds/nsIWindowsRegKey.idl
@@ -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();
+};
diff --git a/xpcom/ds/nsIWritablePropertyBag.idl b/xpcom/ds/nsIWritablePropertyBag.idl
new file mode 100644
index 000000000..e916b7ccd
--- /dev/null
+++ b/xpcom/ds/nsIWritablePropertyBag.idl
@@ -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);
+};
diff --git a/xpcom/ds/nsIWritablePropertyBag2.idl b/xpcom/ds/nsIWritablePropertyBag2.idl
new file mode 100644
index 000000000..50f093905
--- /dev/null
+++ b/xpcom/ds/nsIWritablePropertyBag2.idl
@@ -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);
+};
diff --git a/xpcom/ds/nsMathUtils.h b/xpcom/ds/nsMathUtils.h
new file mode 100644
index 000000000..b10b8144e
--- /dev/null
+++ b/xpcom/ds/nsMathUtils.h
@@ -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
diff --git a/xpcom/ds/nsObserverList.cpp b/xpcom/ds/nsObserverList.cpp
new file mode 100644
index 000000000..fef8831b8
--- /dev/null
+++ b/xpcom/ds/nsObserverList.cpp
@@ -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;
+}
diff --git a/xpcom/ds/nsObserverList.h b/xpcom/ds/nsObserverList.h
new file mode 100644
index 000000000..90a685ea6
--- /dev/null
+++ b/xpcom/ds/nsObserverList.h
@@ -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___ */
diff --git a/xpcom/ds/nsObserverService.cpp b/xpcom/ds/nsObserverService.cpp
new file mode 100644
index 000000000..23cc54fa7
--- /dev/null
+++ b/xpcom/ds/nsObserverService.cpp
@@ -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;
+}
diff --git a/xpcom/ds/nsObserverService.h b/xpcom/ds/nsObserverService.h
new file mode 100644
index 000000000..fa0d9b9d8
--- /dev/null
+++ b/xpcom/ds/nsObserverService.h
@@ -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___ */
diff --git a/xpcom/ds/nsPersistentProperties.cpp b/xpcom/ds/nsPersistentProperties.cpp
new file mode 100644
index 000000000..ea925874c
--- /dev/null
+++ b/xpcom/ds/nsPersistentProperties.cpp
@@ -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;
+}
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/xpcom/ds/nsPersistentProperties.h b/xpcom/ds/nsPersistentProperties.h
new file mode 100644
index 000000000..6c9e9cb51
--- /dev/null
+++ b/xpcom/ds/nsPersistentProperties.h
@@ -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___ */
diff --git a/xpcom/ds/nsProperties.cpp b/xpcom/ds/nsProperties.cpp
new file mode 100644
index 000000000..3a73ac7da
--- /dev/null
+++ b/xpcom/ds/nsProperties.cpp
@@ -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;
+}
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/xpcom/ds/nsProperties.h b/xpcom/ds/nsProperties.h
new file mode 100644
index 000000000..6949017dd
--- /dev/null
+++ b/xpcom/ds/nsProperties.h
@@ -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___ */
diff --git a/xpcom/ds/nsStaticAtom.h b/xpcom/ds/nsStaticAtom.h
new file mode 100644
index 000000000..7e31623db
--- /dev/null
+++ b/xpcom/ds/nsStaticAtom.h
@@ -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
diff --git a/xpcom/ds/nsStaticNameTable.cpp b/xpcom/ds/nsStaticNameTable.cpp
new file mode 100644
index 000000000..26bd172a7
--- /dev/null
+++ b/xpcom/ds/nsStaticNameTable.cpp
@@ -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;
+}
diff --git a/xpcom/ds/nsStaticNameTable.h b/xpcom/ds/nsStaticNameTable.h
new file mode 100644
index 000000000..0b9df3039
--- /dev/null
+++ b/xpcom/ds/nsStaticNameTable.h
@@ -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___ */
diff --git a/xpcom/ds/nsStringEnumerator.cpp b/xpcom/ds/nsStringEnumerator.cpp
new file mode 100644
index 000000000..bdcd53e1a
--- /dev/null
+++ b/xpcom/ds/nsStringEnumerator.cpp
@@ -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);
+}
+
diff --git a/xpcom/ds/nsStringEnumerator.h b/xpcom/ds/nsStringEnumerator.h
new file mode 100644
index 000000000..226afd7f4
--- /dev/null
+++ b/xpcom/ds/nsStringEnumerator.h
@@ -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);
diff --git a/xpcom/ds/nsSupportsArray.cpp b/xpcom/ds/nsSupportsArray.cpp
new file mode 100644
index 000000000..67de8ae16
--- /dev/null
+++ b/xpcom/ds/nsSupportsArray.cpp
@@ -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
+
diff --git a/xpcom/ds/nsSupportsArray.h b/xpcom/ds/nsSupportsArray.h
new file mode 100644
index 000000000..eed611104
--- /dev/null
+++ b/xpcom/ds/nsSupportsArray.h
@@ -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__
diff --git a/xpcom/ds/nsSupportsArrayEnumerator.cpp b/xpcom/ds/nsSupportsArrayEnumerator.cpp
new file mode 100644
index 000000000..3eb6a7a58
--- /dev/null
+++ b/xpcom/ds/nsSupportsArrayEnumerator.cpp
@@ -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
diff --git a/xpcom/ds/nsSupportsArrayEnumerator.h b/xpcom/ds/nsSupportsArrayEnumerator.h
new file mode 100644
index 000000000..bd316d6b9
--- /dev/null
+++ b/xpcom/ds/nsSupportsArrayEnumerator.h
@@ -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
+
diff --git a/xpcom/ds/nsSupportsPrimitives.cpp b/xpcom/ds/nsSupportsPrimitives.cpp
new file mode 100644
index 000000000..aa929b9de
--- /dev/null
+++ b/xpcom/ds/nsSupportsPrimitives.cpp
@@ -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;
+}
diff --git a/xpcom/ds/nsSupportsPrimitives.h b/xpcom/ds/nsSupportsPrimitives.h
new file mode 100644
index 000000000..17ed0f47f
--- /dev/null
+++ b/xpcom/ds/nsSupportsPrimitives.h
@@ -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__ */
diff --git a/xpcom/ds/nsVariant.cpp b/xpcom/ds/nsVariant.cpp
new file mode 100644
index 000000000..edb020139
--- /dev/null
+++ b/xpcom/ds/nsVariant.cpp
@@ -0,0 +1,2220 @@
+/* -*- 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 "nsVariant.h"
+#include "prprf.h"
+#include "prdtoa.h"
+#include <math.h>
+#include "nsCycleCollectionParticipant.h"
+#include "xpt_struct.h"
+#include "nsReadableUtils.h"
+#include "nsMemory.h"
+#include "nsString.h"
+#include "nsCRTGlue.h"
+
+/***************************************************************************/
+// Helpers for static convert functions...
+
+static nsresult
+String2Double(const char* aString, double* aResult)
+{
+ char* next;
+ double value = PR_strtod(aString, &next);
+ if (next == aString) {
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+ *aResult = value;
+ return NS_OK;
+}
+
+static nsresult
+AString2Double(const nsAString& aString, double* aResult)
+{
+ char* pChars = ToNewCString(aString);
+ if (!pChars) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ nsresult rv = String2Double(pChars, aResult);
+ free(pChars);
+ return rv;
+}
+
+static nsresult
+AUTF8String2Double(const nsAUTF8String& aString, double* aResult)
+{
+ return String2Double(PromiseFlatUTF8String(aString).get(), aResult);
+}
+
+static nsresult
+ACString2Double(const nsACString& aString, double* aResult)
+{
+ return String2Double(PromiseFlatCString(aString).get(), aResult);
+}
+
+// Fills aOutData with double, uint32_t, or int32_t.
+// Returns NS_OK, an error code, or a non-NS_OK success code
+nsresult
+nsDiscriminatedUnion::ToManageableNumber(nsDiscriminatedUnion* aOutData) const
+{
+ nsresult rv;
+
+ switch (mType) {
+ // This group results in a int32_t...
+
+#define CASE__NUMBER_INT32(type_, member_) \
+ case nsIDataType::type_ : \
+ aOutData->u.mInt32Value = u.member_ ; \
+ aOutData->mType = nsIDataType::VTYPE_INT32; \
+ return NS_OK;
+
+ CASE__NUMBER_INT32(VTYPE_INT8, mInt8Value)
+ CASE__NUMBER_INT32(VTYPE_INT16, mInt16Value)
+ CASE__NUMBER_INT32(VTYPE_INT32, mInt32Value)
+ CASE__NUMBER_INT32(VTYPE_UINT8, mUint8Value)
+ CASE__NUMBER_INT32(VTYPE_UINT16, mUint16Value)
+ CASE__NUMBER_INT32(VTYPE_BOOL, mBoolValue)
+ CASE__NUMBER_INT32(VTYPE_CHAR, mCharValue)
+ CASE__NUMBER_INT32(VTYPE_WCHAR, mWCharValue)
+
+#undef CASE__NUMBER_INT32
+
+ // This group results in a uint32_t...
+
+ case nsIDataType::VTYPE_UINT32:
+ aOutData->u.mInt32Value = u.mUint32Value;
+ aOutData->mType = nsIDataType::VTYPE_INT32;
+ return NS_OK;
+
+ // This group results in a double...
+
+ case nsIDataType::VTYPE_INT64:
+ case nsIDataType::VTYPE_UINT64:
+ // XXX Need boundary checking here.
+ // We may need to return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA
+ aOutData->u.mDoubleValue = double(u.mInt64Value);
+ aOutData->mType = nsIDataType::VTYPE_DOUBLE;
+ return NS_OK;
+ case nsIDataType::VTYPE_FLOAT:
+ aOutData->u.mDoubleValue = u.mFloatValue;
+ aOutData->mType = nsIDataType::VTYPE_DOUBLE;
+ return NS_OK;
+ case nsIDataType::VTYPE_DOUBLE:
+ aOutData->u.mDoubleValue = u.mDoubleValue;
+ aOutData->mType = nsIDataType::VTYPE_DOUBLE;
+ return NS_OK;
+ case nsIDataType::VTYPE_CHAR_STR:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ rv = String2Double(u.str.mStringValue, &aOutData->u.mDoubleValue);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ aOutData->mType = nsIDataType::VTYPE_DOUBLE;
+ return NS_OK;
+ case nsIDataType::VTYPE_DOMSTRING:
+ case nsIDataType::VTYPE_ASTRING:
+ rv = AString2Double(*u.mAStringValue, &aOutData->u.mDoubleValue);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ aOutData->mType = nsIDataType::VTYPE_DOUBLE;
+ return NS_OK;
+ case nsIDataType::VTYPE_UTF8STRING:
+ rv = AUTF8String2Double(*u.mUTF8StringValue,
+ &aOutData->u.mDoubleValue);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ aOutData->mType = nsIDataType::VTYPE_DOUBLE;
+ return NS_OK;
+ case nsIDataType::VTYPE_CSTRING:
+ rv = ACString2Double(*u.mCStringValue,
+ &aOutData->u.mDoubleValue);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ aOutData->mType = nsIDataType::VTYPE_DOUBLE;
+ return NS_OK;
+ case nsIDataType::VTYPE_WCHAR_STR:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ rv = AString2Double(nsDependentString(u.wstr.mWStringValue),
+ &aOutData->u.mDoubleValue);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ aOutData->mType = nsIDataType::VTYPE_DOUBLE;
+ return NS_OK;
+
+ // This group fails...
+
+ case nsIDataType::VTYPE_VOID:
+ case nsIDataType::VTYPE_ID:
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ case nsIDataType::VTYPE_ARRAY:
+ case nsIDataType::VTYPE_EMPTY_ARRAY:
+ case nsIDataType::VTYPE_EMPTY:
+ default:
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+}
+
+/***************************************************************************/
+// Array helpers...
+
+void
+nsDiscriminatedUnion::FreeArray()
+{
+ NS_ASSERTION(mType == nsIDataType::VTYPE_ARRAY, "bad FreeArray call");
+ NS_ASSERTION(u.array.mArrayValue, "bad array");
+ NS_ASSERTION(u.array.mArrayCount, "bad array count");
+
+#define CASE__FREE_ARRAY_PTR(type_, ctype_) \
+ case nsIDataType::type_ : \
+ { \
+ ctype_** p = (ctype_**) u.array.mArrayValue; \
+ for (uint32_t i = u.array.mArrayCount; i > 0; p++, i--) \
+ if (*p) \
+ free((char*)*p); \
+ break; \
+ }
+
+#define CASE__FREE_ARRAY_IFACE(type_, ctype_) \
+ case nsIDataType::type_ : \
+ { \
+ ctype_** p = (ctype_**) u.array.mArrayValue; \
+ for (uint32_t i = u.array.mArrayCount; i > 0; p++, i--) \
+ if (*p) \
+ (*p)->Release(); \
+ break; \
+ }
+
+ switch (u.array.mArrayType) {
+ case nsIDataType::VTYPE_INT8:
+ case nsIDataType::VTYPE_INT16:
+ case nsIDataType::VTYPE_INT32:
+ case nsIDataType::VTYPE_INT64:
+ case nsIDataType::VTYPE_UINT8:
+ case nsIDataType::VTYPE_UINT16:
+ case nsIDataType::VTYPE_UINT32:
+ case nsIDataType::VTYPE_UINT64:
+ case nsIDataType::VTYPE_FLOAT:
+ case nsIDataType::VTYPE_DOUBLE:
+ case nsIDataType::VTYPE_BOOL:
+ case nsIDataType::VTYPE_CHAR:
+ case nsIDataType::VTYPE_WCHAR:
+ break;
+
+ // XXX We ASSUME that "array of nsID" means "array of pointers to nsID".
+ CASE__FREE_ARRAY_PTR(VTYPE_ID, nsID)
+ CASE__FREE_ARRAY_PTR(VTYPE_CHAR_STR, char)
+ CASE__FREE_ARRAY_PTR(VTYPE_WCHAR_STR, char16_t)
+ CASE__FREE_ARRAY_IFACE(VTYPE_INTERFACE, nsISupports)
+ CASE__FREE_ARRAY_IFACE(VTYPE_INTERFACE_IS, nsISupports)
+
+ // The rest are illegal.
+ case nsIDataType::VTYPE_VOID:
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ case nsIDataType::VTYPE_UTF8STRING:
+ case nsIDataType::VTYPE_CSTRING:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ case nsIDataType::VTYPE_ARRAY:
+ case nsIDataType::VTYPE_EMPTY_ARRAY:
+ case nsIDataType::VTYPE_EMPTY:
+ default:
+ NS_ERROR("bad type in array!");
+ break;
+ }
+
+ // Free the array memory.
+ free((char*)u.array.mArrayValue);
+
+#undef CASE__FREE_ARRAY_PTR
+#undef CASE__FREE_ARRAY_IFACE
+}
+
+static nsresult
+CloneArray(uint16_t aInType, const nsIID* aInIID,
+ uint32_t aInCount, void* aInValue,
+ uint16_t* aOutType, nsIID* aOutIID,
+ uint32_t* aOutCount, void** aOutValue)
+{
+ NS_ASSERTION(aInCount, "bad param");
+ NS_ASSERTION(aInValue, "bad param");
+ NS_ASSERTION(aOutType, "bad param");
+ NS_ASSERTION(aOutCount, "bad param");
+ NS_ASSERTION(aOutValue, "bad param");
+
+ uint32_t allocatedValueCount = 0;
+ nsresult rv = NS_OK;
+ uint32_t i;
+
+ // First we figure out the size of the elements for the new u.array.
+
+ size_t elementSize;
+ size_t allocSize;
+
+ switch (aInType) {
+ case nsIDataType::VTYPE_INT8:
+ elementSize = sizeof(int8_t);
+ break;
+ case nsIDataType::VTYPE_INT16:
+ elementSize = sizeof(int16_t);
+ break;
+ case nsIDataType::VTYPE_INT32:
+ elementSize = sizeof(int32_t);
+ break;
+ case nsIDataType::VTYPE_INT64:
+ elementSize = sizeof(int64_t);
+ break;
+ case nsIDataType::VTYPE_UINT8:
+ elementSize = sizeof(uint8_t);
+ break;
+ case nsIDataType::VTYPE_UINT16:
+ elementSize = sizeof(uint16_t);
+ break;
+ case nsIDataType::VTYPE_UINT32:
+ elementSize = sizeof(uint32_t);
+ break;
+ case nsIDataType::VTYPE_UINT64:
+ elementSize = sizeof(uint64_t);
+ break;
+ case nsIDataType::VTYPE_FLOAT:
+ elementSize = sizeof(float);
+ break;
+ case nsIDataType::VTYPE_DOUBLE:
+ elementSize = sizeof(double);
+ break;
+ case nsIDataType::VTYPE_BOOL:
+ elementSize = sizeof(bool);
+ break;
+ case nsIDataType::VTYPE_CHAR:
+ elementSize = sizeof(char);
+ break;
+ case nsIDataType::VTYPE_WCHAR:
+ elementSize = sizeof(char16_t);
+ break;
+
+ // XXX We ASSUME that "array of nsID" means "array of pointers to nsID".
+ case nsIDataType::VTYPE_ID:
+ case nsIDataType::VTYPE_CHAR_STR:
+ case nsIDataType::VTYPE_WCHAR_STR:
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ elementSize = sizeof(void*);
+ break;
+
+ // The rest are illegal.
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ case nsIDataType::VTYPE_UTF8STRING:
+ case nsIDataType::VTYPE_CSTRING:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ case nsIDataType::VTYPE_VOID:
+ case nsIDataType::VTYPE_ARRAY:
+ case nsIDataType::VTYPE_EMPTY_ARRAY:
+ case nsIDataType::VTYPE_EMPTY:
+ default:
+ NS_ERROR("bad type in array!");
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+
+
+ // Alloc the u.array.
+
+ allocSize = aInCount * elementSize;
+ *aOutValue = moz_xmalloc(allocSize);
+ if (!*aOutValue) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Clone the elements.
+
+ switch (aInType) {
+ case nsIDataType::VTYPE_INT8:
+ case nsIDataType::VTYPE_INT16:
+ case nsIDataType::VTYPE_INT32:
+ case nsIDataType::VTYPE_INT64:
+ case nsIDataType::VTYPE_UINT8:
+ case nsIDataType::VTYPE_UINT16:
+ case nsIDataType::VTYPE_UINT32:
+ case nsIDataType::VTYPE_UINT64:
+ case nsIDataType::VTYPE_FLOAT:
+ case nsIDataType::VTYPE_DOUBLE:
+ case nsIDataType::VTYPE_BOOL:
+ case nsIDataType::VTYPE_CHAR:
+ case nsIDataType::VTYPE_WCHAR:
+ memcpy(*aOutValue, aInValue, allocSize);
+ break;
+
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ if (aOutIID) {
+ *aOutIID = *aInIID;
+ }
+ MOZ_FALLTHROUGH;
+
+ case nsIDataType::VTYPE_INTERFACE: {
+ memcpy(*aOutValue, aInValue, allocSize);
+
+ nsISupports** p = (nsISupports**)*aOutValue;
+ for (i = aInCount; i > 0; ++p, --i)
+ if (*p) {
+ (*p)->AddRef();
+ }
+ break;
+ }
+
+ // XXX We ASSUME that "array of nsID" means "array of pointers to nsID".
+ case nsIDataType::VTYPE_ID: {
+ nsID** inp = (nsID**)aInValue;
+ nsID** outp = (nsID**)*aOutValue;
+ for (i = aInCount; i > 0; --i) {
+ nsID* idp = *(inp++);
+ if (idp) {
+ if (!(*(outp++) = (nsID*)nsMemory::Clone((char*)idp, sizeof(nsID)))) {
+ goto bad;
+ }
+ } else {
+ *(outp++) = nullptr;
+ }
+ allocatedValueCount++;
+ }
+ break;
+ }
+
+ case nsIDataType::VTYPE_CHAR_STR: {
+ char** inp = (char**)aInValue;
+ char** outp = (char**)*aOutValue;
+ for (i = aInCount; i > 0; i--) {
+ char* str = *(inp++);
+ if (str) {
+ if (!(*(outp++) = (char*)nsMemory::Clone(
+ str, (strlen(str) + 1) * sizeof(char)))) {
+ goto bad;
+ }
+ } else {
+ *(outp++) = nullptr;
+ }
+ allocatedValueCount++;
+ }
+ break;
+ }
+
+ case nsIDataType::VTYPE_WCHAR_STR: {
+ char16_t** inp = (char16_t**)aInValue;
+ char16_t** outp = (char16_t**)*aOutValue;
+ for (i = aInCount; i > 0; i--) {
+ char16_t* str = *(inp++);
+ if (str) {
+ if (!(*(outp++) = (char16_t*)nsMemory::Clone(
+ str, (NS_strlen(str) + 1) * sizeof(char16_t)))) {
+ goto bad;
+ }
+ } else {
+ *(outp++) = nullptr;
+ }
+ allocatedValueCount++;
+ }
+ break;
+ }
+
+ // The rest are illegal.
+ case nsIDataType::VTYPE_VOID:
+ case nsIDataType::VTYPE_ARRAY:
+ case nsIDataType::VTYPE_EMPTY_ARRAY:
+ case nsIDataType::VTYPE_EMPTY:
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ case nsIDataType::VTYPE_UTF8STRING:
+ case nsIDataType::VTYPE_CSTRING:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ default:
+ NS_ERROR("bad type in array!");
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+
+ *aOutType = aInType;
+ *aOutCount = aInCount;
+ return NS_OK;
+
+bad:
+ if (*aOutValue) {
+ char** p = (char**)*aOutValue;
+ for (i = allocatedValueCount; i > 0; ++p, --i)
+ if (*p) {
+ free(*p);
+ }
+ free((char*)*aOutValue);
+ *aOutValue = nullptr;
+ }
+ return rv;
+}
+
+/***************************************************************************/
+
+#define TRIVIAL_DATA_CONVERTER(type_, member_, retval_) \
+ if (mType == nsIDataType::type_) { \
+ *retval_ = u.member_; \
+ return NS_OK; \
+ }
+
+#define NUMERIC_CONVERSION_METHOD_BEGIN(type_, Ctype_, name_) \
+nsresult \
+nsDiscriminatedUnion::ConvertTo##name_ (Ctype_* aResult) const \
+{ \
+ TRIVIAL_DATA_CONVERTER(type_, m##name_##Value, aResult) \
+ nsDiscriminatedUnion tempData; \
+ nsresult rv = ToManageableNumber(&tempData); \
+ /* */ \
+ /* NOTE: rv may indicate a success code that we want to preserve */ \
+ /* For the final return. So all the return cases below should return */ \
+ /* this rv when indicating success. */ \
+ /* */ \
+ if (NS_FAILED(rv)) \
+ return rv; \
+ switch(tempData.mType) \
+ {
+
+#define CASE__NUMERIC_CONVERSION_INT32_JUST_CAST(Ctype_) \
+ case nsIDataType::VTYPE_INT32: \
+ *aResult = ( Ctype_ ) tempData.u.mInt32Value; \
+ return rv;
+
+#define CASE__NUMERIC_CONVERSION_INT32_MIN_MAX(Ctype_, min_, max_) \
+ case nsIDataType::VTYPE_INT32: \
+ { \
+ int32_t value = tempData.u.mInt32Value; \
+ if (value < min_ || value > max_) \
+ return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA; \
+ *aResult = ( Ctype_ ) value; \
+ return rv; \
+ }
+
+#define CASE__NUMERIC_CONVERSION_UINT32_JUST_CAST(Ctype_) \
+ case nsIDataType::VTYPE_UINT32: \
+ *aResult = ( Ctype_ ) tempData.u.mUint32Value; \
+ return rv;
+
+#define CASE__NUMERIC_CONVERSION_UINT32_MAX(Ctype_, max_) \
+ case nsIDataType::VTYPE_UINT32: \
+ { \
+ uint32_t value = tempData.u.mUint32Value; \
+ if (value > max_) \
+ return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA; \
+ *aResult = ( Ctype_ ) value; \
+ return rv; \
+ }
+
+#define CASE__NUMERIC_CONVERSION_DOUBLE_JUST_CAST(Ctype_) \
+ case nsIDataType::VTYPE_DOUBLE: \
+ *aResult = ( Ctype_ ) tempData.u.mDoubleValue; \
+ return rv;
+
+#define CASE__NUMERIC_CONVERSION_DOUBLE_MIN_MAX(Ctype_, min_, max_) \
+ case nsIDataType::VTYPE_DOUBLE: \
+ { \
+ double value = tempData.u.mDoubleValue; \
+ if (value < min_ || value > max_) \
+ return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA; \
+ *aResult = ( Ctype_ ) value; \
+ return rv; \
+ }
+
+#define CASE__NUMERIC_CONVERSION_DOUBLE_MIN_MAX_INT(Ctype_, min_, max_) \
+ case nsIDataType::VTYPE_DOUBLE: \
+ { \
+ double value = tempData.u.mDoubleValue; \
+ if (value < min_ || value > max_) \
+ return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA; \
+ *aResult = ( Ctype_ ) value; \
+ return (0.0 == fmod(value,1.0)) ? \
+ rv : NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA; \
+ }
+
+#define CASES__NUMERIC_CONVERSION_NORMAL(Ctype_, min_, max_) \
+ CASE__NUMERIC_CONVERSION_INT32_MIN_MAX(Ctype_, min_, max_) \
+ CASE__NUMERIC_CONVERSION_UINT32_MAX(Ctype_, max_) \
+ CASE__NUMERIC_CONVERSION_DOUBLE_MIN_MAX_INT(Ctype_, min_, max_)
+
+#define NUMERIC_CONVERSION_METHOD_END \
+ default: \
+ NS_ERROR("bad type returned from ToManageableNumber"); \
+ return NS_ERROR_CANNOT_CONVERT_DATA; \
+ } \
+}
+
+#define NUMERIC_CONVERSION_METHOD_NORMAL(type_, Ctype_, name_, min_, max_) \
+ NUMERIC_CONVERSION_METHOD_BEGIN(type_, Ctype_, name_) \
+ CASES__NUMERIC_CONVERSION_NORMAL(Ctype_, min_, max_) \
+ NUMERIC_CONVERSION_METHOD_END
+
+/***************************************************************************/
+// These expand into full public methods...
+
+NUMERIC_CONVERSION_METHOD_NORMAL(VTYPE_INT8, uint8_t, Int8, (-127 - 1), 127)
+NUMERIC_CONVERSION_METHOD_NORMAL(VTYPE_INT16, int16_t, Int16, (-32767 - 1), 32767)
+
+NUMERIC_CONVERSION_METHOD_BEGIN(VTYPE_INT32, int32_t, Int32)
+ CASE__NUMERIC_CONVERSION_INT32_JUST_CAST(int32_t)
+ CASE__NUMERIC_CONVERSION_UINT32_MAX(int32_t, 2147483647)
+ CASE__NUMERIC_CONVERSION_DOUBLE_MIN_MAX_INT(int32_t, (-2147483647 - 1), 2147483647)
+NUMERIC_CONVERSION_METHOD_END
+
+NUMERIC_CONVERSION_METHOD_NORMAL(VTYPE_UINT8, uint8_t, Uint8, 0, 255)
+NUMERIC_CONVERSION_METHOD_NORMAL(VTYPE_UINT16, uint16_t, Uint16, 0, 65535)
+
+NUMERIC_CONVERSION_METHOD_BEGIN(VTYPE_UINT32, uint32_t, Uint32)
+ CASE__NUMERIC_CONVERSION_INT32_MIN_MAX(uint32_t, 0, 2147483647)
+ CASE__NUMERIC_CONVERSION_UINT32_JUST_CAST(uint32_t)
+ CASE__NUMERIC_CONVERSION_DOUBLE_MIN_MAX_INT(uint32_t, 0, 4294967295U)
+NUMERIC_CONVERSION_METHOD_END
+
+// XXX toFloat convertions need to be fixed!
+NUMERIC_CONVERSION_METHOD_BEGIN(VTYPE_FLOAT, float, Float)
+ CASE__NUMERIC_CONVERSION_INT32_JUST_CAST(float)
+ CASE__NUMERIC_CONVERSION_UINT32_JUST_CAST(float)
+ CASE__NUMERIC_CONVERSION_DOUBLE_JUST_CAST(float)
+NUMERIC_CONVERSION_METHOD_END
+
+NUMERIC_CONVERSION_METHOD_BEGIN(VTYPE_DOUBLE, double, Double)
+ CASE__NUMERIC_CONVERSION_INT32_JUST_CAST(double)
+ CASE__NUMERIC_CONVERSION_UINT32_JUST_CAST(double)
+ CASE__NUMERIC_CONVERSION_DOUBLE_JUST_CAST(double)
+NUMERIC_CONVERSION_METHOD_END
+
+// XXX toChar convertions need to be fixed!
+NUMERIC_CONVERSION_METHOD_BEGIN(VTYPE_CHAR, char, Char)
+ CASE__NUMERIC_CONVERSION_INT32_JUST_CAST(char)
+ CASE__NUMERIC_CONVERSION_UINT32_JUST_CAST(char)
+ CASE__NUMERIC_CONVERSION_DOUBLE_JUST_CAST(char)
+NUMERIC_CONVERSION_METHOD_END
+
+// XXX toWChar convertions need to be fixed!
+NUMERIC_CONVERSION_METHOD_BEGIN(VTYPE_WCHAR, char16_t, WChar)
+ CASE__NUMERIC_CONVERSION_INT32_JUST_CAST(char16_t)
+ CASE__NUMERIC_CONVERSION_UINT32_JUST_CAST(char16_t)
+ CASE__NUMERIC_CONVERSION_DOUBLE_JUST_CAST(char16_t)
+NUMERIC_CONVERSION_METHOD_END
+
+#undef NUMERIC_CONVERSION_METHOD_BEGIN
+#undef CASE__NUMERIC_CONVERSION_INT32_JUST_CAST
+#undef CASE__NUMERIC_CONVERSION_INT32_MIN_MAX
+#undef CASE__NUMERIC_CONVERSION_UINT32_JUST_CAST
+#undef CASE__NUMERIC_CONVERSION_UINT32_MIN_MAX
+#undef CASE__NUMERIC_CONVERSION_DOUBLE_JUST_CAST
+#undef CASE__NUMERIC_CONVERSION_DOUBLE_MIN_MAX
+#undef CASE__NUMERIC_CONVERSION_DOUBLE_MIN_MAX_INT
+#undef CASES__NUMERIC_CONVERSION_NORMAL
+#undef NUMERIC_CONVERSION_METHOD_END
+#undef NUMERIC_CONVERSION_METHOD_NORMAL
+
+/***************************************************************************/
+
+// Just leverage a numeric converter for bool (but restrict the values).
+// XXX Is this really what we want to do?
+
+nsresult
+nsDiscriminatedUnion::ConvertToBool(bool* aResult) const
+{
+ TRIVIAL_DATA_CONVERTER(VTYPE_BOOL, mBoolValue, aResult)
+
+ double val;
+ nsresult rv = ConvertToDouble(&val);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ *aResult = 0.0 != val;
+ return rv;
+}
+
+/***************************************************************************/
+
+nsresult
+nsDiscriminatedUnion::ConvertToInt64(int64_t* aResult) const
+{
+ TRIVIAL_DATA_CONVERTER(VTYPE_INT64, mInt64Value, aResult)
+ TRIVIAL_DATA_CONVERTER(VTYPE_UINT64, mUint64Value, aResult)
+
+ nsDiscriminatedUnion tempData;
+ nsresult rv = ToManageableNumber(&tempData);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ switch (tempData.mType) {
+ case nsIDataType::VTYPE_INT32:
+ *aResult = tempData.u.mInt32Value;
+ return rv;
+ case nsIDataType::VTYPE_UINT32:
+ *aResult = tempData.u.mUint32Value;
+ return rv;
+ case nsIDataType::VTYPE_DOUBLE:
+ // XXX should check for data loss here!
+ *aResult = tempData.u.mDoubleValue;
+ return rv;
+ default:
+ NS_ERROR("bad type returned from ToManageableNumber");
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToUint64(uint64_t* aResult) const
+{
+ return ConvertToInt64((int64_t*)aResult);
+}
+
+/***************************************************************************/
+
+bool
+nsDiscriminatedUnion::String2ID(nsID* aPid) const
+{
+ nsAutoString tempString;
+ nsAString* pString;
+
+ switch (mType) {
+ case nsIDataType::VTYPE_CHAR_STR:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ return aPid->Parse(u.str.mStringValue);
+ case nsIDataType::VTYPE_CSTRING:
+ return aPid->Parse(PromiseFlatCString(*u.mCStringValue).get());
+ case nsIDataType::VTYPE_UTF8STRING:
+ return aPid->Parse(PromiseFlatUTF8String(*u.mUTF8StringValue).get());
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ pString = u.mAStringValue;
+ break;
+ case nsIDataType::VTYPE_WCHAR_STR:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ tempString.Assign(u.wstr.mWStringValue);
+ pString = &tempString;
+ break;
+ default:
+ NS_ERROR("bad type in call to String2ID");
+ return false;
+ }
+
+ char* pChars = ToNewCString(*pString);
+ if (!pChars) {
+ return false;
+ }
+ bool result = aPid->Parse(pChars);
+ free(pChars);
+ return result;
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToID(nsID* aResult) const
+{
+ nsID id;
+
+ switch (mType) {
+ case nsIDataType::VTYPE_ID:
+ *aResult = u.mIDValue;
+ return NS_OK;
+ case nsIDataType::VTYPE_INTERFACE:
+ *aResult = NS_GET_IID(nsISupports);
+ return NS_OK;
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ *aResult = u.iface.mInterfaceID;
+ return NS_OK;
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ case nsIDataType::VTYPE_UTF8STRING:
+ case nsIDataType::VTYPE_CSTRING:
+ case nsIDataType::VTYPE_CHAR_STR:
+ case nsIDataType::VTYPE_WCHAR_STR:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ if (!String2ID(&id)) {
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+ *aResult = id;
+ return NS_OK;
+ default:
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+}
+
+/***************************************************************************/
+
+nsresult
+nsDiscriminatedUnion::ToString(nsACString& aOutString) const
+{
+ char* ptr;
+
+ switch (mType) {
+ // all the stuff we don't handle...
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ case nsIDataType::VTYPE_UTF8STRING:
+ case nsIDataType::VTYPE_CSTRING:
+ case nsIDataType::VTYPE_CHAR_STR:
+ case nsIDataType::VTYPE_WCHAR_STR:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ case nsIDataType::VTYPE_WCHAR:
+ NS_ERROR("ToString being called for a string type - screwy logic!");
+ MOZ_FALLTHROUGH;
+
+ // XXX We might want stringified versions of these... ???
+
+ case nsIDataType::VTYPE_VOID:
+ case nsIDataType::VTYPE_EMPTY:
+ aOutString.SetIsVoid(true);
+ return NS_OK;
+
+ case nsIDataType::VTYPE_EMPTY_ARRAY:
+ case nsIDataType::VTYPE_ARRAY:
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ default:
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+
+ // nsID has its own text formatter.
+
+ case nsIDataType::VTYPE_ID:
+ ptr = u.mIDValue.ToString();
+ if (!ptr) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ aOutString.Assign(ptr);
+ free(ptr);
+ return NS_OK;
+
+ // Can't use PR_smprintf for floats, since it's locale-dependent
+#define CASE__APPENDFLOAT_NUMBER(type_, member_) \
+ case nsIDataType::type_ : \
+ { \
+ nsAutoCString str; \
+ str.AppendFloat(u.member_); \
+ aOutString.Assign(str); \
+ return NS_OK; \
+ }
+
+ CASE__APPENDFLOAT_NUMBER(VTYPE_FLOAT, mFloatValue)
+ CASE__APPENDFLOAT_NUMBER(VTYPE_DOUBLE, mDoubleValue)
+
+#undef CASE__APPENDFLOAT_NUMBER
+
+ // the rest can be PR_smprintf'd and use common code.
+
+#define CASE__SMPRINTF_NUMBER(type_, format_, cast_, member_) \
+ case nsIDataType::type_: \
+ ptr = PR_smprintf( format_ , (cast_) u.member_); \
+ break;
+
+ CASE__SMPRINTF_NUMBER(VTYPE_INT8, "%d", int, mInt8Value)
+ CASE__SMPRINTF_NUMBER(VTYPE_INT16, "%d", int, mInt16Value)
+ CASE__SMPRINTF_NUMBER(VTYPE_INT32, "%d", int, mInt32Value)
+ CASE__SMPRINTF_NUMBER(VTYPE_INT64, "%lld", int64_t, mInt64Value)
+
+ CASE__SMPRINTF_NUMBER(VTYPE_UINT8, "%u", unsigned, mUint8Value)
+ CASE__SMPRINTF_NUMBER(VTYPE_UINT16, "%u", unsigned, mUint16Value)
+ CASE__SMPRINTF_NUMBER(VTYPE_UINT32, "%u", unsigned, mUint32Value)
+ CASE__SMPRINTF_NUMBER(VTYPE_UINT64, "%llu", int64_t, mUint64Value)
+
+ // XXX Would we rather print "true" / "false" ?
+ CASE__SMPRINTF_NUMBER(VTYPE_BOOL, "%d", int, mBoolValue)
+
+ CASE__SMPRINTF_NUMBER(VTYPE_CHAR, "%c", char, mCharValue)
+
+#undef CASE__SMPRINTF_NUMBER
+ }
+
+ if (!ptr) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ aOutString.Assign(ptr);
+ PR_smprintf_free(ptr);
+ return NS_OK;
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToAString(nsAString& aResult) const
+{
+ switch (mType) {
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ aResult.Assign(*u.mAStringValue);
+ return NS_OK;
+ case nsIDataType::VTYPE_CSTRING:
+ CopyASCIItoUTF16(*u.mCStringValue, aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_UTF8STRING:
+ CopyUTF8toUTF16(*u.mUTF8StringValue, aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_CHAR_STR:
+ CopyASCIItoUTF16(u.str.mStringValue, aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_WCHAR_STR:
+ aResult.Assign(u.wstr.mWStringValue);
+ return NS_OK;
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ CopyASCIItoUTF16(nsDependentCString(u.str.mStringValue,
+ u.str.mStringLength),
+ aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ aResult.Assign(u.wstr.mWStringValue, u.wstr.mWStringLength);
+ return NS_OK;
+ case nsIDataType::VTYPE_WCHAR:
+ aResult.Assign(u.mWCharValue);
+ return NS_OK;
+ default: {
+ nsAutoCString tempCString;
+ nsresult rv = ToString(tempCString);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ CopyASCIItoUTF16(tempCString, aResult);
+ return NS_OK;
+ }
+ }
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToACString(nsACString& aResult) const
+{
+ switch (mType) {
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ LossyCopyUTF16toASCII(*u.mAStringValue, aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_CSTRING:
+ aResult.Assign(*u.mCStringValue);
+ return NS_OK;
+ case nsIDataType::VTYPE_UTF8STRING:
+ // XXX This is an extra copy that should be avoided
+ // once Jag lands support for UTF8String and associated
+ // conversion methods.
+ LossyCopyUTF16toASCII(NS_ConvertUTF8toUTF16(*u.mUTF8StringValue),
+ aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_CHAR_STR:
+ aResult.Assign(*u.str.mStringValue);
+ return NS_OK;
+ case nsIDataType::VTYPE_WCHAR_STR:
+ LossyCopyUTF16toASCII(nsDependentString(u.wstr.mWStringValue),
+ aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ aResult.Assign(u.str.mStringValue, u.str.mStringLength);
+ return NS_OK;
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ LossyCopyUTF16toASCII(nsDependentString(u.wstr.mWStringValue,
+ u.wstr.mWStringLength),
+ aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_WCHAR: {
+ const char16_t* str = &u.mWCharValue;
+ LossyCopyUTF16toASCII(Substring(str, 1), aResult);
+ return NS_OK;
+ }
+ default:
+ return ToString(aResult);
+ }
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToAUTF8String(nsAUTF8String& aResult) const
+{
+ switch (mType) {
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ CopyUTF16toUTF8(*u.mAStringValue, aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_CSTRING:
+ // XXX Extra copy, can be removed if we're sure CSTRING can
+ // only contain ASCII.
+ CopyUTF16toUTF8(NS_ConvertASCIItoUTF16(*u.mCStringValue),
+ aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_UTF8STRING:
+ aResult.Assign(*u.mUTF8StringValue);
+ return NS_OK;
+ case nsIDataType::VTYPE_CHAR_STR:
+ // XXX Extra copy, can be removed if we're sure CHAR_STR can
+ // only contain ASCII.
+ CopyUTF16toUTF8(NS_ConvertASCIItoUTF16(u.str.mStringValue),
+ aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_WCHAR_STR:
+ CopyUTF16toUTF8(u.wstr.mWStringValue, aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ // XXX Extra copy, can be removed if we're sure CHAR_STR can
+ // only contain ASCII.
+ CopyUTF16toUTF8(NS_ConvertASCIItoUTF16(
+ nsDependentCString(u.str.mStringValue,
+ u.str.mStringLength)), aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ CopyUTF16toUTF8(nsDependentString(u.wstr.mWStringValue,
+ u.wstr.mWStringLength),
+ aResult);
+ return NS_OK;
+ case nsIDataType::VTYPE_WCHAR: {
+ const char16_t* str = &u.mWCharValue;
+ CopyUTF16toUTF8(Substring(str, 1), aResult);
+ return NS_OK;
+ }
+ default: {
+ nsAutoCString tempCString;
+ nsresult rv = ToString(tempCString);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ // XXX Extra copy, can be removed if we're sure tempCString can
+ // only contain ASCII.
+ CopyUTF16toUTF8(NS_ConvertASCIItoUTF16(tempCString), aResult);
+ return NS_OK;
+ }
+ }
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToString(char** aResult) const
+{
+ uint32_t ignored;
+ return ConvertToStringWithSize(&ignored, aResult);
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToWString(char16_t** aResult) const
+{
+ uint32_t ignored;
+ return ConvertToWStringWithSize(&ignored, aResult);
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToStringWithSize(uint32_t* aSize, char** aStr) const
+{
+ nsAutoString tempString;
+ nsAutoCString tempCString;
+ nsresult rv;
+
+ switch (mType) {
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ *aSize = u.mAStringValue->Length();
+ *aStr = ToNewCString(*u.mAStringValue);
+ break;
+ case nsIDataType::VTYPE_CSTRING:
+ *aSize = u.mCStringValue->Length();
+ *aStr = ToNewCString(*u.mCStringValue);
+ break;
+ case nsIDataType::VTYPE_UTF8STRING: {
+ // XXX This is doing 1 extra copy. Need to fix this
+ // when Jag lands UTF8String
+ // we want:
+ // *aSize = *mUTF8StringValue->Length();
+ // *aStr = ToNewCString(*mUTF8StringValue);
+ // But this will have to do for now.
+ const NS_ConvertUTF8toUTF16 tempString16(*u.mUTF8StringValue);
+ *aSize = tempString16.Length();
+ *aStr = ToNewCString(tempString16);
+ break;
+ }
+ case nsIDataType::VTYPE_CHAR_STR: {
+ nsDependentCString cString(u.str.mStringValue);
+ *aSize = cString.Length();
+ *aStr = ToNewCString(cString);
+ break;
+ }
+ case nsIDataType::VTYPE_WCHAR_STR: {
+ nsDependentString string(u.wstr.mWStringValue);
+ *aSize = string.Length();
+ *aStr = ToNewCString(string);
+ break;
+ }
+ case nsIDataType::VTYPE_STRING_SIZE_IS: {
+ nsDependentCString cString(u.str.mStringValue,
+ u.str.mStringLength);
+ *aSize = cString.Length();
+ *aStr = ToNewCString(cString);
+ break;
+ }
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS: {
+ nsDependentString string(u.wstr.mWStringValue,
+ u.wstr.mWStringLength);
+ *aSize = string.Length();
+ *aStr = ToNewCString(string);
+ break;
+ }
+ case nsIDataType::VTYPE_WCHAR:
+ tempString.Assign(u.mWCharValue);
+ *aSize = tempString.Length();
+ *aStr = ToNewCString(tempString);
+ break;
+ default:
+ rv = ToString(tempCString);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ *aSize = tempCString.Length();
+ *aStr = ToNewCString(tempCString);
+ break;
+ }
+
+ return *aStr ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+nsresult
+nsDiscriminatedUnion::ConvertToWStringWithSize(uint32_t* aSize, char16_t** aStr) const
+{
+ nsAutoString tempString;
+ nsAutoCString tempCString;
+ nsresult rv;
+
+ switch (mType) {
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ *aSize = u.mAStringValue->Length();
+ *aStr = ToNewUnicode(*u.mAStringValue);
+ break;
+ case nsIDataType::VTYPE_CSTRING:
+ *aSize = u.mCStringValue->Length();
+ *aStr = ToNewUnicode(*u.mCStringValue);
+ break;
+ case nsIDataType::VTYPE_UTF8STRING: {
+ *aStr = UTF8ToNewUnicode(*u.mUTF8StringValue, aSize);
+ break;
+ }
+ case nsIDataType::VTYPE_CHAR_STR: {
+ nsDependentCString cString(u.str.mStringValue);
+ *aSize = cString.Length();
+ *aStr = ToNewUnicode(cString);
+ break;
+ }
+ case nsIDataType::VTYPE_WCHAR_STR: {
+ nsDependentString string(u.wstr.mWStringValue);
+ *aSize = string.Length();
+ *aStr = ToNewUnicode(string);
+ break;
+ }
+ case nsIDataType::VTYPE_STRING_SIZE_IS: {
+ nsDependentCString cString(u.str.mStringValue,
+ u.str.mStringLength);
+ *aSize = cString.Length();
+ *aStr = ToNewUnicode(cString);
+ break;
+ }
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS: {
+ nsDependentString string(u.wstr.mWStringValue,
+ u.wstr.mWStringLength);
+ *aSize = string.Length();
+ *aStr = ToNewUnicode(string);
+ break;
+ }
+ case nsIDataType::VTYPE_WCHAR:
+ tempString.Assign(u.mWCharValue);
+ *aSize = tempString.Length();
+ *aStr = ToNewUnicode(tempString);
+ break;
+ default:
+ rv = ToString(tempCString);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ *aSize = tempCString.Length();
+ *aStr = ToNewUnicode(tempCString);
+ break;
+ }
+
+ return *aStr ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToISupports(nsISupports** aResult) const
+{
+ switch (mType) {
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ if (u.iface.mInterfaceValue) {
+ return u.iface.mInterfaceValue->
+ QueryInterface(NS_GET_IID(nsISupports), (void**)aResult);
+ } else {
+ *aResult = nullptr;
+ return NS_OK;
+ }
+ default:
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToInterface(nsIID** aIID,
+ void** aInterface) const
+{
+ const nsIID* piid;
+
+ switch (mType) {
+ case nsIDataType::VTYPE_INTERFACE:
+ piid = &NS_GET_IID(nsISupports);
+ break;
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ piid = &u.iface.mInterfaceID;
+ break;
+ default:
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+ }
+
+ *aIID = (nsIID*)nsMemory::Clone(piid, sizeof(nsIID));
+ if (!*aIID) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (u.iface.mInterfaceValue) {
+ return u.iface.mInterfaceValue->QueryInterface(*piid, aInterface);
+ }
+
+ *aInterface = nullptr;
+ return NS_OK;
+}
+
+nsresult
+nsDiscriminatedUnion::ConvertToArray(uint16_t* aType, nsIID* aIID,
+ uint32_t* aCount, void** aPtr) const
+{
+ // XXX perhaps we'd like to add support for converting each of the various
+ // types into an array containing one element of that type. We can leverage
+ // CloneArray to do this if we want to support this.
+
+ if (mType == nsIDataType::VTYPE_ARRAY) {
+ return CloneArray(u.array.mArrayType, &u.array.mArrayInterfaceID,
+ u.array.mArrayCount, u.array.mArrayValue,
+ aType, aIID, aCount, aPtr);
+ }
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+}
+
+/***************************************************************************/
+// static setter functions...
+
+#define DATA_SETTER_PROLOGUE \
+ Cleanup()
+
+#define DATA_SETTER_EPILOGUE(type_) \
+ mType = nsIDataType::type_;
+
+#define DATA_SETTER(type_, member_, value_) \
+ DATA_SETTER_PROLOGUE; \
+ u.member_ = value_; \
+ DATA_SETTER_EPILOGUE(type_)
+
+#define DATA_SETTER_WITH_CAST(type_, member_, cast_, value_) \
+ DATA_SETTER_PROLOGUE; \
+ u.member_ = cast_ value_; \
+ DATA_SETTER_EPILOGUE(type_)
+
+
+/********************************************/
+
+#define CASE__SET_FROM_VARIANT_VTYPE_PROLOGUE(type_) \
+ { \
+
+#define CASE__SET_FROM_VARIANT_VTYPE__GETTER(member_, name_) \
+ rv = aValue->GetAs##name_ (&(u.member_ ));
+
+#define CASE__SET_FROM_VARIANT_VTYPE__GETTER_CAST(cast_, member_, name_) \
+ rv = aValue->GetAs##name_ ( cast_ &(u.member_ ));
+
+#define CASE__SET_FROM_VARIANT_VTYPE_EPILOGUE(type_) \
+ if (NS_SUCCEEDED(rv)) { \
+ mType = nsIDataType::type_ ; \
+ } \
+ break; \
+ }
+
+#define CASE__SET_FROM_VARIANT_TYPE(type_, member_, name_) \
+ case nsIDataType::type_: \
+ CASE__SET_FROM_VARIANT_VTYPE_PROLOGUE(type_) \
+ CASE__SET_FROM_VARIANT_VTYPE__GETTER(member_, name_) \
+ CASE__SET_FROM_VARIANT_VTYPE_EPILOGUE(type_)
+
+#define CASE__SET_FROM_VARIANT_VTYPE_CAST(type_, cast_, member_, name_) \
+ case nsIDataType::type_ : \
+ CASE__SET_FROM_VARIANT_VTYPE_PROLOGUE(type_) \
+ CASE__SET_FROM_VARIANT_VTYPE__GETTER_CAST(cast_, member_, name_) \
+ CASE__SET_FROM_VARIANT_VTYPE_EPILOGUE(type_)
+
+
+nsresult
+nsDiscriminatedUnion::SetFromVariant(nsIVariant* aValue)
+{
+ uint16_t type;
+ nsresult rv;
+
+ Cleanup();
+
+ rv = aValue->GetDataType(&type);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ switch (type) {
+ CASE__SET_FROM_VARIANT_VTYPE_CAST(VTYPE_INT8, (uint8_t*), mInt8Value,
+ Int8)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_INT16, mInt16Value, Int16)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_INT32, mInt32Value, Int32)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_UINT8, mUint8Value, Uint8)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_UINT16, mUint16Value, Uint16)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_UINT32, mUint32Value, Uint32)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_FLOAT, mFloatValue, Float)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_DOUBLE, mDoubleValue, Double)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_BOOL , mBoolValue, Bool)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_CHAR, mCharValue, Char)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_WCHAR, mWCharValue, WChar)
+ CASE__SET_FROM_VARIANT_TYPE(VTYPE_ID, mIDValue, ID)
+
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ case nsIDataType::VTYPE_WCHAR_STR:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ CASE__SET_FROM_VARIANT_VTYPE_PROLOGUE(VTYPE_ASTRING);
+ u.mAStringValue = new nsString();
+ if (!u.mAStringValue) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ rv = aValue->GetAsAString(*u.mAStringValue);
+ if (NS_FAILED(rv)) {
+ delete u.mAStringValue;
+ }
+ CASE__SET_FROM_VARIANT_VTYPE_EPILOGUE(VTYPE_ASTRING)
+
+ case nsIDataType::VTYPE_CSTRING:
+ CASE__SET_FROM_VARIANT_VTYPE_PROLOGUE(VTYPE_CSTRING);
+ u.mCStringValue = new nsCString();
+ if (!u.mCStringValue) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ rv = aValue->GetAsACString(*u.mCStringValue);
+ if (NS_FAILED(rv)) {
+ delete u.mCStringValue;
+ }
+ CASE__SET_FROM_VARIANT_VTYPE_EPILOGUE(VTYPE_CSTRING)
+
+ case nsIDataType::VTYPE_UTF8STRING:
+ CASE__SET_FROM_VARIANT_VTYPE_PROLOGUE(VTYPE_UTF8STRING);
+ u.mUTF8StringValue = new nsUTF8String();
+ if (!u.mUTF8StringValue) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ rv = aValue->GetAsAUTF8String(*u.mUTF8StringValue);
+ if (NS_FAILED(rv)) {
+ delete u.mUTF8StringValue;
+ }
+ CASE__SET_FROM_VARIANT_VTYPE_EPILOGUE(VTYPE_UTF8STRING)
+
+ case nsIDataType::VTYPE_CHAR_STR:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ CASE__SET_FROM_VARIANT_VTYPE_PROLOGUE(VTYPE_STRING_SIZE_IS);
+ rv = aValue->GetAsStringWithSize(&u.str.mStringLength,
+ &u.str.mStringValue);
+ CASE__SET_FROM_VARIANT_VTYPE_EPILOGUE(VTYPE_STRING_SIZE_IS)
+
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ CASE__SET_FROM_VARIANT_VTYPE_PROLOGUE(VTYPE_INTERFACE_IS);
+ // XXX This iid handling is ugly!
+ nsIID* iid;
+ rv = aValue->GetAsInterface(&iid, (void**)&u.iface.mInterfaceValue);
+ if (NS_SUCCEEDED(rv)) {
+ u.iface.mInterfaceID = *iid;
+ free((char*)iid);
+ }
+ CASE__SET_FROM_VARIANT_VTYPE_EPILOGUE(VTYPE_INTERFACE_IS)
+
+ case nsIDataType::VTYPE_ARRAY:
+ CASE__SET_FROM_VARIANT_VTYPE_PROLOGUE(VTYPE_ARRAY);
+ rv = aValue->GetAsArray(&u.array.mArrayType,
+ &u.array.mArrayInterfaceID,
+ &u.array.mArrayCount,
+ &u.array.mArrayValue);
+ CASE__SET_FROM_VARIANT_VTYPE_EPILOGUE(VTYPE_ARRAY)
+
+ case nsIDataType::VTYPE_VOID:
+ SetToVoid();
+ rv = NS_OK;
+ break;
+ case nsIDataType::VTYPE_EMPTY_ARRAY:
+ SetToEmptyArray();
+ rv = NS_OK;
+ break;
+ case nsIDataType::VTYPE_EMPTY:
+ SetToEmpty();
+ rv = NS_OK;
+ break;
+ default:
+ NS_ERROR("bad type in variant!");
+ rv = NS_ERROR_FAILURE;
+ break;
+ }
+ return rv;
+}
+
+void
+nsDiscriminatedUnion::SetFromInt8(uint8_t aValue)
+{
+ DATA_SETTER_WITH_CAST(VTYPE_INT8, mInt8Value, (uint8_t), aValue);
+}
+void
+nsDiscriminatedUnion::SetFromInt16(int16_t aValue)
+{
+ DATA_SETTER(VTYPE_INT16, mInt16Value, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromInt32(int32_t aValue)
+{
+ DATA_SETTER(VTYPE_INT32, mInt32Value, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromInt64(int64_t aValue)
+{
+ DATA_SETTER(VTYPE_INT64, mInt64Value, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromUint8(uint8_t aValue)
+{
+ DATA_SETTER(VTYPE_UINT8, mUint8Value, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromUint16(uint16_t aValue)
+{
+ DATA_SETTER(VTYPE_UINT16, mUint16Value, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromUint32(uint32_t aValue)
+{
+ DATA_SETTER(VTYPE_UINT32, mUint32Value, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromUint64(uint64_t aValue)
+{
+ DATA_SETTER(VTYPE_UINT64, mUint64Value, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromFloat(float aValue)
+{
+ DATA_SETTER(VTYPE_FLOAT, mFloatValue, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromDouble(double aValue)
+{
+ DATA_SETTER(VTYPE_DOUBLE, mDoubleValue, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromBool(bool aValue)
+{
+ DATA_SETTER(VTYPE_BOOL, mBoolValue, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromChar(char aValue)
+{
+ DATA_SETTER(VTYPE_CHAR, mCharValue, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromWChar(char16_t aValue)
+{
+ DATA_SETTER(VTYPE_WCHAR, mWCharValue, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromID(const nsID& aValue)
+{
+ DATA_SETTER(VTYPE_ID, mIDValue, aValue);
+}
+void
+nsDiscriminatedUnion::SetFromAString(const nsAString& aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ u.mAStringValue = new nsString(aValue);
+ DATA_SETTER_EPILOGUE(VTYPE_ASTRING);
+}
+
+void
+nsDiscriminatedUnion::SetFromDOMString(const nsAString& aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ u.mAStringValue = new nsString(aValue);
+ DATA_SETTER_EPILOGUE(VTYPE_DOMSTRING);
+}
+
+void
+nsDiscriminatedUnion::SetFromACString(const nsACString& aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ u.mCStringValue = new nsCString(aValue);
+ DATA_SETTER_EPILOGUE(VTYPE_CSTRING);
+}
+
+void
+nsDiscriminatedUnion::SetFromAUTF8String(const nsAUTF8String& aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ u.mUTF8StringValue = new nsUTF8String(aValue);
+ DATA_SETTER_EPILOGUE(VTYPE_UTF8STRING);
+}
+
+nsresult
+nsDiscriminatedUnion::SetFromString(const char* aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ if (!aValue) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ return SetFromStringWithSize(strlen(aValue), aValue);
+}
+nsresult
+nsDiscriminatedUnion::SetFromWString(const char16_t* aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ if (!aValue) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ return SetFromWStringWithSize(NS_strlen(aValue), aValue);
+}
+void
+nsDiscriminatedUnion::SetFromISupports(nsISupports* aValue)
+{
+ return SetFromInterface(NS_GET_IID(nsISupports), aValue);
+}
+void
+nsDiscriminatedUnion::SetFromInterface(const nsIID& aIID, nsISupports* aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ NS_IF_ADDREF(aValue);
+ u.iface.mInterfaceValue = aValue;
+ u.iface.mInterfaceID = aIID;
+ DATA_SETTER_EPILOGUE(VTYPE_INTERFACE_IS);
+}
+nsresult
+nsDiscriminatedUnion::SetFromArray(uint16_t aType, const nsIID* aIID,
+ uint32_t aCount, void* aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ if (!aValue || !aCount) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ nsresult rv = CloneArray(aType, aIID, aCount, aValue,
+ &u.array.mArrayType,
+ &u.array.mArrayInterfaceID,
+ &u.array.mArrayCount,
+ &u.array.mArrayValue);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ DATA_SETTER_EPILOGUE(VTYPE_ARRAY);
+ return NS_OK;
+}
+nsresult
+nsDiscriminatedUnion::SetFromStringWithSize(uint32_t aSize,
+ const char* aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ if (!aValue) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ if (!(u.str.mStringValue =
+ (char*)nsMemory::Clone(aValue, (aSize + 1) * sizeof(char)))) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ u.str.mStringLength = aSize;
+ DATA_SETTER_EPILOGUE(VTYPE_STRING_SIZE_IS);
+ return NS_OK;
+}
+nsresult
+nsDiscriminatedUnion::SetFromWStringWithSize(uint32_t aSize,
+ const char16_t* aValue)
+{
+ DATA_SETTER_PROLOGUE;
+ if (!aValue) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ if (!(u.wstr.mWStringValue =
+ (char16_t*)nsMemory::Clone(aValue, (aSize + 1) * sizeof(char16_t)))) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ u.wstr.mWStringLength = aSize;
+ DATA_SETTER_EPILOGUE(VTYPE_WSTRING_SIZE_IS);
+ return NS_OK;
+}
+void
+nsDiscriminatedUnion::AllocateWStringWithSize(uint32_t aSize)
+{
+ DATA_SETTER_PROLOGUE;
+ u.wstr.mWStringValue = (char16_t*)moz_xmalloc((aSize + 1) * sizeof(char16_t));
+ u.wstr.mWStringValue[aSize] = '\0';
+ u.wstr.mWStringLength = aSize;
+ DATA_SETTER_EPILOGUE(VTYPE_WSTRING_SIZE_IS);
+}
+void
+nsDiscriminatedUnion::SetToVoid()
+{
+ DATA_SETTER_PROLOGUE;
+ DATA_SETTER_EPILOGUE(VTYPE_VOID);
+}
+void
+nsDiscriminatedUnion::SetToEmpty()
+{
+ DATA_SETTER_PROLOGUE;
+ DATA_SETTER_EPILOGUE(VTYPE_EMPTY);
+}
+void
+nsDiscriminatedUnion::SetToEmptyArray()
+{
+ DATA_SETTER_PROLOGUE;
+ DATA_SETTER_EPILOGUE(VTYPE_EMPTY_ARRAY);
+}
+
+/***************************************************************************/
+
+void
+nsDiscriminatedUnion::Cleanup()
+{
+ switch (mType) {
+ case nsIDataType::VTYPE_INT8:
+ case nsIDataType::VTYPE_INT16:
+ case nsIDataType::VTYPE_INT32:
+ case nsIDataType::VTYPE_INT64:
+ case nsIDataType::VTYPE_UINT8:
+ case nsIDataType::VTYPE_UINT16:
+ case nsIDataType::VTYPE_UINT32:
+ case nsIDataType::VTYPE_UINT64:
+ case nsIDataType::VTYPE_FLOAT:
+ case nsIDataType::VTYPE_DOUBLE:
+ case nsIDataType::VTYPE_BOOL:
+ case nsIDataType::VTYPE_CHAR:
+ case nsIDataType::VTYPE_WCHAR:
+ case nsIDataType::VTYPE_VOID:
+ case nsIDataType::VTYPE_ID:
+ break;
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_DOMSTRING:
+ delete u.mAStringValue;
+ break;
+ case nsIDataType::VTYPE_CSTRING:
+ delete u.mCStringValue;
+ break;
+ case nsIDataType::VTYPE_UTF8STRING:
+ delete u.mUTF8StringValue;
+ break;
+ case nsIDataType::VTYPE_CHAR_STR:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ free((char*)u.str.mStringValue);
+ break;
+ case nsIDataType::VTYPE_WCHAR_STR:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ free((char*)u.wstr.mWStringValue);
+ break;
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ NS_IF_RELEASE(u.iface.mInterfaceValue);
+ break;
+ case nsIDataType::VTYPE_ARRAY:
+ FreeArray();
+ break;
+ case nsIDataType::VTYPE_EMPTY_ARRAY:
+ case nsIDataType::VTYPE_EMPTY:
+ break;
+ default:
+ NS_ERROR("bad type in variant!");
+ break;
+ }
+
+ mType = nsIDataType::VTYPE_EMPTY;
+}
+
+void
+nsDiscriminatedUnion::Traverse(nsCycleCollectionTraversalCallback& aCb) const
+{
+ switch (mType) {
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mData");
+ aCb.NoteXPCOMChild(u.iface.mInterfaceValue);
+ break;
+ case nsIDataType::VTYPE_ARRAY:
+ switch (u.array.mArrayType) {
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS: {
+ nsISupports** p = (nsISupports**)u.array.mArrayValue;
+ for (uint32_t i = u.array.mArrayCount; i > 0; ++p, --i) {
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mData[i]");
+ aCb.NoteXPCOMChild(*p);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/***************************************************************************/
+/***************************************************************************/
+// members...
+
+nsVariantBase::nsVariantBase()
+ : mWritable(true)
+{
+#ifdef DEBUG
+ {
+ // Assert that the nsIDataType consts match the values #defined in
+ // xpt_struct.h. Bad things happen somewhere if they don't.
+ struct THE_TYPES
+ {
+ uint16_t a;
+ uint16_t b;
+ };
+ static const THE_TYPES array[] = {
+ {nsIDataType::VTYPE_INT8 , TD_INT8 },
+ {nsIDataType::VTYPE_INT16 , TD_INT16 },
+ {nsIDataType::VTYPE_INT32 , TD_INT32 },
+ {nsIDataType::VTYPE_INT64 , TD_INT64 },
+ {nsIDataType::VTYPE_UINT8 , TD_UINT8 },
+ {nsIDataType::VTYPE_UINT16 , TD_UINT16 },
+ {nsIDataType::VTYPE_UINT32 , TD_UINT32 },
+ {nsIDataType::VTYPE_UINT64 , TD_UINT64 },
+ {nsIDataType::VTYPE_FLOAT , TD_FLOAT },
+ {nsIDataType::VTYPE_DOUBLE , TD_DOUBLE },
+ {nsIDataType::VTYPE_BOOL , TD_BOOL },
+ {nsIDataType::VTYPE_CHAR , TD_CHAR },
+ {nsIDataType::VTYPE_WCHAR , TD_WCHAR },
+ {nsIDataType::VTYPE_VOID , TD_VOID },
+ {nsIDataType::VTYPE_ID , TD_PNSIID },
+ {nsIDataType::VTYPE_DOMSTRING , TD_DOMSTRING },
+ {nsIDataType::VTYPE_CHAR_STR , TD_PSTRING },
+ {nsIDataType::VTYPE_WCHAR_STR , TD_PWSTRING },
+ {nsIDataType::VTYPE_INTERFACE , TD_INTERFACE_TYPE },
+ {nsIDataType::VTYPE_INTERFACE_IS , TD_INTERFACE_IS_TYPE},
+ {nsIDataType::VTYPE_ARRAY , TD_ARRAY },
+ {nsIDataType::VTYPE_STRING_SIZE_IS , TD_PSTRING_SIZE_IS },
+ {nsIDataType::VTYPE_WSTRING_SIZE_IS , TD_PWSTRING_SIZE_IS },
+ {nsIDataType::VTYPE_UTF8STRING , TD_UTF8STRING },
+ {nsIDataType::VTYPE_CSTRING , TD_CSTRING },
+ {nsIDataType::VTYPE_ASTRING , TD_ASTRING }
+ };
+ static const int length = sizeof(array) / sizeof(array[0]);
+ static bool inited = false;
+ if (!inited) {
+ for (int i = 0; i < length; ++i) {
+ NS_ASSERTION(array[i].a == array[i].b, "bad const declaration");
+ }
+ inited = true;
+ }
+ }
+#endif
+}
+
+// For all the data getters we just forward to the static (and sharable)
+// 'ConvertTo' functions.
+
+NS_IMETHODIMP
+nsVariantBase::GetDataType(uint16_t* aDataType)
+{
+ *aDataType = mData.GetType();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsInt8(uint8_t* aResult)
+{
+ return mData.ConvertToInt8(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsInt16(int16_t* aResult)
+{
+ return mData.ConvertToInt16(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsInt32(int32_t* aResult)
+{
+ return mData.ConvertToInt32(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsInt64(int64_t* aResult)
+{
+ return mData.ConvertToInt64(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsUint8(uint8_t* aResult)
+{
+ return mData.ConvertToUint8(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsUint16(uint16_t* aResult)
+{
+ return mData.ConvertToUint16(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsUint32(uint32_t* aResult)
+{
+ return mData.ConvertToUint32(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsUint64(uint64_t* aResult)
+{
+ return mData.ConvertToUint64(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsFloat(float* aResult)
+{
+ return mData.ConvertToFloat(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsDouble(double* aResult)
+{
+ return mData.ConvertToDouble(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsBool(bool* aResult)
+{
+ return mData.ConvertToBool(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsChar(char* aResult)
+{
+ return mData.ConvertToChar(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsWChar(char16_t* aResult)
+{
+ return mData.ConvertToWChar(aResult);
+}
+
+NS_IMETHODIMP_(nsresult)
+nsVariantBase::GetAsID(nsID* aResult)
+{
+ return mData.ConvertToID(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsAString(nsAString& aResult)
+{
+ return mData.ConvertToAString(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsDOMString(nsAString& aResult)
+{
+ // A DOMString maps to an AString internally, so we can re-use
+ // ConvertToAString here.
+ return mData.ConvertToAString(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsACString(nsACString& aResult)
+{
+ return mData.ConvertToACString(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsAUTF8String(nsAUTF8String& aResult)
+{
+ return mData.ConvertToAUTF8String(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsString(char** aResult)
+{
+ return mData.ConvertToString(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsWString(char16_t** aResult)
+{
+ return mData.ConvertToWString(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsISupports(nsISupports** aResult)
+{
+ return mData.ConvertToISupports(aResult);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsJSVal(JS::MutableHandleValue)
+{
+ // Can only get the jsval from an XPCVariant.
+ return NS_ERROR_CANNOT_CONVERT_DATA;
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsInterface(nsIID** aIID, void** aInterface)
+{
+ return mData.ConvertToInterface(aIID, aInterface);
+}
+
+NS_IMETHODIMP_(nsresult)
+nsVariantBase::GetAsArray(uint16_t* aType, nsIID* aIID,
+ uint32_t* aCount, void** aPtr)
+{
+ return mData.ConvertToArray(aType, aIID, aCount, aPtr);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsStringWithSize(uint32_t* aSize, char** aStr)
+{
+ return mData.ConvertToStringWithSize(aSize, aStr);
+}
+
+NS_IMETHODIMP
+nsVariantBase::GetAsWStringWithSize(uint32_t* aSize, char16_t** aStr)
+{
+ return mData.ConvertToWStringWithSize(aSize, aStr);
+}
+
+/***************************************************************************/
+
+NS_IMETHODIMP
+nsVariantBase::GetWritable(bool* aWritable)
+{
+ *aWritable = mWritable;
+ return NS_OK;
+}
+NS_IMETHODIMP
+nsVariantBase::SetWritable(bool aWritable)
+{
+ if (!mWritable && aWritable) {
+ return NS_ERROR_FAILURE;
+ }
+ mWritable = aWritable;
+ return NS_OK;
+}
+
+/***************************************************************************/
+
+// For all the data setters we just forward to the static (and sharable)
+// 'SetFrom' functions.
+
+NS_IMETHODIMP
+nsVariantBase::SetAsInt8(uint8_t aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromInt8(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsInt16(int16_t aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromInt16(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsInt32(int32_t aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromInt32(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsInt64(int64_t aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromInt64(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsUint8(uint8_t aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromUint8(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsUint16(uint16_t aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromUint16(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsUint32(uint32_t aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromUint32(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsUint64(uint64_t aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromUint64(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsFloat(float aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromFloat(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsDouble(double aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromDouble(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsBool(bool aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromBool(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsChar(char aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromChar(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsWChar(char16_t aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromWChar(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsID(const nsID& aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromID(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsAString(const nsAString& aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromAString(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsDOMString(const nsAString& aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+
+ mData.SetFromDOMString(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsACString(const nsACString& aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromACString(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsAUTF8String(const nsAUTF8String& aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromAUTF8String(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsString(const char* aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ return mData.SetFromString(aValue);
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsWString(const char16_t* aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ return mData.SetFromWString(aValue);
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsISupports(nsISupports* aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromISupports(aValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsInterface(const nsIID& aIID, void* aInterface)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetFromInterface(aIID, (nsISupports*)aInterface);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsArray(uint16_t aType, const nsIID* aIID,
+ uint32_t aCount, void* aPtr)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ return mData.SetFromArray(aType, aIID, aCount, aPtr);
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsStringWithSize(uint32_t aSize, const char* aStr)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ return mData.SetFromStringWithSize(aSize, aStr);
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsWStringWithSize(uint32_t aSize, const char16_t* aStr)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ return mData.SetFromWStringWithSize(aSize, aStr);
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsVoid()
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetToVoid();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsEmpty()
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetToEmpty();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetAsEmptyArray()
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ mData.SetToEmptyArray();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsVariantBase::SetFromVariant(nsIVariant* aValue)
+{
+ if (!mWritable) {
+ return NS_ERROR_OBJECT_IS_IMMUTABLE;
+ }
+ return mData.SetFromVariant(aValue);
+}
+
+/* nsVariant implementation */
+
+NS_IMPL_ISUPPORTS(nsVariant, nsIVariant, nsIWritableVariant)
+
+
+/* nsVariantCC implementation */
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsVariantCC)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+ NS_INTERFACE_MAP_ENTRY(nsIVariant)
+ NS_INTERFACE_MAP_ENTRY(nsIWritableVariant)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsVariantCC)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsVariantCC)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsVariantCC)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsVariantCC)
+ tmp->mData.Traverse(cb);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsVariantCC)
+ tmp->mData.Cleanup();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
diff --git a/xpcom/ds/nsVariant.h b/xpcom/ds/nsVariant.h
new file mode 100644
index 000000000..5be2d18ee
--- /dev/null
+++ b/xpcom/ds/nsVariant.h
@@ -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
diff --git a/xpcom/ds/nsWhitespaceTokenizer.h b/xpcom/ds/nsWhitespaceTokenizer.h
new file mode 100644
index 000000000..bfe32f8dc
--- /dev/null
+++ b/xpcom/ds/nsWhitespaceTokenizer.h
@@ -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 */
diff --git a/xpcom/ds/nsWindowsRegKey.cpp b/xpcom/ds/nsWindowsRegKey.cpp
new file mode 100644
index 000000000..55571d8f9
--- /dev/null
+++ b/xpcom/ds/nsWindowsRegKey.cpp
@@ -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);
+}
diff --git a/xpcom/ds/nsWindowsRegKey.h b/xpcom/ds/nsWindowsRegKey.h
new file mode 100644
index 000000000..d7930579a
--- /dev/null
+++ b/xpcom/ds/nsWindowsRegKey.h
@@ -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__