summaryrefslogtreecommitdiffstats
path: root/xpcom/glue
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /xpcom/glue
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'xpcom/glue')
-rw-r--r--xpcom/glue/AppData.cpp95
-rw-r--r--xpcom/glue/AppData.h63
-rw-r--r--xpcom/glue/AutoRestore.h55
-rw-r--r--xpcom/glue/BlockingResourceBase.cpp511
-rw-r--r--xpcom/glue/BlockingResourceBase.h344
-rw-r--r--xpcom/glue/CondVar.h144
-rw-r--r--xpcom/glue/DeadlockDetector.h382
-rw-r--r--xpcom/glue/EnumeratedArrayCycleCollection.h43
-rw-r--r--xpcom/glue/FileUtils.cpp568
-rw-r--r--xpcom/glue/FileUtils.h220
-rw-r--r--xpcom/glue/GenericFactory.cpp27
-rw-r--r--xpcom/glue/GenericFactory.h43
-rw-r--r--xpcom/glue/GenericModule.cpp98
-rw-r--r--xpcom/glue/IntentionalCrash.h58
-rw-r--r--xpcom/glue/MainThreadUtils.h42
-rw-r--r--xpcom/glue/Monitor.h135
-rw-r--r--xpcom/glue/Mutex.h229
-rw-r--r--xpcom/glue/Observer.h83
-rw-r--r--xpcom/glue/PLDHashTable.cpp801
-rw-r--r--xpcom/glue/PLDHashTable.h621
-rw-r--r--xpcom/glue/ReentrantMonitor.h249
-rw-r--r--xpcom/glue/moz.build123
-rw-r--r--xpcom/glue/nsArrayEnumerator.cpp213
-rw-r--r--xpcom/glue/nsArrayEnumerator.h32
-rw-r--r--xpcom/glue/nsArrayUtils.cpp23
-rw-r--r--xpcom/glue/nsArrayUtils.h40
-rw-r--r--xpcom/glue/nsBaseHashtable.h270
-rw-r--r--xpcom/glue/nsCOMArray.cpp323
-rw-r--r--xpcom/glue/nsCOMArray.h473
-rw-r--r--xpcom/glue/nsCOMPtr.cpp128
-rw-r--r--xpcom/glue/nsCOMPtr.h1472
-rw-r--r--xpcom/glue/nsCRTGlue.cpp441
-rw-r--r--xpcom/glue/nsCRTGlue.h147
-rw-r--r--xpcom/glue/nsCategoryCache.cpp149
-rw-r--r--xpcom/glue/nsCategoryCache.h95
-rw-r--r--xpcom/glue/nsClassHashtable.h140
-rw-r--r--xpcom/glue/nsClassInfoImpl.cpp73
-rw-r--r--xpcom/glue/nsComponentManagerUtils.cpp301
-rw-r--r--xpcom/glue/nsComponentManagerUtils.h247
-rw-r--r--xpcom/glue/nsCycleCollectionNoteChild.h101
-rw-r--r--xpcom/glue/nsCycleCollectionNoteRootCallback.h31
-rw-r--r--xpcom/glue/nsCycleCollectionParticipant.cpp39
-rw-r--r--xpcom/glue/nsCycleCollectionParticipant.h852
-rw-r--r--xpcom/glue/nsCycleCollectionTraversalCallback.h62
-rw-r--r--xpcom/glue/nsDataHashtable.h58
-rw-r--r--xpcom/glue/nsDebug.h460
-rw-r--r--xpcom/glue/nsDeque.cpp361
-rw-r--r--xpcom/glue/nsDeque.h195
-rw-r--r--xpcom/glue/nsEnumeratorUtils.cpp291
-rw-r--r--xpcom/glue/nsEnumeratorUtils.h24
-rw-r--r--xpcom/glue/nsHashKeys.h660
-rw-r--r--xpcom/glue/nsIClassInfoImpl.h179
-rw-r--r--xpcom/glue/nsID.cpp133
-rw-r--r--xpcom/glue/nsID.h179
-rw-r--r--xpcom/glue/nsIInterfaceRequestorUtils.cpp33
-rw-r--r--xpcom/glue/nsIInterfaceRequestorUtils.h49
-rw-r--r--xpcom/glue/nsINIParser.cpp331
-rw-r--r--xpcom/glue/nsINIParser.h118
-rw-r--r--xpcom/glue/nsISupportsImpl.cpp27
-rw-r--r--xpcom/glue/nsISupportsImpl.h1090
-rw-r--r--xpcom/glue/nsISupportsUtils.h145
-rw-r--r--xpcom/glue/nsIWeakReferenceUtils.h102
-rw-r--r--xpcom/glue/nsInterfaceHashtable.h142
-rw-r--r--xpcom/glue/nsJSThingHashtable.h61
-rw-r--r--xpcom/glue/nsMemory.cpp53
-rw-r--r--xpcom/glue/nsMemory.h136
-rw-r--r--xpcom/glue/nsPointerHashKeys.h48
-rw-r--r--xpcom/glue/nsProxyRelease.cpp21
-rw-r--r--xpcom/glue/nsProxyRelease.h353
-rw-r--r--xpcom/glue/nsQuickSort.cpp187
-rw-r--r--xpcom/glue/nsQuickSort.h41
-rw-r--r--xpcom/glue/nsRefPtrHashtable.h191
-rw-r--r--xpcom/glue/nsServiceManagerUtils.h94
-rw-r--r--xpcom/glue/nsStringAPI.cpp1304
-rw-r--r--xpcom/glue/nsStringAPI.h1596
-rw-r--r--xpcom/glue/nsStringGlue.h24
-rw-r--r--xpcom/glue/nsTArray-inl.h463
-rw-r--r--xpcom/glue/nsTArray.cpp29
-rw-r--r--xpcom/glue/nsTArray.h2371
-rw-r--r--xpcom/glue/nsTArrayForwardDeclare.h36
-rw-r--r--xpcom/glue/nsTHashtable.h577
-rw-r--r--xpcom/glue/nsTObserverArray.cpp31
-rw-r--r--xpcom/glue/nsTObserverArray.h520
-rw-r--r--xpcom/glue/nsTPriorityQueue.h161
-rw-r--r--xpcom/glue/nsTWeakRef.h176
-rw-r--r--xpcom/glue/nsTextFormatter.cpp1394
-rw-r--r--xpcom/glue/nsTextFormatter.h80
-rw-r--r--xpcom/glue/nsThreadUtils.cpp472
-rw-r--r--xpcom/glue/nsThreadUtils.h1049
-rw-r--r--xpcom/glue/nsVersionComparator.cpp379
-rw-r--r--xpcom/glue/nsVersionComparator.h174
-rw-r--r--xpcom/glue/nsWeakReference.cpp164
-rw-r--r--xpcom/glue/nsWeakReference.h49
-rw-r--r--xpcom/glue/nsXPTCUtils.h45
-rw-r--r--xpcom/glue/objs.mozbuild48
-rw-r--r--xpcom/glue/standalone/moz.build58
-rw-r--r--xpcom/glue/standalone/nsXPCOMGlue.cpp927
-rw-r--r--xpcom/glue/standalone/nsXPCOMGlue.h49
-rw-r--r--xpcom/glue/standalone/staticruntime/moz.build50
-rw-r--r--xpcom/glue/staticruntime/moz.build48
-rw-r--r--xpcom/glue/tests/gtest/TestArray.cpp169
-rw-r--r--xpcom/glue/tests/gtest/TestFileUtils.cpp283
-rw-r--r--xpcom/glue/tests/gtest/TestGCPostBarriers.cpp140
-rw-r--r--xpcom/glue/tests/gtest/TestNsDeque.cpp342
-rw-r--r--xpcom/glue/tests/gtest/TestThreadUtils.cpp937
-rw-r--r--xpcom/glue/tests/gtest/moz.build22
106 files changed, 30515 insertions, 0 deletions
diff --git a/xpcom/glue/AppData.cpp b/xpcom/glue/AppData.cpp
new file mode 100644
index 000000000..845267e60
--- /dev/null
+++ b/xpcom/glue/AppData.cpp
@@ -0,0 +1,95 @@
+/* -*- 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/AppData.h"
+#include "nsXULAppAPI.h"
+#include "nsINIParser.h"
+#include "nsIFile.h"
+#include "nsCRTGlue.h"
+#include "nsAutoPtr.h"
+
+namespace mozilla {
+
+void
+SetAllocatedString(const char*& aStr, const char* aNewValue)
+{
+ NS_Free(const_cast<char*>(aStr));
+ if (aNewValue) {
+ aStr = NS_strdup(aNewValue);
+ } else {
+ aStr = nullptr;
+ }
+}
+
+void
+SetAllocatedString(const char*& aStr, const nsACString& aNewValue)
+{
+ NS_Free(const_cast<char*>(aStr));
+ if (aNewValue.IsEmpty()) {
+ aStr = nullptr;
+ } else {
+ aStr = ToNewCString(aNewValue);
+ }
+}
+
+ScopedAppData::ScopedAppData(const nsXREAppData* aAppData)
+{
+ Zero();
+
+ this->size = aAppData->size;
+
+ SetAllocatedString(this->vendor, aAppData->vendor);
+ SetAllocatedString(this->name, aAppData->name);
+ SetAllocatedString(this->remotingName, aAppData->remotingName);
+ SetAllocatedString(this->version, aAppData->version);
+ SetAllocatedString(this->buildID, aAppData->buildID);
+ SetAllocatedString(this->ID, aAppData->ID);
+ SetAllocatedString(this->copyright, aAppData->copyright);
+ SetAllocatedString(this->profile, aAppData->profile);
+ SetStrongPtr(this->directory, aAppData->directory);
+ this->flags = aAppData->flags;
+
+ if (aAppData->size > offsetof(nsXREAppData, xreDirectory)) {
+ SetStrongPtr(this->xreDirectory, aAppData->xreDirectory);
+ SetAllocatedString(this->minVersion, aAppData->minVersion);
+ SetAllocatedString(this->maxVersion, aAppData->maxVersion);
+ }
+
+ if (aAppData->size > offsetof(nsXREAppData, crashReporterURL)) {
+ SetAllocatedString(this->crashReporterURL, aAppData->crashReporterURL);
+ }
+
+ if (aAppData->size > offsetof(nsXREAppData, UAName)) {
+ SetAllocatedString(this->UAName, aAppData->UAName);
+ }
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ sandboxBrokerServices = aAppData->sandboxBrokerServices;
+#endif
+}
+
+ScopedAppData::~ScopedAppData()
+{
+ SetAllocatedString(this->vendor, nullptr);
+ SetAllocatedString(this->name, nullptr);
+ SetAllocatedString(this->remotingName, nullptr);
+ SetAllocatedString(this->version, nullptr);
+ SetAllocatedString(this->buildID, nullptr);
+ SetAllocatedString(this->ID, nullptr);
+ SetAllocatedString(this->copyright, nullptr);
+ SetAllocatedString(this->profile, nullptr);
+
+ NS_IF_RELEASE(this->directory);
+
+ SetStrongPtr(this->xreDirectory, (nsIFile*)nullptr);
+ SetAllocatedString(this->minVersion, nullptr);
+ SetAllocatedString(this->maxVersion, nullptr);
+
+ SetAllocatedString(this->crashReporterURL, nullptr);
+ SetAllocatedString(this->UAName, nullptr);
+}
+
+} // namespace mozilla
diff --git a/xpcom/glue/AppData.h b/xpcom/glue/AppData.h
new file mode 100644
index 000000000..0134df32c
--- /dev/null
+++ b/xpcom/glue/AppData.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_AppData_h
+#define mozilla_AppData_h
+
+#include "nsXREAppData.h"
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "nsISupportsUtils.h"
+
+namespace mozilla {
+
+// Like nsXREAppData, but releases all strong refs/allocated memory
+// in the destructor.
+class ScopedAppData : public nsXREAppData
+{
+public:
+ ScopedAppData()
+ {
+ Zero();
+ this->size = sizeof(*this);
+ }
+
+ explicit ScopedAppData(const nsXREAppData* aAppData);
+
+ void Zero() { memset(this, 0, sizeof(*this)); }
+
+ ~ScopedAppData();
+};
+
+/**
+ * Given |aStr| is holding a string allocated with NS_Alloc, or null:
+ * replace the value in |aStr| with a new value.
+ *
+ * @param aNewValue Null is permitted. The string is cloned with NS_strdup.
+ */
+void SetAllocatedString(const char*& aStr, const char* aNewValue);
+
+/**
+ * Given "str" is holding a string allocated with NS_Alloc, or null:
+ * replace the value in "str" with a new value.
+ *
+ * @param aNewValue If |aNewValue| is the empty string, |aStr| will be set
+ * to null.
+ */
+void SetAllocatedString(const char*& aStr, const nsACString& aNewValue);
+
+template<class T>
+void
+SetStrongPtr(T*& aPtr, T* aNewValue)
+{
+ NS_IF_RELEASE(aPtr);
+ aPtr = aNewValue;
+ NS_IF_ADDREF(aPtr);
+}
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/glue/AutoRestore.h b/xpcom/glue/AutoRestore.h
new file mode 100644
index 000000000..20909c92c
--- /dev/null
+++ b/xpcom/glue/AutoRestore.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/. */
+
+/* functions for restoring saved values at the end of a C++ scope */
+
+#ifndef mozilla_AutoRestore_h_
+#define mozilla_AutoRestore_h_
+
+#include "mozilla/Attributes.h" // MOZ_STACK_CLASS
+#include "mozilla/GuardObjects.h"
+
+namespace mozilla {
+
+/**
+ * Save the current value of a variable and restore it when the object
+ * goes out of scope. For example:
+ * {
+ * AutoRestore<bool> savePainting(mIsPainting);
+ * mIsPainting = true;
+ *
+ * // ... your code here ...
+ *
+ * // mIsPainting is reset to its old value at the end of this block
+ * }
+ */
+template<class T>
+class MOZ_RAII AutoRestore
+{
+private:
+ T& mLocation;
+ T mValue;
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+public:
+ explicit AutoRestore(T& aValue MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : mLocation(aValue)
+ , mValue(aValue)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ }
+ ~AutoRestore()
+ {
+ mLocation = mValue;
+ }
+ T SavedValue() const
+ {
+ return mValue;
+ }
+};
+
+} // namespace mozilla
+
+#endif /* !defined(mozilla_AutoRestore_h_) */
diff --git a/xpcom/glue/BlockingResourceBase.cpp b/xpcom/glue/BlockingResourceBase.cpp
new file mode 100644
index 000000000..ada02c72c
--- /dev/null
+++ b/xpcom/glue/BlockingResourceBase.cpp
@@ -0,0 +1,511 @@
+/* -*- 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/BlockingResourceBase.h"
+
+#ifdef DEBUG
+#include "prthread.h"
+
+#include "nsAutoPtr.h"
+
+#ifndef MOZ_CALLSTACK_DISABLED
+#include "CodeAddressService.h"
+#include "nsHashKeys.h"
+#include "mozilla/StackWalk.h"
+#include "nsTHashtable.h"
+#endif
+
+#include "mozilla/CondVar.h"
+#include "mozilla/DeadlockDetector.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/Mutex.h"
+
+#if defined(MOZILLA_INTERNAL_API)
+#include "GeckoProfiler.h"
+#endif //MOZILLA_INTERNAL_API
+
+#endif // ifdef DEBUG
+
+namespace mozilla {
+//
+// BlockingResourceBase implementation
+//
+
+// static members
+const char* const BlockingResourceBase::kResourceTypeName[] = {
+ // needs to be kept in sync with BlockingResourceType
+ "Mutex", "ReentrantMonitor", "CondVar"
+};
+
+#ifdef DEBUG
+
+PRCallOnceType BlockingResourceBase::sCallOnce;
+unsigned BlockingResourceBase::sResourceAcqnChainFrontTPI = (unsigned)-1;
+BlockingResourceBase::DDT* BlockingResourceBase::sDeadlockDetector;
+
+
+void
+BlockingResourceBase::StackWalkCallback(uint32_t aFrameNumber, void* aPc,
+ void* aSp, void* aClosure)
+{
+#ifndef MOZ_CALLSTACK_DISABLED
+ AcquisitionState* state = (AcquisitionState*)aClosure;
+ state->AppendElement(aPc);
+#endif
+}
+
+void
+BlockingResourceBase::GetStackTrace(AcquisitionState& aState)
+{
+#ifndef MOZ_CALLSTACK_DISABLED
+ // Skip this function and the calling function.
+ const uint32_t kSkipFrames = 2;
+
+ aState.Clear();
+
+ // NB: Ignore the return value, there's nothing useful we can do if this
+ // this fails.
+ MozStackWalk(StackWalkCallback, kSkipFrames, 24, &aState, 0, nullptr);
+#endif
+}
+
+/**
+ * PrintCycle
+ * Append to |aOut| detailed information about the circular
+ * dependency in |aCycle|. Returns true if it *appears* that this
+ * cycle may represent an imminent deadlock, but this is merely a
+ * heuristic; the value returned may be a false positive or false
+ * negative.
+ *
+ * *NOT* thread safe. Calls |Print()|.
+ *
+ * FIXME bug 456272 hack alert: because we can't write call
+ * contexts into strings, all info is written to stderr, but only
+ * some info is written into |aOut|
+ */
+bool
+PrintCycle(const BlockingResourceBase::DDT::ResourceAcquisitionArray* aCycle,
+ nsACString& aOut)
+{
+ NS_ASSERTION(aCycle->Length() > 1, "need > 1 element for cycle!");
+
+ bool maybeImminent = true;
+
+ fputs("=== Cyclical dependency starts at\n", stderr);
+ aOut += "Cyclical dependency starts at\n";
+
+ const BlockingResourceBase::DDT::ResourceAcquisitionArray::elem_type res =
+ aCycle->ElementAt(0);
+ maybeImminent &= res->Print(aOut);
+
+ BlockingResourceBase::DDT::ResourceAcquisitionArray::index_type i;
+ BlockingResourceBase::DDT::ResourceAcquisitionArray::size_type len =
+ aCycle->Length();
+ const BlockingResourceBase::DDT::ResourceAcquisitionArray::elem_type* it =
+ 1 + aCycle->Elements();
+ for (i = 1; i < len - 1; ++i, ++it) {
+ fputs("\n--- Next dependency:\n", stderr);
+ aOut += "\nNext dependency:\n";
+
+ maybeImminent &= (*it)->Print(aOut);
+ }
+
+ fputs("\n=== Cycle completed at\n", stderr);
+ aOut += "Cycle completed at\n";
+ (*it)->Print(aOut);
+
+ return maybeImminent;
+}
+
+#ifndef MOZ_CALLSTACK_DISABLED
+struct CodeAddressServiceLock final
+{
+ static void Unlock() { }
+ static void Lock() { }
+ static bool IsLocked() { return true; }
+};
+
+struct CodeAddressServiceStringAlloc final
+{
+ static char* copy(const char* aString) { return ::strdup(aString); }
+ static void free(char* aString) { ::free(aString); }
+};
+
+class CodeAddressServiceStringTable final
+{
+public:
+ CodeAddressServiceStringTable() : mSet(32) {}
+
+ const char* Intern(const char* aString)
+ {
+ nsCharPtrHashKey* e = mSet.PutEntry(aString);
+ return e->GetKey();
+ }
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return mSet.SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+private:
+ typedef nsTHashtable<nsCharPtrHashKey> StringSet;
+ StringSet mSet;
+};
+
+typedef CodeAddressService<CodeAddressServiceStringTable,
+ CodeAddressServiceStringAlloc,
+ CodeAddressServiceLock> WalkTheStackCodeAddressService;
+#endif
+
+bool
+BlockingResourceBase::Print(nsACString& aOut) const
+{
+ fprintf(stderr, "--- %s : %s",
+ kResourceTypeName[mType], mName);
+ aOut += BlockingResourceBase::kResourceTypeName[mType];
+ aOut += " : ";
+ aOut += mName;
+
+ bool acquired = IsAcquired();
+
+ if (acquired) {
+ fputs(" (currently acquired)\n", stderr);
+ aOut += " (currently acquired)\n";
+ }
+
+ fputs(" calling context\n", stderr);
+#ifdef MOZ_CALLSTACK_DISABLED
+ fputs(" [stack trace unavailable]\n", stderr);
+#else
+ const AcquisitionState& state = acquired ? mAcquired : mFirstSeen;
+
+ WalkTheStackCodeAddressService addressService;
+
+ for (uint32_t i = 0; i < state.Length(); i++) {
+ const size_t kMaxLength = 1024;
+ char buffer[kMaxLength];
+ addressService.GetLocation(i + 1, state[i], buffer, kMaxLength);
+ const char* fmt = " %s\n";
+ aOut.AppendLiteral(" ");
+ aOut.Append(buffer);
+ aOut.AppendLiteral("\n");
+ fprintf(stderr, fmt, buffer);
+ }
+
+#endif
+
+ return acquired;
+}
+
+
+BlockingResourceBase::BlockingResourceBase(
+ const char* aName,
+ BlockingResourceBase::BlockingResourceType aType)
+ : mName(aName)
+ , mType(aType)
+#ifdef MOZ_CALLSTACK_DISABLED
+ , mAcquired(false)
+#else
+ , mAcquired()
+#endif
+{
+ MOZ_ASSERT(mName, "Name must be nonnull");
+ // PR_CallOnce guaranatees that InitStatics is called in a
+ // thread-safe way
+ if (PR_SUCCESS != PR_CallOnce(&sCallOnce, InitStatics)) {
+ NS_RUNTIMEABORT("can't initialize blocking resource static members");
+ }
+
+ mChainPrev = 0;
+ sDeadlockDetector->Add(this);
+}
+
+
+BlockingResourceBase::~BlockingResourceBase()
+{
+ // we don't check for really obviously bad things like freeing
+ // Mutexes while they're still locked. it is assumed that the
+ // base class, or its underlying primitive, will check for such
+ // stupid mistakes.
+ mChainPrev = 0; // racy only for stupidly buggy client code
+ if (sDeadlockDetector) {
+ sDeadlockDetector->Remove(this);
+ }
+}
+
+
+size_t
+BlockingResourceBase::SizeOfDeadlockDetector(MallocSizeOf aMallocSizeOf)
+{
+ return sDeadlockDetector ?
+ sDeadlockDetector->SizeOfIncludingThis(aMallocSizeOf) : 0;
+}
+
+
+PRStatus
+BlockingResourceBase::InitStatics()
+{
+ PR_NewThreadPrivateIndex(&sResourceAcqnChainFrontTPI, 0);
+ sDeadlockDetector = new DDT();
+ if (!sDeadlockDetector) {
+ NS_RUNTIMEABORT("can't allocate deadlock detector");
+ }
+ return PR_SUCCESS;
+}
+
+
+void
+BlockingResourceBase::Shutdown()
+{
+ delete sDeadlockDetector;
+ sDeadlockDetector = 0;
+}
+
+
+void
+BlockingResourceBase::CheckAcquire()
+{
+ if (mType == eCondVar) {
+ NS_NOTYETIMPLEMENTED(
+ "FIXME bug 456272: annots. to allow CheckAcquire()ing condvars");
+ return;
+ }
+
+ BlockingResourceBase* chainFront = ResourceChainFront();
+ nsAutoPtr<DDT::ResourceAcquisitionArray> cycle(
+ sDeadlockDetector->CheckAcquisition(
+ chainFront ? chainFront : 0, this));
+ if (!cycle) {
+ return;
+ }
+
+#ifndef MOZ_CALLSTACK_DISABLED
+ // Update the current stack before printing.
+ GetStackTrace(mAcquired);
+#endif
+
+ fputs("###!!! ERROR: Potential deadlock detected:\n", stderr);
+ nsAutoCString out("Potential deadlock detected:\n");
+ bool maybeImminent = PrintCycle(cycle, out);
+
+ if (maybeImminent) {
+ fputs("\n###!!! Deadlock may happen NOW!\n\n", stderr);
+ out.AppendLiteral("\n###!!! Deadlock may happen NOW!\n\n");
+ } else {
+ fputs("\nDeadlock may happen for some other execution\n\n",
+ stderr);
+ out.AppendLiteral("\nDeadlock may happen for some other execution\n\n");
+ }
+
+ // XXX can customize behavior on whether we /think/ deadlock is
+ // XXX about to happen. for example:
+ // XXX if (maybeImminent)
+ // NS_RUNTIMEABORT(out.get());
+ NS_ERROR(out.get());
+}
+
+
+void
+BlockingResourceBase::Acquire()
+{
+ if (mType == eCondVar) {
+ NS_NOTYETIMPLEMENTED(
+ "FIXME bug 456272: annots. to allow Acquire()ing condvars");
+ return;
+ }
+ NS_ASSERTION(!IsAcquired(),
+ "reacquiring already acquired resource");
+
+ ResourceChainAppend(ResourceChainFront());
+
+#ifdef MOZ_CALLSTACK_DISABLED
+ mAcquired = true;
+#else
+ // Take a stack snapshot.
+ GetStackTrace(mAcquired);
+ if (mFirstSeen.IsEmpty()) {
+ mFirstSeen = mAcquired;
+ }
+#endif
+}
+
+
+void
+BlockingResourceBase::Release()
+{
+ if (mType == eCondVar) {
+ NS_NOTYETIMPLEMENTED(
+ "FIXME bug 456272: annots. to allow Release()ing condvars");
+ return;
+ }
+
+ BlockingResourceBase* chainFront = ResourceChainFront();
+ NS_ASSERTION(chainFront && IsAcquired(),
+ "Release()ing something that hasn't been Acquire()ed");
+
+ if (chainFront == this) {
+ ResourceChainRemove();
+ } else {
+ // not an error, but makes code hard to reason about.
+ NS_WARNING("Resource acquired is being released in non-LIFO order; why?\n");
+ nsCString tmp;
+ Print(tmp);
+
+ // remove this resource from wherever it lives in the chain
+ // we walk backwards in order of acquisition:
+ // (1) ...node<-prev<-curr...
+ // / /
+ // (2) ...prev<-curr...
+ BlockingResourceBase* curr = chainFront;
+ BlockingResourceBase* prev = nullptr;
+ while (curr && (prev = curr->mChainPrev) && (prev != this)) {
+ curr = prev;
+ }
+ if (prev == this) {
+ curr->mChainPrev = prev->mChainPrev;
+ }
+ }
+
+ ClearAcquisitionState();
+}
+
+
+//
+// Debug implementation of (OffTheBooks)Mutex
+void
+OffTheBooksMutex::Lock()
+{
+ CheckAcquire();
+ PR_Lock(mLock);
+ Acquire(); // protected by mLock
+}
+
+void
+OffTheBooksMutex::Unlock()
+{
+ Release(); // protected by mLock
+ PRStatus status = PR_Unlock(mLock);
+ NS_ASSERTION(PR_SUCCESS == status, "bad Mutex::Unlock()");
+}
+
+
+//
+// Debug implementation of ReentrantMonitor
+void
+ReentrantMonitor::Enter()
+{
+ BlockingResourceBase* chainFront = ResourceChainFront();
+
+ // the code below implements monitor reentrancy semantics
+
+ if (this == chainFront) {
+ // immediately re-entered the monitor: acceptable
+ PR_EnterMonitor(mReentrantMonitor);
+ ++mEntryCount;
+ return;
+ }
+
+ // this is sort of a hack around not recording the thread that
+ // owns this monitor
+ if (chainFront) {
+ for (BlockingResourceBase* br = ResourceChainPrev(chainFront);
+ br;
+ br = ResourceChainPrev(br)) {
+ if (br == this) {
+ NS_WARNING(
+ "Re-entering ReentrantMonitor after acquiring other resources.");
+
+ // show the caller why this is potentially bad
+ CheckAcquire();
+
+ PR_EnterMonitor(mReentrantMonitor);
+ ++mEntryCount;
+ return;
+ }
+ }
+ }
+
+ CheckAcquire();
+ PR_EnterMonitor(mReentrantMonitor);
+ NS_ASSERTION(mEntryCount == 0, "ReentrantMonitor isn't free!");
+ Acquire(); // protected by mReentrantMonitor
+ mEntryCount = 1;
+}
+
+void
+ReentrantMonitor::Exit()
+{
+ if (--mEntryCount == 0) {
+ Release(); // protected by mReentrantMonitor
+ }
+ PRStatus status = PR_ExitMonitor(mReentrantMonitor);
+ NS_ASSERTION(PR_SUCCESS == status, "bad ReentrantMonitor::Exit()");
+}
+
+nsresult
+ReentrantMonitor::Wait(PRIntervalTime aInterval)
+{
+ AssertCurrentThreadIn();
+
+ // save monitor state and reset it to empty
+ int32_t savedEntryCount = mEntryCount;
+ AcquisitionState savedAcquisitionState = GetAcquisitionState();
+ BlockingResourceBase* savedChainPrev = mChainPrev;
+ mEntryCount = 0;
+ ClearAcquisitionState();
+ mChainPrev = 0;
+
+ nsresult rv;
+#if defined(MOZILLA_INTERNAL_API)
+ {
+ GeckoProfilerSleepRAII profiler_sleep;
+#endif //MOZILLA_INTERNAL_API
+
+ // give up the monitor until we're back from Wait()
+ rv = PR_Wait(mReentrantMonitor, aInterval) == PR_SUCCESS ? NS_OK :
+ NS_ERROR_FAILURE;
+
+#if defined(MOZILLA_INTERNAL_API)
+ }
+#endif //MOZILLA_INTERNAL_API
+
+ // restore saved state
+ mEntryCount = savedEntryCount;
+ SetAcquisitionState(savedAcquisitionState);
+ mChainPrev = savedChainPrev;
+
+ return rv;
+}
+
+
+//
+// Debug implementation of CondVar
+nsresult
+CondVar::Wait(PRIntervalTime aInterval)
+{
+ AssertCurrentThreadOwnsMutex();
+
+ // save mutex state and reset to empty
+ AcquisitionState savedAcquisitionState = mLock->GetAcquisitionState();
+ BlockingResourceBase* savedChainPrev = mLock->mChainPrev;
+ mLock->ClearAcquisitionState();
+ mLock->mChainPrev = 0;
+
+ // give up mutex until we're back from Wait()
+ nsresult rv =
+ PR_WaitCondVar(mCvar, aInterval) == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE;
+
+ // restore saved state
+ mLock->SetAcquisitionState(savedAcquisitionState);
+ mLock->mChainPrev = savedChainPrev;
+
+ return rv;
+}
+
+#endif // ifdef DEBUG
+
+
+} // namespace mozilla
diff --git a/xpcom/glue/BlockingResourceBase.h b/xpcom/glue/BlockingResourceBase.h
new file mode 100644
index 000000000..d70fbcbbf
--- /dev/null
+++ b/xpcom/glue/BlockingResourceBase.h
@@ -0,0 +1,344 @@
+/* -*- 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_BlockingResourceBase_h
+#define mozilla_BlockingResourceBase_h
+
+#include "mozilla/Logging.h"
+
+#include "nscore.h"
+#include "nsDebug.h"
+#include "nsError.h"
+#include "nsISupportsImpl.h"
+
+#ifdef DEBUG
+
+// NB: Comment this out to enable callstack tracking.
+#define MOZ_CALLSTACK_DISABLED
+
+#include "prinit.h"
+
+#include "nsStringGlue.h"
+
+#ifndef MOZ_CALLSTACK_DISABLED
+#include "nsTArray.h"
+#endif
+
+#include "nsXPCOM.h"
+#endif
+
+//
+// This header is not meant to be included by client code.
+//
+
+namespace mozilla {
+
+#ifdef DEBUG
+template <class T> class DeadlockDetector;
+#endif
+
+/**
+ * BlockingResourceBase
+ * Base class of resources that might block clients trying to acquire them.
+ * Does debugging and deadlock detection in DEBUG builds.
+ **/
+class BlockingResourceBase
+{
+public:
+ // Needs to be kept in sync with kResourceTypeNames.
+ enum BlockingResourceType { eMutex, eReentrantMonitor, eCondVar };
+
+ /**
+ * kResourceTypeName
+ * Human-readable version of BlockingResourceType enum.
+ */
+ static const char* const kResourceTypeName[];
+
+
+#ifdef DEBUG
+
+ static size_t
+ SizeOfDeadlockDetector(MallocSizeOf aMallocSizeOf);
+
+ /**
+ * Print
+ * Write a description of this blocking resource to |aOut|. If
+ * the resource appears to be currently acquired, the current
+ * acquisition context is printed and true is returned.
+ * Otherwise, we print the context from |aFirstSeen|, the
+ * first acquisition from which the code calling |Print()|
+ * became interested in us, and return false.
+ *
+ * *NOT* thread safe. Reads |mAcquisitionContext| without
+ * synchronization, but this will not cause correctness
+ * problems.
+ *
+ * FIXME bug 456272: hack alert: because we can't write call
+ * contexts into strings, all info is written to stderr, but
+ * only some info is written into |aOut|
+ */
+ bool Print(nsACString& aOut) const;
+
+ size_t
+ SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+ {
+ // NB: |mName| is not reported as it's expected to be a static string.
+ // If we switch to a nsString it should be added to the tally.
+ // |mChainPrev| is not reported because its memory is not owned.
+ size_t n = aMallocSizeOf(this);
+ return n;
+ }
+
+ // ``DDT'' = ``Deadlock Detector Type''
+ typedef DeadlockDetector<BlockingResourceBase> DDT;
+
+protected:
+#ifdef MOZ_CALLSTACK_DISABLED
+ typedef bool AcquisitionState;
+#else
+ typedef AutoTArray<void*, 24> AcquisitionState;
+#endif
+
+ /**
+ * BlockingResourceBase
+ * Initialize this blocking resource. Also hooks the resource into
+ * instrumentation code.
+ *
+ * Thread safe.
+ *
+ * @param aName A meaningful, unique name that can be used in
+ * error messages, et al.
+ * @param aType The specific type of |this|, if any.
+ **/
+ BlockingResourceBase(const char* aName, BlockingResourceType aType);
+
+ ~BlockingResourceBase();
+
+ /**
+ * CheckAcquire
+ *
+ * Thread safe.
+ **/
+ void CheckAcquire();
+
+ /**
+ * Acquire
+ *
+ * *NOT* thread safe. Requires ownership of underlying resource.
+ **/
+ void Acquire(); //NS_NEEDS_RESOURCE(this)
+
+ /**
+ * Release
+ * Remove this resource from the current thread's acquisition chain.
+ * The resource does not have to be at the front of the chain, although
+ * it is confusing to release resources in a different order than they
+ * are acquired. This generates a warning.
+ *
+ * *NOT* thread safe. Requires ownership of underlying resource.
+ **/
+ void Release(); //NS_NEEDS_RESOURCE(this)
+
+ /**
+ * ResourceChainFront
+ *
+ * Thread safe.
+ *
+ * @return the front of the resource acquisition chain, i.e., the last
+ * resource acquired.
+ */
+ static BlockingResourceBase* ResourceChainFront()
+ {
+ return
+ (BlockingResourceBase*)PR_GetThreadPrivate(sResourceAcqnChainFrontTPI);
+ }
+
+ /**
+ * ResourceChainPrev
+ *
+ * *NOT* thread safe. Requires ownership of underlying resource.
+ */
+ static BlockingResourceBase* ResourceChainPrev(
+ const BlockingResourceBase* aResource)
+ {
+ return aResource->mChainPrev;
+ } //NS_NEEDS_RESOURCE(this)
+
+ /**
+ * ResourceChainAppend
+ * Set |this| to the front of the resource acquisition chain, and link
+ * |this| to |aPrev|.
+ *
+ * *NOT* thread safe. Requires ownership of underlying resource.
+ */
+ void ResourceChainAppend(BlockingResourceBase* aPrev)
+ {
+ mChainPrev = aPrev;
+ PR_SetThreadPrivate(sResourceAcqnChainFrontTPI, this);
+ } //NS_NEEDS_RESOURCE(this)
+
+ /**
+ * ResourceChainRemove
+ * Remove |this| from the front of the resource acquisition chain.
+ *
+ * *NOT* thread safe. Requires ownership of underlying resource.
+ */
+ void ResourceChainRemove()
+ {
+ NS_ASSERTION(this == ResourceChainFront(), "not at chain front");
+ PR_SetThreadPrivate(sResourceAcqnChainFrontTPI, mChainPrev);
+ } //NS_NEEDS_RESOURCE(this)
+
+ /**
+ * GetAcquisitionState
+ * Return whether or not this resource was acquired.
+ *
+ * *NOT* thread safe. Requires ownership of underlying resource.
+ */
+ AcquisitionState GetAcquisitionState()
+ {
+ return mAcquired;
+ }
+
+ /**
+ * SetAcquisitionState
+ * Set whether or not this resource was acquired.
+ *
+ * *NOT* thread safe. Requires ownership of underlying resource.
+ */
+ void SetAcquisitionState(const AcquisitionState& aAcquisitionState)
+ {
+ mAcquired = aAcquisitionState;
+ }
+
+ /**
+ * ClearAcquisitionState
+ * Indicate this resource is not acquired.
+ *
+ * *NOT* thread safe. Requires ownership of underlying resource.
+ */
+ void ClearAcquisitionState()
+ {
+#ifdef MOZ_CALLSTACK_DISABLED
+ mAcquired = false;
+#else
+ mAcquired.Clear();
+#endif
+ }
+
+ /**
+ * IsAcquired
+ * Indicates if this resource is acquired.
+ *
+ * *NOT* thread safe. Requires ownership of underlying resource.
+ */
+ bool IsAcquired() const
+ {
+#ifdef MOZ_CALLSTACK_DISABLED
+ return mAcquired;
+#else
+ return !mAcquired.IsEmpty();
+#endif
+ }
+
+ /**
+ * mChainPrev
+ * A series of resource acquisitions creates a chain of orders. This
+ * chain is implemented as a linked list; |mChainPrev| points to the
+ * resource most recently Acquire()'d before this one.
+ **/
+ BlockingResourceBase* mChainPrev;
+
+private:
+ /**
+ * mName
+ * A descriptive name for this resource. Used in error
+ * messages etc.
+ */
+ const char* mName;
+
+ /**
+ * mType
+ * The more specific type of this resource. Used to implement
+ * special semantics (e.g., reentrancy of monitors).
+ **/
+ BlockingResourceType mType;
+
+ /**
+ * mAcquired
+ * Indicates if this resource is currently acquired.
+ */
+ AcquisitionState mAcquired;
+
+#ifndef MOZ_CALLSTACK_DISABLED
+ /**
+ * mFirstSeen
+ * Inidicates where this resource was first acquired.
+ */
+ AcquisitionState mFirstSeen;
+#endif
+
+ /**
+ * sCallOnce
+ * Ensures static members are initialized only once, and in a
+ * thread-safe way.
+ */
+ static PRCallOnceType sCallOnce;
+
+ /**
+ * sResourceAcqnChainFrontTPI
+ * Thread-private index to the front of each thread's resource
+ * acquisition chain.
+ */
+ static unsigned sResourceAcqnChainFrontTPI;
+
+ /**
+ * sDeadlockDetector
+ * Does as named.
+ */
+ static DDT* sDeadlockDetector;
+
+ /**
+ * InitStatics
+ * Inititialize static members of BlockingResourceBase that can't
+ * be statically initialized.
+ *
+ * *NOT* thread safe.
+ */
+ static PRStatus InitStatics();
+
+ /**
+ * Shutdown
+ * Free static members.
+ *
+ * *NOT* thread safe.
+ */
+ static void Shutdown();
+
+ static void StackWalkCallback(uint32_t aFrameNumber, void* aPc,
+ void* aSp, void* aClosure);
+ static void GetStackTrace(AcquisitionState& aState);
+
+# ifdef MOZILLA_INTERNAL_API
+ // so it can call BlockingResourceBase::Shutdown()
+ friend void LogTerm();
+# endif // ifdef MOZILLA_INTERNAL_API
+
+#else // non-DEBUG implementation
+
+ BlockingResourceBase(const char* aName, BlockingResourceType aType) {}
+
+ ~BlockingResourceBase() {}
+
+#endif
+};
+
+
+} // namespace mozilla
+
+
+#endif // mozilla_BlockingResourceBase_h
diff --git a/xpcom/glue/CondVar.h b/xpcom/glue/CondVar.h
new file mode 100644
index 000000000..5d05464ec
--- /dev/null
+++ b/xpcom/glue/CondVar.h
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_CondVar_h
+#define mozilla_CondVar_h
+
+#include "prcvar.h"
+
+#include "mozilla/BlockingResourceBase.h"
+#include "mozilla/Mutex.h"
+
+#ifdef MOZILLA_INTERNAL_API
+#include "GeckoProfiler.h"
+#endif //MOZILLA_INTERNAL_API
+
+namespace mozilla {
+
+
+/**
+ * CondVar
+ * Vanilla condition variable. Please don't use this unless you have a
+ * compelling reason --- Monitor provides a simpler API.
+ */
+class CondVar : BlockingResourceBase
+{
+public:
+ /**
+ * CondVar
+ *
+ * The CALLER owns |aLock|.
+ *
+ * @param aLock A Mutex to associate with this condition variable.
+ * @param aName A name which can reference this monitor
+ * @returns If failure, nullptr.
+ * If success, a valid Monitor* which must be destroyed
+ * by Monitor::DestroyMonitor()
+ **/
+ CondVar(Mutex& aLock, const char* aName)
+ : BlockingResourceBase(aName, eCondVar)
+ , mLock(&aLock)
+ {
+ MOZ_COUNT_CTOR(CondVar);
+ // |aLock| must necessarily already be known to the deadlock detector
+ mCvar = PR_NewCondVar(mLock->mLock);
+ if (!mCvar) {
+ NS_RUNTIMEABORT("Can't allocate mozilla::CondVar");
+ }
+ }
+
+ /**
+ * ~CondVar
+ * Clean up after this CondVar, but NOT its associated Mutex.
+ **/
+ ~CondVar()
+ {
+ NS_ASSERTION(mCvar && mLock,
+ "improperly constructed CondVar or double free");
+ PR_DestroyCondVar(mCvar);
+ mCvar = 0;
+ mLock = 0;
+ MOZ_COUNT_DTOR(CondVar);
+ }
+
+#ifndef DEBUG
+ /**
+ * Wait
+ * @see prcvar.h
+ **/
+ nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT)
+ {
+
+#ifdef MOZILLA_INTERNAL_API
+ GeckoProfilerSleepRAII profiler_sleep;
+#endif //MOZILLA_INTERNAL_API
+ // NSPR checks for lock ownership
+ return PR_WaitCondVar(mCvar, aInterval) == PR_SUCCESS ? NS_OK :
+ NS_ERROR_FAILURE;
+ }
+#else
+ nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT);
+#endif // ifndef DEBUG
+
+ /**
+ * Notify
+ * @see prcvar.h
+ **/
+ nsresult Notify()
+ {
+ // NSPR checks for lock ownership
+ return PR_NotifyCondVar(mCvar) == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE;
+ }
+
+ /**
+ * NotifyAll
+ * @see prcvar.h
+ **/
+ nsresult NotifyAll()
+ {
+ // NSPR checks for lock ownership
+ return PR_NotifyAllCondVar(mCvar) == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE;
+ }
+
+#ifdef DEBUG
+ /**
+ * AssertCurrentThreadOwnsMutex
+ * @see Mutex::AssertCurrentThreadOwns
+ **/
+ void AssertCurrentThreadOwnsMutex()
+ {
+ mLock->AssertCurrentThreadOwns();
+ }
+
+ /**
+ * AssertNotCurrentThreadOwnsMutex
+ * @see Mutex::AssertNotCurrentThreadOwns
+ **/
+ void AssertNotCurrentThreadOwnsMutex()
+ {
+ mLock->AssertNotCurrentThreadOwns();
+ }
+
+#else
+ void AssertCurrentThreadOwnsMutex() {}
+ void AssertNotCurrentThreadOwnsMutex() {}
+
+#endif // ifdef DEBUG
+
+private:
+ CondVar();
+ CondVar(CondVar&);
+ CondVar& operator=(CondVar&);
+
+ Mutex* mLock;
+ PRCondVar* mCvar;
+};
+
+
+} // namespace mozilla
+
+
+#endif // ifndef mozilla_CondVar_h
diff --git a/xpcom/glue/DeadlockDetector.h b/xpcom/glue/DeadlockDetector.h
new file mode 100644
index 000000000..382e4ef4a
--- /dev/null
+++ b/xpcom/glue/DeadlockDetector.h
@@ -0,0 +1,382 @@
+/* -*- 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_DeadlockDetector_h
+#define mozilla_DeadlockDetector_h
+
+#include "mozilla/Attributes.h"
+
+#include <stdlib.h>
+
+#include "prlock.h"
+
+#include "nsClassHashtable.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+
+/**
+ * DeadlockDetector
+ *
+ * The following is an approximate description of how the deadlock detector
+ * works.
+ *
+ * The deadlock detector ensures that all blocking resources are
+ * acquired according to a partial order P. One type of blocking
+ * resource is a lock. If a lock l1 is acquired (locked) before l2,
+ * then we say that |l1 <_P l2|. The detector flags an error if two
+ * locks l1 and l2 have an inconsistent ordering in P; that is, if
+ * both |l1 <_P l2| and |l2 <_P l1|. This is a potential error
+ * because a thread acquiring l1,l2 according to the first order might
+ * race with a thread acquiring them according to the second order.
+ * If this happens under the right conditions, then the acquisitions
+ * will deadlock.
+ *
+ * This deadlock detector doesn't know at compile-time what P is. So,
+ * it tries to discover the order at run time. More precisely, it
+ * finds <i>some</i> order P, then tries to find chains of resource
+ * acquisitions that violate P. An example acquisition sequence, and
+ * the orders they impose, is
+ * l1.lock() // current chain: [ l1 ]
+ * // order: { }
+ *
+ * l2.lock() // current chain: [ l1, l2 ]
+ * // order: { l1 <_P l2 }
+ *
+ * l3.lock() // current chain: [ l1, l2, l3 ]
+ * // order: { l1 <_P l2, l2 <_P l3, l1 <_P l3 }
+ * // (note: <_P is transitive, so also |l1 <_P l3|)
+ *
+ * l2.unlock() // current chain: [ l1, l3 ]
+ * // order: { l1 <_P l2, l2 <_P l3, l1 <_P l3 }
+ * // (note: it's OK, but weird, that l2 was unlocked out
+ * // of order. we still have l1 <_P l3).
+ *
+ * l2.lock() // current chain: [ l1, l3, l2 ]
+ * // order: { l1 <_P l2, l2 <_P l3, l1 <_P l3,
+ * l3 <_P l2 (!!!) }
+ * BEEP BEEP! Here the detector will flag a potential error, since
+ * l2 and l3 were used inconsistently (and potentially in ways that
+ * would deadlock).
+ */
+template<typename T>
+class DeadlockDetector
+{
+public:
+ typedef nsTArray<const T*> ResourceAcquisitionArray;
+
+private:
+ struct OrderingEntry;
+ typedef nsTArray<OrderingEntry*> HashEntryArray;
+ typedef typename HashEntryArray::index_type index_type;
+ typedef typename HashEntryArray::size_type size_type;
+ static const index_type NoIndex = HashEntryArray::NoIndex;
+
+ /**
+ * Value type for the ordering table. Contains the other
+ * resources on which an ordering constraint |key < other|
+ * exists. The catch is that we also store the calling context at
+ * which the other resource was acquired; this improves the
+ * quality of error messages when potential deadlock is detected.
+ */
+ struct OrderingEntry
+ {
+ explicit OrderingEntry(const T* aResource)
+ : mOrderedLT() // FIXME bug 456272: set to empirical dep size?
+ , mExternalRefs()
+ , mResource(aResource)
+ {
+ }
+ ~OrderingEntry()
+ {
+ }
+
+ size_t
+ SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+ {
+ size_t n = aMallocSizeOf(this);
+ n += mOrderedLT.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ n += mExternalRefs.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ return n;
+ }
+
+ HashEntryArray mOrderedLT; // this <_o Other
+ HashEntryArray mExternalRefs; // hash entries that reference this
+ const T* mResource;
+ };
+
+ // Throwaway RAII lock to make the following code safer.
+ struct PRAutoLock
+ {
+ explicit PRAutoLock(PRLock* aLock) : mLock(aLock) { PR_Lock(mLock); }
+ ~PRAutoLock() { PR_Unlock(mLock); }
+ PRLock* mLock;
+ };
+
+public:
+ static const uint32_t kDefaultNumBuckets;
+
+ /**
+ * DeadlockDetector
+ * Create a new deadlock detector.
+ *
+ * @param aNumResourcesGuess Guess at approximate number of resources
+ * that will be checked.
+ */
+ explicit DeadlockDetector(uint32_t aNumResourcesGuess = kDefaultNumBuckets)
+ : mOrdering(aNumResourcesGuess)
+ {
+ mLock = PR_NewLock();
+ if (!mLock) {
+ NS_RUNTIMEABORT("couldn't allocate deadlock detector lock");
+ }
+ }
+
+ /**
+ * ~DeadlockDetector
+ *
+ * *NOT* thread safe.
+ */
+ ~DeadlockDetector()
+ {
+ PR_DestroyLock(mLock);
+ }
+
+ size_t
+ SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+ {
+ size_t n = aMallocSizeOf(this);
+
+ {
+ PRAutoLock _(mLock);
+ n += mOrdering.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = mOrdering.ConstIter(); !iter.Done(); iter.Next()) {
+ // NB: Key is accounted for in the entry.
+ n += iter.Data()->SizeOfIncludingThis(aMallocSizeOf);
+ }
+ }
+
+ return n;
+ }
+
+ /**
+ * Add
+ * Make the deadlock detector aware of |aResource|.
+ *
+ * WARNING: The deadlock detector owns |aResource|.
+ *
+ * Thread safe.
+ *
+ * @param aResource Resource to make deadlock detector aware of.
+ */
+ void Add(const T* aResource)
+ {
+ PRAutoLock _(mLock);
+ mOrdering.Put(aResource, new OrderingEntry(aResource));
+ }
+
+ void Remove(const T* aResource)
+ {
+ PRAutoLock _(mLock);
+
+ OrderingEntry* entry = mOrdering.Get(aResource);
+
+ // Iterate the external refs and remove the entry from them.
+ HashEntryArray& refs = entry->mExternalRefs;
+ for (index_type i = 0; i < refs.Length(); i++) {
+ refs[i]->mOrderedLT.RemoveElementSorted(entry);
+ }
+
+ // Iterate orders and remove this entry from their refs.
+ HashEntryArray& orders = entry->mOrderedLT;
+ for (index_type i = 0; i < orders.Length(); i++) {
+ orders[i]->mExternalRefs.RemoveElementSorted(entry);
+ }
+
+ // Now the entry can be safely removed.
+ mOrdering.Remove(aResource);
+ }
+
+ /**
+ * CheckAcquisition This method is called after acquiring |aLast|,
+ * but before trying to acquire |aProposed|.
+ * It determines whether actually trying to acquire |aProposed|
+ * will create problems. It is OK if |aLast| is nullptr; this is
+ * interpreted as |aProposed| being the thread's first acquisition
+ * of its current chain.
+ *
+ * Iff acquiring |aProposed| may lead to deadlock for some thread
+ * interleaving (including the current one!), the cyclical
+ * dependency from which this was deduced is returned. Otherwise,
+ * 0 is returned.
+ *
+ * If a potential deadlock is detected and a resource cycle is
+ * returned, it is the *caller's* responsibility to free it.
+ *
+ * Thread safe.
+ *
+ * @param aLast Last resource acquired by calling thread (or 0).
+ * @param aProposed Resource calling thread proposes to acquire.
+ */
+ ResourceAcquisitionArray* CheckAcquisition(const T* aLast,
+ const T* aProposed)
+ {
+ if (!aLast) {
+ // don't check if |0 < aProposed|; just vamoose
+ return 0;
+ }
+
+ NS_ASSERTION(aProposed, "null resource");
+ PRAutoLock _(mLock);
+
+ OrderingEntry* proposed = mOrdering.Get(aProposed);
+ NS_ASSERTION(proposed, "missing ordering entry");
+
+ OrderingEntry* current = mOrdering.Get(aLast);
+ NS_ASSERTION(current, "missing ordering entry");
+
+ // this is the crux of the deadlock detector algorithm
+
+ if (current == proposed) {
+ // reflexive deadlock. fastpath b/c InTransitiveClosure is
+ // not applicable here.
+ ResourceAcquisitionArray* cycle = new ResourceAcquisitionArray();
+ if (!cycle) {
+ NS_RUNTIMEABORT("can't allocate dep. cycle array");
+ }
+ cycle->AppendElement(current->mResource);
+ cycle->AppendElement(aProposed);
+ return cycle;
+ }
+ if (InTransitiveClosure(current, proposed)) {
+ // we've already established |aLast < aProposed|. all is well.
+ return 0;
+ }
+ if (InTransitiveClosure(proposed, current)) {
+ // the order |aProposed < aLast| has been deduced, perhaps
+ // transitively. we're attempting to violate that
+ // constraint by acquiring resources in the order
+ // |aLast < aProposed|, and thus we may deadlock under the
+ // right conditions.
+ ResourceAcquisitionArray* cycle = GetDeductionChain(proposed, current);
+ // show how acquiring |aProposed| would complete the cycle
+ cycle->AppendElement(aProposed);
+ return cycle;
+ }
+ // |aLast|, |aProposed| are unordered according to our
+ // poset. this is fine, but we now need to add this
+ // ordering constraint.
+ current->mOrderedLT.InsertElementSorted(proposed);
+ proposed->mExternalRefs.InsertElementSorted(current);
+ return 0;
+ }
+
+ /**
+ * Return true iff |aTarget| is in the transitive closure of |aStart|
+ * over the ordering relation `<_this'.
+ *
+ * @precondition |aStart != aTarget|
+ */
+ bool InTransitiveClosure(const OrderingEntry* aStart,
+ const OrderingEntry* aTarget) const
+ {
+ // NB: Using a static comparator rather than default constructing one shows
+ // a 9% improvement in scalability tests on some systems.
+ static nsDefaultComparator<const OrderingEntry*, const OrderingEntry*> comp;
+ if (aStart->mOrderedLT.BinaryIndexOf(aTarget, comp) != NoIndex) {
+ return true;
+ }
+
+ index_type i = 0;
+ size_type len = aStart->mOrderedLT.Length();
+ for (auto it = aStart->mOrderedLT.Elements(); i < len; ++i, ++it) {
+ if (InTransitiveClosure(*it, aTarget)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return an array of all resource acquisitions
+ * aStart <_this r1 <_this r2 <_ ... <_ aTarget
+ * from which |aStart <_this aTarget| was deduced, including
+ * |aStart| and |aTarget|.
+ *
+ * Nb: there may be multiple deductions of |aStart <_this
+ * aTarget|. This function returns the first ordering found by
+ * depth-first search.
+ *
+ * Nb: |InTransitiveClosure| could be replaced by this function.
+ * However, this one is more expensive because we record the DFS
+ * search stack on the heap whereas the other doesn't.
+ *
+ * @precondition |aStart != aTarget|
+ */
+ ResourceAcquisitionArray* GetDeductionChain(const OrderingEntry* aStart,
+ const OrderingEntry* aTarget)
+ {
+ ResourceAcquisitionArray* chain = new ResourceAcquisitionArray();
+ if (!chain) {
+ NS_RUNTIMEABORT("can't allocate dep. cycle array");
+ }
+ chain->AppendElement(aStart->mResource);
+
+ NS_ASSERTION(GetDeductionChain_Helper(aStart, aTarget, chain),
+ "GetDeductionChain called when there's no deadlock");
+ return chain;
+ }
+
+ // precondition: |aStart != aTarget|
+ // invariant: |aStart| is the last element in |aChain|
+ bool GetDeductionChain_Helper(const OrderingEntry* aStart,
+ const OrderingEntry* aTarget,
+ ResourceAcquisitionArray* aChain)
+ {
+ if (aStart->mOrderedLT.BinaryIndexOf(aTarget) != NoIndex) {
+ aChain->AppendElement(aTarget->mResource);
+ return true;
+ }
+
+ index_type i = 0;
+ size_type len = aStart->mOrderedLT.Length();
+ for (auto it = aStart->mOrderedLT.Elements(); i < len; ++i, ++it) {
+ aChain->AppendElement((*it)->mResource);
+ if (GetDeductionChain_Helper(*it, aTarget, aChain)) {
+ return true;
+ }
+ aChain->RemoveElementAt(aChain->Length() - 1);
+ }
+ return false;
+ }
+
+ /**
+ * The partial order on resource acquisitions used by the deadlock
+ * detector.
+ */
+ nsClassHashtable<nsPtrHashKey<const T>, OrderingEntry> mOrdering;
+
+
+ /**
+ * Protects contentious methods.
+ * Nb: can't use mozilla::Mutex since we are used as its deadlock
+ * detector.
+ */
+ PRLock* mLock;
+
+private:
+ DeadlockDetector(const DeadlockDetector& aDD) = delete;
+ DeadlockDetector& operator=(const DeadlockDetector& aDD) = delete;
+};
+
+
+template<typename T>
+// FIXME bug 456272: tune based on average workload
+const uint32_t DeadlockDetector<T>::kDefaultNumBuckets = 32;
+
+
+} // namespace mozilla
+
+#endif // ifndef mozilla_DeadlockDetector_h
diff --git a/xpcom/glue/EnumeratedArrayCycleCollection.h b/xpcom/glue/EnumeratedArrayCycleCollection.h
new file mode 100644
index 000000000..faabb5a52
--- /dev/null
+++ b/xpcom/glue/EnumeratedArrayCycleCollection.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 EnumeratedArrayCycleCollection_h_
+#define EnumeratedArrayCycleCollection_h_
+
+#include "mozilla/EnumeratedArray.h"
+#include "nsCycleCollectionTraversalCallback.h"
+
+template<typename IndexType,
+ IndexType SizeAsEnumValue,
+ typename ValueType>
+inline void
+ImplCycleCollectionUnlink(mozilla::EnumeratedArray<IndexType,
+ SizeAsEnumValue,
+ ValueType>& aField)
+{
+ for (size_t i = 0; i < size_t(SizeAsEnumValue); ++i) {
+ aField[IndexType(i)] = nullptr;
+ }
+}
+
+template<typename IndexType,
+ IndexType SizeAsEnumValue,
+ typename ValueType>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ mozilla::EnumeratedArray<IndexType,
+ SizeAsEnumValue,
+ ValueType>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ aFlags |= CycleCollectionEdgeNameArrayFlag;
+ for (size_t i = 0; i < size_t(SizeAsEnumValue); ++i) {
+ ImplCycleCollectionTraverse(aCallback, aField[IndexType(i)], aName, aFlags);
+ }
+}
+
+#endif // EnumeratedArrayCycleCollection_h_
diff --git a/xpcom/glue/FileUtils.cpp b/xpcom/glue/FileUtils.cpp
new file mode 100644
index 000000000..699812461
--- /dev/null
+++ b/xpcom/glue/FileUtils.cpp
@@ -0,0 +1,568 @@
+/* -*- 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 <errno.h>
+#include <stdio.h>
+
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "private/pprio.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/FileUtils.h"
+
+#if defined(XP_MACOSX)
+#include <fcntl.h>
+#include <unistd.h>
+#include <mach/machine.h>
+#include <mach-o/fat.h>
+#include <mach-o/loader.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <limits.h>
+#elif defined(XP_UNIX)
+#include <fcntl.h>
+#include <unistd.h>
+#if defined(LINUX)
+#include <elf.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#elif defined(XP_WIN)
+#include <windows.h>
+#endif
+
+// Functions that are not to be used in standalone glue must be implemented
+// within this #if block
+#if !defined(XPCOM_GLUE)
+
+bool
+mozilla::fallocate(PRFileDesc* aFD, int64_t aLength)
+{
+#if defined(HAVE_POSIX_FALLOCATE)
+ return posix_fallocate(PR_FileDesc2NativeHandle(aFD), 0, aLength) == 0;
+#elif defined(XP_WIN)
+ int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR);
+ if (oldpos == -1) {
+ return false;
+ }
+
+ if (PR_Seek64(aFD, aLength, PR_SEEK_SET) != aLength) {
+ return false;
+ }
+
+ bool retval = (0 != SetEndOfFile((HANDLE)PR_FileDesc2NativeHandle(aFD)));
+
+ PR_Seek64(aFD, oldpos, PR_SEEK_SET);
+ return retval;
+#elif defined(XP_MACOSX)
+ int fd = PR_FileDesc2NativeHandle(aFD);
+ fstore_t store = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, aLength};
+ // Try to get a continous chunk of disk space
+ int ret = fcntl(fd, F_PREALLOCATE, &store);
+ if (ret == -1) {
+ // OK, perhaps we are too fragmented, allocate non-continuous
+ store.fst_flags = F_ALLOCATEALL;
+ ret = fcntl(fd, F_PREALLOCATE, &store);
+ if (ret == -1) {
+ return false;
+ }
+ }
+ return ftruncate(fd, aLength) == 0;
+#elif defined(XP_UNIX)
+ // The following is copied from fcntlSizeHint in sqlite
+ /* If the OS does not have posix_fallocate(), fake it. First use
+ ** ftruncate() to set the file size, then write a single byte to
+ ** the last byte in each block within the extended region. This
+ ** is the same technique used by glibc to implement posix_fallocate()
+ ** on systems that do not have a real fallocate() system call.
+ */
+ int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR);
+ if (oldpos == -1) {
+ return false;
+ }
+
+ struct stat buf;
+ int fd = PR_FileDesc2NativeHandle(aFD);
+ if (fstat(fd, &buf)) {
+ return false;
+ }
+
+ if (buf.st_size >= aLength) {
+ return false;
+ }
+
+ const int nBlk = buf.st_blksize;
+
+ if (!nBlk) {
+ return false;
+ }
+
+ if (ftruncate(fd, aLength)) {
+ return false;
+ }
+
+ int nWrite; // Return value from write()
+ int64_t iWrite = ((buf.st_size + 2 * nBlk - 1) / nBlk) * nBlk - 1; // Next offset to write to
+ while (iWrite < aLength) {
+ nWrite = 0;
+ if (PR_Seek64(aFD, iWrite, PR_SEEK_SET) == iWrite) {
+ nWrite = PR_Write(aFD, "", 1);
+ }
+ if (nWrite != 1) {
+ break;
+ }
+ iWrite += nBlk;
+ }
+
+ PR_Seek64(aFD, oldpos, PR_SEEK_SET);
+ return nWrite == 1;
+#endif
+ return false;
+}
+
+#ifdef ReadSysFile_PRESENT
+
+bool
+mozilla::ReadSysFile(
+ const char* aFilename,
+ char* aBuf,
+ size_t aBufSize)
+{
+ int fd = MOZ_TEMP_FAILURE_RETRY(open(aFilename, O_RDONLY));
+ if (fd < 0) {
+ return false;
+ }
+ ScopedClose autoClose(fd);
+ if (aBufSize == 0) {
+ return true;
+ }
+ ssize_t bytesRead;
+ size_t offset = 0;
+ do {
+ bytesRead = MOZ_TEMP_FAILURE_RETRY(read(fd, aBuf + offset,
+ aBufSize - offset));
+ if (bytesRead == -1) {
+ return false;
+ }
+ offset += bytesRead;
+ } while (bytesRead > 0 && offset < aBufSize);
+ MOZ_ASSERT(offset <= aBufSize);
+ if (offset > 0 && aBuf[offset - 1] == '\n') {
+ offset--;
+ }
+ if (offset == aBufSize) {
+ MOZ_ASSERT(offset > 0);
+ offset--;
+ }
+ aBuf[offset] = '\0';
+ return true;
+}
+
+bool
+mozilla::ReadSysFile(
+ const char* aFilename,
+ int* aVal)
+{
+ char valBuf[32];
+ if (!ReadSysFile(aFilename, valBuf, sizeof(valBuf))) {
+ return false;
+ }
+ return sscanf(valBuf, "%d", aVal) == 1;
+}
+
+bool
+mozilla::ReadSysFile(
+ const char* aFilename,
+ bool* aVal)
+{
+ int v;
+ if (!ReadSysFile(aFilename, &v)) {
+ return false;
+ }
+ *aVal = (v != 0);
+ return true;
+}
+
+#endif /* ReadSysFile_PRESENT */
+
+#ifdef WriteSysFile_PRESENT
+
+bool
+mozilla::WriteSysFile(
+ const char* aFilename,
+ const char* aBuf)
+{
+ size_t aBufSize = strlen(aBuf);
+ int fd = MOZ_TEMP_FAILURE_RETRY(open(aFilename, O_WRONLY));
+ if (fd < 0) {
+ return false;
+ }
+ ScopedClose autoClose(fd);
+ ssize_t bytesWritten;
+ size_t offset = 0;
+ do {
+ bytesWritten = MOZ_TEMP_FAILURE_RETRY(write(fd, aBuf + offset,
+ aBufSize - offset));
+ if (bytesWritten == -1) {
+ return false;
+ }
+ offset += bytesWritten;
+ } while (bytesWritten > 0 && offset < aBufSize);
+ MOZ_ASSERT(offset == aBufSize);
+ return true;
+}
+
+#endif /* WriteSysFile_PRESENT */
+
+void
+mozilla::ReadAheadLib(nsIFile* aFile)
+{
+#if defined(XP_WIN)
+ nsAutoString path;
+ if (!aFile || NS_FAILED(aFile->GetPath(path))) {
+ return;
+ }
+ ReadAheadLib(path.get());
+#elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
+ nsAutoCString nativePath;
+ if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) {
+ return;
+ }
+ ReadAheadLib(nativePath.get());
+#endif
+}
+
+void
+mozilla::ReadAheadFile(nsIFile* aFile, const size_t aOffset,
+ const size_t aCount, mozilla::filedesc_t* aOutFd)
+{
+#if defined(XP_WIN)
+ nsAutoString path;
+ if (!aFile || NS_FAILED(aFile->GetPath(path))) {
+ return;
+ }
+ ReadAheadFile(path.get(), aOffset, aCount, aOutFd);
+#elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
+ nsAutoCString nativePath;
+ if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) {
+ return;
+ }
+ ReadAheadFile(nativePath.get(), aOffset, aCount, aOutFd);
+#endif
+}
+
+#endif // !defined(XPCOM_GLUE)
+
+#if defined(LINUX) && !defined(ANDROID)
+
+static const unsigned int bufsize = 4096;
+
+#ifdef __LP64__
+typedef Elf64_Ehdr Elf_Ehdr;
+typedef Elf64_Phdr Elf_Phdr;
+static const unsigned char ELFCLASS = ELFCLASS64;
+typedef Elf64_Off Elf_Off;
+#else
+typedef Elf32_Ehdr Elf_Ehdr;
+typedef Elf32_Phdr Elf_Phdr;
+static const unsigned char ELFCLASS = ELFCLASS32;
+typedef Elf32_Off Elf_Off;
+#endif
+
+#elif defined(XP_MACOSX)
+
+#if defined(__i386__)
+static const uint32_t CPU_TYPE = CPU_TYPE_X86;
+#elif defined(__x86_64__)
+static const uint32_t CPU_TYPE = CPU_TYPE_X86_64;
+#elif defined(__ppc__)
+static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC;
+#elif defined(__ppc64__)
+static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC64;
+#else
+#error Unsupported CPU type
+#endif
+
+#ifdef __LP64__
+#undef LC_SEGMENT
+#define LC_SEGMENT LC_SEGMENT_64
+#undef MH_MAGIC
+#define MH_MAGIC MH_MAGIC_64
+#define cpu_mach_header mach_header_64
+#define segment_command segment_command_64
+#else
+#define cpu_mach_header mach_header
+#endif
+
+class ScopedMMap
+{
+public:
+ explicit ScopedMMap(const char* aFilePath)
+ : buf(nullptr)
+ {
+ fd = open(aFilePath, O_RDONLY);
+ if (fd < 0) {
+ return;
+ }
+ struct stat st;
+ if (fstat(fd, &st) < 0) {
+ return;
+ }
+ size = st.st_size;
+ buf = (char*)mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
+ }
+ ~ScopedMMap()
+ {
+ if (buf) {
+ munmap(buf, size);
+ }
+ if (fd >= 0) {
+ close(fd);
+ }
+ }
+ operator char*() { return buf; }
+ int getFd() { return fd; }
+private:
+ int fd;
+ char* buf;
+ size_t size;
+};
+#endif
+
+void
+mozilla::ReadAhead(mozilla::filedesc_t aFd, const size_t aOffset,
+ const size_t aCount)
+{
+#if defined(XP_WIN)
+
+ LARGE_INTEGER fpOriginal;
+ LARGE_INTEGER fpOffset;
+#if defined(HAVE_LONG_LONG)
+ fpOffset.QuadPart = 0;
+#else
+ fpOffset.u.LowPart = 0;
+ fpOffset.u.HighPart = 0;
+#endif
+
+ // Get the current file pointer so that we can restore it. This isn't
+ // really necessary other than to provide the same semantics regarding the
+ // file pointer that other platforms do
+ if (!SetFilePointerEx(aFd, fpOffset, &fpOriginal, FILE_CURRENT)) {
+ return;
+ }
+
+ if (aOffset) {
+#if defined(HAVE_LONG_LONG)
+ fpOffset.QuadPart = static_cast<LONGLONG>(aOffset);
+#else
+ fpOffset.u.LowPart = aOffset;
+ fpOffset.u.HighPart = 0;
+#endif
+
+ if (!SetFilePointerEx(aFd, fpOffset, nullptr, FILE_BEGIN)) {
+ return;
+ }
+ }
+
+ char buf[64 * 1024];
+ size_t totalBytesRead = 0;
+ DWORD dwBytesRead;
+ // Do dummy reads to trigger kernel-side readhead via FILE_FLAG_SEQUENTIAL_SCAN.
+ // Abort when underfilling because during testing the buffers are read fully
+ // A buffer that's not keeping up would imply that readahead isn't working right
+ while (totalBytesRead < aCount &&
+ ReadFile(aFd, buf, sizeof(buf), &dwBytesRead, nullptr) &&
+ dwBytesRead == sizeof(buf)) {
+ totalBytesRead += dwBytesRead;
+ }
+
+ // Restore the file pointer
+ SetFilePointerEx(aFd, fpOriginal, nullptr, FILE_BEGIN);
+
+#elif defined(LINUX) && !defined(ANDROID)
+
+ readahead(aFd, aOffset, aCount);
+
+#elif defined(XP_MACOSX)
+
+ struct radvisory ra;
+ ra.ra_offset = aOffset;
+ ra.ra_count = aCount;
+ // The F_RDADVISE fcntl is equivalent to Linux' readahead() system call.
+ fcntl(aFd, F_RDADVISE, &ra);
+
+#endif
+}
+
+void
+mozilla::ReadAheadLib(mozilla::pathstr_t aFilePath)
+{
+ if (!aFilePath) {
+ return;
+ }
+#if defined(XP_WIN)
+ ReadAheadFile(aFilePath);
+#elif defined(LINUX) && !defined(ANDROID)
+ int fd = open(aFilePath, O_RDONLY);
+ if (fd < 0) {
+ return;
+ }
+
+ union
+ {
+ char buf[bufsize];
+ Elf_Ehdr ehdr;
+ } elf;
+ // Read ELF header (ehdr) and program header table (phdr).
+ // We check that the ELF magic is found, that the ELF class matches
+ // our own, and that the program header table as defined in the ELF
+ // headers fits in the buffer we read.
+ if ((read(fd, elf.buf, bufsize) <= 0) ||
+ (memcmp(elf.buf, ELFMAG, 4)) ||
+ (elf.ehdr.e_ident[EI_CLASS] != ELFCLASS) ||
+ // Upcast e_phentsize so the multiplication is done in the same precision
+ // as the subsequent addition, to satisfy static analyzers and avoid
+ // issues with abnormally large program header tables.
+ (elf.ehdr.e_phoff + (static_cast<Elf_Off>(elf.ehdr.e_phentsize) *
+ elf.ehdr.e_phnum) >= bufsize)) {
+ close(fd);
+ return;
+ }
+ // The program header table contains segment definitions. One such
+ // segment type is PT_LOAD, which describes how the dynamic loader
+ // is going to map the file in memory. We use that information to
+ // find the biggest offset from the library that will be mapped in
+ // memory.
+ Elf_Phdr* phdr = (Elf_Phdr*)&elf.buf[elf.ehdr.e_phoff];
+ Elf_Off end = 0;
+ for (int phnum = elf.ehdr.e_phnum; phnum; phdr++, phnum--) {
+ if ((phdr->p_type == PT_LOAD) &&
+ (end < phdr->p_offset + phdr->p_filesz)) {
+ end = phdr->p_offset + phdr->p_filesz;
+ }
+ }
+ // Let the kernel read ahead what the dynamic loader is going to
+ // map in memory soon after.
+ if (end > 0) {
+ ReadAhead(fd, 0, end);
+ }
+ close(fd);
+#elif defined(XP_MACOSX)
+ ScopedMMap buf(aFilePath);
+ char* base = buf;
+ if (!base) {
+ return;
+ }
+
+ // An OSX binary might either be a fat (universal) binary or a
+ // Mach-O binary. A fat binary actually embeds several Mach-O
+ // binaries. If we have a fat binary, find the offset where the
+ // Mach-O binary for our CPU type can be found.
+ struct fat_header* fh = (struct fat_header*)base;
+
+ if (OSSwapBigToHostInt32(fh->magic) == FAT_MAGIC) {
+ uint32_t nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch);
+ struct fat_arch* arch = (struct fat_arch*)&buf[sizeof(struct fat_header)];
+ for (; nfat_arch; arch++, nfat_arch--) {
+ if (OSSwapBigToHostInt32(arch->cputype) == CPU_TYPE) {
+ base += OSSwapBigToHostInt32(arch->offset);
+ break;
+ }
+ }
+ if (base == buf) {
+ return;
+ }
+ }
+
+ // Check Mach-O magic in the Mach header
+ struct cpu_mach_header* mh = (struct cpu_mach_header*)base;
+ if (mh->magic != MH_MAGIC) {
+ return;
+ }
+
+ // The Mach header is followed by a sequence of load commands.
+ // Each command has a header containing the command type and the
+ // command size. LD_SEGMENT commands describes how the dynamic
+ // loader is going to map the file in memory. We use that
+ // information to find the biggest offset from the library that
+ // will be mapped in memory.
+ char* cmd = &base[sizeof(struct cpu_mach_header)];
+ uint32_t end = 0;
+ for (uint32_t ncmds = mh->ncmds; ncmds; ncmds--) {
+ struct segment_command* sh = (struct segment_command*)cmd;
+ if (sh->cmd != LC_SEGMENT) {
+ continue;
+ }
+ if (end < sh->fileoff + sh->filesize) {
+ end = sh->fileoff + sh->filesize;
+ }
+ cmd += sh->cmdsize;
+ }
+ // Let the kernel read ahead what the dynamic loader is going to
+ // map in memory soon after.
+ if (end > 0) {
+ ReadAhead(buf.getFd(), base - buf, end);
+ }
+#endif
+}
+
+void
+mozilla::ReadAheadFile(mozilla::pathstr_t aFilePath, const size_t aOffset,
+ const size_t aCount, mozilla::filedesc_t* aOutFd)
+{
+#if defined(XP_WIN)
+ if (!aFilePath) {
+ if (aOutFd) {
+ *aOutFd = INVALID_HANDLE_VALUE;
+ }
+ return;
+ }
+ HANDLE fd = CreateFileW(aFilePath, GENERIC_READ, FILE_SHARE_READ, nullptr,
+ OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
+ if (aOutFd) {
+ *aOutFd = fd;
+ }
+ if (fd == INVALID_HANDLE_VALUE) {
+ return;
+ }
+ ReadAhead(fd, aOffset, aCount);
+ if (!aOutFd) {
+ CloseHandle(fd);
+ }
+#elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
+ if (!aFilePath) {
+ if (aOutFd) {
+ *aOutFd = -1;
+ }
+ return;
+ }
+ int fd = open(aFilePath, O_RDONLY);
+ if (aOutFd) {
+ *aOutFd = fd;
+ }
+ if (fd < 0) {
+ return;
+ }
+ size_t count;
+ if (aCount == SIZE_MAX) {
+ struct stat st;
+ if (fstat(fd, &st) < 0) {
+ if (!aOutFd) {
+ close(fd);
+ }
+ return;
+ }
+ count = st.st_size;
+ } else {
+ count = aCount;
+ }
+ ReadAhead(fd, aOffset, count);
+ if (!aOutFd) {
+ close(fd);
+ }
+#endif
+}
+
diff --git a/xpcom/glue/FileUtils.h b/xpcom/glue/FileUtils.h
new file mode 100644
index 000000000..aaf912b21
--- /dev/null
+++ b/xpcom/glue/FileUtils.h
@@ -0,0 +1,220 @@
+/* -*- 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_FileUtils_h
+#define mozilla_FileUtils_h
+
+#include "nscore.h" // nullptr
+
+#if defined(XP_UNIX)
+# include <unistd.h>
+#elif defined(XP_WIN)
+# include <io.h>
+#endif
+#include "prio.h"
+
+#include "mozilla/Scoped.h"
+#include "nsIFile.h"
+#include <errno.h>
+#include <limits.h>
+
+namespace mozilla {
+
+#if defined(XP_WIN)
+typedef void* filedesc_t;
+typedef const wchar_t* pathstr_t;
+#else
+typedef int filedesc_t;
+typedef const char* pathstr_t;
+#endif
+
+/**
+ * ScopedCloseFD is a RAII wrapper for POSIX file descriptors
+ *
+ * Instances |close()| their fds when they go out of scope.
+ */
+struct ScopedCloseFDTraits
+{
+ typedef int type;
+ static type empty() { return -1; }
+ static void release(type aFd)
+ {
+ if (aFd != -1) {
+ while (close(aFd) == -1 && errno == EINTR) {
+ }
+ }
+ }
+};
+typedef Scoped<ScopedCloseFDTraits> ScopedClose;
+
+#if !defined(XPCOM_GLUE)
+
+/**
+ * AutoFDClose is a RAII wrapper for PRFileDesc.
+ *
+ * Instances |PR_Close| their fds when they go out of scope.
+ **/
+struct ScopedClosePRFDTraits
+{
+ typedef PRFileDesc* type;
+ static type empty() { return nullptr; }
+ static void release(type aFd)
+ {
+ if (aFd) {
+ PR_Close(aFd);
+ }
+ }
+};
+typedef Scoped<ScopedClosePRFDTraits> AutoFDClose;
+
+/* RAII wrapper for FILE descriptors */
+struct ScopedCloseFileTraits
+{
+ typedef FILE* type;
+ static type empty() { return nullptr; }
+ static void release(type aFile)
+ {
+ if (aFile) {
+ fclose(aFile);
+ }
+ }
+};
+typedef Scoped<ScopedCloseFileTraits> ScopedCloseFile;
+
+/**
+ * Fallocate efficiently and continuously allocates files via fallocate-type APIs.
+ * This is useful for avoiding fragmentation.
+ * On sucess the file be padded with zeros to grow to aLength.
+ *
+ * @param aFD file descriptor.
+ * @param aLength length of file to grow to.
+ * @return true on success.
+ */
+bool fallocate(PRFileDesc* aFD, int64_t aLength);
+
+/**
+ * Use readahead to preload shared libraries into the file cache before loading.
+ * WARNING: This function should not be used without a telemetry field trial
+ * demonstrating a clear performance improvement!
+ *
+ * @param aFile nsIFile representing path to shared library
+ */
+void ReadAheadLib(nsIFile* aFile);
+
+/**
+ * Use readahead to preload a file into the file cache before reading.
+ * WARNING: This function should not be used without a telemetry field trial
+ * demonstrating a clear performance improvement!
+ *
+ * @param aFile nsIFile representing path to shared library
+ * @param aOffset Offset into the file to begin preloading
+ * @param aCount Number of bytes to preload (SIZE_MAX implies file size)
+ * @param aOutFd Pointer to file descriptor. If specified, ReadAheadFile will
+ * return its internal, opened file descriptor instead of closing it.
+ */
+void ReadAheadFile(nsIFile* aFile, const size_t aOffset = 0,
+ const size_t aCount = SIZE_MAX,
+ filedesc_t* aOutFd = nullptr);
+
+#endif // !defined(XPCOM_GLUE)
+
+/**
+ * Use readahead to preload shared libraries into the file cache before loading.
+ * WARNING: This function should not be used without a telemetry field trial
+ * demonstrating a clear performance improvement!
+ *
+ * @param aFilePath path to shared library
+ */
+void ReadAheadLib(pathstr_t aFilePath);
+
+/**
+ * Use readahead to preload a file into the file cache before loading.
+ * WARNING: This function should not be used without a telemetry field trial
+ * demonstrating a clear performance improvement!
+ *
+ * @param aFilePath path to shared library
+ * @param aOffset Offset into the file to begin preloading
+ * @param aCount Number of bytes to preload (SIZE_MAX implies file size)
+ * @param aOutFd Pointer to file descriptor. If specified, ReadAheadFile will
+ * return its internal, opened file descriptor instead of closing it.
+ */
+void ReadAheadFile(pathstr_t aFilePath, const size_t aOffset = 0,
+ const size_t aCount = SIZE_MAX,
+ filedesc_t* aOutFd = nullptr);
+
+/**
+ * Use readahead to preload a file into the file cache before reading.
+ * When this function exits, the file pointer is guaranteed to be in the same
+ * position it was in before this function was called.
+ * WARNING: This function should not be used without a telemetry field trial
+ * demonstrating a clear performance improvement!
+ *
+ * @param aFd file descriptor opened for read access
+ * (on Windows, file must be opened with FILE_FLAG_SEQUENTIAL_SCAN)
+ * @param aOffset Offset into the file to begin preloading
+ * @param aCount Number of bytes to preload (SIZE_MAX implies file size)
+ */
+void ReadAhead(filedesc_t aFd, const size_t aOffset = 0,
+ const size_t aCount = SIZE_MAX);
+
+
+#if defined(MOZ_WIDGET_GONK) || defined(XP_UNIX)
+#define MOZ_TEMP_FAILURE_RETRY(exp) (__extension__({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; \
+}))
+#endif
+
+/* Define ReadSysFile() and WriteSysFile() only on GONK to avoid unnecessary
+ * libxul bloat. Also define it in debug builds, so that unit tests for it can
+ * be written and run in non-GONK builds. */
+#if (defined(MOZ_WIDGET_GONK) || defined(DEBUG)) && defined(XP_UNIX)
+
+#ifndef ReadSysFile_PRESENT
+#define ReadSysFile_PRESENT
+#endif /* ReadSysFile_PRESENT */
+
+#ifndef WriteSysFile_PRESENT
+#define WriteSysFile_PRESENT
+#endif /* WriteSysFile_PRESENT */
+
+/**
+ * Read the contents of a file.
+ * This function is intended for reading a single-lined text files from
+ * /sys/. If the file ends with a newline ('\n') then it will be discarded.
+ * The output buffer will always be '\0'-terminated on successful completion.
+ * If aBufSize == 0, then this function will return true if the file exists
+ * and is readable (it will not attempt to read anything from it).
+ * On failure the contents of aBuf after this call will be undefined and the
+ * value of the global variable errno will be set accordingly.
+ * @return true on success, notice that less than requested bytes could have
+ * been read if the file was smaller
+ */
+bool ReadSysFile(const char* aFilename, char* aBuf, size_t aBufSize);
+
+/**
+ * Parse the contents of a file, assuming it contains a decimal integer.
+ * @return true on success
+ */
+bool ReadSysFile(const char* aFilename, int* aVal);
+
+/**
+ * Parse the contents of a file, assuming it contains a boolean value
+ * (either 0 or 1).
+ * @return true on success
+ */
+bool ReadSysFile(const char* aFilename, bool* aVal);
+
+bool WriteSysFile(const char* aFilename, const char* aBuf);
+
+#endif /* (MOZ_WIDGET_GONK || DEBUG) && XP_UNIX */
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/glue/GenericFactory.cpp b/xpcom/glue/GenericFactory.cpp
new file mode 100644
index 000000000..5bf0b97a4
--- /dev/null
+++ b/xpcom/glue/GenericFactory.cpp
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/GenericFactory.h"
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(GenericFactory, nsIFactory)
+
+NS_IMETHODIMP
+GenericFactory::CreateInstance(nsISupports* aOuter, REFNSIID aIID,
+ void** aResult)
+{
+ return mCtor(aOuter, aIID, aResult);
+}
+
+NS_IMETHODIMP
+GenericFactory::LockFactory(bool aLock)
+{
+ NS_ERROR("Vestigial method, never called!");
+ return NS_ERROR_FAILURE;
+}
+
+} // namespace mozilla
diff --git a/xpcom/glue/GenericFactory.h b/xpcom/glue/GenericFactory.h
new file mode 100644
index 000000000..4561f2a2d
--- /dev/null
+++ b/xpcom/glue/GenericFactory.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 mozilla_GenericFactory_h
+#define mozilla_GenericFactory_h
+
+#include "mozilla/Attributes.h"
+
+#include "mozilla/Module.h"
+
+namespace mozilla {
+
+/**
+ * A generic factory which uses a constructor function to create instances.
+ * This class is intended for use by the component manager and the generic
+ * module.
+ */
+class GenericFactory final : public nsIFactory
+{
+ ~GenericFactory() {}
+
+public:
+ typedef Module::ConstructorProcPtr ConstructorProcPtr;
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIFACTORY
+
+ explicit GenericFactory(ConstructorProcPtr aCtor)
+ : mCtor(aCtor)
+ {
+ NS_ASSERTION(mCtor, "GenericFactory with no constructor");
+ }
+
+private:
+ ConstructorProcPtr mCtor;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_GenericFactory_h
diff --git a/xpcom/glue/GenericModule.cpp b/xpcom/glue/GenericModule.cpp
new file mode 100644
index 000000000..0b5cd3a7c
--- /dev/null
+++ b/xpcom/glue/GenericModule.cpp
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ModuleUtils.h"
+#include "mozilla/GenericFactory.h"
+
+#include "nsICategoryManager.h"
+#include "nsIComponentManager.h"
+#include "nsIComponentRegistrar.h"
+#include "nsServiceManagerUtils.h"
+#include "nsXPCOMCID.h"
+#include "nsStringAPI.h"
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(GenericModule, nsIModule)
+
+NS_IMETHODIMP
+GenericModule::GetClassObject(nsIComponentManager* aCompMgr,
+ const nsCID& aCID,
+ const nsIID& aIID,
+ void** aResult)
+{
+ for (const Module::CIDEntry* e = mData->mCIDs; e->cid; ++e) {
+ if (e->cid->Equals(aCID)) {
+ nsCOMPtr<nsIFactory> f;
+ if (e->getFactoryProc) {
+ f = e->getFactoryProc(*mData, *e);
+ } else {
+ NS_ASSERTION(e->constructorProc, "No constructor proc?");
+ f = new GenericFactory(e->constructorProc);
+ }
+ if (!f) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return f->QueryInterface(aIID, aResult);
+ }
+ }
+ NS_ERROR("Asking a module for a CID it doesn't implement.");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+GenericModule::RegisterSelf(nsIComponentManager* aCompMgr,
+ nsIFile* aLocation,
+ const char* aLoaderStr,
+ const char* aType)
+{
+ nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(aCompMgr);
+ for (const Module::CIDEntry* e = mData->mCIDs; e->cid; ++e) {
+ registrar->RegisterFactoryLocation(*e->cid, "", nullptr, aLocation,
+ aLoaderStr, aType);
+ }
+
+ for (const Module::ContractIDEntry* e = mData->mContractIDs;
+ e && e->contractid;
+ ++e) {
+ registrar->RegisterFactoryLocation(*e->cid, "", e->contractid, aLocation,
+ aLoaderStr, aType);
+ }
+
+ nsCOMPtr<nsICategoryManager> catman;
+ for (const Module::CategoryEntry* e = mData->mCategoryEntries;
+ e && e->category;
+ ++e) {
+ if (!catman) {
+ catman = do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+ }
+
+ nsAutoCString prevValue;
+ catman->AddCategoryEntry(e->category, e->entry, e->value, true, true,
+ getter_Copies(prevValue));
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+GenericModule::UnregisterSelf(nsIComponentManager* aCompMgr,
+ nsIFile* aFile,
+ const char* aLoaderStr)
+{
+ NS_ERROR("Nobody should ever call UnregisterSelf!");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+GenericModule::CanUnload(nsIComponentManager* aCompMgr, bool* aResult)
+{
+ NS_ERROR("Nobody should ever call CanUnload!");
+ *aResult = false;
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/xpcom/glue/IntentionalCrash.h b/xpcom/glue/IntentionalCrash.h
new file mode 100644
index 000000000..d01006dff
--- /dev/null
+++ b/xpcom/glue/IntentionalCrash.h
@@ -0,0 +1,58 @@
+/* -*- 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 <string>
+#include <sstream>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifdef XP_WIN
+#include <process.h>
+#define getpid _getpid
+#else
+#include <unistd.h>
+#endif
+
+#ifndef mozilla_IntentionalCrash_h
+#define mozilla_IntentionalCrash_h
+
+namespace mozilla {
+
+inline void
+NoteIntentionalCrash(const char* aProcessType)
+{
+ char* f = getenv("XPCOM_MEM_BLOAT_LOG");
+ if (!f) {
+ return;
+ }
+
+ fprintf(stderr, "XPCOM_MEM_BLOAT_LOG: %s\n", f);
+
+ std::string bloatLog(f);
+
+ bool hasExt = false;
+ if (bloatLog.size() >= 4 &&
+ bloatLog.compare(bloatLog.size() - 4, 4, ".log", 4) == 0) {
+ hasExt = true;
+ bloatLog.erase(bloatLog.size() - 4, 4);
+ }
+
+ std::ostringstream bloatName;
+ bloatName << bloatLog << "_" << aProcessType << "_pid" << getpid();
+ if (hasExt) {
+ bloatName << ".log";
+ }
+
+ fprintf(stderr, "Writing to log: %s\n", bloatName.str().c_str());
+
+ FILE* processfd = fopen(bloatName.str().c_str(), "a");
+ fprintf(processfd, "==> process %d will purposefully crash\n", getpid());
+ fclose(processfd);
+}
+
+} // namespace mozilla
+
+#endif // mozilla_IntentionalCrash_h
diff --git a/xpcom/glue/MainThreadUtils.h b/xpcom/glue/MainThreadUtils.h
new file mode 100644
index 000000000..8d76f84ec
--- /dev/null
+++ b/xpcom/glue/MainThreadUtils.h
@@ -0,0 +1,42 @@
+/* -*- 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 MainThreadUtils_h_
+#define MainThreadUtils_h_
+
+#include "nscore.h"
+
+class nsIThread;
+
+/**
+ * Get a reference to the main thread.
+ *
+ * @param aResult
+ * The resulting nsIThread object.
+ */
+extern nsresult NS_GetMainThread(nsIThread** aResult);
+
+#ifdef MOZILLA_INTERNAL_API
+// Fast access to the current thread. Do not release the returned pointer! If
+// you want to use this pointer from some other thread, then you will need to
+// AddRef it. Otherwise, you should only consider this pointer valid from code
+// running on the current thread.
+extern nsIThread* NS_GetCurrentThread();
+#endif
+
+#ifdef MOZILLA_INTERNAL_API
+bool NS_IsMainThread();
+#else
+/**
+ * Test to see if the current thread is the main thread.
+ *
+ * @returns true if the current thread is the main thread, and false
+ * otherwise.
+ */
+extern bool NS_IsMainThread();
+#endif
+
+#endif // MainThreadUtils_h_
diff --git a/xpcom/glue/Monitor.h b/xpcom/glue/Monitor.h
new file mode 100644
index 000000000..60750acb3
--- /dev/null
+++ b/xpcom/glue/Monitor.h
@@ -0,0 +1,135 @@
+/* -*- 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_Monitor_h
+#define mozilla_Monitor_h
+
+#include "mozilla/CondVar.h"
+#include "mozilla/Mutex.h"
+
+namespace mozilla {
+
+/**
+ * Monitor provides a *non*-reentrant monitor: *not* a Java-style
+ * monitor. If your code needs support for reentrancy, use
+ * ReentrantMonitor instead. (Rarely should reentrancy be needed.)
+ *
+ * Instead of directly calling Monitor methods, it's safer and simpler
+ * to instead use the RAII wrappers MonitorAutoLock and
+ * MonitorAutoUnlock.
+ */
+class Monitor
+{
+public:
+ explicit Monitor(const char* aName)
+ : mMutex(aName)
+ , mCondVar(mMutex, "[Monitor.mCondVar]")
+ {
+ }
+
+ ~Monitor() {}
+
+ void Lock() { mMutex.Lock(); }
+ void Unlock() { mMutex.Unlock(); }
+
+ nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT)
+ {
+ return mCondVar.Wait(aInterval);
+ }
+
+ nsresult Notify() { return mCondVar.Notify(); }
+ nsresult NotifyAll() { return mCondVar.NotifyAll(); }
+
+ void AssertCurrentThreadOwns() const
+ {
+ mMutex.AssertCurrentThreadOwns();
+ }
+
+ void AssertNotCurrentThreadOwns() const
+ {
+ mMutex.AssertNotCurrentThreadOwns();
+ }
+
+private:
+ Monitor();
+ Monitor(const Monitor&);
+ Monitor& operator=(const Monitor&);
+
+ Mutex mMutex;
+ CondVar mCondVar;
+};
+
+/**
+ * Lock the monitor for the lexical scope instances of this class are
+ * bound to (except for MonitorAutoUnlock in nested scopes).
+ *
+ * The monitor must be unlocked when instances of this class are
+ * created.
+ */
+class MOZ_STACK_CLASS MonitorAutoLock
+{
+public:
+ explicit MonitorAutoLock(Monitor& aMonitor)
+ : mMonitor(&aMonitor)
+ {
+ mMonitor->Lock();
+ }
+
+ ~MonitorAutoLock()
+ {
+ mMonitor->Unlock();
+ }
+
+ nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT)
+ {
+ return mMonitor->Wait(aInterval);
+ }
+
+ nsresult Notify() { return mMonitor->Notify(); }
+ nsresult NotifyAll() { return mMonitor->NotifyAll(); }
+
+private:
+ MonitorAutoLock();
+ MonitorAutoLock(const MonitorAutoLock&);
+ MonitorAutoLock& operator=(const MonitorAutoLock&);
+ static void* operator new(size_t) CPP_THROW_NEW;
+
+ Monitor* mMonitor;
+};
+
+/**
+ * Unlock the monitor for the lexical scope instances of this class
+ * are bound to (except for MonitorAutoLock in nested scopes).
+ *
+ * The monitor must be locked by the current thread when instances of
+ * this class are created.
+ */
+class MOZ_STACK_CLASS MonitorAutoUnlock
+{
+public:
+ explicit MonitorAutoUnlock(Monitor& aMonitor)
+ : mMonitor(&aMonitor)
+ {
+ mMonitor->Unlock();
+ }
+
+ ~MonitorAutoUnlock()
+ {
+ mMonitor->Lock();
+ }
+
+private:
+ MonitorAutoUnlock();
+ MonitorAutoUnlock(const MonitorAutoUnlock&);
+ MonitorAutoUnlock& operator=(const MonitorAutoUnlock&);
+ static void* operator new(size_t) CPP_THROW_NEW;
+
+ Monitor* mMonitor;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_Monitor_h
diff --git a/xpcom/glue/Mutex.h b/xpcom/glue/Mutex.h
new file mode 100644
index 000000000..16ad44f4c
--- /dev/null
+++ b/xpcom/glue/Mutex.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 mozilla_Mutex_h
+#define mozilla_Mutex_h
+
+#include "prlock.h"
+
+#include "mozilla/BlockingResourceBase.h"
+#include "mozilla/GuardObjects.h"
+
+//
+// Provides:
+//
+// - Mutex, a non-recursive mutex
+// - MutexAutoLock, an RAII class for ensuring that Mutexes are properly
+// locked and unlocked
+// - MutexAutoUnlock, complementary sibling to MutexAutoLock
+//
+// - OffTheBooksMutex, a non-recursive mutex that doesn't do leak checking
+// - OffTheBooksMutexAuto{Lock,Unlock} - Like MutexAuto{Lock,Unlock}, but for
+// an OffTheBooksMutex.
+//
+// Using MutexAutoLock/MutexAutoUnlock etc. is MUCH preferred to making bare
+// calls to Lock and Unlock.
+//
+namespace mozilla {
+
+/**
+ * OffTheBooksMutex is identical to Mutex, except that OffTheBooksMutex doesn't
+ * include leak checking. Sometimes you want to intentionally "leak" a mutex
+ * until shutdown; in these cases, OffTheBooksMutex is for you.
+ */
+class OffTheBooksMutex : BlockingResourceBase
+{
+public:
+ /**
+ * @param aName A name which can reference this lock
+ * @returns If failure, nullptr
+ * If success, a valid Mutex* which must be destroyed
+ * by Mutex::DestroyMutex()
+ **/
+ explicit OffTheBooksMutex(const char* aName)
+ : BlockingResourceBase(aName, eMutex)
+ {
+ mLock = PR_NewLock();
+ if (!mLock) {
+ NS_RUNTIMEABORT("Can't allocate mozilla::Mutex");
+ }
+ }
+
+ ~OffTheBooksMutex()
+ {
+ NS_ASSERTION(mLock,
+ "improperly constructed Lock or double free");
+ // NSPR does consistency checks for us
+ PR_DestroyLock(mLock);
+ mLock = 0;
+ }
+
+#ifndef DEBUG
+ /**
+ * Lock
+ * @see prlock.h
+ **/
+ void Lock() { PR_Lock(mLock); }
+
+ /**
+ * Unlock
+ * @see prlock.h
+ **/
+ void Unlock() { PR_Unlock(mLock); }
+
+ /**
+ * AssertCurrentThreadOwns
+ * @see prlock.h
+ **/
+ void AssertCurrentThreadOwns() const {}
+
+ /**
+ * AssertNotCurrentThreadOwns
+ * @see prlock.h
+ **/
+ void AssertNotCurrentThreadOwns() const {}
+
+#else
+ void Lock();
+ void Unlock();
+
+ void AssertCurrentThreadOwns() const
+ {
+ PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(mLock);
+ }
+
+ void AssertNotCurrentThreadOwns() const
+ {
+ // FIXME bug 476536
+ }
+
+#endif // ifndef DEBUG
+
+private:
+ OffTheBooksMutex();
+ OffTheBooksMutex(const OffTheBooksMutex&);
+ OffTheBooksMutex& operator=(const OffTheBooksMutex&);
+
+ PRLock* mLock;
+
+ friend class CondVar;
+
+ // MozPromise needs to access mLock for debugging purpose.
+ template<typename, typename, bool>
+ friend class MozPromise;
+};
+
+/**
+ * Mutex
+ * When possible, use MutexAutoLock/MutexAutoUnlock to lock/unlock this
+ * mutex within a scope, instead of calling Lock/Unlock directly.
+ */
+class Mutex : public OffTheBooksMutex
+{
+public:
+ explicit Mutex(const char* aName)
+ : OffTheBooksMutex(aName)
+ {
+ MOZ_COUNT_CTOR(Mutex);
+ }
+
+ ~Mutex()
+ {
+ MOZ_COUNT_DTOR(Mutex);
+ }
+
+private:
+ Mutex();
+ Mutex(const Mutex&);
+ Mutex& operator=(const Mutex&);
+};
+
+/**
+ * MutexAutoLock
+ * Acquires the Mutex when it enters scope, and releases it when it leaves
+ * scope.
+ *
+ * MUCH PREFERRED to bare calls to Mutex.Lock and Unlock.
+ */
+template<typename T>
+class MOZ_RAII BaseAutoLock
+{
+public:
+ /**
+ * Constructor
+ * The constructor aquires the given lock. The destructor
+ * releases the lock.
+ *
+ * @param aLock A valid mozilla::Mutex* returned by
+ * mozilla::Mutex::NewMutex.
+ **/
+ explicit BaseAutoLock(T& aLock MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : mLock(&aLock)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ NS_ASSERTION(mLock, "null mutex");
+ mLock->Lock();
+ }
+
+ ~BaseAutoLock(void)
+ {
+ mLock->Unlock();
+ }
+
+private:
+ BaseAutoLock();
+ BaseAutoLock(BaseAutoLock&);
+ BaseAutoLock& operator=(BaseAutoLock&);
+ static void* operator new(size_t) CPP_THROW_NEW;
+
+ T* mLock;
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+typedef BaseAutoLock<Mutex> MutexAutoLock;
+typedef BaseAutoLock<OffTheBooksMutex> OffTheBooksMutexAutoLock;
+
+/**
+ * MutexAutoUnlock
+ * Releases the Mutex when it enters scope, and re-acquires it when it leaves
+ * scope.
+ *
+ * MUCH PREFERRED to bare calls to Mutex.Unlock and Lock.
+ */
+template<typename T>
+class MOZ_RAII BaseAutoUnlock
+{
+public:
+ explicit BaseAutoUnlock(T& aLock MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : mLock(&aLock)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ NS_ASSERTION(mLock, "null lock");
+ mLock->Unlock();
+ }
+
+ ~BaseAutoUnlock()
+ {
+ mLock->Lock();
+ }
+
+private:
+ BaseAutoUnlock();
+ BaseAutoUnlock(BaseAutoUnlock&);
+ BaseAutoUnlock& operator=(BaseAutoUnlock&);
+ static void* operator new(size_t) CPP_THROW_NEW;
+
+ T* mLock;
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+typedef BaseAutoUnlock<Mutex> MutexAutoUnlock;
+typedef BaseAutoUnlock<OffTheBooksMutex> OffTheBooksMutexAutoUnlock;
+
+} // namespace mozilla
+
+
+#endif // ifndef mozilla_Mutex_h
diff --git a/xpcom/glue/Observer.h b/xpcom/glue/Observer.h
new file mode 100644
index 000000000..958e5e4a9
--- /dev/null
+++ b/xpcom/glue/Observer.h
@@ -0,0 +1,83 @@
+/* -*- 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_Observer_h
+#define mozilla_Observer_h
+
+#include "nsTArray.h"
+
+namespace mozilla {
+
+/**
+ * Observer<T> provides a way for a class to observe something.
+ * When an event has to be broadcasted to all Observer<T>, Notify() method
+ * is called.
+ * T represents the type of the object passed in argument to Notify().
+ *
+ * @see ObserverList.
+ */
+template<class T>
+class Observer
+{
+public:
+ virtual ~Observer() {}
+ virtual void Notify(const T& aParam) = 0;
+};
+
+/**
+ * ObserverList<T> tracks Observer<T> and can notify them when Broadcast() is
+ * called.
+ * T represents the type of the object passed in argument to Broadcast() and
+ * sent to Observer<T> objects through Notify().
+ *
+ * @see Observer.
+ */
+template<class T>
+class ObserverList
+{
+public:
+ /**
+ * Note: When calling AddObserver, it's up to the caller to make sure the
+ * object isn't going to be release as long as RemoveObserver hasn't been
+ * called.
+ *
+ * @see RemoveObserver()
+ */
+ void AddObserver(Observer<T>* aObserver)
+ {
+ mObservers.AppendElement(aObserver);
+ }
+
+ /**
+ * Remove the observer from the observer list.
+ * @return Whether the observer has been found in the list.
+ */
+ bool RemoveObserver(Observer<T>* aObserver)
+ {
+ return mObservers.RemoveElement(aObserver);
+ }
+
+ uint32_t Length()
+ {
+ return mObservers.Length();
+ }
+
+ void Broadcast(const T& aParam)
+ {
+ nsTArray<Observer<T>*> observersCopy(mObservers);
+ uint32_t size = observersCopy.Length();
+ for (uint32_t i = 0; i < size; ++i) {
+ observersCopy[i]->Notify(aParam);
+ }
+ }
+
+protected:
+ nsTArray<Observer<T>*> mObservers;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_Observer_h
diff --git a/xpcom/glue/PLDHashTable.cpp b/xpcom/glue/PLDHashTable.cpp
new file mode 100644
index 000000000..6152e9000
--- /dev/null
+++ b/xpcom/glue/PLDHashTable.cpp
@@ -0,0 +1,801 @@
+/* -*- 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 <new>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "PLDHashTable.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/OperatorNewExtensions.h"
+#include "nsAlgorithm.h"
+#include "mozilla/Likely.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/ChaosMode.h"
+
+using namespace mozilla;
+
+#ifdef DEBUG
+
+class AutoReadOp
+{
+ Checker& mChk;
+public:
+ explicit AutoReadOp(Checker& aChk) : mChk(aChk) { mChk.StartReadOp(); }
+ ~AutoReadOp() { mChk.EndReadOp(); }
+};
+
+class AutoWriteOp
+{
+ Checker& mChk;
+public:
+ explicit AutoWriteOp(Checker& aChk) : mChk(aChk) { mChk.StartWriteOp(); }
+ ~AutoWriteOp() { mChk.EndWriteOp(); }
+};
+
+class AutoIteratorRemovalOp
+{
+ Checker& mChk;
+public:
+ explicit AutoIteratorRemovalOp(Checker& aChk)
+ : mChk(aChk)
+ {
+ mChk.StartIteratorRemovalOp();
+ }
+ ~AutoIteratorRemovalOp() { mChk.EndIteratorRemovalOp(); }
+};
+
+class AutoDestructorOp
+{
+ Checker& mChk;
+public:
+ explicit AutoDestructorOp(Checker& aChk)
+ : mChk(aChk)
+ {
+ mChk.StartDestructorOp();
+ }
+ ~AutoDestructorOp() { mChk.EndDestructorOp(); }
+};
+
+#endif
+
+/* static */ PLDHashNumber
+PLDHashTable::HashStringKey(const void* aKey)
+{
+ return HashString(static_cast<const char*>(aKey));
+}
+
+/* static */ PLDHashNumber
+PLDHashTable::HashVoidPtrKeyStub(const void* aKey)
+{
+ return (PLDHashNumber)(ptrdiff_t)aKey >> 2;
+}
+
+/* static */ bool
+PLDHashTable::MatchEntryStub(const PLDHashEntryHdr* aEntry, const void* aKey)
+{
+ const PLDHashEntryStub* stub = (const PLDHashEntryStub*)aEntry;
+
+ return stub->key == aKey;
+}
+
+/* static */ bool
+PLDHashTable::MatchStringKey(const PLDHashEntryHdr* aEntry, const void* aKey)
+{
+ const PLDHashEntryStub* stub = (const PLDHashEntryStub*)aEntry;
+
+ // XXX tolerate null keys on account of sloppy Mozilla callers.
+ return stub->key == aKey ||
+ (stub->key && aKey &&
+ strcmp((const char*)stub->key, (const char*)aKey) == 0);
+}
+
+/* static */ void
+PLDHashTable::MoveEntryStub(PLDHashTable* aTable,
+ const PLDHashEntryHdr* aFrom,
+ PLDHashEntryHdr* aTo)
+{
+ memcpy(aTo, aFrom, aTable->mEntrySize);
+}
+
+/* static */ void
+PLDHashTable::ClearEntryStub(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
+{
+ memset(aEntry, 0, aTable->mEntrySize);
+}
+
+static const PLDHashTableOps gStubOps = {
+ PLDHashTable::HashVoidPtrKeyStub,
+ PLDHashTable::MatchEntryStub,
+ PLDHashTable::MoveEntryStub,
+ PLDHashTable::ClearEntryStub,
+ nullptr
+};
+
+/* static */ const PLDHashTableOps*
+PLDHashTable::StubOps()
+{
+ return &gStubOps;
+}
+
+static bool
+SizeOfEntryStore(uint32_t aCapacity, uint32_t aEntrySize, uint32_t* aNbytes)
+{
+ uint64_t nbytes64 = uint64_t(aCapacity) * uint64_t(aEntrySize);
+ *aNbytes = aCapacity * aEntrySize;
+ return uint64_t(*aNbytes) == nbytes64; // returns false on overflow
+}
+
+// Compute max and min load numbers (entry counts). We have a secondary max
+// that allows us to overload a table reasonably if it cannot be grown further
+// (i.e. if ChangeTable() fails). The table slows down drastically if the
+// secondary max is too close to 1, but 0.96875 gives only a slight slowdown
+// while allowing 1.3x more elements.
+static inline uint32_t
+MaxLoad(uint32_t aCapacity)
+{
+ return aCapacity - (aCapacity >> 2); // == aCapacity * 0.75
+}
+static inline uint32_t
+MaxLoadOnGrowthFailure(uint32_t aCapacity)
+{
+ return aCapacity - (aCapacity >> 5); // == aCapacity * 0.96875
+}
+static inline uint32_t
+MinLoad(uint32_t aCapacity)
+{
+ return aCapacity >> 2; // == aCapacity * 0.25
+}
+
+// Compute the minimum capacity (and the Log2 of that capacity) for a table
+// containing |aLength| elements while respecting the following contraints:
+// - table must be at most 75% full;
+// - capacity must be a power of two;
+// - capacity cannot be too small.
+static inline void
+BestCapacity(uint32_t aLength, uint32_t* aCapacityOut,
+ uint32_t* aLog2CapacityOut)
+{
+ // Compute the smallest capacity allowing |aLength| elements to be inserted
+ // without rehashing.
+ uint32_t capacity = (aLength * 4 + (3 - 1)) / 3; // == ceil(aLength * 4 / 3)
+ if (capacity < PLDHashTable::kMinCapacity) {
+ capacity = PLDHashTable::kMinCapacity;
+ }
+
+ // Round up capacity to next power-of-two.
+ uint32_t log2 = CeilingLog2(capacity);
+ capacity = 1u << log2;
+ MOZ_ASSERT(capacity <= PLDHashTable::kMaxCapacity);
+
+ *aCapacityOut = capacity;
+ *aLog2CapacityOut = log2;
+}
+
+/* static */ MOZ_ALWAYS_INLINE uint32_t
+PLDHashTable::HashShift(uint32_t aEntrySize, uint32_t aLength)
+{
+ if (aLength > kMaxInitialLength) {
+ MOZ_CRASH("Initial length is too large");
+ }
+
+ uint32_t capacity, log2;
+ BestCapacity(aLength, &capacity, &log2);
+
+ uint32_t nbytes;
+ if (!SizeOfEntryStore(capacity, aEntrySize, &nbytes)) {
+ MOZ_CRASH("Initial entry store size is too large");
+ }
+
+ // Compute the hashShift value.
+ return kHashBits - log2;
+}
+
+PLDHashTable::PLDHashTable(const PLDHashTableOps* aOps, uint32_t aEntrySize,
+ uint32_t aLength)
+ : mOps(aOps)
+ , mHashShift(HashShift(aEntrySize, aLength))
+ , mEntrySize(aEntrySize)
+ , mEntryCount(0)
+ , mRemovedCount(0)
+ , mEntryStore()
+#ifdef DEBUG
+ , mChecker()
+#endif
+{
+}
+
+PLDHashTable&
+PLDHashTable::operator=(PLDHashTable&& aOther)
+{
+ if (this == &aOther) {
+ return *this;
+ }
+
+ // Destruct |this|.
+ this->~PLDHashTable();
+
+ // |mOps| and |mEntrySize| are const so we can't assign them. Instead, we
+ // require that they are equal. The justification for this is that they're
+ // conceptually part of the type -- indeed, if PLDHashTable was a templated
+ // type like nsTHashtable, they *would* be part of the type -- so it only
+ // makes sense to assign in cases where they match.
+ MOZ_RELEASE_ASSERT(mOps == aOther.mOps);
+ MOZ_RELEASE_ASSERT(mEntrySize == aOther.mEntrySize);
+
+ // Move non-const pieces over.
+ mHashShift = Move(aOther.mHashShift);
+ mEntryCount = Move(aOther.mEntryCount);
+ mRemovedCount = Move(aOther.mRemovedCount);
+ mEntryStore = Move(aOther.mEntryStore);
+#ifdef DEBUG
+ mChecker = Move(aOther.mChecker);
+#endif
+
+ // Clear up |aOther| so its destruction will be a no-op.
+ {
+#ifdef DEBUG
+ AutoDestructorOp op(mChecker);
+#endif
+ aOther.mEntryStore.Set(nullptr);
+ }
+
+ return *this;
+}
+
+PLDHashNumber
+PLDHashTable::Hash1(PLDHashNumber aHash0)
+{
+ return aHash0 >> mHashShift;
+}
+
+// Double hashing needs the second hash code to be relatively prime to table
+// size, so we simply make hash2 odd.
+void
+PLDHashTable::Hash2(PLDHashNumber aHash,
+ uint32_t& aHash2Out, uint32_t& aSizeMaskOut)
+{
+ uint32_t sizeLog2 = kHashBits - mHashShift;
+ aHash2Out = ((aHash << sizeLog2) >> mHashShift) | 1;
+ aSizeMaskOut = (PLDHashNumber(1) << sizeLog2) - 1;
+}
+
+// Reserve mKeyHash 0 for free entries and 1 for removed-entry sentinels. Note
+// that a removed-entry sentinel need be stored only if the removed entry had
+// a colliding entry added after it. Therefore we can use 1 as the collision
+// flag in addition to the removed-entry sentinel value. Multiplicative hash
+// uses the high order bits of mKeyHash, so this least-significant reservation
+// should not hurt the hash function's effectiveness much.
+
+// Match an entry's mKeyHash against an unstored one computed from a key.
+/* static */ bool
+PLDHashTable::MatchEntryKeyhash(PLDHashEntryHdr* aEntry, PLDHashNumber aKeyHash)
+{
+ return (aEntry->mKeyHash & ~kCollisionFlag) == aKeyHash;
+}
+
+// Compute the address of the indexed entry in table.
+PLDHashEntryHdr*
+PLDHashTable::AddressEntry(uint32_t aIndex)
+{
+ return reinterpret_cast<PLDHashEntryHdr*>(
+ mEntryStore.Get() + aIndex * mEntrySize);
+}
+
+PLDHashTable::~PLDHashTable()
+{
+#ifdef DEBUG
+ AutoDestructorOp op(mChecker);
+#endif
+
+ if (!mEntryStore.Get()) {
+ return;
+ }
+
+ // Clear any remaining live entries.
+ char* entryAddr = mEntryStore.Get();
+ char* entryLimit = entryAddr + Capacity() * mEntrySize;
+ while (entryAddr < entryLimit) {
+ PLDHashEntryHdr* entry = (PLDHashEntryHdr*)entryAddr;
+ if (EntryIsLive(entry)) {
+ mOps->clearEntry(this, entry);
+ }
+ entryAddr += mEntrySize;
+ }
+
+ // Entry storage is freed last, by ~EntryStore().
+}
+
+void
+PLDHashTable::ClearAndPrepareForLength(uint32_t aLength)
+{
+ // Get these values before the destructor clobbers them.
+ const PLDHashTableOps* ops = mOps;
+ uint32_t entrySize = mEntrySize;
+
+ this->~PLDHashTable();
+ new (KnownNotNull, this) PLDHashTable(ops, entrySize, aLength);
+}
+
+void
+PLDHashTable::Clear()
+{
+ ClearAndPrepareForLength(kDefaultInitialLength);
+}
+
+// If |Reason| is |ForAdd|, the return value is always non-null and it may be
+// a previously-removed entry. If |Reason| is |ForSearchOrRemove|, the return
+// value is null on a miss, and will never be a previously-removed entry on a
+// hit. This distinction is a bit grotty but this function is hot enough that
+// these differences are worthwhile.
+template <PLDHashTable::SearchReason Reason>
+PLDHashEntryHdr* NS_FASTCALL
+PLDHashTable::SearchTable(const void* aKey, PLDHashNumber aKeyHash)
+{
+ MOZ_ASSERT(mEntryStore.Get());
+ NS_ASSERTION(!(aKeyHash & kCollisionFlag),
+ "!(aKeyHash & kCollisionFlag)");
+
+ // Compute the primary hash address.
+ PLDHashNumber hash1 = Hash1(aKeyHash);
+ PLDHashEntryHdr* entry = AddressEntry(hash1);
+
+ // Miss: return space for a new entry.
+ if (EntryIsFree(entry)) {
+ return (Reason == ForAdd) ? entry : nullptr;
+ }
+
+ // Hit: return entry.
+ PLDHashMatchEntry matchEntry = mOps->matchEntry;
+ if (MatchEntryKeyhash(entry, aKeyHash) &&
+ matchEntry(entry, aKey)) {
+ return entry;
+ }
+
+ // Collision: double hash.
+ PLDHashNumber hash2;
+ uint32_t sizeMask;
+ Hash2(aKeyHash, hash2, sizeMask);
+
+ // Save the first removed entry pointer so Add() can recycle it. (Only used
+ // if Reason==ForAdd.)
+ PLDHashEntryHdr* firstRemoved = nullptr;
+
+ for (;;) {
+ if (Reason == ForAdd) {
+ if (MOZ_UNLIKELY(EntryIsRemoved(entry))) {
+ if (!firstRemoved) {
+ firstRemoved = entry;
+ }
+ } else {
+ entry->mKeyHash |= kCollisionFlag;
+ }
+ }
+
+ hash1 -= hash2;
+ hash1 &= sizeMask;
+
+ entry = AddressEntry(hash1);
+ if (EntryIsFree(entry)) {
+ return (Reason == ForAdd) ? (firstRemoved ? firstRemoved : entry)
+ : nullptr;
+ }
+
+ if (MatchEntryKeyhash(entry, aKeyHash) &&
+ matchEntry(entry, aKey)) {
+ return entry;
+ }
+ }
+
+ // NOTREACHED
+ return nullptr;
+}
+
+// This is a copy of SearchTable(), used by ChangeTable(), hardcoded to
+// 1. assume |Reason| is |ForAdd|,
+// 2. assume that |aKey| will never match an existing entry, and
+// 3. assume that no entries have been removed from the current table
+// structure.
+// Avoiding the need for |aKey| means we can avoid needing a way to map entries
+// to keys, which means callers can use complex key types more easily.
+MOZ_ALWAYS_INLINE PLDHashEntryHdr*
+PLDHashTable::FindFreeEntry(PLDHashNumber aKeyHash)
+{
+ MOZ_ASSERT(mEntryStore.Get());
+ NS_ASSERTION(!(aKeyHash & kCollisionFlag),
+ "!(aKeyHash & kCollisionFlag)");
+
+ // Compute the primary hash address.
+ PLDHashNumber hash1 = Hash1(aKeyHash);
+ PLDHashEntryHdr* entry = AddressEntry(hash1);
+
+ // Miss: return space for a new entry.
+ if (EntryIsFree(entry)) {
+ return entry;
+ }
+
+ // Collision: double hash.
+ PLDHashNumber hash2;
+ uint32_t sizeMask;
+ Hash2(aKeyHash, hash2, sizeMask);
+
+ for (;;) {
+ NS_ASSERTION(!EntryIsRemoved(entry),
+ "!EntryIsRemoved(entry)");
+ entry->mKeyHash |= kCollisionFlag;
+
+ hash1 -= hash2;
+ hash1 &= sizeMask;
+
+ entry = AddressEntry(hash1);
+ if (EntryIsFree(entry)) {
+ return entry;
+ }
+ }
+
+ // NOTREACHED
+}
+
+bool
+PLDHashTable::ChangeTable(int32_t aDeltaLog2)
+{
+ MOZ_ASSERT(mEntryStore.Get());
+
+ // Look, but don't touch, until we succeed in getting new entry store.
+ int32_t oldLog2 = kHashBits - mHashShift;
+ int32_t newLog2 = oldLog2 + aDeltaLog2;
+ uint32_t newCapacity = 1u << newLog2;
+ if (newCapacity > kMaxCapacity) {
+ return false;
+ }
+
+ uint32_t nbytes;
+ if (!SizeOfEntryStore(newCapacity, mEntrySize, &nbytes)) {
+ return false; // overflowed
+ }
+
+ char* newEntryStore = (char*)malloc(nbytes);
+ if (!newEntryStore) {
+ return false;
+ }
+
+ // We can't fail from here on, so update table parameters.
+ mHashShift = kHashBits - newLog2;
+ mRemovedCount = 0;
+
+ // Assign the new entry store to table.
+ memset(newEntryStore, 0, nbytes);
+ char* oldEntryStore;
+ char* oldEntryAddr;
+ oldEntryAddr = oldEntryStore = mEntryStore.Get();
+ mEntryStore.Set(newEntryStore);
+ PLDHashMoveEntry moveEntry = mOps->moveEntry;
+
+ // Copy only live entries, leaving removed ones behind.
+ uint32_t oldCapacity = 1u << oldLog2;
+ for (uint32_t i = 0; i < oldCapacity; ++i) {
+ PLDHashEntryHdr* oldEntry = (PLDHashEntryHdr*)oldEntryAddr;
+ if (EntryIsLive(oldEntry)) {
+ oldEntry->mKeyHash &= ~kCollisionFlag;
+ PLDHashEntryHdr* newEntry = FindFreeEntry(oldEntry->mKeyHash);
+ NS_ASSERTION(EntryIsFree(newEntry), "EntryIsFree(newEntry)");
+ moveEntry(this, oldEntry, newEntry);
+ newEntry->mKeyHash = oldEntry->mKeyHash;
+ }
+ oldEntryAddr += mEntrySize;
+ }
+
+ free(oldEntryStore);
+ return true;
+}
+
+MOZ_ALWAYS_INLINE PLDHashNumber
+PLDHashTable::ComputeKeyHash(const void* aKey)
+{
+ MOZ_ASSERT(mEntryStore.Get());
+
+ PLDHashNumber keyHash = mOps->hashKey(aKey);
+ keyHash *= kGoldenRatio;
+
+ // Avoid 0 and 1 hash codes, they indicate free and removed entries.
+ if (keyHash < 2) {
+ keyHash -= 2;
+ }
+ keyHash &= ~kCollisionFlag;
+
+ return keyHash;
+}
+
+PLDHashEntryHdr*
+PLDHashTable::Search(const void* aKey)
+{
+#ifdef DEBUG
+ AutoReadOp op(mChecker);
+#endif
+
+ PLDHashEntryHdr* entry = mEntryStore.Get()
+ ? SearchTable<ForSearchOrRemove>(aKey,
+ ComputeKeyHash(aKey))
+ : nullptr;
+ return entry;
+}
+
+PLDHashEntryHdr*
+PLDHashTable::Add(const void* aKey, const mozilla::fallible_t&)
+{
+#ifdef DEBUG
+ AutoWriteOp op(mChecker);
+#endif
+
+ // Allocate the entry storage if it hasn't already been allocated.
+ if (!mEntryStore.Get()) {
+ uint32_t nbytes;
+ // We already checked this in the constructor, so it must still be true.
+ MOZ_RELEASE_ASSERT(SizeOfEntryStore(CapacityFromHashShift(), mEntrySize,
+ &nbytes));
+ mEntryStore.Set((char*)malloc(nbytes));
+ if (!mEntryStore.Get()) {
+ return nullptr;
+ }
+ memset(mEntryStore.Get(), 0, nbytes);
+ }
+
+ // If alpha is >= .75, grow or compress the table. If aKey is already in the
+ // table, we may grow once more than necessary, but only if we are on the
+ // edge of being overloaded.
+ uint32_t capacity = Capacity();
+ if (mEntryCount + mRemovedCount >= MaxLoad(capacity)) {
+ // Compress if a quarter or more of all entries are removed.
+ int deltaLog2;
+ if (mRemovedCount >= capacity >> 2) {
+ deltaLog2 = 0;
+ } else {
+ deltaLog2 = 1;
+ }
+
+ // Grow or compress the table. If ChangeTable() fails, allow overloading up
+ // to the secondary max. Once we hit the secondary max, return null.
+ if (!ChangeTable(deltaLog2) &&
+ mEntryCount + mRemovedCount >= MaxLoadOnGrowthFailure(capacity)) {
+ return nullptr;
+ }
+ }
+
+ // Look for entry after possibly growing, so we don't have to add it,
+ // then skip it while growing the table and re-add it after.
+ PLDHashNumber keyHash = ComputeKeyHash(aKey);
+ PLDHashEntryHdr* entry = SearchTable<ForAdd>(aKey, keyHash);
+ if (!EntryIsLive(entry)) {
+ // Initialize the entry, indicating that it's no longer free.
+ if (EntryIsRemoved(entry)) {
+ mRemovedCount--;
+ keyHash |= kCollisionFlag;
+ }
+ if (mOps->initEntry) {
+ mOps->initEntry(entry, aKey);
+ }
+ entry->mKeyHash = keyHash;
+ mEntryCount++;
+ }
+
+ return entry;
+}
+
+PLDHashEntryHdr*
+PLDHashTable::Add(const void* aKey)
+{
+ PLDHashEntryHdr* entry = Add(aKey, fallible);
+ if (!entry) {
+ if (!mEntryStore.Get()) {
+ // We OOM'd while allocating the initial entry storage.
+ uint32_t nbytes;
+ (void) SizeOfEntryStore(CapacityFromHashShift(), mEntrySize, &nbytes);
+ NS_ABORT_OOM(nbytes);
+ } else {
+ // We failed to resize the existing entry storage, either due to OOM or
+ // because we exceeded the maximum table capacity or size; report it as
+ // an OOM. The multiplication by 2 gets us the size we tried to allocate,
+ // which is double the current size.
+ NS_ABORT_OOM(2 * EntrySize() * EntryCount());
+ }
+ }
+ return entry;
+}
+
+void
+PLDHashTable::Remove(const void* aKey)
+{
+#ifdef DEBUG
+ AutoWriteOp op(mChecker);
+#endif
+
+ PLDHashEntryHdr* entry = mEntryStore.Get()
+ ? SearchTable<ForSearchOrRemove>(aKey,
+ ComputeKeyHash(aKey))
+ : nullptr;
+ if (entry) {
+ RawRemove(entry);
+ ShrinkIfAppropriate();
+ }
+}
+
+void
+PLDHashTable::RemoveEntry(PLDHashEntryHdr* aEntry)
+{
+#ifdef DEBUG
+ AutoWriteOp op(mChecker);
+#endif
+
+ RawRemove(aEntry);
+ ShrinkIfAppropriate();
+}
+
+void
+PLDHashTable::RawRemove(PLDHashEntryHdr* aEntry)
+{
+ // Unfortunately, we can only do weak checking here. That's because
+ // RawRemove() can be called legitimately while an Enumerate() call is
+ // active, which doesn't fit well into how Checker's mState variable works.
+ MOZ_ASSERT(mChecker.IsWritable());
+
+ MOZ_ASSERT(mEntryStore.Get());
+
+ MOZ_ASSERT(EntryIsLive(aEntry), "EntryIsLive(aEntry)");
+
+ // Load keyHash first in case clearEntry() goofs it.
+ PLDHashNumber keyHash = aEntry->mKeyHash;
+ mOps->clearEntry(this, aEntry);
+ if (keyHash & kCollisionFlag) {
+ MarkEntryRemoved(aEntry);
+ mRemovedCount++;
+ } else {
+ MarkEntryFree(aEntry);
+ }
+ mEntryCount--;
+}
+
+// Shrink or compress if a quarter or more of all entries are removed, or if the
+// table is underloaded according to the minimum alpha, and is not minimal-size
+// already.
+void
+PLDHashTable::ShrinkIfAppropriate()
+{
+ uint32_t capacity = Capacity();
+ if (mRemovedCount >= capacity >> 2 ||
+ (capacity > kMinCapacity && mEntryCount <= MinLoad(capacity))) {
+ uint32_t log2;
+ BestCapacity(mEntryCount, &capacity, &log2);
+
+ int32_t deltaLog2 = log2 - (kHashBits - mHashShift);
+ MOZ_ASSERT(deltaLog2 <= 0);
+
+ (void) ChangeTable(deltaLog2);
+ }
+}
+
+size_t
+PLDHashTable::ShallowSizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+#ifdef DEBUG
+ AutoReadOp op(mChecker);
+#endif
+
+ return aMallocSizeOf(mEntryStore.Get());
+}
+
+size_t
+PLDHashTable::ShallowSizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ return aMallocSizeOf(this) + ShallowSizeOfExcludingThis(aMallocSizeOf);
+}
+
+PLDHashTable::Iterator::Iterator(Iterator&& aOther)
+ : mTable(aOther.mTable)
+ , mStart(aOther.mStart)
+ , mLimit(aOther.mLimit)
+ , mCurrent(aOther.mCurrent)
+ , mNexts(aOther.mNexts)
+ , mNextsLimit(aOther.mNextsLimit)
+ , mHaveRemoved(aOther.mHaveRemoved)
+{
+ // No need to change |mChecker| here.
+ aOther.mTable = nullptr;
+ aOther.mStart = nullptr;
+ aOther.mLimit = nullptr;
+ aOther.mCurrent = nullptr;
+ aOther.mNexts = 0;
+ aOther.mNextsLimit = 0;
+ aOther.mHaveRemoved = false;
+}
+
+PLDHashTable::Iterator::Iterator(PLDHashTable* aTable)
+ : mTable(aTable)
+ , mStart(mTable->mEntryStore.Get())
+ , mLimit(mTable->mEntryStore.Get() + mTable->Capacity() * mTable->mEntrySize)
+ , mCurrent(mTable->mEntryStore.Get())
+ , mNexts(0)
+ , mNextsLimit(mTable->EntryCount())
+ , mHaveRemoved(false)
+{
+#ifdef DEBUG
+ mTable->mChecker.StartReadOp();
+#endif
+
+ if (ChaosMode::isActive(ChaosFeature::HashTableIteration) &&
+ mTable->Capacity() > 0) {
+ // Start iterating at a random entry. It would be even more chaotic to
+ // iterate in fully random order, but that's harder.
+ mCurrent += ChaosMode::randomUint32LessThan(mTable->Capacity()) *
+ mTable->mEntrySize;
+ }
+
+ // Advance to the first live entry, if there is one.
+ if (!Done()) {
+ while (IsOnNonLiveEntry()) {
+ MoveToNextEntry();
+ }
+ }
+}
+
+PLDHashTable::Iterator::~Iterator()
+{
+ if (mTable) {
+ if (mHaveRemoved) {
+ mTable->ShrinkIfAppropriate();
+ }
+#ifdef DEBUG
+ mTable->mChecker.EndReadOp();
+#endif
+ }
+}
+
+MOZ_ALWAYS_INLINE bool
+PLDHashTable::Iterator::IsOnNonLiveEntry() const
+{
+ MOZ_ASSERT(!Done());
+ return !EntryIsLive(reinterpret_cast<PLDHashEntryHdr*>(mCurrent));
+}
+
+MOZ_ALWAYS_INLINE void
+PLDHashTable::Iterator::MoveToNextEntry()
+{
+ mCurrent += mTable->mEntrySize;
+ if (mCurrent == mLimit) {
+ mCurrent = mStart; // Wrap-around. Possible due to Chaos Mode.
+ }
+}
+
+void
+PLDHashTable::Iterator::Next()
+{
+ MOZ_ASSERT(!Done());
+
+ mNexts++;
+
+ // Advance to the next live entry, if there is one.
+ if (!Done()) {
+ do {
+ MoveToNextEntry();
+ } while (IsOnNonLiveEntry());
+ }
+}
+
+void
+PLDHashTable::Iterator::Remove()
+{
+ // This cast is needed for the same reason as the one in the destructor.
+ mTable->RawRemove(Get());
+ mHaveRemoved = true;
+}
+
+#ifdef DEBUG
+void
+PLDHashTable::MarkImmutable()
+{
+ mChecker.SetNonWritable();
+}
+#endif
diff --git a/xpcom/glue/PLDHashTable.h b/xpcom/glue/PLDHashTable.h
new file mode 100644
index 000000000..cd1323dbe
--- /dev/null
+++ b/xpcom/glue/PLDHashTable.h
@@ -0,0 +1,621 @@
+/* -*- 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 PLDHashTable_h
+#define PLDHashTable_h
+
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h" // for MOZ_ALWAYS_INLINE
+#include "mozilla/fallible.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Move.h"
+#include "mozilla/Types.h"
+#include "nscore.h"
+
+typedef uint32_t PLDHashNumber;
+
+class PLDHashTable;
+struct PLDHashTableOps;
+
+// Table entry header structure.
+//
+// In order to allow in-line allocation of key and value, we do not declare
+// either here. Instead, the API uses const void *key as a formal parameter.
+// The key need not be stored in the entry; it may be part of the value, but
+// need not be stored at all.
+//
+// Callback types are defined below and grouped into the PLDHashTableOps
+// structure, for single static initialization per hash table sub-type.
+//
+// Each hash table sub-type should make its entry type a subclass of
+// PLDHashEntryHdr. The mKeyHash member contains the result of multiplying the
+// hash code returned from the hashKey callback (see below) by kGoldenRatio,
+// then constraining the result to avoid the magic 0 and 1 values. The stored
+// mKeyHash value is table size invariant, and it is maintained automatically
+// -- users need never access it.
+struct PLDHashEntryHdr
+{
+private:
+ friend class PLDHashTable;
+
+ PLDHashNumber mKeyHash;
+};
+
+#ifdef DEBUG
+
+// This class does three kinds of checking:
+//
+// - that calls to one of |mOps| or to an enumerator do not cause re-entry into
+// the table in an unsafe way;
+//
+// - that multiple threads do not access the table in an unsafe way;
+//
+// - that a table marked as immutable is not modified.
+//
+// "Safe" here means that multiple concurrent read operations are ok, but a
+// write operation (i.e. one that can cause the entry storage to be reallocated
+// or destroyed) cannot safely run concurrently with another read or write
+// operation. This meaning of "safe" is only partial; for example, it does not
+// cover whether a single entry in the table is modified by two separate
+// threads. (Doing such checking would be much harder.)
+//
+// It does this with two variables:
+//
+// - mState, which embodies a tri-stage tagged union with the following
+// variants:
+// - Idle
+// - Read(n), where 'n' is the number of concurrent read operations
+// - Write
+//
+// - mIsWritable, which indicates if the table is mutable.
+//
+class Checker
+{
+public:
+ constexpr Checker() : mState(kIdle), mIsWritable(1) {}
+
+ Checker& operator=(Checker&& aOther) {
+ // Atomic<> doesn't have an |operator=(Atomic<>&&)|.
+ mState = uint32_t(aOther.mState);
+ mIsWritable = uint32_t(aOther.mIsWritable);
+
+ aOther.mState = kIdle;
+
+ return *this;
+ }
+
+ static bool IsIdle(uint32_t aState) { return aState == kIdle; }
+ static bool IsRead(uint32_t aState) { return kRead1 <= aState &&
+ aState <= kReadMax; }
+ static bool IsRead1(uint32_t aState) { return aState == kRead1; }
+ static bool IsWrite(uint32_t aState) { return aState == kWrite; }
+
+ bool IsIdle() const { return mState == kIdle; }
+
+ bool IsWritable() const { return !!mIsWritable; }
+
+ void SetNonWritable() { mIsWritable = 0; }
+
+ // NOTE: the obvious way to implement these functions is to (a) check
+ // |mState| is reasonable, and then (b) update |mState|. But the lack of
+ // atomicity in such an implementation can cause problems if we get unlucky
+ // thread interleaving between (a) and (b).
+ //
+ // So instead for |mState| we are careful to (a) first get |mState|'s old
+ // value and assign it a new value in single atomic operation, and only then
+ // (b) check the old value was reasonable. This ensures we don't have
+ // interleaving problems.
+ //
+ // For |mIsWritable| we don't need to be as careful because it can only in
+ // transition in one direction (from writable to non-writable).
+
+ void StartReadOp()
+ {
+ uint32_t oldState = mState++; // this is an atomic increment
+ MOZ_ASSERT(IsIdle(oldState) || IsRead(oldState));
+ MOZ_ASSERT(oldState < kReadMax); // check for overflow
+ }
+
+ void EndReadOp()
+ {
+ uint32_t oldState = mState--; // this is an atomic decrement
+ MOZ_ASSERT(IsRead(oldState));
+ }
+
+ void StartWriteOp()
+ {
+ MOZ_ASSERT(IsWritable());
+ uint32_t oldState = mState.exchange(kWrite);
+ MOZ_ASSERT(IsIdle(oldState));
+ }
+
+ void EndWriteOp()
+ {
+ // Check again that the table is writable, in case it was marked as
+ // non-writable just after the IsWritable() assertion in StartWriteOp()
+ // occurred.
+ MOZ_ASSERT(IsWritable());
+ uint32_t oldState = mState.exchange(kIdle);
+ MOZ_ASSERT(IsWrite(oldState));
+ }
+
+ void StartIteratorRemovalOp()
+ {
+ // When doing removals at the end of iteration, we go from Read1 state to
+ // Write and then back.
+ MOZ_ASSERT(IsWritable());
+ uint32_t oldState = mState.exchange(kWrite);
+ MOZ_ASSERT(IsRead1(oldState));
+ }
+
+ void EndIteratorRemovalOp()
+ {
+ // Check again that the table is writable, in case it was marked as
+ // non-writable just after the IsWritable() assertion in
+ // StartIteratorRemovalOp() occurred.
+ MOZ_ASSERT(IsWritable());
+ uint32_t oldState = mState.exchange(kRead1);
+ MOZ_ASSERT(IsWrite(oldState));
+ }
+
+ void StartDestructorOp()
+ {
+ // A destructor op is like a write, but the table doesn't need to be
+ // writable.
+ uint32_t oldState = mState.exchange(kWrite);
+ MOZ_ASSERT(IsIdle(oldState));
+ }
+
+ void EndDestructorOp()
+ {
+ uint32_t oldState = mState.exchange(kIdle);
+ MOZ_ASSERT(IsWrite(oldState));
+ }
+
+private:
+ // Things of note about the representation of |mState|.
+ // - The values between kRead1..kReadMax represent valid Read(n) values.
+ // - kIdle and kRead1 are deliberately chosen so that incrementing the -
+ // former gives the latter.
+ // - 9999 concurrent readers should be enough for anybody.
+ static const uint32_t kIdle = 0;
+ static const uint32_t kRead1 = 1;
+ static const uint32_t kReadMax = 9999;
+ static const uint32_t kWrite = 10000;
+
+ mutable mozilla::Atomic<uint32_t> mState;
+ mutable mozilla::Atomic<uint32_t> mIsWritable;
+};
+#endif
+
+// A PLDHashTable may be allocated on the stack or within another structure or
+// class. No entry storage is allocated until the first element is added. This
+// means that empty hash tables are cheap, which is good because they are
+// common.
+//
+// There used to be a long, math-heavy comment here about the merits of
+// double hashing vs. chaining; it was removed in bug 1058335. In short, double
+// hashing is more space-efficient unless the element size gets large (in which
+// case you should keep using double hashing but switch to using pointer
+// elements). Also, with double hashing, you can't safely hold an entry pointer
+// and use it after an add or remove operation, unless you sample Generation()
+// before adding or removing, and compare the sample after, dereferencing the
+// entry pointer only if Generation() has not changed.
+class PLDHashTable
+{
+private:
+ // This class maintains the invariant that every time the entry store is
+ // changed, the generation is updated.
+ class EntryStore
+ {
+ private:
+ char* mEntryStore;
+ uint32_t mGeneration;
+
+ public:
+ EntryStore() : mEntryStore(nullptr), mGeneration(0) {}
+
+ ~EntryStore()
+ {
+ free(mEntryStore);
+ mEntryStore = nullptr;
+ mGeneration++; // a little paranoid, but why not be extra safe?
+ }
+
+ char* Get() { return mEntryStore; }
+ const char* Get() const { return mEntryStore; }
+
+ void Set(char* aEntryStore)
+ {
+ mEntryStore = aEntryStore;
+ mGeneration++;
+ }
+
+ uint32_t Generation() const { return mGeneration; }
+ };
+
+ const PLDHashTableOps* const mOps; // Virtual operations; see below.
+ int16_t mHashShift; // Multiplicative hash shift.
+ const uint32_t mEntrySize; // Number of bytes in an entry.
+ uint32_t mEntryCount; // Number of entries in table.
+ uint32_t mRemovedCount; // Removed entry sentinels in table.
+ EntryStore mEntryStore; // (Lazy) entry storage and generation.
+
+#ifdef DEBUG
+ mutable Checker mChecker;
+#endif
+
+public:
+ // Table capacity limit; do not exceed. The max capacity used to be 1<<23 but
+ // that occasionally that wasn't enough. Making it much bigger than 1<<26
+ // probably isn't worthwhile -- tables that big are kind of ridiculous.
+ // Also, the growth operation will (deliberately) fail if |capacity *
+ // mEntrySize| overflows a uint32_t, and mEntrySize is always at least 8
+ // bytes.
+ static const uint32_t kMaxCapacity = ((uint32_t)1 << 26);
+
+ static const uint32_t kMinCapacity = 8;
+
+ // Making this half of kMaxCapacity ensures it'll fit. Nobody should need an
+ // initial length anywhere nearly this large, anyway.
+ static const uint32_t kMaxInitialLength = kMaxCapacity / 2;
+
+ // This gives a default initial capacity of 8.
+ static const uint32_t kDefaultInitialLength = 4;
+
+ // Initialize the table with |aOps| and |aEntrySize|. The table's initial
+ // capacity is chosen such that |aLength| elements can be inserted without
+ // rehashing; if |aLength| is a power-of-two, this capacity will be
+ // |2*length|. However, because entry storage is allocated lazily, this
+ // initial capacity won't be relevant until the first element is added; prior
+ // to that the capacity will be zero.
+ //
+ // This will crash if |aEntrySize| and/or |aLength| are too large.
+ PLDHashTable(const PLDHashTableOps* aOps, uint32_t aEntrySize,
+ uint32_t aLength = kDefaultInitialLength);
+
+ PLDHashTable(PLDHashTable&& aOther)
+ // These two fields are |const|. Initialize them here because the
+ // move assignment operator cannot modify them.
+ : mOps(aOther.mOps)
+ , mEntrySize(aOther.mEntrySize)
+ // Initialize this field because it is required for a safe call to the
+ // destructor, which the move assignment operator does.
+ , mEntryStore()
+#ifdef DEBUG
+ , mChecker()
+#endif
+ {
+ *this = mozilla::Move(aOther);
+ }
+
+ PLDHashTable& operator=(PLDHashTable&& aOther);
+
+ ~PLDHashTable();
+
+ // This should be used rarely.
+ const PLDHashTableOps* Ops() const { return mOps; }
+
+ // Size in entries (gross, not net of free and removed sentinels) for table.
+ // This can be zero if no elements have been added yet, in which case the
+ // entry storage will not have yet been allocated.
+ uint32_t Capacity() const
+ {
+ return mEntryStore.Get() ? CapacityFromHashShift() : 0;
+ }
+
+ uint32_t EntrySize() const { return mEntrySize; }
+ uint32_t EntryCount() const { return mEntryCount; }
+ uint32_t Generation() const { return mEntryStore.Generation(); }
+
+ // To search for a |key| in |table|, call:
+ //
+ // entry = table.Search(key);
+ //
+ // If |entry| is non-null, |key| was found. If |entry| is null, key was not
+ // found.
+ PLDHashEntryHdr* Search(const void* aKey);
+
+ // To add an entry identified by |key| to table, call:
+ //
+ // entry = table.Add(key, mozilla::fallible);
+ //
+ // If |entry| is null upon return, then the table is severely overloaded and
+ // memory can't be allocated for entry storage.
+ //
+ // Otherwise, |aEntry->mKeyHash| has been set so that
+ // PLDHashTable::EntryIsFree(entry) is false, and it is up to the caller to
+ // initialize the key and value parts of the entry sub-type, if they have not
+ // been set already (i.e. if entry was not already in the table, and if the
+ // optional initEntry hook was not used).
+ PLDHashEntryHdr* Add(const void* aKey, const mozilla::fallible_t&);
+
+ // This is like the other Add() function, but infallible, and so never
+ // returns null.
+ PLDHashEntryHdr* Add(const void* aKey);
+
+ // To remove an entry identified by |key| from table, call:
+ //
+ // table.Remove(key);
+ //
+ // If |key|'s entry is found, it is cleared (via table->mOps->clearEntry).
+ // The table's capacity may be reduced afterwards.
+ void Remove(const void* aKey);
+
+ // To remove an entry found by a prior search, call:
+ //
+ // table.RemoveEntry(entry);
+ //
+ // The entry, which must be present and in use, is cleared (via
+ // table->mOps->clearEntry). The table's capacity may be reduced afterwards.
+ void RemoveEntry(PLDHashEntryHdr* aEntry);
+
+ // Remove an entry already accessed via Search() or Add().
+ //
+ // NB: this is a "raw" or low-level method. It does not shrink the table if
+ // it is underloaded. Don't use it unless necessary and you know what you are
+ // doing, and if so, please explain in a comment why it is necessary instead
+ // of RemoveEntry().
+ void RawRemove(PLDHashEntryHdr* aEntry);
+
+ // This function is equivalent to
+ // ClearAndPrepareForLength(kDefaultInitialLength).
+ void Clear();
+
+ // This function clears the table's contents and frees its entry storage,
+ // leaving it in a empty state ready to be used again. Afterwards, when the
+ // first element is added the entry storage that gets allocated will have a
+ // capacity large enough to fit |aLength| elements without rehashing.
+ //
+ // It's conceptually the same as calling the destructor and then re-calling
+ // the constructor with the original |aOps| and |aEntrySize| arguments, and
+ // a new |aLength| argument.
+ void ClearAndPrepareForLength(uint32_t aLength);
+
+ // Measure the size of the table's entry storage. If the entries contain
+ // pointers to other heap blocks, you have to iterate over the table and
+ // measure those separately; hence the "Shallow" prefix.
+ size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+ // Like ShallowSizeOfExcludingThis(), but includes sizeof(*this).
+ size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+#ifdef DEBUG
+ // Mark a table as immutable for the remainder of its lifetime. This
+ // changes the implementation from asserting one set of invariants to
+ // asserting a different set.
+ void MarkImmutable();
+#endif
+
+ // If you use PLDHashEntryStub or a subclass of it as your entry struct, and
+ // if your entries move via memcpy and clear via memset(0), you can use these
+ // stub operations.
+ static const PLDHashTableOps* StubOps();
+
+ // The individual stub operations in StubOps().
+ static PLDHashNumber HashVoidPtrKeyStub(const void* aKey);
+ static bool MatchEntryStub(const PLDHashEntryHdr* aEntry, const void* aKey);
+ static void MoveEntryStub(PLDHashTable* aTable, const PLDHashEntryHdr* aFrom,
+ PLDHashEntryHdr* aTo);
+ static void ClearEntryStub(PLDHashTable* aTable, PLDHashEntryHdr* aEntry);
+
+ // Hash/match operations for tables holding C strings.
+ static PLDHashNumber HashStringKey(const void* aKey);
+ static bool MatchStringKey(const PLDHashEntryHdr* aEntry, const void* aKey);
+
+ // This is an iterator for PLDHashtable. Assertions will detect some, but not
+ // all, mid-iteration table modifications that might invalidate (e.g.
+ // reallocate) the entry storage.
+ //
+ // Any element can be removed during iteration using Remove(). If any
+ // elements are removed, the table may be resized once iteration ends.
+ //
+ // Example usage:
+ //
+ // for (auto iter = table.Iter(); !iter.Done(); iter.Next()) {
+ // auto entry = static_cast<FooEntry*>(iter.Get());
+ // // ... do stuff with |entry| ...
+ // // ... possibly call iter.Remove() once ...
+ // }
+ //
+ // or:
+ //
+ // for (PLDHashTable::Iterator iter(&table); !iter.Done(); iter.Next()) {
+ // auto entry = static_cast<FooEntry*>(iter.Get());
+ // // ... do stuff with |entry| ...
+ // // ... possibly call iter.Remove() once ...
+ // }
+ //
+ // The latter form is more verbose but is easier to work with when
+ // making subclasses of Iterator.
+ //
+ class Iterator
+ {
+ public:
+ explicit Iterator(PLDHashTable* aTable);
+ Iterator(Iterator&& aOther);
+ ~Iterator();
+
+ // Have we finished?
+ bool Done() const { return mNexts == mNextsLimit; }
+
+ // Get the current entry.
+ PLDHashEntryHdr* Get() const
+ {
+ MOZ_ASSERT(!Done());
+
+ PLDHashEntryHdr* entry = reinterpret_cast<PLDHashEntryHdr*>(mCurrent);
+ MOZ_ASSERT(EntryIsLive(entry));
+ return entry;
+ }
+
+ // Advance to the next entry.
+ void Next();
+
+ // Remove the current entry. Must only be called once per entry, and Get()
+ // must not be called on that entry afterwards.
+ void Remove();
+
+ protected:
+ PLDHashTable* mTable; // Main table pointer.
+
+ private:
+ char* mStart; // The first entry.
+ char* mLimit; // One past the last entry.
+ char* mCurrent; // Pointer to the current entry.
+ uint32_t mNexts; // Number of Next() calls.
+ uint32_t mNextsLimit; // Next() call limit.
+
+ bool mHaveRemoved; // Have any elements been removed?
+
+ bool IsOnNonLiveEntry() const;
+ void MoveToNextEntry();
+
+ Iterator() = delete;
+ Iterator(const Iterator&) = delete;
+ Iterator& operator=(const Iterator&) = delete;
+ Iterator& operator=(const Iterator&&) = delete;
+ };
+
+ Iterator Iter() { return Iterator(this); }
+
+ // Use this if you need to initialize an Iterator in a const method. If you
+ // use this case, you should not call Remove() on the iterator.
+ Iterator ConstIter() const
+ {
+ return Iterator(const_cast<PLDHashTable*>(this));
+ }
+
+private:
+ // Multiplicative hash uses an unsigned 32 bit integer and the golden ratio,
+ // expressed as a fixed-point 32-bit fraction.
+ static const uint32_t kHashBits = 32;
+ static const uint32_t kGoldenRatio = 0x9E3779B9U;
+
+ static uint32_t HashShift(uint32_t aEntrySize, uint32_t aLength);
+
+ static const PLDHashNumber kCollisionFlag = 1;
+
+ static bool EntryIsFree(PLDHashEntryHdr* aEntry)
+ {
+ return aEntry->mKeyHash == 0;
+ }
+ static bool EntryIsRemoved(PLDHashEntryHdr* aEntry)
+ {
+ return aEntry->mKeyHash == 1;
+ }
+ static bool EntryIsLive(PLDHashEntryHdr* aEntry)
+ {
+ return aEntry->mKeyHash >= 2;
+ }
+
+ static void MarkEntryFree(PLDHashEntryHdr* aEntry)
+ {
+ aEntry->mKeyHash = 0;
+ }
+ static void MarkEntryRemoved(PLDHashEntryHdr* aEntry)
+ {
+ aEntry->mKeyHash = 1;
+ }
+
+ PLDHashNumber Hash1(PLDHashNumber aHash0);
+ void Hash2(PLDHashNumber aHash, uint32_t& aHash2Out, uint32_t& aSizeMaskOut);
+
+ static bool MatchEntryKeyhash(PLDHashEntryHdr* aEntry, PLDHashNumber aHash);
+ PLDHashEntryHdr* AddressEntry(uint32_t aIndex);
+
+ // We store mHashShift rather than sizeLog2 to optimize the collision-free
+ // case in SearchTable.
+ uint32_t CapacityFromHashShift() const
+ {
+ return ((uint32_t)1 << (kHashBits - mHashShift));
+ }
+
+ PLDHashNumber ComputeKeyHash(const void* aKey);
+
+ enum SearchReason { ForSearchOrRemove, ForAdd };
+
+ template <SearchReason Reason>
+ PLDHashEntryHdr* NS_FASTCALL
+ SearchTable(const void* aKey, PLDHashNumber aKeyHash);
+
+ PLDHashEntryHdr* FindFreeEntry(PLDHashNumber aKeyHash);
+
+ bool ChangeTable(int aDeltaLog2);
+
+ void ShrinkIfAppropriate();
+
+ PLDHashTable(const PLDHashTable& aOther) = delete;
+ PLDHashTable& operator=(const PLDHashTable& aOther) = delete;
+};
+
+// Compute the hash code for a given key to be looked up, added, or removed.
+// A hash code may have any PLDHashNumber value.
+typedef PLDHashNumber (*PLDHashHashKey)(const void* aKey);
+
+// Compare the key identifying aEntry with the provided key parameter. Return
+// true if keys match, false otherwise.
+typedef bool (*PLDHashMatchEntry)(const PLDHashEntryHdr* aEntry,
+ const void* aKey);
+
+// Copy the data starting at aFrom to the new entry storage at aTo. Do not add
+// reference counts for any strong references in the entry, however, as this
+// is a "move" operation: the old entry storage at from will be freed without
+// any reference-decrementing callback shortly.
+typedef void (*PLDHashMoveEntry)(PLDHashTable* aTable,
+ const PLDHashEntryHdr* aFrom,
+ PLDHashEntryHdr* aTo);
+
+// Clear the entry and drop any strong references it holds. This callback is
+// invoked by Remove(), but only if the given key is found in the table.
+typedef void (*PLDHashClearEntry)(PLDHashTable* aTable,
+ PLDHashEntryHdr* aEntry);
+
+// Initialize a new entry, apart from mKeyHash. This function is called when
+// Add() finds no existing entry for the given key, and must add a new one. At
+// that point, |aEntry->mKeyHash| is not set yet, to avoid claiming the last
+// free entry in a severely overloaded table.
+typedef void (*PLDHashInitEntry)(PLDHashEntryHdr* aEntry, const void* aKey);
+
+// Finally, the "vtable" structure for PLDHashTable. The first four hooks
+// must be provided by implementations; they're called unconditionally by the
+// generic PLDHashTable.cpp code. Hooks after these may be null.
+//
+// Summary of allocation-related hook usage with C++ placement new emphasis:
+// initEntry Call placement new using default key-based ctor.
+// moveEntry Call placement new using copy ctor, run dtor on old
+// entry storage.
+// clearEntry Run dtor on entry.
+//
+// Note the reason why initEntry is optional: the default hooks (stubs) clear
+// entry storage: On successful Add(tbl, key), the returned entry pointer
+// addresses an entry struct whose mKeyHash member has been set non-zero, but
+// all other entry members are still clear (null). Add() callers can test such
+// members to see whether the entry was newly created by the Add() call that
+// just succeeded. If placement new or similar initialization is required,
+// define an |initEntry| hook. Of course, the |clearEntry| hook must zero or
+// null appropriately.
+//
+// XXX assumes 0 is null for pointer types.
+struct PLDHashTableOps
+{
+ // Mandatory hooks. All implementations must provide these.
+ PLDHashHashKey hashKey;
+ PLDHashMatchEntry matchEntry;
+ PLDHashMoveEntry moveEntry;
+ PLDHashClearEntry clearEntry;
+
+ // Optional hooks start here. If null, these are not called.
+ PLDHashInitEntry initEntry;
+};
+
+// A minimal entry is a subclass of PLDHashEntryHdr and has a void* key pointer.
+struct PLDHashEntryStub : public PLDHashEntryHdr
+{
+ const void* key;
+};
+
+#endif /* PLDHashTable_h */
diff --git a/xpcom/glue/ReentrantMonitor.h b/xpcom/glue/ReentrantMonitor.h
new file mode 100644
index 000000000..0798fe2af
--- /dev/null
+++ b/xpcom/glue/ReentrantMonitor.h
@@ -0,0 +1,249 @@
+/* -*- 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_ReentrantMonitor_h
+#define mozilla_ReentrantMonitor_h
+
+#include "prmon.h"
+
+#ifdef MOZILLA_INTERNAL_API
+#include "GeckoProfiler.h"
+#endif //MOZILLA_INTERNAL_API
+
+#include "mozilla/BlockingResourceBase.h"
+
+//
+// Provides:
+//
+// - ReentrantMonitor, a Java-like monitor
+// - ReentrantMonitorAutoEnter, an RAII class for ensuring that
+// ReentrantMonitors are properly entered and exited
+//
+// Using ReentrantMonitorAutoEnter is MUCH preferred to making bare calls to
+// ReentrantMonitor.Enter and Exit.
+//
+namespace mozilla {
+
+
+/**
+ * ReentrantMonitor
+ * Java-like monitor.
+ * When possible, use ReentrantMonitorAutoEnter to hold this monitor within a
+ * scope, instead of calling Enter/Exit directly.
+ **/
+class ReentrantMonitor : BlockingResourceBase
+{
+public:
+ /**
+ * ReentrantMonitor
+ * @param aName A name which can reference this monitor
+ */
+ explicit ReentrantMonitor(const char* aName)
+ : BlockingResourceBase(aName, eReentrantMonitor)
+#ifdef DEBUG
+ , mEntryCount(0)
+#endif
+ {
+ MOZ_COUNT_CTOR(ReentrantMonitor);
+ mReentrantMonitor = PR_NewMonitor();
+ if (!mReentrantMonitor) {
+ NS_RUNTIMEABORT("Can't allocate mozilla::ReentrantMonitor");
+ }
+ }
+
+ /**
+ * ~ReentrantMonitor
+ **/
+ ~ReentrantMonitor()
+ {
+ NS_ASSERTION(mReentrantMonitor,
+ "improperly constructed ReentrantMonitor or double free");
+ PR_DestroyMonitor(mReentrantMonitor);
+ mReentrantMonitor = 0;
+ MOZ_COUNT_DTOR(ReentrantMonitor);
+ }
+
+#ifndef DEBUG
+ /**
+ * Enter
+ * @see prmon.h
+ **/
+ void Enter() { PR_EnterMonitor(mReentrantMonitor); }
+
+ /**
+ * Exit
+ * @see prmon.h
+ **/
+ void Exit() { PR_ExitMonitor(mReentrantMonitor); }
+
+ /**
+ * Wait
+ * @see prmon.h
+ **/
+ nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT)
+ {
+#ifdef MOZILLA_INTERNAL_API
+ GeckoProfilerSleepRAII profiler_sleep;
+#endif //MOZILLA_INTERNAL_API
+ return PR_Wait(mReentrantMonitor, aInterval) == PR_SUCCESS ?
+ NS_OK : NS_ERROR_FAILURE;
+ }
+
+#else // ifndef DEBUG
+ void Enter();
+ void Exit();
+ nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT);
+
+#endif // ifndef DEBUG
+
+ /**
+ * Notify
+ * @see prmon.h
+ **/
+ nsresult Notify()
+ {
+ return PR_Notify(mReentrantMonitor) == PR_SUCCESS ? NS_OK :
+ NS_ERROR_FAILURE;
+ }
+
+ /**
+ * NotifyAll
+ * @see prmon.h
+ **/
+ nsresult NotifyAll()
+ {
+ return PR_NotifyAll(mReentrantMonitor) == PR_SUCCESS ? NS_OK :
+ NS_ERROR_FAILURE;
+ }
+
+#ifdef DEBUG
+ /**
+ * AssertCurrentThreadIn
+ * @see prmon.h
+ **/
+ void AssertCurrentThreadIn()
+ {
+ PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mReentrantMonitor);
+ }
+
+ /**
+ * AssertNotCurrentThreadIn
+ * @see prmon.h
+ **/
+ void AssertNotCurrentThreadIn()
+ {
+ // FIXME bug 476536
+ }
+
+#else
+ void AssertCurrentThreadIn() {}
+ void AssertNotCurrentThreadIn() {}
+
+#endif // ifdef DEBUG
+
+private:
+ ReentrantMonitor();
+ ReentrantMonitor(const ReentrantMonitor&);
+ ReentrantMonitor& operator=(const ReentrantMonitor&);
+
+ PRMonitor* mReentrantMonitor;
+#ifdef DEBUG
+ int32_t mEntryCount;
+#endif
+};
+
+
+/**
+ * ReentrantMonitorAutoEnter
+ * Enters the ReentrantMonitor when it enters scope, and exits it when
+ * it leaves scope.
+ *
+ * MUCH PREFERRED to bare calls to ReentrantMonitor.Enter and Exit.
+ */
+class MOZ_STACK_CLASS ReentrantMonitorAutoEnter
+{
+public:
+ /**
+ * Constructor
+ * The constructor aquires the given lock. The destructor
+ * releases the lock.
+ *
+ * @param aReentrantMonitor A valid mozilla::ReentrantMonitor*.
+ **/
+ explicit ReentrantMonitorAutoEnter(mozilla::ReentrantMonitor& aReentrantMonitor)
+ : mReentrantMonitor(&aReentrantMonitor)
+ {
+ NS_ASSERTION(mReentrantMonitor, "null monitor");
+ mReentrantMonitor->Enter();
+ }
+
+ ~ReentrantMonitorAutoEnter(void)
+ {
+ mReentrantMonitor->Exit();
+ }
+
+ nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT)
+ {
+ return mReentrantMonitor->Wait(aInterval);
+ }
+
+ nsresult Notify() { return mReentrantMonitor->Notify(); }
+ nsresult NotifyAll() { return mReentrantMonitor->NotifyAll(); }
+
+private:
+ ReentrantMonitorAutoEnter();
+ ReentrantMonitorAutoEnter(const ReentrantMonitorAutoEnter&);
+ ReentrantMonitorAutoEnter& operator=(const ReentrantMonitorAutoEnter&);
+ static void* operator new(size_t) CPP_THROW_NEW;
+
+ mozilla::ReentrantMonitor* mReentrantMonitor;
+};
+
+/**
+ * ReentrantMonitorAutoExit
+ * Exit the ReentrantMonitor when it enters scope, and enters it when it leaves
+ * scope.
+ *
+ * MUCH PREFERRED to bare calls to ReentrantMonitor.Exit and Enter.
+ */
+class MOZ_STACK_CLASS ReentrantMonitorAutoExit
+{
+public:
+ /**
+ * Constructor
+ * The constructor releases the given lock. The destructor
+ * acquires the lock. The lock must be held before constructing
+ * this object!
+ *
+ * @param aReentrantMonitor A valid mozilla::ReentrantMonitor*. It
+ * must be already locked.
+ **/
+ explicit ReentrantMonitorAutoExit(ReentrantMonitor& aReentrantMonitor)
+ : mReentrantMonitor(&aReentrantMonitor)
+ {
+ NS_ASSERTION(mReentrantMonitor, "null monitor");
+ mReentrantMonitor->AssertCurrentThreadIn();
+ mReentrantMonitor->Exit();
+ }
+
+ ~ReentrantMonitorAutoExit(void)
+ {
+ mReentrantMonitor->Enter();
+ }
+
+private:
+ ReentrantMonitorAutoExit();
+ ReentrantMonitorAutoExit(const ReentrantMonitorAutoExit&);
+ ReentrantMonitorAutoExit& operator=(const ReentrantMonitorAutoExit&);
+ static void* operator new(size_t) CPP_THROW_NEW;
+
+ ReentrantMonitor* mReentrantMonitor;
+};
+
+} // namespace mozilla
+
+
+#endif // ifndef mozilla_ReentrantMonitor_h
diff --git a/xpcom/glue/moz.build b/xpcom/glue/moz.build
new file mode 100644
index 000000000..95c18b273
--- /dev/null
+++ b/xpcom/glue/moz.build
@@ -0,0 +1,123 @@
+# -*- 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/.
+
+with Files('nsString*'):
+ BUG_COMPONENT = ('Core', 'String')
+
+DIRS += ['standalone']
+
+# On win we build two glue libs - glue linked to crt dlls here and in staticruntime we build
+# a statically linked glue lib.
+if CONFIG['OS_ARCH'] == 'WINNT':
+ DIRS += ['staticruntime']
+
+EXPORTS += [
+ 'MainThreadUtils.h',
+ 'nsArrayEnumerator.h',
+ 'nsArrayUtils.h',
+ 'nsBaseHashtable.h',
+ 'nsCategoryCache.h',
+ 'nsClassHashtable.h',
+ 'nsCOMArray.h',
+ 'nsComponentManagerUtils.h',
+ 'nsCOMPtr.h',
+ 'nsCRTGlue.h',
+ 'nsCycleCollectionNoteChild.h',
+ 'nsCycleCollectionNoteRootCallback.h',
+ 'nsCycleCollectionParticipant.h',
+ 'nsCycleCollectionTraversalCallback.h',
+ 'nsDataHashtable.h',
+ 'nsDebug.h',
+ 'nsDeque.h',
+ 'nsEnumeratorUtils.h',
+ 'nsHashKeys.h',
+ 'nsIClassInfoImpl.h',
+ 'nsID.h',
+ 'nsIInterfaceRequestorUtils.h',
+ 'nsINIParser.h',
+ 'nsInterfaceHashtable.h',
+ 'nsISupportsImpl.h',
+ 'nsISupportsUtils.h',
+ 'nsIWeakReferenceUtils.h',
+ 'nsJSThingHashtable.h',
+ 'nsMemory.h',
+ 'nsPointerHashKeys.h',
+ 'nsProxyRelease.h',
+ 'nsQuickSort.h',
+ 'nsRefPtrHashtable.h',
+ 'nsServiceManagerUtils.h',
+ 'nsStringAPI.h',
+ 'nsStringGlue.h',
+ 'nsTArray-inl.h',
+ 'nsTArray.h',
+ 'nsTArrayForwardDeclare.h',
+ 'nsTextFormatter.h',
+ 'nsTHashtable.h',
+ 'nsThreadUtils.h',
+ 'nsTObserverArray.h',
+ 'nsTPriorityQueue.h',
+ 'nsTWeakRef.h',
+ 'nsVersionComparator.h',
+ 'nsWeakReference.h',
+ 'nsXPTCUtils.h',
+ 'PLDHashTable.h',
+]
+
+EXPORTS.mozilla += [
+ 'AppData.h',
+ 'AutoRestore.h',
+ 'BlockingResourceBase.h',
+ 'CondVar.h',
+ 'DeadlockDetector.h',
+ 'EnumeratedArrayCycleCollection.h',
+ 'FileUtils.h',
+ 'GenericFactory.h',
+ 'IntentionalCrash.h',
+ 'Monitor.h',
+ 'Mutex.h',
+ 'Observer.h',
+ 'ReentrantMonitor.h',
+]
+
+include('objs.mozbuild')
+
+UNIFIED_SOURCES += xpcom_gluens_src_cppsrcs
+UNIFIED_SOURCES += xpcom_glue_src_cppsrcs
+
+UNIFIED_SOURCES += [
+ 'GenericModule.cpp',
+ 'nsStringAPI.cpp',
+]
+
+Library('xpcomglue_s')
+
+SDK_LIBRARY = True
+
+FORCE_STATIC_LIB = True
+
+if CONFIG['_MSC_VER']:
+ DEFINES['_USE_ANSI_CPP'] = True
+ # Don't include directives about which CRT to use
+ CFLAGS += ['-Zl']
+ CXXFLAGS += ['-Zl']
+
+LOCAL_INCLUDES += [
+ '../build',
+ '../threads',
+]
+
+if CONFIG['ENABLE_TESTS']:
+ DIRS += ['tests/gtest']
+
+# Include fallible for third party code using the xpcom glue
+USE_LIBS += [
+ 'fallible',
+]
+
+# Force to build a static library only
+NO_EXPAND_LIBS = True
+
+DIST_INSTALL = True
diff --git a/xpcom/glue/nsArrayEnumerator.cpp b/xpcom/glue/nsArrayEnumerator.cpp
new file mode 100644
index 000000000..2d2ef6da7
--- /dev/null
+++ b/xpcom/glue/nsArrayEnumerator.cpp
@@ -0,0 +1,213 @@
+/* -*- 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/Attributes.h"
+
+#include "nsArrayEnumerator.h"
+
+#include "nsIArray.h"
+#include "nsISimpleEnumerator.h"
+
+#include "nsCOMArray.h"
+#include "nsCOMPtr.h"
+#include "mozilla/RefPtr.h"
+
+class nsSimpleArrayEnumerator final : public nsISimpleEnumerator
+{
+public:
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator interface
+ NS_DECL_NSISIMPLEENUMERATOR
+
+ // nsSimpleArrayEnumerator methods
+ explicit nsSimpleArrayEnumerator(nsIArray* aValueArray)
+ : mValueArray(aValueArray)
+ , mIndex(0)
+ {
+ }
+
+private:
+ ~nsSimpleArrayEnumerator() {}
+
+protected:
+ nsCOMPtr<nsIArray> mValueArray;
+ uint32_t mIndex;
+};
+
+NS_IMPL_ISUPPORTS(nsSimpleArrayEnumerator, nsISimpleEnumerator)
+
+NS_IMETHODIMP
+nsSimpleArrayEnumerator::HasMoreElements(bool* aResult)
+{
+ NS_PRECONDITION(aResult != 0, "null ptr");
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (!mValueArray) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ uint32_t cnt;
+ nsresult rv = mValueArray->GetLength(&cnt);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ *aResult = (mIndex < cnt);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSimpleArrayEnumerator::GetNext(nsISupports** aResult)
+{
+ NS_PRECONDITION(aResult != 0, "null ptr");
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (!mValueArray) {
+ *aResult = nullptr;
+ return NS_OK;
+ }
+
+ uint32_t cnt;
+ nsresult rv = mValueArray->GetLength(&cnt);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (mIndex >= cnt) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return mValueArray->QueryElementAt(mIndex++, NS_GET_IID(nsISupports),
+ (void**)aResult);
+}
+
+nsresult
+NS_NewArrayEnumerator(nsISimpleEnumerator** aResult, nsIArray* aArray)
+{
+ RefPtr<nsSimpleArrayEnumerator> enumer = new nsSimpleArrayEnumerator(aArray);
+ enumer.forget(aResult);
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// enumerator implementation for nsCOMArray
+// creates a snapshot of the array in question
+// you MUST use NS_NewArrayEnumerator to create this, so that
+// allocation is done correctly
+class nsCOMArrayEnumerator final : public nsISimpleEnumerator
+{
+public:
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator interface
+ NS_DECL_NSISIMPLEENUMERATOR
+
+ // nsSimpleArrayEnumerator methods
+ nsCOMArrayEnumerator() : mIndex(0) {}
+
+ // specialized operator to make sure we make room for mValues
+ void* operator new(size_t aSize, const nsCOMArray_base& aArray) CPP_THROW_NEW;
+ void operator delete(void* aPtr) { ::operator delete(aPtr); }
+
+private:
+ ~nsCOMArrayEnumerator(void);
+
+protected:
+ uint32_t mIndex; // current position
+ uint32_t mArraySize; // size of the array
+
+ // this is actually bigger
+ nsISupports* mValueArray[1];
+};
+
+NS_IMPL_ISUPPORTS(nsCOMArrayEnumerator, nsISimpleEnumerator)
+
+nsCOMArrayEnumerator::~nsCOMArrayEnumerator()
+{
+ // only release the entries that we haven't visited yet
+ for (; mIndex < mArraySize; ++mIndex) {
+ NS_IF_RELEASE(mValueArray[mIndex]);
+ }
+}
+
+NS_IMETHODIMP
+nsCOMArrayEnumerator::HasMoreElements(bool* aResult)
+{
+ NS_PRECONDITION(aResult != 0, "null ptr");
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ *aResult = (mIndex < mArraySize);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCOMArrayEnumerator::GetNext(nsISupports** aResult)
+{
+ NS_PRECONDITION(aResult != 0, "null ptr");
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (mIndex >= mArraySize) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // pass the ownership of the reference to the caller. Since
+ // we AddRef'ed during creation of |this|, there is no need
+ // to AddRef here
+ *aResult = mValueArray[mIndex++];
+
+ // this really isn't necessary. just pretend this happens, since
+ // we'll never visit this value again!
+ // mValueArray[(mIndex-1)] = nullptr;
+
+ return NS_OK;
+}
+
+void*
+nsCOMArrayEnumerator::operator new(size_t aSize,
+ const nsCOMArray_base& aArray) CPP_THROW_NEW
+{
+ // create enough space such that mValueArray points to a large
+ // enough value. Note that the initial value of aSize gives us
+ // space for mValueArray[0], so we must subtract
+ aSize += (aArray.Count() - 1) * sizeof(aArray[0]);
+
+ // do the actual allocation
+ nsCOMArrayEnumerator* result =
+ static_cast<nsCOMArrayEnumerator*>(::operator new(aSize));
+
+ // now need to copy over the values, and addref each one
+ // now this might seem like a lot of work, but we're actually just
+ // doing all our AddRef's ahead of time since GetNext() doesn't
+ // need to AddRef() on the way out
+ uint32_t i;
+ uint32_t max = result->mArraySize = aArray.Count();
+ for (i = 0; i < max; ++i) {
+ result->mValueArray[i] = aArray[i];
+ NS_IF_ADDREF(result->mValueArray[i]);
+ }
+
+ return result;
+}
+
+nsresult
+NS_NewArrayEnumerator(nsISimpleEnumerator** aResult,
+ const nsCOMArray_base& aArray)
+{
+ RefPtr<nsCOMArrayEnumerator> enumerator = new (aArray) nsCOMArrayEnumerator();
+ enumerator.forget(aResult);
+ return NS_OK;
+}
diff --git a/xpcom/glue/nsArrayEnumerator.h b/xpcom/glue/nsArrayEnumerator.h
new file mode 100644
index 000000000..341ae86ed
--- /dev/null
+++ b/xpcom/glue/nsArrayEnumerator.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsArrayEnumerator_h__
+#define nsArrayEnumerator_h__
+
+// enumerator implementation for nsIArray
+
+#include "nscore.h"
+
+class nsISimpleEnumerator;
+class nsIArray;
+class nsCOMArray_base;
+
+// Create an enumerator for an existing nsIArray implementation
+// The enumerator holds an owning reference to the array.
+nsresult
+NS_NewArrayEnumerator(nsISimpleEnumerator** aResult,
+ nsIArray* aArray);
+
+// create an enumerator for an existing nsCOMArray<T> implementation
+// The enumerator will hold an owning reference to each ELEMENT in
+// the array. This means that the nsCOMArray<T> can safely go away
+// without its objects going away.
+nsresult
+NS_NewArrayEnumerator(nsISimpleEnumerator** aResult,
+ const nsCOMArray_base& aArray);
+
+#endif
diff --git a/xpcom/glue/nsArrayUtils.cpp b/xpcom/glue/nsArrayUtils.cpp
new file mode 100644
index 000000000..480863737
--- /dev/null
+++ b/xpcom/glue/nsArrayUtils.cpp
@@ -0,0 +1,23 @@
+/* -*- 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 "nsArrayUtils.h"
+
+//
+// do_QueryElementAt helper stuff
+//
+nsresult
+nsQueryArrayElementAt::operator()(const nsIID& aIID, void** aResult) const
+{
+ nsresult status = mArray ? mArray->QueryElementAt(mIndex, aIID, aResult) :
+ NS_ERROR_NULL_POINTER;
+
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+
+ return status;
+}
diff --git a/xpcom/glue/nsArrayUtils.h b/xpcom/glue/nsArrayUtils.h
new file mode 100644
index 000000000..68032e8c0
--- /dev/null
+++ b/xpcom/glue/nsArrayUtils.h
@@ -0,0 +1,40 @@
+/* -*- 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 nsArrayUtils_h__
+#define nsArrayUtils_h__
+
+#include "nsCOMPtr.h"
+#include "nsIArray.h"
+
+// helper class for do_QueryElementAt
+class MOZ_STACK_CLASS nsQueryArrayElementAt final : public nsCOMPtr_helper
+{
+public:
+ nsQueryArrayElementAt(nsIArray* aArray, uint32_t aIndex,
+ nsresult* aErrorPtr)
+ : mArray(aArray)
+ , mIndex(aIndex)
+ , mErrorPtr(aErrorPtr)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID& aIID, void**) const
+ override;
+
+private:
+ nsIArray* MOZ_NON_OWNING_REF mArray;
+ uint32_t mIndex;
+ nsresult* mErrorPtr;
+};
+
+inline const nsQueryArrayElementAt
+do_QueryElementAt(nsIArray* aArray, uint32_t aIndex, nsresult* aErrorPtr = 0)
+{
+ return nsQueryArrayElementAt(aArray, aIndex, aErrorPtr);
+}
+
+#endif // nsArrayUtils_h__
diff --git a/xpcom/glue/nsBaseHashtable.h b/xpcom/glue/nsBaseHashtable.h
new file mode 100644
index 000000000..f52df3dd1
--- /dev/null
+++ b/xpcom/glue/nsBaseHashtable.h
@@ -0,0 +1,270 @@
+/* -*- 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 nsBaseHashtable_h__
+#define nsBaseHashtable_h__
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Move.h"
+#include "nsTHashtable.h"
+#include "nsDebug.h"
+
+template<class KeyClass, class DataType, class UserDataType>
+class nsBaseHashtable; // forward declaration
+
+/**
+ * the private nsTHashtable::EntryType class used by nsBaseHashtable
+ * @see nsTHashtable for the specification of this class
+ * @see nsBaseHashtable for template parameters
+ */
+template<class KeyClass, class DataType>
+class nsBaseHashtableET : public KeyClass
+{
+public:
+ DataType mData;
+ friend class nsTHashtable<nsBaseHashtableET<KeyClass, DataType>>;
+
+private:
+ typedef typename KeyClass::KeyType KeyType;
+ typedef typename KeyClass::KeyTypePointer KeyTypePointer;
+
+ explicit nsBaseHashtableET(KeyTypePointer aKey);
+ nsBaseHashtableET(nsBaseHashtableET<KeyClass, DataType>&& aToMove);
+ ~nsBaseHashtableET();
+};
+
+/**
+ * templated hashtable for simple data types
+ * This class manages simple data types that do not need construction or
+ * destruction.
+ *
+ * @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h
+ * for a complete specification.
+ * @param DataType the datatype stored in the hashtable,
+ * for example, uint32_t or nsCOMPtr. If UserDataType is not the same,
+ * DataType must implicitly cast to UserDataType
+ * @param UserDataType the user sees, for example uint32_t or nsISupports*
+ */
+template<class KeyClass, class DataType, class UserDataType>
+class nsBaseHashtable
+ : protected nsTHashtable<nsBaseHashtableET<KeyClass, DataType>>
+{
+ typedef mozilla::fallible_t fallible_t;
+
+public:
+ typedef typename KeyClass::KeyType KeyType;
+ typedef nsBaseHashtableET<KeyClass, DataType> EntryType;
+
+ using nsTHashtable<EntryType>::Contains;
+
+ nsBaseHashtable() {}
+ explicit nsBaseHashtable(uint32_t aInitLength)
+ : nsTHashtable<EntryType>(aInitLength)
+ {
+ }
+
+ /**
+ * Return the number of entries in the table.
+ * @return number of entries
+ */
+ uint32_t Count() const { return nsTHashtable<EntryType>::Count(); }
+
+ /**
+ * retrieve the value for a key.
+ * @param aKey the key to retreive
+ * @param aData data associated with this key will be placed at this
+ * pointer. If you only need to check if the key exists, aData
+ * may be null.
+ * @return true if the key exists. If key does not exist, aData is not
+ * modified.
+ */
+ bool Get(KeyType aKey, UserDataType* aData) const
+ {
+ EntryType* ent = this->GetEntry(aKey);
+ if (!ent) {
+ return false;
+ }
+
+ if (aData) {
+ *aData = ent->mData;
+ }
+
+ return true;
+ }
+
+ /**
+ * Get the value, returning a zero-initialized POD or a default-initialized
+ * object if the entry is not present in the table.
+ *
+ * @param aKey the key to retrieve
+ * @return The found value, or UserDataType{} if no entry was found with the
+ * given key.
+ * @note If zero/default-initialized values are stored in the table, it is
+ * not possible to distinguish between such a value and a missing entry.
+ */
+ UserDataType Get(KeyType aKey) const
+ {
+ EntryType* ent = this->GetEntry(aKey);
+ if (!ent) {
+ return UserDataType{};
+ }
+
+ return ent->mData;
+ }
+
+ /**
+ * Add key to the table if not already present, and return a reference to its
+ * value. If key is not already in the table then the value is default
+ * constructed.
+ */
+ DataType& GetOrInsert(const KeyType& aKey)
+ {
+ EntryType* ent = this->GetEntry(aKey);
+ if (ent) {
+ return ent->mData;
+ }
+
+ ent = this->PutEntry(aKey);
+ return ent->mData;
+ }
+
+ /**
+ * put a new value for the associated key
+ * @param aKey the key to put
+ * @param aData the new data
+ * @return always true, unless memory allocation failed
+ */
+ void Put(KeyType aKey, const UserDataType& aData)
+ {
+ if (!Put(aKey, aData, mozilla::fallible)) {
+ NS_ABORT_OOM(this->mTable.EntrySize() * this->mTable.EntryCount());
+ }
+ }
+
+ MOZ_MUST_USE bool Put(KeyType aKey, const UserDataType& aData,
+ const fallible_t&)
+ {
+ EntryType* ent = this->PutEntry(aKey, mozilla::fallible);
+ if (!ent) {
+ return false;
+ }
+
+ ent->mData = aData;
+
+ return true;
+ }
+
+ /**
+ * remove the data for the associated key
+ * @param aKey the key to remove from the hashtable
+ */
+ void Remove(KeyType aKey) { this->RemoveEntry(aKey); }
+
+ // This is an iterator that also allows entry removal. Example usage:
+ //
+ // for (auto iter = table.Iter(); !iter.Done(); iter.Next()) {
+ // const KeyType key = iter.Key();
+ // const UserDataType data = iter.UserData();
+ // // or
+ // const DataType& data = iter.Data();
+ // // ... do stuff with |key| and/or |data| ...
+ // // ... possibly call iter.Remove() once ...
+ // }
+ //
+ class Iterator : public PLDHashTable::Iterator
+ {
+ public:
+ typedef PLDHashTable::Iterator Base;
+
+ explicit Iterator(nsBaseHashtable* aTable) : Base(&aTable->mTable) {}
+ Iterator(Iterator&& aOther) : Base(aOther.mTable) {}
+ ~Iterator() {}
+
+ KeyType Key() const { return static_cast<EntryType*>(Get())->GetKey(); }
+ UserDataType UserData() const
+ {
+ return static_cast<EntryType*>(Get())->mData;
+ }
+ DataType& Data() const { return static_cast<EntryType*>(Get())->mData; }
+
+ private:
+ Iterator() = delete;
+ Iterator(const Iterator&) = delete;
+ Iterator& operator=(const Iterator&) = delete;
+ Iterator& operator=(const Iterator&&) = delete;
+ };
+
+ Iterator Iter() { return Iterator(this); }
+
+ Iterator ConstIter() const
+ {
+ return Iterator(const_cast<nsBaseHashtable*>(this));
+ }
+
+ /**
+ * reset the hashtable, removing all entries
+ */
+ void Clear() { nsTHashtable<EntryType>::Clear(); }
+
+ /**
+ * Measure the size of the table's entry storage. The size of things pointed
+ * to by entries must be measured separately; hence the "Shallow" prefix.
+ *
+ * @param aMallocSizeOf the function used to measure heap-allocated blocks
+ * @return the summed size of the table's storage
+ */
+ size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return this->mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ /**
+ * Like ShallowSizeOfExcludingThis, but includes sizeof(*this).
+ */
+ size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return aMallocSizeOf(this) + ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ /**
+ * Swap the elements in this hashtable with the elements in aOther.
+ */
+ void SwapElements(nsBaseHashtable& aOther)
+ {
+ nsTHashtable<EntryType>::SwapElements(aOther);
+ }
+
+
+#ifdef DEBUG
+ using nsTHashtable<EntryType>::MarkImmutable;
+#endif
+};
+
+//
+// nsBaseHashtableET definitions
+//
+
+template<class KeyClass, class DataType>
+nsBaseHashtableET<KeyClass, DataType>::nsBaseHashtableET(KeyTypePointer aKey)
+ : KeyClass(aKey)
+ , mData()
+{
+}
+
+template<class KeyClass, class DataType>
+nsBaseHashtableET<KeyClass, DataType>::nsBaseHashtableET(
+ nsBaseHashtableET<KeyClass, DataType>&& aToMove)
+ : KeyClass(mozilla::Move(aToMove))
+ , mData(mozilla::Move(aToMove.mData))
+{
+}
+
+template<class KeyClass, class DataType>
+nsBaseHashtableET<KeyClass, DataType>::~nsBaseHashtableET()
+{
+}
+
+#endif // nsBaseHashtable_h__
diff --git a/xpcom/glue/nsCOMArray.cpp b/xpcom/glue/nsCOMArray.cpp
new file mode 100644
index 000000000..3522f7b0d
--- /dev/null
+++ b/xpcom/glue/nsCOMArray.cpp
@@ -0,0 +1,323 @@
+/* -*- 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 "nsCOMArray.h"
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/OperatorNewExtensions.h"
+
+#include "nsCOMPtr.h"
+
+// This specialization is private to nsCOMArray.
+// It exists solely to automatically zero-out newly created array elements.
+template<>
+class nsTArrayElementTraits<nsISupports*>
+{
+ typedef nsISupports* E;
+public:
+ // Zero out the value
+ static inline void Construct(E* aE)
+ {
+ new (mozilla::KnownNotNull, static_cast<void*>(aE)) E();
+ }
+ // Invoke the copy-constructor in place.
+ template<class A>
+ static inline void Construct(E* aE, const A& aArg)
+ {
+ new (mozilla::KnownNotNull, static_cast<void*>(aE)) E(aArg);
+ }
+ // Invoke the destructor in place.
+ static inline void Destruct(E* aE)
+ {
+ aE->~E();
+ }
+};
+
+static void ReleaseObjects(nsTArray<nsISupports*>& aArray);
+
+// implementations of non-trivial methods in nsCOMArray_base
+
+nsCOMArray_base::nsCOMArray_base(const nsCOMArray_base& aOther)
+{
+ // make sure we do only one allocation
+ mArray.SetCapacity(aOther.Count());
+ AppendObjects(aOther);
+}
+
+nsCOMArray_base::~nsCOMArray_base()
+{
+ Clear();
+}
+
+int32_t
+nsCOMArray_base::IndexOf(nsISupports* aObject, uint32_t aStartIndex) const
+{
+ return mArray.IndexOf(aObject, aStartIndex);
+}
+
+int32_t
+nsCOMArray_base::IndexOfObject(nsISupports* aObject) const
+{
+ nsCOMPtr<nsISupports> supports = do_QueryInterface(aObject);
+ if (NS_WARN_IF(!supports)) {
+ return -1;
+ }
+
+ uint32_t i, count;
+ int32_t retval = -1;
+ count = mArray.Length();
+ for (i = 0; i < count; ++i) {
+ nsCOMPtr<nsISupports> arrayItem = do_QueryInterface(mArray[i]);
+ if (arrayItem == supports) {
+ retval = i;
+ break;
+ }
+ }
+ return retval;
+}
+
+bool
+nsCOMArray_base::EnumerateForwards(nsBaseArrayEnumFunc aFunc, void* aData) const
+{
+ for (uint32_t index = 0; index < mArray.Length(); ++index) {
+ if (!(*aFunc)(mArray[index], aData)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+nsCOMArray_base::EnumerateBackwards(nsBaseArrayEnumFunc aFunc, void* aData) const
+{
+ for (uint32_t index = mArray.Length(); index--; ) {
+ if (!(*aFunc)(mArray[index], aData)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int
+nsCOMArray_base::nsCOMArrayComparator(const void* aElement1,
+ const void* aElement2,
+ void* aData)
+{
+ nsCOMArrayComparatorContext* ctx =
+ static_cast<nsCOMArrayComparatorContext*>(aData);
+ return (*ctx->mComparatorFunc)(*static_cast<nsISupports* const*>(aElement1),
+ *static_cast<nsISupports* const*>(aElement2),
+ ctx->mData);
+}
+
+void
+nsCOMArray_base::Sort(nsBaseArrayComparatorFunc aFunc, void* aData)
+{
+ if (mArray.Length() > 1) {
+ nsCOMArrayComparatorContext ctx = {aFunc, aData};
+ NS_QuickSort(mArray.Elements(), mArray.Length(), sizeof(nsISupports*),
+ nsCOMArrayComparator, &ctx);
+ }
+}
+
+bool
+nsCOMArray_base::InsertObjectAt(nsISupports* aObject, int32_t aIndex)
+{
+ if ((uint32_t)aIndex > mArray.Length()) {
+ return false;
+ }
+
+ if (!mArray.InsertElementAt(aIndex, aObject)) {
+ return false;
+ }
+
+ NS_IF_ADDREF(aObject);
+ return true;
+}
+
+void
+nsCOMArray_base::InsertElementAt(uint32_t aIndex, nsISupports* aElement)
+{
+ mArray.InsertElementAt(aIndex, aElement);
+ NS_IF_ADDREF(aElement);
+}
+
+void
+nsCOMArray_base::InsertElementAt(uint32_t aIndex, already_AddRefed<nsISupports> aElement)
+{
+ mArray.InsertElementAt(aIndex, aElement.take());
+}
+
+bool
+nsCOMArray_base::InsertObjectsAt(const nsCOMArray_base& aObjects, int32_t aIndex)
+{
+ if ((uint32_t)aIndex > mArray.Length()) {
+ return false;
+ }
+
+ if (!mArray.InsertElementsAt(aIndex, aObjects.mArray)) {
+ return false;
+ }
+
+ // need to addref all these
+ uint32_t count = aObjects.Length();
+ for (uint32_t i = 0; i < count; ++i) {
+ NS_IF_ADDREF(aObjects[i]);
+ }
+
+ return true;
+}
+
+void
+nsCOMArray_base::InsertElementsAt(uint32_t aIndex,
+ const nsCOMArray_base& aElements)
+{
+ mArray.InsertElementsAt(aIndex, aElements.mArray);
+
+ // need to addref all these
+ uint32_t count = aElements.Length();
+ for (uint32_t i = 0; i < count; ++i) {
+ NS_IF_ADDREF(aElements[i]);
+ }
+}
+
+void
+nsCOMArray_base::InsertElementsAt(uint32_t aIndex,
+ nsISupports* const* aElements,
+ uint32_t aCount)
+{
+ mArray.InsertElementsAt(aIndex, aElements, aCount);
+
+ // need to addref all these
+ for (uint32_t i = 0; i < aCount; ++i) {
+ NS_IF_ADDREF(aElements[i]);
+ }
+}
+
+void
+nsCOMArray_base::ReplaceObjectAt(nsISupports* aObject, int32_t aIndex)
+{
+ mArray.EnsureLengthAtLeast(aIndex + 1);
+ nsISupports* oldObject = mArray[aIndex];
+ // Make sure to addref first, in case aObject == oldObject
+ NS_IF_ADDREF(mArray[aIndex] = aObject);
+ NS_IF_RELEASE(oldObject);
+}
+
+bool
+nsCOMArray_base::RemoveObject(nsISupports* aObject)
+{
+ bool result = mArray.RemoveElement(aObject);
+ if (result) {
+ NS_IF_RELEASE(aObject);
+ }
+ return result;
+}
+
+bool
+nsCOMArray_base::RemoveObjectAt(int32_t aIndex)
+{
+ if (uint32_t(aIndex) < mArray.Length()) {
+ nsISupports* element = mArray[aIndex];
+
+ mArray.RemoveElementAt(aIndex);
+ NS_IF_RELEASE(element);
+ return true;
+ }
+
+ return false;
+}
+
+void
+nsCOMArray_base::RemoveElementAt(uint32_t aIndex)
+{
+ nsISupports* element = mArray[aIndex];
+ mArray.RemoveElementAt(aIndex);
+ NS_IF_RELEASE(element);
+}
+
+bool
+nsCOMArray_base::RemoveObjectsAt(int32_t aIndex, int32_t aCount)
+{
+ if (uint32_t(aIndex) + uint32_t(aCount) <= mArray.Length()) {
+ nsTArray<nsISupports*> elementsToDestroy(aCount);
+ elementsToDestroy.AppendElements(mArray.Elements() + aIndex, aCount);
+ mArray.RemoveElementsAt(aIndex, aCount);
+ ReleaseObjects(elementsToDestroy);
+ return true;
+ }
+
+ return false;
+}
+
+void
+nsCOMArray_base::RemoveElementsAt(uint32_t aIndex, uint32_t aCount)
+{
+ nsTArray<nsISupports*> elementsToDestroy(aCount);
+ elementsToDestroy.AppendElements(mArray.Elements() + aIndex, aCount);
+ mArray.RemoveElementsAt(aIndex, aCount);
+ ReleaseObjects(elementsToDestroy);
+}
+
+// useful for destructors
+void
+ReleaseObjects(nsTArray<nsISupports*>& aArray)
+{
+ for (uint32_t i = 0; i < aArray.Length(); ++i) {
+ NS_IF_RELEASE(aArray[i]);
+ }
+}
+
+void
+nsCOMArray_base::Clear()
+{
+ nsTArray<nsISupports*> objects;
+ objects.SwapElements(mArray);
+ ReleaseObjects(objects);
+}
+
+bool
+nsCOMArray_base::SetCount(int32_t aNewCount)
+{
+ NS_ASSERTION(aNewCount >= 0, "SetCount(negative index)");
+ if (aNewCount < 0) {
+ return false;
+ }
+
+ int32_t count = mArray.Length();
+ if (count > aNewCount) {
+ RemoveObjectsAt(aNewCount, mArray.Length() - aNewCount);
+ }
+ mArray.SetLength(aNewCount);
+ return true;
+}
+
+void
+nsCOMArray_base::Adopt(nsISupports** aElements, uint32_t aSize)
+{
+ Clear();
+ mArray.AppendElements(aElements, aSize);
+
+ // Free the allocated array as well.
+ NS_Free(aElements);
+}
+
+uint32_t
+nsCOMArray_base::Forget(nsISupports*** aElements)
+{
+ uint32_t length = Length();
+ size_t array_size = sizeof(nsISupports*) * length;
+ nsISupports** array = static_cast<nsISupports**>(NS_Alloc(array_size));
+ memmove(array, Elements(), array_size);
+ *aElements = array;
+ // Don't Release the contained pointers; the caller of the method will
+ // do this eventually.
+ mArray.Clear();
+
+ return length;
+}
diff --git a/xpcom/glue/nsCOMArray.h b/xpcom/glue/nsCOMArray.h
new file mode 100644
index 000000000..56d077a2f
--- /dev/null
+++ b/xpcom/glue/nsCOMArray.h
@@ -0,0 +1,473 @@
+/* -*- 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 nsCOMArray_h__
+#define nsCOMArray_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/MemoryReporting.h"
+
+#include "nsCycleCollectionNoteChild.h"
+#include "nsTArray.h"
+#include "nsISupports.h"
+
+// See below for the definition of nsCOMArray<T>
+
+// a class that's nsISupports-specific, so that we can contain the
+// work of this class in the XPCOM dll
+class nsCOMArray_base
+{
+ friend class nsArrayBase;
+protected:
+ nsCOMArray_base() {}
+ explicit nsCOMArray_base(int32_t aCount) : mArray(aCount) {}
+ nsCOMArray_base(const nsCOMArray_base& aOther);
+ ~nsCOMArray_base();
+
+ int32_t IndexOf(nsISupports* aObject, uint32_t aStartIndex = 0) const;
+ bool Contains(nsISupports* aObject) const
+ {
+ return IndexOf(aObject) != -1;
+ }
+
+ int32_t IndexOfObject(nsISupports* aObject) const;
+ bool ContainsObject(nsISupports* aObject) const
+ {
+ return IndexOfObject(aObject) != -1;
+ }
+
+ typedef bool (*nsBaseArrayEnumFunc)(void* aElement, void* aData);
+
+ // enumerate through the array with a callback.
+ bool EnumerateForwards(nsBaseArrayEnumFunc aFunc, void* aData) const;
+
+ bool EnumerateBackwards(nsBaseArrayEnumFunc aFunc, void* aData) const;
+
+ typedef int (*nsBaseArrayComparatorFunc)(nsISupports* aElement1,
+ nsISupports* aElement2,
+ void* aData);
+
+ struct nsCOMArrayComparatorContext
+ {
+ nsBaseArrayComparatorFunc mComparatorFunc;
+ void* mData;
+ };
+
+ static int nsCOMArrayComparator(const void* aElement1, const void* aElement2,
+ void* aData);
+ void Sort(nsBaseArrayComparatorFunc aFunc, void* aData);
+
+ bool InsertObjectAt(nsISupports* aObject, int32_t aIndex);
+ void InsertElementAt(uint32_t aIndex, nsISupports* aElement);
+ void InsertElementAt(uint32_t aIndex, already_AddRefed<nsISupports> aElement);
+ bool InsertObjectsAt(const nsCOMArray_base& aObjects, int32_t aIndex);
+ void InsertElementsAt(uint32_t aIndex, const nsCOMArray_base& aElements);
+ void InsertElementsAt(uint32_t aIndex, nsISupports* const* aElements,
+ uint32_t aCount);
+ void ReplaceObjectAt(nsISupports* aObject, int32_t aIndex);
+ void ReplaceElementAt(uint32_t aIndex, nsISupports* aElement)
+ {
+ nsISupports* oldElement = mArray[aIndex];
+ NS_IF_ADDREF(mArray[aIndex] = aElement);
+ NS_IF_RELEASE(oldElement);
+ }
+ bool AppendObject(nsISupports* aObject)
+ {
+ return InsertObjectAt(aObject, Count());
+ }
+ void AppendElement(nsISupports* aElement)
+ {
+ InsertElementAt(Length(), aElement);
+ }
+ void AppendElement(already_AddRefed<nsISupports> aElement)
+ {
+ InsertElementAt(Length(), mozilla::Move(aElement));
+ }
+
+ bool AppendObjects(const nsCOMArray_base& aObjects)
+ {
+ return InsertObjectsAt(aObjects, Count());
+ }
+ void AppendElements(const nsCOMArray_base& aElements)
+ {
+ return InsertElementsAt(Length(), aElements);
+ }
+ void AppendElements(nsISupports* const* aElements, uint32_t aCount)
+ {
+ return InsertElementsAt(Length(), aElements, aCount);
+ }
+ bool RemoveObject(nsISupports* aObject);
+ nsISupports** Elements() { return mArray.Elements(); }
+ void SwapElements(nsCOMArray_base& aOther)
+ {
+ mArray.SwapElements(aOther.mArray);
+ }
+
+ void Adopt(nsISupports** aElements, uint32_t aCount);
+ uint32_t Forget(nsISupports*** aElements);
+public:
+ // elements in the array (including null elements!)
+ int32_t Count() const { return mArray.Length(); }
+ // nsTArray-compatible version
+ uint32_t Length() const { return mArray.Length(); }
+ bool IsEmpty() const { return mArray.IsEmpty(); }
+
+ // If the array grows, the newly created entries will all be null;
+ // if the array shrinks, the excess entries will all be released.
+ bool SetCount(int32_t aNewCount);
+ // nsTArray-compatible version
+ void TruncateLength(uint32_t aNewLength)
+ {
+ if (mArray.Length() > aNewLength) {
+ RemoveElementsAt(aNewLength, mArray.Length() - aNewLength);
+ }
+ }
+
+ // remove all elements in the array, and call NS_RELEASE on each one
+ void Clear();
+
+ nsISupports* ObjectAt(int32_t aIndex) const { return mArray[aIndex]; }
+ // nsTArray-compatible version
+ nsISupports* ElementAt(uint32_t aIndex) const { return mArray[aIndex]; }
+
+ nsISupports* SafeObjectAt(int32_t aIndex) const
+ {
+ return mArray.SafeElementAt(aIndex, nullptr);
+ }
+ // nsTArray-compatible version
+ nsISupports* SafeElementAt(uint32_t aIndex) const
+ {
+ return mArray.SafeElementAt(aIndex, nullptr);
+ }
+
+ nsISupports* operator[](int32_t aIndex) const { return mArray[aIndex]; }
+
+ // remove an element at a specific position, shrinking the array
+ // as necessary
+ bool RemoveObjectAt(int32_t aIndex);
+ // nsTArray-compatible version
+ void RemoveElementAt(uint32_t aIndex);
+
+ // remove a range of elements at a specific position, shrinking the array
+ // as necessary
+ bool RemoveObjectsAt(int32_t aIndex, int32_t aCount);
+ // nsTArray-compatible version
+ void RemoveElementsAt(uint32_t aIndex, uint32_t aCount);
+
+ void SwapElementsAt(uint32_t aIndex1, uint32_t aIndex2)
+ {
+ nsISupports* tmp = mArray[aIndex1];
+ mArray[aIndex1] = mArray[aIndex2];
+ mArray[aIndex2] = tmp;
+ }
+
+ // Ensures there is enough space to store a total of aCapacity objects.
+ // This method never deletes any objects.
+ void SetCapacity(uint32_t aCapacity) { mArray.SetCapacity(aCapacity); }
+ uint32_t Capacity() { return mArray.Capacity(); }
+
+ // Measures the size of the array's element storage. If you want to measure
+ // anything hanging off the array, you must iterate over the elements and
+ // measure them individually; hence the "Shallow" prefix. Note that because
+ // each element in an nsCOMArray<T> is actually a T* any such iteration
+ // should use a SizeOfIncludingThis() function on each element rather than a
+ // SizeOfExcludingThis() function, so that the memory taken by the T itself
+ // is included as well as anything it points to.
+ size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return mArray.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+
+private:
+
+ // the actual storage
+ nsTArray<nsISupports*> mArray;
+
+ // don't implement these, defaults will muck with refcounts!
+ nsCOMArray_base& operator=(const nsCOMArray_base& aOther) = delete;
+};
+
+inline void
+ImplCycleCollectionUnlink(nsCOMArray_base& aField)
+{
+ aField.Clear();
+}
+
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ nsCOMArray_base& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ aFlags |= CycleCollectionEdgeNameArrayFlag;
+ int32_t length = aField.Count();
+ for (int32_t i = 0; i < length; ++i) {
+ CycleCollectionNoteChild(aCallback, aField[i], aName, aFlags);
+ }
+}
+
+
+// a non-XPCOM, refcounting array of XPCOM objects
+// used as a member variable or stack variable - this object is NOT
+// refcounted, but the objects that it holds are
+//
+// most of the read-only accessors like ObjectAt()/etc do NOT refcount
+// on the way out. This means that you can do one of two things:
+//
+// * does an addref, but holds onto a reference
+// nsCOMPtr<T> foo = array[i];
+//
+// * avoids the refcount, but foo might go stale if array[i] is ever
+// * modified/removed. Be careful not to NS_RELEASE(foo)!
+// T* foo = array[i];
+//
+// This array will accept null as an argument for any object, and will store
+// null in the array. But that also means that methods like ObjectAt() may
+// return null when referring to an existing, but null entry in the array.
+template<class T>
+class nsCOMArray : public nsCOMArray_base
+{
+public:
+ nsCOMArray() {}
+ explicit nsCOMArray(int32_t aCount) : nsCOMArray_base(aCount) {}
+ explicit nsCOMArray(const nsCOMArray<T>& aOther) : nsCOMArray_base(aOther) {}
+ nsCOMArray(nsCOMArray<T>&& aOther) { SwapElements(aOther); }
+ ~nsCOMArray() {}
+
+ // We have a move assignment operator, but no copy assignment operator.
+ nsCOMArray<T>& operator=(nsCOMArray<T> && aOther)
+ {
+ SwapElements(aOther);
+ return *this;
+ }
+
+ // these do NOT refcount on the way out, for speed
+ T* ObjectAt(int32_t aIndex) const
+ {
+ return static_cast<T*>(nsCOMArray_base::ObjectAt(aIndex));
+ }
+ // nsTArray-compatible version
+ T* ElementAt(uint32_t aIndex) const
+ {
+ return static_cast<T*>(nsCOMArray_base::ElementAt(aIndex));
+ }
+
+ // these do NOT refcount on the way out, for speed
+ T* SafeObjectAt(int32_t aIndex) const
+ {
+ return static_cast<T*>(nsCOMArray_base::SafeObjectAt(aIndex));
+ }
+ // nsTArray-compatible version
+ T* SafeElementAt(uint32_t aIndex) const
+ {
+ return static_cast<T*>(nsCOMArray_base::SafeElementAt(aIndex));
+ }
+
+ // indexing operator for syntactic sugar
+ T* operator[](int32_t aIndex) const { return ObjectAt(aIndex); }
+
+ // index of the element in question.. does NOT refcount
+ // note: this does not check COM object identity. Use
+ // IndexOfObject() for that purpose
+ int32_t IndexOf(T* aObject, uint32_t aStartIndex = 0) const
+ {
+ return nsCOMArray_base::IndexOf(aObject, aStartIndex);
+ }
+ bool Contains(T* aObject) const
+ {
+ return nsCOMArray_base::Contains(aObject);
+ }
+
+ // index of the element in question.. be careful!
+ // this is much slower than IndexOf() because it uses
+ // QueryInterface to determine actual COM identity of the object
+ // if you need to do this frequently then consider enforcing
+ // COM object identity before adding/comparing elements
+ int32_t IndexOfObject(T* aObject) const
+ {
+ return nsCOMArray_base::IndexOfObject(aObject);
+ }
+ bool ContainsObject(nsISupports* aObject) const
+ {
+ return nsCOMArray_base::ContainsObject(aObject);
+ }
+
+ // inserts aObject at aIndex, shifting the objects at aIndex and
+ // later to make space
+ bool InsertObjectAt(T* aObject, int32_t aIndex)
+ {
+ return nsCOMArray_base::InsertObjectAt(aObject, aIndex);
+ }
+ // nsTArray-compatible version
+ void InsertElementAt(uint32_t aIndex, T* aElement)
+ {
+ nsCOMArray_base::InsertElementAt(aIndex, aElement);
+ }
+
+ // inserts the objects from aObject at aIndex, shifting the
+ // objects at aIndex and later to make space
+ bool InsertObjectsAt(const nsCOMArray<T>& aObjects, int32_t aIndex)
+ {
+ return nsCOMArray_base::InsertObjectsAt(aObjects, aIndex);
+ }
+ // nsTArray-compatible version
+ void InsertElementsAt(uint32_t aIndex, const nsCOMArray<T>& aElements)
+ {
+ nsCOMArray_base::InsertElementsAt(aIndex, aElements);
+ }
+ void InsertElementsAt(uint32_t aIndex, T* const* aElements, uint32_t aCount)
+ {
+ nsCOMArray_base::InsertElementsAt(
+ aIndex, reinterpret_cast<nsISupports* const*>(aElements), aCount);
+ }
+
+ // replaces an existing element. Warning: if the array grows,
+ // the newly created entries will all be null
+ void ReplaceObjectAt(T* aObject, int32_t aIndex)
+ {
+ nsCOMArray_base::ReplaceObjectAt(aObject, aIndex);
+ }
+ // nsTArray-compatible version
+ void ReplaceElementAt(uint32_t aIndex, T* aElement)
+ {
+ nsCOMArray_base::ReplaceElementAt(aIndex, aElement);
+ }
+
+ // Enumerator callback function. Return false to stop
+ // Here's a more readable form:
+ // bool enumerate(T* aElement, void* aData)
+ typedef bool (*nsCOMArrayEnumFunc)(T* aElement, void* aData);
+
+ // enumerate through the array with a callback.
+ bool EnumerateForwards(nsCOMArrayEnumFunc aFunc, void* aData)
+ {
+ return nsCOMArray_base::EnumerateForwards(nsBaseArrayEnumFunc(aFunc),
+ aData);
+ }
+
+ bool EnumerateBackwards(nsCOMArrayEnumFunc aFunc, void* aData)
+ {
+ return nsCOMArray_base::EnumerateBackwards(nsBaseArrayEnumFunc(aFunc),
+ aData);
+ }
+
+ typedef int (*nsCOMArrayComparatorFunc)(T* aElement1, T* aElement2,
+ void* aData);
+
+ void Sort(nsCOMArrayComparatorFunc aFunc, void* aData)
+ {
+ nsCOMArray_base::Sort(nsBaseArrayComparatorFunc(aFunc), aData);
+ }
+
+ // append an object, growing the array as necessary
+ bool AppendObject(T* aObject)
+ {
+ return nsCOMArray_base::AppendObject(aObject);
+ }
+ // nsTArray-compatible version
+ void AppendElement(T* aElement)
+ {
+ nsCOMArray_base::AppendElement(aElement);
+ }
+ void AppendElement(already_AddRefed<T> aElement)
+ {
+ nsCOMArray_base::AppendElement(mozilla::Move(aElement));
+ }
+
+ // append objects, growing the array as necessary
+ bool AppendObjects(const nsCOMArray<T>& aObjects)
+ {
+ return nsCOMArray_base::AppendObjects(aObjects);
+ }
+ // nsTArray-compatible version
+ void AppendElements(const nsCOMArray<T>& aElements)
+ {
+ return nsCOMArray_base::AppendElements(aElements);
+ }
+ void AppendElements(T* const* aElements, uint32_t aCount)
+ {
+ InsertElementsAt(Length(), aElements, aCount);
+ }
+
+ // remove the first instance of the given object and shrink the
+ // array as necessary
+ // Warning: if you pass null here, it will remove the first null element
+ bool RemoveObject(T* aObject)
+ {
+ return nsCOMArray_base::RemoveObject(aObject);
+ }
+ // nsTArray-compatible version
+ bool RemoveElement(T* aElement)
+ {
+ return nsCOMArray_base::RemoveObject(aElement);
+ }
+
+ T** Elements()
+ {
+ return reinterpret_cast<T**>(nsCOMArray_base::Elements());
+ }
+ void SwapElements(nsCOMArray<T>& aOther)
+ {
+ nsCOMArray_base::SwapElements(aOther);
+ }
+
+ /**
+ * Adopt parameters that resulted from an XPIDL outparam. The aElements
+ * parameter will be freed as a result of the call.
+ *
+ * Example usage:
+ * nsCOMArray<nsISomeInterface> array;
+ * nsISomeInterface** elements;
+ * uint32_t length;
+ * ptr->GetSomeArray(&elements, &length);
+ * array.Adopt(elements, length);
+ */
+ void Adopt(T** aElements, uint32_t aSize)
+ {
+ nsCOMArray_base::Adopt(reinterpret_cast<nsISupports**>(aElements), aSize);
+ }
+
+ /**
+ * Export the contents of this array to an XPIDL outparam. The array will be
+ * Clear()'d after this operation.
+ *
+ * Example usage:
+ * nsCOMArray<nsISomeInterface> array;
+ * *length = array.Forget(retval);
+ */
+ uint32_t Forget(T*** aElements)
+ {
+ return nsCOMArray_base::Forget(reinterpret_cast<nsISupports***>(aElements));
+ }
+
+private:
+
+ // don't implement these!
+ nsCOMArray<T>& operator=(const nsCOMArray<T>& aOther) = delete;
+};
+
+template<typename T>
+inline void
+ImplCycleCollectionUnlink(nsCOMArray<T>& aField)
+{
+ aField.Clear();
+}
+
+template<typename E>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ nsCOMArray<E>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ aFlags |= CycleCollectionEdgeNameArrayFlag;
+ int32_t length = aField.Count();
+ for (int32_t i = 0; i < length; ++i) {
+ CycleCollectionNoteChild(aCallback, aField[i], aName, aFlags);
+ }
+}
+
+#endif
diff --git a/xpcom/glue/nsCOMPtr.cpp b/xpcom/glue/nsCOMPtr.cpp
new file mode 100644
index 000000000..263fa62c2
--- /dev/null
+++ b/xpcom/glue/nsCOMPtr.cpp
@@ -0,0 +1,128 @@
+/* -*- 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 "nsCOMPtr.h"
+
+nsresult
+nsQueryInterface::operator()(const nsIID& aIID, void** aAnswer) const
+{
+ nsresult status;
+ if (mRawPtr) {
+ status = mRawPtr->QueryInterface(aIID, aAnswer);
+ } else {
+ status = NS_ERROR_NULL_POINTER;
+ }
+
+ return status;
+}
+
+nsresult
+nsQueryInterfaceWithError::operator()(const nsIID& aIID, void** aAnswer) const
+{
+ nsresult status;
+ if (mRawPtr) {
+ status = mRawPtr->QueryInterface(aIID, aAnswer);
+ } else {
+ status = NS_ERROR_NULL_POINTER;
+ }
+
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+void
+nsCOMPtr_base::assign_with_AddRef(nsISupports* aRawPtr)
+{
+ if (aRawPtr) {
+ NSCAP_ADDREF(this, aRawPtr);
+ }
+ assign_assuming_AddRef(aRawPtr);
+}
+
+void
+nsCOMPtr_base::assign_from_qi(const nsQueryInterface aQI, const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aQI(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
+}
+
+void
+nsCOMPtr_base::assign_from_qi_with_error(const nsQueryInterfaceWithError& aQI,
+ const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aQI(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
+}
+
+void
+nsCOMPtr_base::assign_from_gs_cid(const nsGetServiceByCID aGS,
+ const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aGS(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
+}
+
+void
+nsCOMPtr_base::assign_from_gs_cid_with_error(
+ const nsGetServiceByCIDWithError& aGS, const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aGS(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
+}
+
+void
+nsCOMPtr_base::assign_from_gs_contractid(const nsGetServiceByContractID aGS,
+ const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aGS(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
+}
+
+void
+nsCOMPtr_base::assign_from_gs_contractid_with_error(
+ const nsGetServiceByContractIDWithError& aGS, const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aGS(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
+}
+
+void
+nsCOMPtr_base::assign_from_helper(const nsCOMPtr_helper& aHelper,
+ const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aHelper(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
+}
+
+void**
+nsCOMPtr_base::begin_assignment()
+{
+ assign_assuming_AddRef(nullptr);
+ return reinterpret_cast<void**>(&mRawPtr);
+}
diff --git a/xpcom/glue/nsCOMPtr.h b/xpcom/glue/nsCOMPtr.h
new file mode 100644
index 000000000..373f55cbb
--- /dev/null
+++ b/xpcom/glue/nsCOMPtr.h
@@ -0,0 +1,1472 @@
+/* -*- 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 nsCOMPtr_h___
+#define nsCOMPtr_h___
+
+/*
+ * Having problems?
+ *
+ * See the User Manual at:
+ * http://www.mozilla.org/projects/xpcom/nsCOMPtr.html
+ *
+ *
+ * nsCOMPtr
+ * better than a raw pointer
+ * for owning objects
+ * -- scc
+ */
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Move.h"
+#include "mozilla/TypeTraits.h"
+
+#include "nsDebug.h" // for |NS_ASSERTION|
+#include "nsISupportsUtils.h" // for |nsresult|, |NS_ADDREF|, |NS_GET_TEMPLATE_IID| et al
+#include "mozilla/RefPtr.h"
+
+#include "nsCycleCollectionNoteChild.h"
+
+
+/*
+ * WARNING: This file defines several macros for internal use only. These
+ * macros begin with the prefix |NSCAP_|. Do not use these macros in your own
+ * code. They are for internal use only for cross-platform compatibility, and
+ * are subject to change without notice.
+ */
+
+
+#ifdef _MSC_VER
+ // Under VC++, we win by inlining StartAssignment.
+ #define NSCAP_FEATURE_INLINE_STARTASSIGNMENT
+
+ // Also under VC++, at the highest warning level, we are overwhelmed with
+ // warnings about (unused) inline functions being removed. This is to be
+ // expected with templates, so we disable the warning.
+ #pragma warning( disable: 4514 )
+#endif
+
+#define NSCAP_FEATURE_USE_BASE
+
+#ifdef DEBUG
+ #define NSCAP_FEATURE_TEST_DONTQUERY_CASES
+ #undef NSCAP_FEATURE_USE_BASE
+#endif
+
+#ifdef __GNUC__
+ // Our use of nsCOMPtr_base::mRawPtr violates the C++ standard's aliasing
+ // rules. Mark it with the may_alias attribute so that gcc 3.3 and higher
+ // don't reorder instructions based on aliasing assumptions for
+ // this variable. Fortunately, gcc versions < 3.3 do not do any
+ // optimizations that break nsCOMPtr.
+
+ #define NS_MAY_ALIAS_PTR(t) t* __attribute__((__may_alias__))
+#else
+ #define NS_MAY_ALIAS_PTR(t) t*
+#endif
+
+#if defined(NSCAP_DISABLE_DEBUG_PTR_TYPES)
+ #define NSCAP_FEATURE_USE_BASE
+#endif
+
+/*
+ * The following three macros (NSCAP_ADDREF, NSCAP_RELEASE, and
+ * NSCAP_LOG_ASSIGNMENT) allow external clients the ability to add logging or
+ * other interesting debug facilities. In fact, if you want |nsCOMPtr| to
+ * participate in the standard logging facility, you provide
+ * (e.g., in "nsISupportsImpl.h") suitable definitions
+ *
+ * #define NSCAP_ADDREF(this, ptr) NS_ADDREF(ptr)
+ * #define NSCAP_RELEASE(this, ptr) NS_RELEASE(ptr)
+ */
+
+#ifndef NSCAP_ADDREF
+ #define NSCAP_ADDREF(this, ptr) (ptr)->AddRef()
+#endif
+
+#ifndef NSCAP_RELEASE
+ #define NSCAP_RELEASE(this, ptr) (ptr)->Release()
+#endif
+
+// Clients can define |NSCAP_LOG_ASSIGNMENT| to perform logging.
+#ifdef NSCAP_LOG_ASSIGNMENT
+ // Remember that |NSCAP_LOG_ASSIGNMENT| was defined by some client so that we
+ // know to instantiate |~nsGetterAddRefs| in turn to note the external
+ // assignment into the |nsCOMPtr|.
+ #define NSCAP_LOG_EXTERNAL_ASSIGNMENT
+#else
+ // ...otherwise, just strip it out of the code
+ #define NSCAP_LOG_ASSIGNMENT(this, ptr)
+#endif
+
+#ifndef NSCAP_LOG_RELEASE
+ #define NSCAP_LOG_RELEASE(this, ptr)
+#endif
+
+namespace mozilla {
+template<class T> class OwningNonNull;
+} // namespace mozilla
+
+template<class T>
+inline already_AddRefed<T>
+dont_AddRef(T* aRawPtr)
+{
+ return already_AddRefed<T>(aRawPtr);
+}
+
+template<class T>
+inline already_AddRefed<T>&&
+dont_AddRef(already_AddRefed<T>&& aAlreadyAddRefedPtr)
+{
+ return mozilla::Move(aAlreadyAddRefedPtr);
+}
+
+
+/*
+ * An nsCOMPtr_helper transforms commonly called getters into typesafe forms
+ * that are more convenient to call, and more efficient to use with |nsCOMPtr|s.
+ * Good candidates for helpers are |QueryInterface()|, |CreateInstance()|, etc.
+ *
+ * Here are the rules for a helper:
+ * - it implements |operator()| to produce an interface pointer
+ * - (except for its name) |operator()| is a valid [XP]COM `getter'
+ * - the interface pointer that it returns is already |AddRef()|ed (as from
+ * any good getter)
+ * - it matches the type requested with the supplied |nsIID| argument
+ * - its constructor provides an optional |nsresult*| that |operator()| can
+ * fill in with an error when it is executed
+ *
+ * See |class nsGetInterface| for an example.
+ */
+class MOZ_STACK_CLASS nsCOMPtr_helper
+{
+public:
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const = 0;
+};
+
+/*
+ * nsQueryInterface could have been implemented as an nsCOMPtr_helper to avoid
+ * adding specialized machinery in nsCOMPtr, but do_QueryInterface is called
+ * often enough that the codesize savings are big enough to warrant the
+ * specialcasing.
+ */
+class MOZ_STACK_CLASS nsQueryInterface final
+{
+public:
+ explicit
+ nsQueryInterface(nsISupports* aRawPtr) : mRawPtr(aRawPtr) {}
+
+ nsresult NS_FASTCALL operator()(const nsIID& aIID, void**) const;
+
+private:
+ nsISupports* MOZ_OWNING_REF mRawPtr;
+};
+
+class nsQueryInterfaceWithError final
+{
+public:
+ nsQueryInterfaceWithError(nsISupports* aRawPtr, nsresult* aError)
+ : mRawPtr(aRawPtr)
+ , mErrorPtr(aError)
+ {
+ }
+
+ nsresult NS_FASTCALL operator()(const nsIID& aIID, void**) const;
+
+private:
+ nsISupports* MOZ_OWNING_REF mRawPtr;
+ nsresult* mErrorPtr;
+};
+
+inline nsQueryInterface
+do_QueryInterface(nsISupports* aRawPtr)
+{
+ return nsQueryInterface(aRawPtr);
+}
+
+inline nsQueryInterfaceWithError
+do_QueryInterface(nsISupports* aRawPtr, nsresult* aError)
+{
+ return nsQueryInterfaceWithError(aRawPtr, aError);
+}
+
+template<class T>
+inline void
+do_QueryInterface(already_AddRefed<T>&)
+{
+ // This signature exists solely to _stop_ you from doing the bad thing.
+ // Saying |do_QueryInterface()| on a pointer that is not otherwise owned by
+ // someone else is an automatic leak. See bug 8221.
+}
+
+template<class T>
+inline void
+do_QueryInterface(already_AddRefed<T>&, nsresult*)
+{
+ // This signature exists solely to _stop_ you from doing the bad thing.
+ // Saying |do_QueryInterface()| on a pointer that is not otherwise owned by
+ // someone else is an automatic leak. See bug 8221.
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// Using servicemanager with COMPtrs
+class nsGetServiceByCID final
+{
+public:
+ explicit nsGetServiceByCID(const nsCID& aCID) : mCID(aCID) {}
+
+ nsresult NS_FASTCALL operator()(const nsIID&, void**) const;
+
+private:
+ const nsCID& mCID;
+};
+
+class nsGetServiceByCIDWithError final
+{
+public:
+ nsGetServiceByCIDWithError(const nsCID& aCID, nsresult* aErrorPtr)
+ : mCID(aCID)
+ , mErrorPtr(aErrorPtr)
+ {
+ }
+
+ nsresult NS_FASTCALL operator()(const nsIID&, void**) const;
+
+private:
+ const nsCID& mCID;
+ nsresult* mErrorPtr;
+};
+
+class nsGetServiceByContractID final
+{
+public:
+ explicit nsGetServiceByContractID(const char* aContractID)
+ : mContractID(aContractID)
+ {
+ }
+
+ nsresult NS_FASTCALL operator()(const nsIID&, void**) const;
+
+private:
+ const char* mContractID;
+};
+
+class nsGetServiceByContractIDWithError final
+{
+public:
+ nsGetServiceByContractIDWithError(const char* aContractID, nsresult* aErrorPtr)
+ : mContractID(aContractID)
+ , mErrorPtr(aErrorPtr)
+ {
+ }
+
+ nsresult NS_FASTCALL operator()(const nsIID&, void**) const;
+
+private:
+ const char* mContractID;
+ nsresult* mErrorPtr;
+};
+
+/**
+ * Factors implementation for all template versions of nsCOMPtr.
+ *
+ * Here's the way people normally do things like this:
+ *
+ * template<class T> class Foo { ... };
+ * template<> class Foo<void*> { ... };
+ * template<class T> class Foo<T*> : private Foo<void*> { ... };
+ */
+class nsCOMPtr_base
+{
+public:
+ explicit nsCOMPtr_base(nsISupports* aRawPtr = nullptr) : mRawPtr(aRawPtr) {}
+
+ NS_CONSTRUCTOR_FASTCALL ~nsCOMPtr_base()
+ {
+ NSCAP_LOG_RELEASE(this, mRawPtr);
+ if (mRawPtr) {
+ NSCAP_RELEASE(this, mRawPtr);
+ }
+ }
+
+ void NS_FASTCALL
+ assign_with_AddRef(nsISupports*);
+ void NS_FASTCALL
+ assign_from_qi(const nsQueryInterface, const nsIID&);
+ void NS_FASTCALL
+ assign_from_qi_with_error(const nsQueryInterfaceWithError&, const nsIID&);
+ void NS_FASTCALL
+ assign_from_gs_cid(const nsGetServiceByCID, const nsIID&);
+ void NS_FASTCALL
+ assign_from_gs_cid_with_error(const nsGetServiceByCIDWithError&, const nsIID&);
+ void NS_FASTCALL
+ assign_from_gs_contractid(const nsGetServiceByContractID, const nsIID&);
+ void NS_FASTCALL
+ assign_from_gs_contractid_with_error(const nsGetServiceByContractIDWithError&,
+ const nsIID&);
+ void NS_FASTCALL
+ assign_from_helper(const nsCOMPtr_helper&, const nsIID&);
+ void** NS_FASTCALL
+ begin_assignment();
+
+protected:
+ NS_MAY_ALIAS_PTR(nsISupports) MOZ_OWNING_REF mRawPtr;
+
+ void assign_assuming_AddRef(nsISupports* aNewPtr)
+ {
+ // |AddRef()|ing the new value (before entering this function) before
+ // |Release()|ing the old lets us safely ignore the self-assignment case.
+ // We must, however, be careful only to |Release()| _after_ doing the
+ // assignment, in case the |Release()| leads to our _own_ destruction,
+ // which would, in turn, cause an incorrect second |Release()| of our old
+ // pointer. Thank <waterson@netscape.com> for discovering this.
+ nsISupports* oldPtr = mRawPtr;
+ mRawPtr = aNewPtr;
+ NSCAP_LOG_ASSIGNMENT(this, aNewPtr);
+ NSCAP_LOG_RELEASE(this, oldPtr);
+ if (oldPtr) {
+ NSCAP_RELEASE(this, oldPtr);
+ }
+ }
+};
+
+// template<class T> class nsGetterAddRefs;
+
+// Helper for assert_validity method
+template<class T>
+char (&TestForIID(decltype(&NS_GET_TEMPLATE_IID(T))))[2];
+template<class T>
+char TestForIID(...);
+
+template<class T>
+class nsCOMPtr final
+#ifdef NSCAP_FEATURE_USE_BASE
+ : private nsCOMPtr_base
+#endif
+{
+
+#ifdef NSCAP_FEATURE_USE_BASE
+ #define NSCAP_CTOR_BASE(x) nsCOMPtr_base(x)
+#else
+ #define NSCAP_CTOR_BASE(x) mRawPtr(x)
+
+private:
+ void assign_with_AddRef(nsISupports*);
+ void assign_from_qi(const nsQueryInterface, const nsIID&);
+ void assign_from_qi_with_error(const nsQueryInterfaceWithError&, const nsIID&);
+ void assign_from_gs_cid(const nsGetServiceByCID, const nsIID&);
+ void assign_from_gs_cid_with_error(const nsGetServiceByCIDWithError&,
+ const nsIID&);
+ void assign_from_gs_contractid(const nsGetServiceByContractID, const nsIID&);
+ void assign_from_gs_contractid_with_error(
+ const nsGetServiceByContractIDWithError&, const nsIID&);
+ void assign_from_helper(const nsCOMPtr_helper&, const nsIID&);
+ void** begin_assignment();
+
+ void assign_assuming_AddRef(T* aNewPtr)
+ {
+ T* oldPtr = mRawPtr;
+ mRawPtr = aNewPtr;
+ NSCAP_LOG_ASSIGNMENT(this, aNewPtr);
+ NSCAP_LOG_RELEASE(this, oldPtr);
+ if (oldPtr) {
+ NSCAP_RELEASE(this, oldPtr);
+ }
+ }
+
+private:
+ T* MOZ_OWNING_REF mRawPtr;
+#endif
+
+ void assert_validity()
+ {
+ static_assert(1 < sizeof(TestForIID<T>(nullptr)), "nsCOMPtr only works "
+ "for types with IIDs. Either use RefPtr; add an IID to "
+ "your type with NS_DECLARE_STATIC_IID_ACCESSOR/"
+ "NS_DEFINE_STATIC_IID_ACCESSOR; or make the nsCOMPtr point "
+ "to a base class with an IID.");
+ }
+
+public:
+ typedef T element_type;
+
+#ifndef NSCAP_FEATURE_USE_BASE
+ ~nsCOMPtr()
+ {
+ NSCAP_LOG_RELEASE(this, mRawPtr);
+ if (mRawPtr) {
+ NSCAP_RELEASE(this, mRawPtr);
+ }
+ }
+#endif
+
+#ifdef NSCAP_FEATURE_TEST_DONTQUERY_CASES
+ void Assert_NoQueryNeeded()
+ {
+ if (mRawPtr) {
+ nsCOMPtr<T> query_result(do_QueryInterface(mRawPtr));
+ NS_ASSERTION(query_result.get() == mRawPtr, "QueryInterface needed");
+ }
+ }
+
+ #define NSCAP_ASSERT_NO_QUERY_NEEDED() Assert_NoQueryNeeded();
+#else
+ #define NSCAP_ASSERT_NO_QUERY_NEEDED()
+#endif
+
+
+ // Constructors
+
+ nsCOMPtr()
+ : NSCAP_CTOR_BASE(nullptr)
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ }
+
+ MOZ_IMPLICIT nsCOMPtr(decltype(nullptr))
+ : NSCAP_CTOR_BASE(nullptr)
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ }
+
+ nsCOMPtr(const nsCOMPtr<T>& aSmartPtr)
+ : NSCAP_CTOR_BASE(aSmartPtr.mRawPtr)
+ {
+ assert_validity();
+ if (mRawPtr) {
+ NSCAP_ADDREF(this, mRawPtr);
+ }
+ NSCAP_LOG_ASSIGNMENT(this, aSmartPtr.mRawPtr);
+ }
+
+ nsCOMPtr(nsCOMPtr<T>&& aSmartPtr)
+ : NSCAP_CTOR_BASE(aSmartPtr.mRawPtr)
+ {
+ assert_validity();
+ aSmartPtr.mRawPtr = nullptr;
+ NSCAP_LOG_ASSIGNMENT(this, mRawPtr);
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ }
+
+ MOZ_IMPLICIT nsCOMPtr(T* aRawPtr)
+ : NSCAP_CTOR_BASE(aRawPtr)
+ {
+ assert_validity();
+ if (mRawPtr) {
+ NSCAP_ADDREF(this, mRawPtr);
+ }
+ NSCAP_LOG_ASSIGNMENT(this, aRawPtr);
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ }
+
+ MOZ_IMPLICIT nsCOMPtr(already_AddRefed<T>& aSmartPtr)
+ : NSCAP_CTOR_BASE(aSmartPtr.take())
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, mRawPtr);
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ }
+
+ // Construct from |otherComPtr.forget()|.
+ MOZ_IMPLICIT nsCOMPtr(already_AddRefed<T>&& aSmartPtr)
+ : NSCAP_CTOR_BASE(aSmartPtr.take())
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, mRawPtr);
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ }
+
+ // Construct from |already_AddRefed|.
+ template<typename U>
+ MOZ_IMPLICIT nsCOMPtr(already_AddRefed<U>& aSmartPtr)
+ : NSCAP_CTOR_BASE(static_cast<T*>(aSmartPtr.take()))
+ {
+ assert_validity();
+ // But make sure that U actually inherits from T.
+ static_assert(mozilla::IsBaseOf<T, U>::value,
+ "U is not a subclass of T");
+ NSCAP_LOG_ASSIGNMENT(this, static_cast<T*>(mRawPtr));
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ }
+
+ // Construct from |otherComPtr.forget()|.
+ template<typename U>
+ MOZ_IMPLICIT nsCOMPtr(already_AddRefed<U>&& aSmartPtr)
+ : NSCAP_CTOR_BASE(static_cast<T*>(aSmartPtr.take()))
+ {
+ assert_validity();
+ // But make sure that U actually inherits from T.
+ static_assert(mozilla::IsBaseOf<T, U>::value,
+ "U is not a subclass of T");
+ NSCAP_LOG_ASSIGNMENT(this, static_cast<T*>(mRawPtr));
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ }
+
+ // Construct from |do_QueryInterface(expr)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsQueryInterface aQI)
+ : NSCAP_CTOR_BASE(nullptr)
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_qi(aQI, NS_GET_TEMPLATE_IID(T));
+ }
+
+ // Construct from |do_QueryInterface(expr, &rv)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsQueryInterfaceWithError& aQI)
+ : NSCAP_CTOR_BASE(nullptr)
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_qi_with_error(aQI, NS_GET_TEMPLATE_IID(T));
+ }
+
+ // Construct from |do_GetService(cid_expr)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByCID aGS)
+ : NSCAP_CTOR_BASE(nullptr)
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_gs_cid(aGS, NS_GET_TEMPLATE_IID(T));
+ }
+
+ // Construct from |do_GetService(cid_expr, &rv)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByCIDWithError& aGS)
+ : NSCAP_CTOR_BASE(nullptr)
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_gs_cid_with_error(aGS, NS_GET_TEMPLATE_IID(T));
+ }
+
+ // Construct from |do_GetService(contractid_expr)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByContractID aGS)
+ : NSCAP_CTOR_BASE(nullptr)
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_gs_contractid(aGS, NS_GET_TEMPLATE_IID(T));
+ }
+
+ // Construct from |do_GetService(contractid_expr, &rv)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByContractIDWithError& aGS)
+ : NSCAP_CTOR_BASE(nullptr)
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_gs_contractid_with_error(aGS, NS_GET_TEMPLATE_IID(T));
+ }
+
+ // And finally, anything else we might need to construct from can exploit the
+ // nsCOMPtr_helper facility.
+ MOZ_IMPLICIT nsCOMPtr(const nsCOMPtr_helper& aHelper)
+ : NSCAP_CTOR_BASE(nullptr)
+ {
+ assert_validity();
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_helper(aHelper, NS_GET_TEMPLATE_IID(T));
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ }
+
+ // Defined in OwningNonNull.h
+ template<class U>
+ MOZ_IMPLICIT nsCOMPtr(const mozilla::OwningNonNull<U>& aOther);
+
+
+ // Assignment operators
+
+ nsCOMPtr<T>& operator=(const nsCOMPtr<T>& aRhs)
+ {
+ assign_with_AddRef(aRhs.mRawPtr);
+ return *this;
+ }
+
+ nsCOMPtr<T>& operator=(T* aRhs)
+ {
+ assign_with_AddRef(aRhs);
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ return *this;
+ }
+
+ nsCOMPtr<T>& operator=(decltype(nullptr))
+ {
+ assign_assuming_AddRef(nullptr);
+ return *this;
+ }
+
+ // Assign from |already_AddRefed|.
+ template<typename U>
+ nsCOMPtr<T>& operator=(already_AddRefed<U>& aRhs)
+ {
+ // Make sure that U actually inherits from T
+ static_assert(mozilla::IsBaseOf<T, U>::value,
+ "U is not a subclass of T");
+ assign_assuming_AddRef(static_cast<T*>(aRhs.take()));
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ return *this;
+ }
+
+ // Assign from |otherComPtr.forget()|.
+ template<typename U>
+ nsCOMPtr<T>& operator=(already_AddRefed<U>&& aRhs)
+ {
+ // Make sure that U actually inherits from T
+ static_assert(mozilla::IsBaseOf<T, U>::value,
+ "U is not a subclass of T");
+ assign_assuming_AddRef(static_cast<T*>(aRhs.take()));
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ return *this;
+ }
+
+ // Assign from |do_QueryInterface(expr)|.
+ nsCOMPtr<T>& operator=(const nsQueryInterface aRhs)
+ {
+ assign_from_qi(aRhs, NS_GET_TEMPLATE_IID(T));
+ return *this;
+ }
+
+ // Assign from |do_QueryInterface(expr, &rv)|.
+ nsCOMPtr<T>& operator=(const nsQueryInterfaceWithError& aRhs)
+ {
+ assign_from_qi_with_error(aRhs, NS_GET_TEMPLATE_IID(T));
+ return *this;
+ }
+
+ // Assign from |do_GetService(cid_expr)|.
+ nsCOMPtr<T>& operator=(const nsGetServiceByCID aRhs)
+ {
+ assign_from_gs_cid(aRhs, NS_GET_TEMPLATE_IID(T));
+ return *this;
+ }
+
+ // Assign from |do_GetService(cid_expr, &rv)|.
+ nsCOMPtr<T>& operator=(const nsGetServiceByCIDWithError& aRhs)
+ {
+ assign_from_gs_cid_with_error(aRhs, NS_GET_TEMPLATE_IID(T));
+ return *this;
+ }
+
+ // Assign from |do_GetService(contractid_expr)|.
+ nsCOMPtr<T>& operator=(const nsGetServiceByContractID aRhs)
+ {
+ assign_from_gs_contractid(aRhs, NS_GET_TEMPLATE_IID(T));
+ return *this;
+ }
+
+ // Assign from |do_GetService(contractid_expr, &rv)|.
+ nsCOMPtr<T>& operator=(const nsGetServiceByContractIDWithError& aRhs)
+ {
+ assign_from_gs_contractid_with_error(aRhs, NS_GET_TEMPLATE_IID(T));
+ return *this;
+ }
+
+ // And finally, anything else we might need to assign from can exploit the
+ // nsCOMPtr_helper facility.
+ nsCOMPtr<T>& operator=(const nsCOMPtr_helper& aRhs)
+ {
+ assign_from_helper(aRhs, NS_GET_TEMPLATE_IID(T));
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ return *this;
+ }
+
+ // Defined in OwningNonNull.h
+ template<class U>
+ nsCOMPtr<T>& operator=(const mozilla::OwningNonNull<U>& aOther);
+
+ // Exchange ownership with |aRhs|; can save a pair of refcount operations.
+ void swap(nsCOMPtr<T>& aRhs)
+ {
+#ifdef NSCAP_FEATURE_USE_BASE
+ nsISupports* temp = aRhs.mRawPtr;
+#else
+ T* temp = aRhs.mRawPtr;
+#endif
+ NSCAP_LOG_ASSIGNMENT(&aRhs, mRawPtr);
+ NSCAP_LOG_ASSIGNMENT(this, temp);
+ NSCAP_LOG_RELEASE(this, mRawPtr);
+ NSCAP_LOG_RELEASE(&aRhs, temp);
+ aRhs.mRawPtr = mRawPtr;
+ mRawPtr = temp;
+ // |aRhs| maintains the same invariants, so we don't need to |NSCAP_ASSERT_NO_QUERY_NEEDED|
+ }
+
+ // Exchange ownership with |aRhs|; can save a pair of refcount operations.
+ void swap(T*& aRhs)
+ {
+#ifdef NSCAP_FEATURE_USE_BASE
+ nsISupports* temp = aRhs;
+#else
+ T* temp = aRhs;
+#endif
+ NSCAP_LOG_ASSIGNMENT(this, temp);
+ NSCAP_LOG_RELEASE(this, mRawPtr);
+ aRhs = reinterpret_cast<T*>(mRawPtr);
+ mRawPtr = temp;
+ NSCAP_ASSERT_NO_QUERY_NEEDED();
+ }
+
+
+ // Other pointer operators
+
+ // Return the value of mRawPtr and null out mRawPtr. Useful for
+ // already_AddRefed return values.
+ already_AddRefed<T> forget()
+ {
+ T* temp = nullptr;
+ swap(temp);
+ return already_AddRefed<T>(temp);
+ }
+
+ // Set the target of aRhs to the value of mRawPtr and null out mRawPtr.
+ // Useful to avoid unnecessary AddRef/Release pairs with "out" parameters
+ // where aRhs bay be a T** or an I** where I is a base class of T.
+ template<typename I>
+ void forget(I** aRhs)
+ {
+ NS_ASSERTION(aRhs, "Null pointer passed to forget!");
+ NSCAP_LOG_RELEASE(this, mRawPtr);
+ *aRhs = get();
+ mRawPtr = nullptr;
+ }
+
+ // Prefer the implicit conversion provided automatically by
+ // |operator T*() const|. Use |get()| to resolve ambiguity or to get a
+ // castable pointer.
+ T* get() const { return reinterpret_cast<T*>(mRawPtr); }
+
+ // Makes an nsCOMPtr act like its underlying raw pointer type whenever it is
+ // used in a context where a raw pointer is expected. It is this operator
+ // that makes an nsCOMPtr substitutable for a raw pointer.
+ //
+ // Prefer the implicit use of this operator to calling |get()|, except where
+ // necessary to resolve ambiguity.
+ operator T*() const
+#ifdef MOZ_HAVE_REF_QUALIFIERS
+ &
+#endif
+ { return get(); }
+
+#ifdef MOZ_HAVE_REF_QUALIFIERS
+ // Don't allow implicit conversion of temporary nsCOMPtr to raw pointer,
+ // because the refcount might be one and the pointer will immediately become
+ // invalid.
+ operator T*() const && = delete;
+
+ // Needed to avoid the deleted operator above
+ explicit operator bool() const { return !!mRawPtr; }
+#endif
+
+ T* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN
+ {
+ MOZ_ASSERT(mRawPtr != nullptr,
+ "You can't dereference a NULL nsCOMPtr with operator->().");
+ return get();
+ }
+
+ // These are not intended to be used by clients. See |address_of| below.
+ nsCOMPtr<T>* get_address() { return this; }
+ const nsCOMPtr<T>* get_address() const { return this; }
+
+public:
+ T& operator*() const
+ {
+ MOZ_ASSERT(mRawPtr != nullptr,
+ "You can't dereference a NULL nsCOMPtr with operator*().");
+ return *get();
+ }
+
+ T** StartAssignment()
+ {
+#ifndef NSCAP_FEATURE_INLINE_STARTASSIGNMENT
+ return reinterpret_cast<T**>(begin_assignment());
+#else
+ assign_assuming_AddRef(nullptr);
+ return reinterpret_cast<T**>(&mRawPtr);
+#endif
+ }
+};
+
+
+/*
+ * Specializing nsCOMPtr for nsISupports allows us to use nsCOMPtr<nsISupports>
+ * the same way people use nsISupports* and void*, i.e., as a `catch-all'
+ * pointing to any valid [XP]COM interface. Otherwise, an nsCOMPtr<nsISupports>
+ * would only be able to point to the single [XP]COM-correct nsISupports
+ * instance within an object; extra querying ensues. Clients need to be able to
+ * pass around arbitrary interface pointers, without hassles, through
+ * intermediary code that doesn't know the exact type.
+ */
+template<>
+class nsCOMPtr<nsISupports>
+ : private nsCOMPtr_base
+{
+public:
+ typedef nsISupports element_type;
+
+ // Constructors
+
+ nsCOMPtr()
+ : nsCOMPtr_base(nullptr)
+ {
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ }
+
+ MOZ_IMPLICIT nsCOMPtr(decltype(nullptr))
+ : nsCOMPtr_base(nullptr)
+ {
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ }
+
+ nsCOMPtr(const nsCOMPtr<nsISupports>& aSmartPtr)
+ : nsCOMPtr_base(aSmartPtr.mRawPtr)
+ {
+ if (mRawPtr) {
+ NSCAP_ADDREF(this, mRawPtr);
+ }
+ NSCAP_LOG_ASSIGNMENT(this, aSmartPtr.mRawPtr);
+ }
+
+ MOZ_IMPLICIT nsCOMPtr(nsISupports* aRawPtr)
+ : nsCOMPtr_base(aRawPtr)
+ {
+ if (mRawPtr) {
+ NSCAP_ADDREF(this, mRawPtr);
+ }
+ NSCAP_LOG_ASSIGNMENT(this, aRawPtr);
+ }
+
+ // Construct from |already_AddRefed|.
+ MOZ_IMPLICIT nsCOMPtr(already_AddRefed<nsISupports>& aSmartPtr)
+ : nsCOMPtr_base(aSmartPtr.take())
+ {
+ NSCAP_LOG_ASSIGNMENT(this, mRawPtr);
+ }
+
+ // Construct from |otherComPtr.forget()|.
+ MOZ_IMPLICIT nsCOMPtr(already_AddRefed<nsISupports>&& aSmartPtr)
+ : nsCOMPtr_base(aSmartPtr.take())
+ {
+ NSCAP_LOG_ASSIGNMENT(this, mRawPtr);
+ }
+
+ // Construct from |do_QueryInterface(expr)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsQueryInterface aQI)
+ : nsCOMPtr_base(nullptr)
+ {
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_qi(aQI, NS_GET_IID(nsISupports));
+ }
+
+ // Construct from |do_QueryInterface(expr, &rv)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsQueryInterfaceWithError& aQI)
+ : nsCOMPtr_base(nullptr)
+ {
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_qi_with_error(aQI, NS_GET_IID(nsISupports));
+ }
+
+ // Construct from |do_GetService(cid_expr)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByCID aGS)
+ : nsCOMPtr_base(nullptr)
+ {
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_gs_cid(aGS, NS_GET_IID(nsISupports));
+ }
+
+ // Construct from |do_GetService(cid_expr, &rv)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByCIDWithError& aGS)
+ : nsCOMPtr_base(nullptr)
+ {
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_gs_cid_with_error(aGS, NS_GET_IID(nsISupports));
+ }
+
+ // Construct from |do_GetService(contractid_expr)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByContractID aGS)
+ : nsCOMPtr_base(nullptr)
+ {
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_gs_contractid(aGS, NS_GET_IID(nsISupports));
+ }
+
+ // Construct from |do_GetService(contractid_expr, &rv)|.
+ MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByContractIDWithError& aGS)
+ : nsCOMPtr_base(nullptr)
+ {
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_gs_contractid_with_error(aGS, NS_GET_IID(nsISupports));
+ }
+
+ // And finally, anything else we might need to construct from can exploit
+ // the |nsCOMPtr_helper| facility
+ MOZ_IMPLICIT nsCOMPtr(const nsCOMPtr_helper& aHelper)
+ : nsCOMPtr_base(nullptr)
+ {
+ NSCAP_LOG_ASSIGNMENT(this, nullptr);
+ assign_from_helper(aHelper, NS_GET_IID(nsISupports));
+ }
+
+
+ // Assignment operators
+
+ nsCOMPtr<nsISupports>& operator=(const nsCOMPtr<nsISupports>& aRhs)
+ {
+ assign_with_AddRef(aRhs.mRawPtr);
+ return *this;
+ }
+
+ nsCOMPtr<nsISupports>& operator=(nsISupports* aRhs)
+ {
+ assign_with_AddRef(aRhs);
+ return *this;
+ }
+
+ nsCOMPtr<nsISupports>& operator=(decltype(nullptr))
+ {
+ assign_assuming_AddRef(nullptr);
+ return *this;
+ }
+
+ // Assign from |already_AddRefed|.
+ nsCOMPtr<nsISupports>& operator=(already_AddRefed<nsISupports>& aRhs)
+ {
+ assign_assuming_AddRef(aRhs.take());
+ return *this;
+ }
+
+ // Assign from |otherComPtr.forget()|.
+ nsCOMPtr<nsISupports>& operator=(already_AddRefed<nsISupports>&& aRhs)
+ {
+ assign_assuming_AddRef(aRhs.take());
+ return *this;
+ }
+
+ // Assign from |do_QueryInterface(expr)|.
+ nsCOMPtr<nsISupports>& operator=(const nsQueryInterface aRhs)
+ {
+ assign_from_qi(aRhs, NS_GET_IID(nsISupports));
+ return *this;
+ }
+
+ // Assign from |do_QueryInterface(expr, &rv)|.
+ nsCOMPtr<nsISupports>& operator=(const nsQueryInterfaceWithError& aRhs)
+ {
+ assign_from_qi_with_error(aRhs, NS_GET_IID(nsISupports));
+ return *this;
+ }
+
+ // Assign from |do_GetService(cid_expr)|.
+ nsCOMPtr<nsISupports>& operator=(const nsGetServiceByCID aRhs)
+ {
+ assign_from_gs_cid(aRhs, NS_GET_IID(nsISupports));
+ return *this;
+ }
+
+ // Assign from |do_GetService(cid_expr, &rv)|.
+ nsCOMPtr<nsISupports>& operator=(const nsGetServiceByCIDWithError& aRhs)
+ {
+ assign_from_gs_cid_with_error(aRhs, NS_GET_IID(nsISupports));
+ return *this;
+ }
+
+ // Assign from |do_GetService(contractid_expr)|.
+ nsCOMPtr<nsISupports>& operator=(const nsGetServiceByContractID aRhs)
+ {
+ assign_from_gs_contractid(aRhs, NS_GET_IID(nsISupports));
+ return *this;
+ }
+
+ // Assign from |do_GetService(contractid_expr, &rv)|.
+ nsCOMPtr<nsISupports>& operator=(const nsGetServiceByContractIDWithError& aRhs)
+ {
+ assign_from_gs_contractid_with_error(aRhs, NS_GET_IID(nsISupports));
+ return *this;
+ }
+
+ // And finally, anything else we might need to assign from can exploit the
+ // nsCOMPtr_helper facility
+ nsCOMPtr<nsISupports>& operator=(const nsCOMPtr_helper& aRhs)
+ {
+ assign_from_helper(aRhs, NS_GET_IID(nsISupports));
+ return *this;
+ }
+
+ // Exchange ownership with |aRhs|; can save a pair of refcount operations.
+ void swap(nsCOMPtr<nsISupports>& aRhs)
+ {
+ nsISupports* temp = aRhs.mRawPtr;
+ NSCAP_LOG_ASSIGNMENT(&aRhs, mRawPtr);
+ NSCAP_LOG_ASSIGNMENT(this, temp);
+ NSCAP_LOG_RELEASE(this, mRawPtr);
+ NSCAP_LOG_RELEASE(&aRhs, temp);
+ aRhs.mRawPtr = mRawPtr;
+ mRawPtr = temp;
+ }
+
+ // Exchange ownership with |aRhs|; can save a pair of refcount operations.
+ void swap(nsISupports*& aRhs)
+ {
+ nsISupports* temp = aRhs;
+ NSCAP_LOG_ASSIGNMENT(this, temp);
+ NSCAP_LOG_RELEASE(this, mRawPtr);
+ aRhs = mRawPtr;
+ mRawPtr = temp;
+ }
+
+ // Return the value of mRawPtr and null out mRawPtr. Useful for
+ // already_AddRefed return values.
+ already_AddRefed<nsISupports> forget()
+ {
+ nsISupports* temp = nullptr;
+ swap(temp);
+ return already_AddRefed<nsISupports>(temp);
+ }
+
+ // Set the target of aRhs to the value of mRawPtr and null out mRawPtr.
+ // Useful to avoid unnecessary AddRef/Release pairs with "out"
+ // parameters.
+ void forget(nsISupports** aRhs)
+ {
+ NS_ASSERTION(aRhs, "Null pointer passed to forget!");
+ *aRhs = nullptr;
+ swap(*aRhs);
+ }
+
+ // Other pointer operators
+
+ // Prefer the implicit conversion provided automatically by
+ // |operator nsISupports*() const|. Use |get()| to resolve ambiguity or to
+ // get a castable pointer.
+ nsISupports* get() const { return reinterpret_cast<nsISupports*>(mRawPtr); }
+
+ // Makes an nsCOMPtr act like its underlying raw pointer type whenever it is
+ // used in a context where a raw pointer is expected. It is this operator
+ // that makes an nsCOMPtr substitutable for a raw pointer.
+ //
+ // Prefer the implicit use of this operator to calling |get()|, except where
+ // necessary to resolve ambiguity/
+ operator nsISupports* () const { return get(); }
+
+ nsISupports* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN
+ {
+ MOZ_ASSERT(mRawPtr != nullptr,
+ "You can't dereference a NULL nsCOMPtr with operator->().");
+ return get();
+ }
+
+ // These are not intended to be used by clients. See |address_of| below.
+ nsCOMPtr<nsISupports>* get_address() { return this; }
+ const nsCOMPtr<nsISupports>* get_address() const { return this; }
+
+public:
+
+ nsISupports& operator*() const
+ {
+ MOZ_ASSERT(mRawPtr != nullptr,
+ "You can't dereference a NULL nsCOMPtr with operator*().");
+ return *get();
+ }
+
+ nsISupports** StartAssignment()
+ {
+#ifndef NSCAP_FEATURE_INLINE_STARTASSIGNMENT
+ return reinterpret_cast<nsISupports**>(begin_assignment());
+#else
+ assign_assuming_AddRef(nullptr);
+ return reinterpret_cast<nsISupports**>(&mRawPtr);
+#endif
+ }
+};
+
+template<typename T>
+inline void
+ImplCycleCollectionUnlink(nsCOMPtr<T>& aField)
+{
+ aField = nullptr;
+}
+
+template<typename T>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ nsCOMPtr<T>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ CycleCollectionNoteChild(aCallback, aField.get(), aName, aFlags);
+}
+
+#ifndef NSCAP_FEATURE_USE_BASE
+template<class T>
+void
+nsCOMPtr<T>::assign_with_AddRef(nsISupports* aRawPtr)
+{
+ if (aRawPtr) {
+ NSCAP_ADDREF(this, aRawPtr);
+ }
+ assign_assuming_AddRef(reinterpret_cast<T*>(aRawPtr));
+}
+
+template<class T>
+void
+nsCOMPtr<T>::assign_from_qi(const nsQueryInterface aQI, const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aQI(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<T*>(newRawPtr));
+}
+
+template<class T>
+void
+nsCOMPtr<T>::assign_from_qi_with_error(const nsQueryInterfaceWithError& aQI,
+ const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aQI(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<T*>(newRawPtr));
+}
+
+template<class T>
+void
+nsCOMPtr<T>::assign_from_gs_cid(const nsGetServiceByCID aGS, const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aGS(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<T*>(newRawPtr));
+}
+
+template<class T>
+void
+nsCOMPtr<T>::assign_from_gs_cid_with_error(const nsGetServiceByCIDWithError& aGS,
+ const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aGS(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<T*>(newRawPtr));
+}
+
+template<class T>
+void
+nsCOMPtr<T>::assign_from_gs_contractid(const nsGetServiceByContractID aGS,
+ const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aGS(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<T*>(newRawPtr));
+}
+
+template<class T>
+void
+nsCOMPtr<T>::assign_from_gs_contractid_with_error(
+ const nsGetServiceByContractIDWithError& aGS, const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aGS(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<T*>(newRawPtr));
+}
+
+template<class T>
+void
+nsCOMPtr<T>::assign_from_helper(const nsCOMPtr_helper& helper, const nsIID& aIID)
+{
+ void* newRawPtr;
+ if (NS_FAILED(helper(aIID, &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<T*>(newRawPtr));
+}
+
+template<class T>
+void**
+nsCOMPtr<T>::begin_assignment()
+{
+ assign_assuming_AddRef(nullptr);
+ union
+ {
+ T** mT;
+ void** mVoid;
+ } result;
+ result.mT = &mRawPtr;
+ return result.mVoid;
+}
+#endif
+
+template<class T>
+inline nsCOMPtr<T>*
+address_of(nsCOMPtr<T>& aPtr)
+{
+ return aPtr.get_address();
+}
+
+template<class T>
+inline const nsCOMPtr<T>*
+address_of(const nsCOMPtr<T>& aPtr)
+{
+ return aPtr.get_address();
+}
+
+/**
+ * This class is designed to be used for anonymous temporary objects in the
+ * argument list of calls that return COM interface pointers, e.g.,
+ *
+ * nsCOMPtr<IFoo> fooP;
+ * ...->QueryInterface(iid, getter_AddRefs(fooP))
+ *
+ * DO NOT USE THIS TYPE DIRECTLY IN YOUR CODE. Use |getter_AddRefs()| instead.
+ *
+ * When initialized with a |nsCOMPtr|, as in the example above, it returns
+ * a |void**|, a |T**|, or an |nsISupports**| as needed, that the outer call
+ * (|QueryInterface| in this case) can fill in.
+ *
+ * This type should be a nested class inside |nsCOMPtr<T>|.
+ */
+template<class T>
+class nsGetterAddRefs
+{
+public:
+ explicit nsGetterAddRefs(nsCOMPtr<T>& aSmartPtr)
+ : mTargetSmartPtr(aSmartPtr)
+ {
+ }
+
+#if defined(NSCAP_FEATURE_TEST_DONTQUERY_CASES) || defined(NSCAP_LOG_EXTERNAL_ASSIGNMENT)
+ ~nsGetterAddRefs()
+ {
+#ifdef NSCAP_LOG_EXTERNAL_ASSIGNMENT
+ NSCAP_LOG_ASSIGNMENT(reinterpret_cast<void*>(address_of(mTargetSmartPtr)),
+ mTargetSmartPtr.get());
+#endif
+
+#ifdef NSCAP_FEATURE_TEST_DONTQUERY_CASES
+ mTargetSmartPtr.Assert_NoQueryNeeded();
+#endif
+ }
+#endif
+
+ operator void**()
+ {
+ return reinterpret_cast<void**>(mTargetSmartPtr.StartAssignment());
+ }
+
+ operator T**() { return mTargetSmartPtr.StartAssignment(); }
+ T*& operator*() { return *(mTargetSmartPtr.StartAssignment()); }
+
+private:
+ nsCOMPtr<T>& mTargetSmartPtr;
+};
+
+
+template<>
+class nsGetterAddRefs<nsISupports>
+{
+public:
+ explicit nsGetterAddRefs(nsCOMPtr<nsISupports>& aSmartPtr)
+ : mTargetSmartPtr(aSmartPtr)
+ {
+ }
+
+#ifdef NSCAP_LOG_EXTERNAL_ASSIGNMENT
+ ~nsGetterAddRefs()
+ {
+ NSCAP_LOG_ASSIGNMENT(reinterpret_cast<void*>(address_of(mTargetSmartPtr)),
+ mTargetSmartPtr.get());
+ }
+#endif
+
+ operator void**()
+ {
+ return reinterpret_cast<void**>(mTargetSmartPtr.StartAssignment());
+ }
+
+ operator nsISupports**() { return mTargetSmartPtr.StartAssignment(); }
+ nsISupports*& operator*() { return *(mTargetSmartPtr.StartAssignment()); }
+
+private:
+ nsCOMPtr<nsISupports>& mTargetSmartPtr;
+};
+
+template<class T>
+inline nsGetterAddRefs<T>
+getter_AddRefs(nsCOMPtr<T>& aSmartPtr)
+{
+ return nsGetterAddRefs<T>(aSmartPtr);
+}
+
+template<class T, class DestinationType>
+inline nsresult
+CallQueryInterface(T* aSource, nsGetterAddRefs<DestinationType> aDestination)
+{
+ return CallQueryInterface(aSource,
+ static_cast<DestinationType**>(aDestination));
+}
+
+
+// Comparing two |nsCOMPtr|s
+
+template<class T, class U>
+inline bool
+operator==(const nsCOMPtr<T>& aLhs, const nsCOMPtr<U>& aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) == static_cast<const U*>(aRhs.get());
+}
+
+
+template<class T, class U>
+inline bool
+operator!=(const nsCOMPtr<T>& aLhs, const nsCOMPtr<U>& aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) != static_cast<const U*>(aRhs.get());
+}
+
+
+// Comparing an |nsCOMPtr| to a raw pointer
+
+template<class T, class U>
+inline bool
+operator==(const nsCOMPtr<T>& aLhs, const U* aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) == aRhs;
+}
+
+template<class T, class U>
+inline bool
+operator==(const U* aLhs, const nsCOMPtr<T>& aRhs)
+{
+ return aLhs == static_cast<const T*>(aRhs.get());
+}
+
+template<class T, class U>
+inline bool
+operator!=(const nsCOMPtr<T>& aLhs, const U* aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) != aRhs;
+}
+
+template<class T, class U>
+inline bool
+operator!=(const U* aLhs, const nsCOMPtr<T>& aRhs)
+{
+ return aLhs != static_cast<const T*>(aRhs.get());
+}
+
+template<class T, class U>
+inline bool
+operator==(const nsCOMPtr<T>& aLhs, U* aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) == const_cast<const U*>(aRhs);
+}
+
+template<class T, class U>
+inline bool
+operator==(U* aLhs, const nsCOMPtr<T>& aRhs)
+{
+ return const_cast<const U*>(aLhs) == static_cast<const T*>(aRhs.get());
+}
+
+template<class T, class U>
+inline bool
+operator!=(const nsCOMPtr<T>& aLhs, U* aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) != const_cast<const U*>(aRhs);
+}
+
+template<class T, class U>
+inline bool
+operator!=(U* aLhs, const nsCOMPtr<T>& aRhs)
+{
+ return const_cast<const U*>(aLhs) != static_cast<const T*>(aRhs.get());
+}
+
+
+
+// Comparing an |nsCOMPtr| to |nullptr|
+
+template<class T>
+inline bool
+operator==(const nsCOMPtr<T>& aLhs, decltype(nullptr))
+{
+ return aLhs.get() == nullptr;
+}
+
+template<class T>
+inline bool
+operator==(decltype(nullptr), const nsCOMPtr<T>& aRhs)
+{
+ return nullptr == aRhs.get();
+}
+
+template<class T>
+inline bool
+operator!=(const nsCOMPtr<T>& aLhs, decltype(nullptr))
+{
+ return aLhs.get() != nullptr;
+}
+
+template<class T>
+inline bool
+operator!=(decltype(nullptr), const nsCOMPtr<T>& aRhs)
+{
+ return nullptr != aRhs.get();
+}
+
+
+// Comparing any two [XP]COM objects for identity
+
+inline bool
+SameCOMIdentity(nsISupports* aLhs, nsISupports* aRhs)
+{
+ return nsCOMPtr<nsISupports>(do_QueryInterface(aLhs)) ==
+ nsCOMPtr<nsISupports>(do_QueryInterface(aRhs));
+}
+
+
+
+template<class SourceType, class DestinationType>
+inline nsresult
+CallQueryInterface(nsCOMPtr<SourceType>& aSourcePtr, DestinationType** aDestPtr)
+{
+ return CallQueryInterface(aSourcePtr.get(), aDestPtr);
+}
+
+template <class T>
+RefPtr<T>::RefPtr(const nsCOMPtr_helper& aHelper)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aHelper(NS_GET_TEMPLATE_IID(T), &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ mRawPtr = static_cast<T*>(newRawPtr);
+}
+
+template <class T>
+RefPtr<T>&
+RefPtr<T>::operator=(const nsCOMPtr_helper& aHelper)
+{
+ void* newRawPtr;
+ if (NS_FAILED(aHelper(NS_GET_TEMPLATE_IID(T), &newRawPtr))) {
+ newRawPtr = nullptr;
+ }
+ assign_assuming_AddRef(static_cast<T*>(newRawPtr));
+ return *this;
+}
+
+
+#endif // !defined(nsCOMPtr_h___)
diff --git a/xpcom/glue/nsCRTGlue.cpp b/xpcom/glue/nsCRTGlue.cpp
new file mode 100644
index 000000000..7a9f6db03
--- /dev/null
+++ b/xpcom/glue/nsCRTGlue.cpp
@@ -0,0 +1,441 @@
+/* -*- 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 "nsCRTGlue.h"
+#include "nsXPCOM.h"
+#include "nsDebug.h"
+#include "prtime.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "mozilla/Sprintf.h"
+
+#ifdef XP_WIN
+#include <io.h>
+#include <windows.h>
+#include "mozilla/UniquePtr.h"
+#endif
+
+#ifdef ANDROID
+#include <android/log.h>
+#include <unistd.h>
+#endif
+
+using namespace mozilla;
+
+const char*
+NS_strspnp(const char* aDelims, const char* aStr)
+{
+ const char* d;
+ do {
+ for (d = aDelims; *d != '\0'; ++d) {
+ if (*aStr == *d) {
+ ++aStr;
+ break;
+ }
+ }
+ } while (*d);
+
+ return aStr;
+}
+
+char*
+NS_strtok(const char* aDelims, char** aStr)
+{
+ if (!*aStr) {
+ return nullptr;
+ }
+
+ char* ret = (char*)NS_strspnp(aDelims, *aStr);
+
+ if (!*ret) {
+ *aStr = ret;
+ return nullptr;
+ }
+
+ char* i = ret;
+ do {
+ for (const char* d = aDelims; *d != '\0'; ++d) {
+ if (*i == *d) {
+ *i = '\0';
+ *aStr = ++i;
+ return ret;
+ }
+ }
+ ++i;
+ } while (*i);
+
+ *aStr = nullptr;
+ return ret;
+}
+
+uint32_t
+NS_strlen(const char16_t* aString)
+{
+ MOZ_ASSERT(aString);
+ const char16_t* end;
+
+ for (end = aString; *end; ++end) {
+ // empty loop
+ }
+
+ return end - aString;
+}
+
+int
+NS_strcmp(const char16_t* aStrA, const char16_t* aStrB)
+{
+ while (*aStrB) {
+ int r = *aStrA - *aStrB;
+ if (r) {
+ return r;
+ }
+
+ ++aStrA;
+ ++aStrB;
+ }
+
+ return *aStrA != '\0';
+}
+
+int
+NS_strncmp(const char16_t* aStrA, const char16_t* aStrB, size_t aLen)
+{
+ while (aLen && *aStrB) {
+ int r = *aStrA - *aStrB;
+ if (r) {
+ return r;
+ }
+
+ ++aStrA;
+ ++aStrB;
+ --aLen;
+ }
+
+ return aLen ? *aStrA != '\0' : *aStrA - *aStrB;
+}
+
+char16_t*
+NS_strdup(const char16_t* aString)
+{
+ uint32_t len = NS_strlen(aString);
+ return NS_strndup(aString, len);
+}
+
+template<typename CharT>
+CharT*
+NS_strndup(const CharT* aString, uint32_t aLen)
+{
+ auto newBuf = (CharT*)NS_Alloc((aLen + 1) * sizeof(CharT));
+ if (newBuf) {
+ memcpy(newBuf, aString, aLen * sizeof(CharT));
+ newBuf[aLen] = '\0';
+ }
+ return newBuf;
+}
+
+template char16_t* NS_strndup<char16_t>(const char16_t* aString, uint32_t aLen);
+template char* NS_strndup<char>(const char* aString, uint32_t aLen);
+
+char*
+NS_strdup(const char* aString)
+{
+ uint32_t len = strlen(aString);
+ char* str = (char*)NS_Alloc(len + 1);
+ if (str) {
+ memcpy(str, aString, len);
+ str[len] = '\0';
+ }
+ return str;
+}
+
+// This table maps uppercase characters to lower case characters;
+// characters that are neither upper nor lower case are unaffected.
+const unsigned char nsLowerUpperUtils::kUpper2Lower[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64,
+
+ // upper band mapped to lower [A-Z] => [a-z]
+ 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+ 112,113,114,115,116,117,118,119,120,121,122,
+
+ 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+ 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
+ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
+ 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
+ 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
+ 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
+ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
+};
+
+const unsigned char nsLowerUpperUtils::kLower2Upper[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96,
+
+ // lower band mapped to upper [a-z] => [A-Z]
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
+
+ 123,124,125,126,127,
+ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
+ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
+ 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
+ 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
+ 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
+ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
+};
+
+bool
+NS_IsUpper(char aChar)
+{
+ return aChar != (char)nsLowerUpperUtils::kUpper2Lower[(unsigned char)aChar];
+}
+
+bool
+NS_IsLower(char aChar)
+{
+ return aChar != (char)nsLowerUpperUtils::kLower2Upper[(unsigned char)aChar];
+}
+
+bool
+NS_IsAscii(char16_t aChar)
+{
+ return (0x0080 > aChar);
+}
+
+bool
+NS_IsAscii(const char16_t* aString)
+{
+ while (*aString) {
+ if (0x0080 <= *aString) {
+ return false;
+ }
+ aString++;
+ }
+ return true;
+}
+
+bool
+NS_IsAscii(const char* aString)
+{
+ while (*aString) {
+ if (0x80 & *aString) {
+ return false;
+ }
+ aString++;
+ }
+ return true;
+}
+
+bool
+NS_IsAscii(const char* aString, uint32_t aLength)
+{
+ const char* end = aString + aLength;
+ while (aString < end) {
+ if (0x80 & *aString) {
+ return false;
+ }
+ ++aString;
+ }
+ return true;
+}
+
+bool
+NS_IsAsciiAlpha(char16_t aChar)
+{
+ return (aChar >= 'A' && aChar <= 'Z') ||
+ (aChar >= 'a' && aChar <= 'z');
+}
+
+bool
+NS_IsAsciiWhitespace(char16_t aChar)
+{
+ return aChar == ' ' ||
+ aChar == '\r' ||
+ aChar == '\n' ||
+ aChar == '\t';
+}
+
+bool
+NS_IsAsciiDigit(char16_t aChar)
+{
+ return aChar >= '0' && aChar <= '9';
+}
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+
+void
+NS_MakeRandomString(char* aBuf, int32_t aBufLen)
+{
+#define TABLE_SIZE 36
+ static const char table[] = {
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
+ 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+ 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9'
+ };
+
+ // turn PR_Now() into milliseconds since epoch
+ // and salt rand with that.
+ static unsigned int seed = 0;
+ if (seed == 0) {
+ double fpTime = double(PR_Now());
+ seed = (unsigned int)(fpTime * 1e-6 + 0.5); // use 1e-6, granularity of PR_Now() on the mac is seconds
+ srand(seed);
+ }
+
+ int32_t i;
+ for (i = 0; i < aBufLen; ++i) {
+ *aBuf++ = table[rand() % TABLE_SIZE];
+ }
+ *aBuf = 0;
+}
+
+#endif
+
+static StderrCallback sStderrCallback = nullptr;
+
+void
+set_stderr_callback(StderrCallback aCallback)
+{
+ sStderrCallback = aCallback;
+}
+
+#if defined(ANDROID) && !defined(RELEASE_OR_BETA)
+static FILE* sStderrCopy = nullptr;
+
+void
+stderr_to_file(const char* aFmt, va_list aArgs)
+{
+ vfprintf(sStderrCopy, aFmt, aArgs);
+}
+
+void
+copy_stderr_to_file(const char* aFile)
+{
+ if (sStderrCopy) {
+ return;
+ }
+ size_t buflen = strlen(aFile) + 16;
+ char* buf = (char*)malloc(buflen);
+ snprintf(buf, buflen, "%s.%u", aFile, (uint32_t)getpid());
+ sStderrCopy = fopen(buf, "w");
+ free(buf);
+ set_stderr_callback(stderr_to_file);
+}
+#endif
+
+#ifdef HAVE_VA_COPY
+#define VARARGS_ASSIGN(foo, bar) VA_COPY(foo,bar)
+#elif defined(HAVE_VA_LIST_AS_ARRAY)
+#define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0]
+#else
+#define VARARGS_ASSIGN(foo, bar) (foo) = (bar)
+#endif
+
+#if defined(XP_WIN)
+void
+vprintf_stderr(const char* aFmt, va_list aArgs)
+{
+ if (sStderrCallback) {
+ va_list argsCpy;
+ VARARGS_ASSIGN(argsCpy, aArgs);
+ sStderrCallback(aFmt, aArgs);
+ va_end(argsCpy);
+ }
+
+ if (IsDebuggerPresent()) {
+ int lengthNeeded = _vscprintf(aFmt, aArgs);
+ if (lengthNeeded) {
+ lengthNeeded++;
+ auto buf = MakeUnique<char[]>(lengthNeeded);
+ if (buf) {
+ va_list argsCpy;
+ VARARGS_ASSIGN(argsCpy, aArgs);
+ vsnprintf(buf.get(), lengthNeeded, aFmt, argsCpy);
+ buf[lengthNeeded - 1] = '\0';
+ va_end(argsCpy);
+ OutputDebugStringA(buf.get());
+ }
+ }
+ }
+
+ FILE* fp = _fdopen(_dup(2), "a");
+ if (!fp) {
+ return;
+ }
+
+ vfprintf(fp, aFmt, aArgs);
+
+ fclose(fp);
+}
+
+#elif defined(ANDROID)
+void
+vprintf_stderr(const char* aFmt, va_list aArgs)
+{
+ if (sStderrCallback) {
+ va_list argsCpy;
+ VARARGS_ASSIGN(argsCpy, aArgs);
+ sStderrCallback(aFmt, aArgs);
+ va_end(argsCpy);
+ }
+
+ __android_log_vprint(ANDROID_LOG_INFO, "Gecko", aFmt, aArgs);
+}
+#else
+void
+vprintf_stderr(const char* aFmt, va_list aArgs)
+{
+ if (sStderrCallback) {
+ va_list argsCpy;
+ VARARGS_ASSIGN(argsCpy, aArgs);
+ sStderrCallback(aFmt, aArgs);
+ va_end(argsCpy);
+ }
+
+ vfprintf(stderr, aFmt, aArgs);
+}
+#endif
+
+void
+printf_stderr(const char* aFmt, ...)
+{
+ va_list args;
+ va_start(args, aFmt);
+ vprintf_stderr(aFmt, args);
+ va_end(args);
+}
+
+void
+fprintf_stderr(FILE* aFile, const char* aFmt, ...)
+{
+ va_list args;
+ va_start(args, aFmt);
+ if (aFile == stderr) {
+ vprintf_stderr(aFmt, args);
+ } else {
+ vfprintf(aFile, aFmt, args);
+ }
+ va_end(args);
+}
diff --git a/xpcom/glue/nsCRTGlue.h b/xpcom/glue/nsCRTGlue.h
new file mode 100644
index 000000000..8caa1ae27
--- /dev/null
+++ b/xpcom/glue/nsCRTGlue.h
@@ -0,0 +1,147 @@
+/* -*- 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 nsCRTGlue_h__
+#define nsCRTGlue_h__
+
+#include "nscore.h"
+
+/**
+ * Scan a string for the first character that is *not* in a set of
+ * delimiters. If the string is only delimiter characters, the end of the
+ * string is returned.
+ *
+ * @param aDelims The set of delimiters (null-terminated)
+ * @param aStr The string to search (null-terminated)
+ */
+const char* NS_strspnp(const char* aDelims, const char* aStr);
+
+/**
+ * Tokenize a string. This function is similar to the strtok function in the
+ * C standard library, but it does not use static variables to maintain state
+ * and is therefore thread and reentrancy-safe.
+ *
+ * Any leading delimiters in str are skipped. Then the string is scanned
+ * until an additional delimiter or end-of-string is found. The final
+ * delimiter is set to '\0'.
+ *
+ * @param aDelims The set of delimiters.
+ * @param aStr The string to search. This is an in-out parameter; it is
+ * reset to the end of the found token + 1, or to the
+ * end-of-string if there are no more tokens.
+ * @return The token. If no token is found (the string is only
+ * delimiter characters), nullptr is returned.
+ */
+char* NS_strtok(const char* aDelims, char** aStr);
+
+/**
+ * "strlen" for char16_t strings
+ */
+uint32_t NS_strlen(const char16_t* aString);
+
+/**
+ * "strcmp" for char16_t strings
+ */
+int NS_strcmp(const char16_t* aStrA, const char16_t* aStrB);
+
+/**
+ * "strncmp" for char16_t strings
+ */
+int NS_strncmp(const char16_t* aStrA, const char16_t* aStrB, size_t aLen);
+
+/**
+ * "strdup" for char16_t strings, uses the NS_Alloc allocator.
+ */
+char16_t* NS_strdup(const char16_t* aString);
+
+/**
+ * "strdup", but using the NS_Alloc allocator.
+ */
+char* NS_strdup(const char* aString);
+
+/**
+ * strndup for char16_t or char strings (normal strndup is not available on
+ * windows). This function will ensure that the new string is
+ * null-terminated. Uses the NS_Alloc allocator.
+ *
+ * CharT may be either char16_t or char.
+ */
+template<typename CharT>
+CharT* NS_strndup(const CharT* aString, uint32_t aLen);
+
+// The following case-conversion methods only deal in the ascii repertoire
+// A-Z and a-z
+
+// semi-private data declarations... don't use these directly.
+class nsLowerUpperUtils
+{
+public:
+ static const unsigned char kLower2Upper[256];
+ static const unsigned char kUpper2Lower[256];
+};
+
+inline char
+NS_ToUpper(char aChar)
+{
+ return (char)nsLowerUpperUtils::kLower2Upper[(unsigned char)aChar];
+}
+
+inline char
+NS_ToLower(char aChar)
+{
+ return (char)nsLowerUpperUtils::kUpper2Lower[(unsigned char)aChar];
+}
+
+bool NS_IsUpper(char aChar);
+bool NS_IsLower(char aChar);
+
+bool NS_IsAscii(char16_t aChar);
+bool NS_IsAscii(const char16_t* aString);
+bool NS_IsAsciiAlpha(char16_t aChar);
+bool NS_IsAsciiDigit(char16_t aChar);
+bool NS_IsAsciiWhitespace(char16_t aChar);
+bool NS_IsAscii(const char* aString);
+bool NS_IsAscii(const char* aString, uint32_t aLength);
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+void NS_MakeRandomString(char* aBuf, int32_t aBufLen);
+#endif
+
+#define FF '\f'
+#define TAB '\t'
+
+#define CRSTR "\015"
+#define LFSTR "\012"
+#define CRLF "\015\012" /* A CR LF equivalent string */
+
+// We use the most restrictive filesystem as our default set of illegal filename
+// characters. This is currently Windows.
+#define OS_FILE_ILLEGAL_CHARACTERS "/:*?\"<>|"
+// We also provide a list of all known file path separators for all filesystems.
+// This can be used in replacement of FILE_PATH_SEPARATOR when you need to
+// identify or replace all known path separators.
+#define KNOWN_PATH_SEPARATORS "\\/"
+
+#if defined(XP_MACOSX)
+ #define FILE_PATH_SEPARATOR "/"
+#elif defined(XP_WIN)
+ #define FILE_PATH_SEPARATOR "\\"
+#elif defined(XP_UNIX)
+ #define FILE_PATH_SEPARATOR "/"
+#else
+ #error need_to_define_your_file_path_separator_and_maybe_illegal_characters
+#endif
+
+// Not all these control characters are illegal in all OSs, but we don't really
+// want them appearing in filenames
+#define CONTROL_CHARACTERS "\001\002\003\004\005\006\007" \
+ "\010\011\012\013\014\015\016\017" \
+ "\020\021\022\023\024\025\026\027" \
+ "\030\031\032\033\034\035\036\037"
+
+#define FILE_ILLEGAL_CHARACTERS CONTROL_CHARACTERS OS_FILE_ILLEGAL_CHARACTERS
+
+#endif // nsCRTGlue_h__
diff --git a/xpcom/glue/nsCategoryCache.cpp b/xpcom/glue/nsCategoryCache.cpp
new file mode 100644
index 000000000..30501b8e3
--- /dev/null
+++ b/xpcom/glue/nsCategoryCache.cpp
@@ -0,0 +1,149 @@
+/* -*- 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 "nsIObserverService.h"
+#include "mozilla/Services.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIStringEnumerator.h"
+
+#include "nsXPCOMCID.h"
+
+#include "nsCategoryCache.h"
+
+nsCategoryObserver::nsCategoryObserver(const char* aCategory)
+ : mCategory(aCategory)
+ , mObserversRemoved(false)
+{
+ // First, enumerate the currently existing entries
+ nsCOMPtr<nsICategoryManager> catMan =
+ do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+ if (!catMan) {
+ return;
+ }
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ nsresult rv = catMan->EnumerateCategory(aCategory,
+ getter_AddRefs(enumerator));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ nsCOMPtr<nsIUTF8StringEnumerator> strings = do_QueryInterface(enumerator);
+ MOZ_ASSERT(strings);
+
+ bool more;
+ while (NS_SUCCEEDED(strings->HasMore(&more)) && more) {
+ nsAutoCString entryName;
+ strings->GetNext(entryName);
+
+ nsCString entryValue;
+ rv = catMan->GetCategoryEntry(aCategory,
+ entryName.get(),
+ getter_Copies(entryValue));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsISupports> service = do_GetService(entryValue.get());
+ if (service) {
+ mHash.Put(entryName, service);
+ }
+ }
+ }
+
+ // Now, listen for changes
+ nsCOMPtr<nsIObserverService> serv =
+ mozilla::services::GetObserverService();
+ if (serv) {
+ serv->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+ serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID, false);
+ serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID, false);
+ serv->AddObserver(this, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID, false);
+ }
+}
+
+nsCategoryObserver::~nsCategoryObserver()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsCategoryObserver, nsIObserver)
+
+void
+nsCategoryObserver::ListenerDied()
+{
+ RemoveObservers();
+}
+
+void
+nsCategoryObserver::RemoveObservers()
+{
+ if (mObserversRemoved) {
+ return;
+ }
+
+ mObserversRemoved = true;
+ nsCOMPtr<nsIObserverService> obsSvc =
+ mozilla::services::GetObserverService();
+ if (obsSvc) {
+ obsSvc->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+ obsSvc->RemoveObserver(this, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID);
+ obsSvc->RemoveObserver(this, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID);
+ obsSvc->RemoveObserver(this, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID);
+ }
+}
+
+NS_IMETHODIMP
+nsCategoryObserver::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
+ mHash.Clear();
+ RemoveObservers();
+
+ return NS_OK;
+ }
+
+ if (!aData ||
+ !nsDependentString(aData).Equals(NS_ConvertASCIItoUTF16(mCategory))) {
+ return NS_OK;
+ }
+
+ nsAutoCString str;
+ nsCOMPtr<nsISupportsCString> strWrapper(do_QueryInterface(aSubject));
+ if (strWrapper) {
+ strWrapper->GetData(str);
+ }
+
+ if (strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID) == 0) {
+ // We may get an add notification even when we already have an entry. This
+ // is due to the notification happening asynchronously, so if the entry gets
+ // added and an nsCategoryObserver gets instantiated before events get
+ // processed, we'd get the notification for an existing entry.
+ // Do nothing in that case.
+ if (mHash.GetWeak(str)) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsICategoryManager> catMan =
+ do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+ if (!catMan) {
+ return NS_OK;
+ }
+
+ nsCString entryValue;
+ catMan->GetCategoryEntry(mCategory.get(),
+ str.get(),
+ getter_Copies(entryValue));
+
+ nsCOMPtr<nsISupports> service = do_GetService(entryValue.get());
+
+ if (service) {
+ mHash.Put(str, service);
+ }
+ } else if (strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID) == 0) {
+ mHash.Remove(str);
+ } else if (strcmp(aTopic, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID) == 0) {
+ mHash.Clear();
+ }
+ return NS_OK;
+}
diff --git a/xpcom/glue/nsCategoryCache.h b/xpcom/glue/nsCategoryCache.h
new file mode 100644
index 000000000..023aa7a75
--- /dev/null
+++ b/xpcom/glue/nsCategoryCache.h
@@ -0,0 +1,95 @@
+/* -*- 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 nsCategoryCache_h_
+#define nsCategoryCache_h_
+
+#include "mozilla/Attributes.h"
+
+#include "nsICategoryManager.h"
+#include "nsIObserver.h"
+#include "nsISimpleEnumerator.h"
+#include "nsISupportsPrimitives.h"
+
+#include "nsServiceManagerUtils.h"
+
+#include "nsAutoPtr.h"
+#include "nsCOMArray.h"
+#include "nsInterfaceHashtable.h"
+
+#include "nsXPCOM.h"
+
+class nsCategoryObserver final : public nsIObserver
+{
+ ~nsCategoryObserver();
+
+public:
+ explicit nsCategoryObserver(const char* aCategory);
+
+ void ListenerDied();
+ nsInterfaceHashtable<nsCStringHashKey, nsISupports>& GetHash()
+ {
+ return mHash;
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+private:
+ void RemoveObservers();
+
+ nsInterfaceHashtable<nsCStringHashKey, nsISupports> mHash;
+ nsCString mCategory;
+ bool mObserversRemoved;
+};
+
+/**
+ * This is a helper class that caches services that are registered in a certain
+ * category. The intended usage is that a service stores a variable of type
+ * nsCategoryCache<nsIFoo> in a member variable, where nsIFoo is the interface
+ * that these services should implement. The constructor of this class should
+ * then get the name of the category.
+ */
+template<class T>
+class nsCategoryCache final
+{
+public:
+ explicit nsCategoryCache(const char* aCategory)
+ : mCategoryName(aCategory)
+ {
+ }
+ ~nsCategoryCache()
+ {
+ if (mObserver) {
+ mObserver->ListenerDied();
+ }
+ }
+
+ void GetEntries(nsCOMArray<T>& aResult)
+ {
+ // Lazy initialization, so that services in this category can't
+ // cause reentrant getService (bug 386376)
+ if (!mObserver) {
+ mObserver = new nsCategoryObserver(mCategoryName.get());
+ }
+
+ for (auto iter = mObserver->GetHash().Iter(); !iter.Done(); iter.Next()) {
+ nsISupports* entry = iter.UserData();
+ nsCOMPtr<T> service = do_QueryInterface(entry);
+ if (service) {
+ aResult.AppendElement(service.forget());
+ }
+ }
+ }
+
+private:
+ // Not to be implemented
+ nsCategoryCache(const nsCategoryCache<T>&);
+
+ nsCString mCategoryName;
+ RefPtr<nsCategoryObserver> mObserver;
+};
+
+#endif
diff --git a/xpcom/glue/nsClassHashtable.h b/xpcom/glue/nsClassHashtable.h
new file mode 100644
index 000000000..53ca5676b
--- /dev/null
+++ b/xpcom/glue/nsClassHashtable.h
@@ -0,0 +1,140 @@
+/* -*- 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 nsClassHashtable_h__
+#define nsClassHashtable_h__
+
+#include "mozilla/Move.h"
+#include "nsBaseHashtable.h"
+#include "nsHashKeys.h"
+#include "nsAutoPtr.h"
+
+/**
+ * templated hashtable class maps keys to C++ object pointers.
+ * See nsBaseHashtable for complete declaration.
+ * @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h
+ * for a complete specification.
+ * @param Class the class-type being wrapped
+ * @see nsInterfaceHashtable, nsClassHashtable
+ */
+template<class KeyClass, class T>
+class nsClassHashtable
+ : public nsBaseHashtable<KeyClass, nsAutoPtr<T>, T*>
+{
+public:
+ typedef typename KeyClass::KeyType KeyType;
+ typedef T* UserDataType;
+ typedef nsBaseHashtable<KeyClass, nsAutoPtr<T>, T*> base_type;
+
+ using base_type::IsEmpty;
+
+ nsClassHashtable() {}
+ explicit nsClassHashtable(uint32_t aInitLength)
+ : nsBaseHashtable<KeyClass, nsAutoPtr<T>, T*>(aInitLength)
+ {
+ }
+
+ /**
+ * Looks up aKey in the hash table. If it doesn't exist a new object of
+ * KeyClass will be created (using the arguments provided) and then returned.
+ */
+ template<typename... Args>
+ UserDataType LookupOrAdd(KeyType aKey, Args&&... aConstructionArgs);
+
+ /**
+ * @copydoc nsBaseHashtable::Get
+ * @param aData if the key doesn't exist, pData will be set to nullptr.
+ */
+ bool Get(KeyType aKey, UserDataType* aData) const;
+
+ /**
+ * @copydoc nsBaseHashtable::Get
+ * @returns nullptr if the key is not present.
+ */
+ UserDataType Get(KeyType aKey) const;
+
+ /**
+ * Remove the entry for the given key from the hashtable and return it in
+ * aOut. If the key is not in the hashtable, aOut's pointer is set to
+ * nullptr.
+ *
+ * Normally, an entry is deleted when it's removed from an nsClassHashtable,
+ * but this function transfers ownership of the entry back to the caller
+ * through aOut -- the entry will be deleted when aOut goes out of scope.
+ *
+ * @param aKey the key to get and remove from the hashtable
+ */
+ void RemoveAndForget(KeyType aKey, nsAutoPtr<T>& aOut);
+};
+
+//
+// nsClassHashtable definitions
+//
+
+template<class KeyClass, class T>
+template<typename... Args>
+T*
+nsClassHashtable<KeyClass, T>::LookupOrAdd(KeyType aKey,
+ Args&&... aConstructionArgs)
+{
+ typename base_type::EntryType* ent = this->PutEntry(aKey);
+ if (!ent->mData) {
+ ent->mData = new T(mozilla::Forward<Args>(aConstructionArgs)...);
+ }
+ return ent->mData;
+}
+
+template<class KeyClass, class T>
+bool
+nsClassHashtable<KeyClass, T>::Get(KeyType aKey, T** aRetVal) const
+{
+ typename base_type::EntryType* ent = this->GetEntry(aKey);
+
+ if (ent) {
+ if (aRetVal) {
+ *aRetVal = ent->mData;
+ }
+
+ return true;
+ }
+
+ if (aRetVal) {
+ *aRetVal = nullptr;
+ }
+
+ return false;
+}
+
+template<class KeyClass, class T>
+T*
+nsClassHashtable<KeyClass, T>::Get(KeyType aKey) const
+{
+ typename base_type::EntryType* ent = this->GetEntry(aKey);
+ if (!ent) {
+ return nullptr;
+ }
+
+ return ent->mData;
+}
+
+template<class KeyClass, class T>
+void
+nsClassHashtable<KeyClass, T>::RemoveAndForget(KeyType aKey, nsAutoPtr<T>& aOut)
+{
+ aOut = nullptr;
+
+ typename base_type::EntryType* ent = this->GetEntry(aKey);
+ if (!ent) {
+ return;
+ }
+
+ // Transfer ownership from ent->mData into aOut.
+ aOut = mozilla::Move(ent->mData);
+
+ this->Remove(aKey);
+}
+
+#endif // nsClassHashtable_h__
diff --git a/xpcom/glue/nsClassInfoImpl.cpp b/xpcom/glue/nsClassInfoImpl.cpp
new file mode 100644
index 000000000..6eb34f9f7
--- /dev/null
+++ b/xpcom/glue/nsClassInfoImpl.cpp
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIClassInfoImpl.h"
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+GenericClassInfo::AddRef()
+{
+ return 2;
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+GenericClassInfo::Release()
+{
+ return 1;
+}
+
+NS_IMPL_QUERY_INTERFACE(GenericClassInfo, nsIClassInfo)
+
+NS_IMETHODIMP
+GenericClassInfo::GetInterfaces(uint32_t* aCount, nsIID*** aArray)
+{
+ return mData->getinterfaces(aCount, aArray);
+}
+
+NS_IMETHODIMP
+GenericClassInfo::GetScriptableHelper(nsIXPCScriptable** aHelper)
+{
+ if (mData->getscriptablehelper) {
+ return mData->getscriptablehelper(aHelper);
+ }
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+GenericClassInfo::GetContractID(char** aContractID)
+{
+ NS_ERROR("GetContractID not implemented");
+ *aContractID = nullptr;
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+GenericClassInfo::GetClassDescription(char** aDescription)
+{
+ *aDescription = nullptr;
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+GenericClassInfo::GetClassID(nsCID** aClassID)
+{
+ NS_ERROR("GetClassID not implemented");
+ *aClassID = nullptr;
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+GenericClassInfo::GetFlags(uint32_t* aFlags)
+{
+ *aFlags = mData->flags;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+GenericClassInfo::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc)
+{
+ *aClassIDNoAlloc = mData->cid;
+ return NS_OK;
+}
diff --git a/xpcom/glue/nsComponentManagerUtils.cpp b/xpcom/glue/nsComponentManagerUtils.cpp
new file mode 100644
index 000000000..d8a590fa7
--- /dev/null
+++ b/xpcom/glue/nsComponentManagerUtils.cpp
@@ -0,0 +1,301 @@
+/* -*- 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 nsXPCOM_h__
+#include "nsXPCOM.h"
+#endif
+
+#ifndef nsCOMPtr_h__
+#include "nsCOMPtr.h"
+#endif
+
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+
+#include "nsIComponentManager.h"
+
+#ifndef MOZILLA_INTERNAL_API
+
+nsresult
+CallGetService(const nsCID& aCID, const nsIID& aIID, void** aResult)
+{
+ nsCOMPtr<nsIServiceManager> servMgr;
+ nsresult status = NS_GetServiceManager(getter_AddRefs(servMgr));
+ if (servMgr) {
+ status = servMgr->GetService(aCID, aIID, aResult);
+ }
+ return status;
+}
+
+nsresult
+CallGetService(const char* aContractID, const nsIID& aIID, void** aResult)
+{
+ nsCOMPtr<nsIServiceManager> servMgr;
+ nsresult status = NS_GetServiceManager(getter_AddRefs(servMgr));
+ if (servMgr) {
+ status = servMgr->GetServiceByContractID(aContractID, aIID, aResult);
+ }
+ return status;
+}
+
+#else
+
+#include "nsComponentManager.h"
+
+nsresult
+CallGetService(const nsCID& aCID, const nsIID& aIID, void** aResult)
+{
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (NS_WARN_IF(!compMgr)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return compMgr->nsComponentManagerImpl::GetService(aCID, aIID, aResult);
+}
+
+nsresult
+CallGetService(const char* aContractID, const nsIID& aIID, void** aResult)
+{
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (NS_WARN_IF(!compMgr)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return compMgr->nsComponentManagerImpl::GetServiceByContractID(aContractID,
+ aIID,
+ aResult);
+}
+
+#endif
+
+#ifndef MOZILLA_INTERNAL_API
+
+nsresult
+CallCreateInstance(const nsCID& aCID, nsISupports* aDelegate,
+ const nsIID& aIID, void** aResult)
+{
+ nsCOMPtr<nsIComponentManager> compMgr;
+ nsresult status = NS_GetComponentManager(getter_AddRefs(compMgr));
+ if (compMgr) {
+ status = compMgr->CreateInstance(aCID, aDelegate, aIID, aResult);
+ }
+ return status;
+}
+
+nsresult
+CallCreateInstance(const char* aContractID, nsISupports* aDelegate,
+ const nsIID& aIID, void** aResult)
+{
+ nsCOMPtr<nsIComponentManager> compMgr;
+ nsresult status = NS_GetComponentManager(getter_AddRefs(compMgr));
+ if (compMgr)
+ status = compMgr->CreateInstanceByContractID(aContractID, aDelegate,
+ aIID, aResult);
+ return status;
+}
+
+nsresult
+CallGetClassObject(const nsCID& aCID, const nsIID& aIID, void** aResult)
+{
+ nsCOMPtr<nsIComponentManager> compMgr;
+ nsresult status = NS_GetComponentManager(getter_AddRefs(compMgr));
+ if (compMgr) {
+ status = compMgr->GetClassObject(aCID, aIID, aResult);
+ }
+ return status;
+}
+
+nsresult
+CallGetClassObject(const char* aContractID, const nsIID& aIID, void** aResult)
+{
+ nsCOMPtr<nsIComponentManager> compMgr;
+ nsresult status = NS_GetComponentManager(getter_AddRefs(compMgr));
+ if (compMgr)
+ status = compMgr->GetClassObjectByContractID(aContractID, aIID,
+ aResult);
+ return status;
+}
+
+#else
+
+#include "nsComponentManager.h"
+
+nsresult
+CallCreateInstance(const nsCID& aCID, nsISupports* aDelegate,
+ const nsIID& aIID, void** aResult)
+{
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (NS_WARN_IF(!compMgr)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return compMgr->nsComponentManagerImpl::CreateInstance(aCID, aDelegate, aIID,
+ aResult);
+}
+
+nsresult
+CallCreateInstance(const char* aContractID, nsISupports* aDelegate,
+ const nsIID& aIID, void** aResult)
+{
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (NS_WARN_IF(!compMgr)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return
+ compMgr->nsComponentManagerImpl::CreateInstanceByContractID(aContractID,
+ aDelegate, aIID,
+ aResult);
+}
+
+nsresult
+CallGetClassObject(const nsCID& aCID, const nsIID& aIID, void** aResult)
+{
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (NS_WARN_IF(!compMgr)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return compMgr->nsComponentManagerImpl::GetClassObject(aCID, aIID, aResult);
+}
+
+nsresult
+CallGetClassObject(const char* aContractID, const nsIID& aIID, void** aResult)
+{
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (NS_WARN_IF(!compMgr)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return
+ compMgr->nsComponentManagerImpl::GetClassObjectByContractID(aContractID,
+ aIID, aResult);
+}
+
+#endif
+
+nsresult
+nsCreateInstanceByCID::operator()(const nsIID& aIID, void** aInstancePtr) const
+{
+ nsresult status = CallCreateInstance(mCID, nullptr, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+nsresult
+nsCreateInstanceByContractID::operator()(const nsIID& aIID,
+ void** aInstancePtr) const
+{
+ nsresult status = CallCreateInstance(mContractID, nullptr, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+nsresult
+nsCreateInstanceFromFactory::operator()(const nsIID& aIID,
+ void** aInstancePtr) const
+{
+ nsresult status = mFactory->CreateInstance(nullptr, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+
+nsresult
+nsGetClassObjectByCID::operator()(const nsIID& aIID, void** aInstancePtr) const
+{
+ nsresult status = CallGetClassObject(mCID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+nsresult
+nsGetClassObjectByContractID::operator()(const nsIID& aIID,
+ void** aInstancePtr) const
+{
+ nsresult status = CallGetClassObject(mContractID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+
+nsresult
+nsGetServiceByCID::operator()(const nsIID& aIID, void** aInstancePtr) const
+{
+ nsresult status = CallGetService(mCID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+
+ return status;
+}
+
+nsresult
+nsGetServiceByCIDWithError::operator()(const nsIID& aIID,
+ void** aInstancePtr) const
+{
+ nsresult status = CallGetService(mCID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+nsresult
+nsGetServiceByContractID::operator()(const nsIID& aIID,
+ void** aInstancePtr) const
+{
+ nsresult status = CallGetService(mContractID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+
+ return status;
+}
+
+nsresult
+nsGetServiceByContractIDWithError::operator()(const nsIID& aIID,
+ void** aInstancePtr) const
+{
+ nsresult status = CallGetService(mContractID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
diff --git a/xpcom/glue/nsComponentManagerUtils.h b/xpcom/glue/nsComponentManagerUtils.h
new file mode 100644
index 000000000..2a7a4fbd7
--- /dev/null
+++ b/xpcom/glue/nsComponentManagerUtils.h
@@ -0,0 +1,247 @@
+/* -*- 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 nsComponentManagerUtils_h__
+#define nsComponentManagerUtils_h__
+
+#include "nscore.h"
+#include "nsCOMPtr.h"
+
+#include "nsIFactory.h"
+
+
+nsresult CallCreateInstance(const nsCID& aClass, nsISupports* aDelegate,
+ const nsIID& aIID, void** aResult);
+
+nsresult CallCreateInstance(const char* aContractID, nsISupports* aDelegate,
+ const nsIID& aIID, void** aResult);
+
+nsresult CallGetClassObject(const nsCID& aClass, const nsIID& aIID,
+ void** aResult);
+
+nsresult CallGetClassObject(const char* aContractID, const nsIID& aIID,
+ void** aResult);
+
+
+class MOZ_STACK_CLASS nsCreateInstanceByCID final : public nsCOMPtr_helper
+{
+public:
+ nsCreateInstanceByCID(const nsCID& aCID, nsresult* aErrorPtr)
+ : mCID(aCID)
+ , mErrorPtr(aErrorPtr)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const
+ override;
+
+private:
+ const nsCID& mCID;
+ nsresult* mErrorPtr;
+};
+
+class MOZ_STACK_CLASS nsCreateInstanceByContractID final : public nsCOMPtr_helper
+{
+public:
+ nsCreateInstanceByContractID(const char* aContractID, nsresult* aErrorPtr)
+ : mContractID(aContractID)
+ , mErrorPtr(aErrorPtr)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const override;
+
+private:
+ const char* mContractID;
+ nsresult* mErrorPtr;
+};
+
+class MOZ_STACK_CLASS nsCreateInstanceFromFactory final : public nsCOMPtr_helper
+{
+public:
+ nsCreateInstanceFromFactory(nsIFactory* aFactory, nsresult* aErrorPtr)
+ : mFactory(aFactory)
+ , mErrorPtr(aErrorPtr)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const override;
+
+private:
+ nsIFactory* MOZ_NON_OWNING_REF mFactory;
+ nsresult* mErrorPtr;
+};
+
+
+inline const nsCreateInstanceByCID
+do_CreateInstance(const nsCID& aCID, nsresult* aError = 0)
+{
+ return nsCreateInstanceByCID(aCID, aError);
+}
+
+inline const nsCreateInstanceByContractID
+do_CreateInstance(const char* aContractID, nsresult* aError = 0)
+{
+ return nsCreateInstanceByContractID(aContractID, aError);
+}
+
+inline const nsCreateInstanceFromFactory
+do_CreateInstance(nsIFactory* aFactory, nsresult* aError = 0)
+{
+ return nsCreateInstanceFromFactory(aFactory, aError);
+}
+
+
+class MOZ_STACK_CLASS nsGetClassObjectByCID final : public nsCOMPtr_helper
+{
+public:
+ nsGetClassObjectByCID(const nsCID& aCID, nsresult* aErrorPtr)
+ : mCID(aCID)
+ , mErrorPtr(aErrorPtr)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const override;
+
+private:
+ const nsCID& mCID;
+ nsresult* mErrorPtr;
+};
+
+class MOZ_STACK_CLASS nsGetClassObjectByContractID final : public nsCOMPtr_helper
+{
+public:
+ nsGetClassObjectByContractID(const char* aContractID, nsresult* aErrorPtr)
+ : mContractID(aContractID)
+ , mErrorPtr(aErrorPtr)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const override;
+
+private:
+ const char* mContractID;
+ nsresult* mErrorPtr;
+};
+
+/**
+ * do_GetClassObject can be used to improve performance of callers
+ * that call |CreateInstance| many times. They can cache the factory
+ * and call do_CreateInstance or CallCreateInstance with the cached
+ * factory rather than having the component manager retrieve it every
+ * time.
+ */
+inline const nsGetClassObjectByCID
+do_GetClassObject(const nsCID& aCID, nsresult* aError = 0)
+{
+ return nsGetClassObjectByCID(aCID, aError);
+}
+
+inline const nsGetClassObjectByContractID
+do_GetClassObject(const char* aContractID, nsresult* aError = 0)
+{
+ return nsGetClassObjectByContractID(aContractID, aError);
+}
+
+// type-safe shortcuts for calling |CreateInstance|
+template<class DestinationType>
+inline nsresult
+CallCreateInstance(const nsCID& aClass,
+ nsISupports* aDelegate,
+ DestinationType** aDestination)
+{
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return CallCreateInstance(aClass, aDelegate,
+ NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template<class DestinationType>
+inline nsresult
+CallCreateInstance(const nsCID& aClass, DestinationType** aDestination)
+{
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return CallCreateInstance(aClass, nullptr,
+ NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template<class DestinationType>
+inline nsresult
+CallCreateInstance(const char* aContractID,
+ nsISupports* aDelegate,
+ DestinationType** aDestination)
+{
+ NS_PRECONDITION(aContractID, "null parameter");
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return CallCreateInstance(aContractID,
+ aDelegate,
+ NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template<class DestinationType>
+inline nsresult
+CallCreateInstance(const char* aContractID, DestinationType** aDestination)
+{
+ NS_PRECONDITION(aContractID, "null parameter");
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return CallCreateInstance(aContractID, nullptr,
+ NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template<class DestinationType>
+inline nsresult
+CallCreateInstance(nsIFactory* aFactory,
+ nsISupports* aDelegate,
+ DestinationType** aDestination)
+{
+ NS_PRECONDITION(aFactory, "null parameter");
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return aFactory->CreateInstance(aDelegate,
+ NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template<class DestinationType>
+inline nsresult
+CallCreateInstance(nsIFactory* aFactory, DestinationType** aDestination)
+{
+ NS_PRECONDITION(aFactory, "null parameter");
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return aFactory->CreateInstance(nullptr,
+ NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template<class DestinationType>
+inline nsresult
+CallGetClassObject(const nsCID& aClass, DestinationType** aDestination)
+{
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return CallGetClassObject(aClass, NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template<class DestinationType>
+inline nsresult
+CallGetClassObject(const char* aContractID, DestinationType** aDestination)
+{
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return CallGetClassObject(aContractID, NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+#endif /* nsComponentManagerUtils_h__ */
diff --git a/xpcom/glue/nsCycleCollectionNoteChild.h b/xpcom/glue/nsCycleCollectionNoteChild.h
new file mode 100644
index 000000000..5d47caefd
--- /dev/null
+++ b/xpcom/glue/nsCycleCollectionNoteChild.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This header will be included by headers that define refpointer and array classes
+// in order to specialize CC helpers such as ImplCycleCollectionTraverse for them.
+
+#ifndef nsCycleCollectionNoteChild_h__
+#define nsCycleCollectionNoteChild_h__
+
+#include "nsCycleCollectionTraversalCallback.h"
+#include "mozilla/Likely.h"
+#include "mozilla/TypeTraits.h"
+
+enum
+{
+ CycleCollectionEdgeNameArrayFlag = 1
+};
+
+// Just a helper for appending "[i]". Didn't want to pull in string headers here.
+void
+CycleCollectionNoteEdgeNameImpl(nsCycleCollectionTraversalCallback& aCallback,
+ const char* aName,
+ uint32_t aFlags = 0);
+
+// Should be inlined so that in the no-debug-info case this is just a simple if().
+MOZ_ALWAYS_INLINE void
+CycleCollectionNoteEdgeName(nsCycleCollectionTraversalCallback& aCallback,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ if (MOZ_UNLIKELY(aCallback.WantDebugInfo())) {
+ CycleCollectionNoteEdgeNameImpl(aCallback, aName, aFlags);
+ }
+}
+
+#define NS_CYCLE_COLLECTION_INNERCLASS \
+ cycleCollection
+
+#define NS_CYCLE_COLLECTION_INNERNAME \
+ _cycleCollectorGlobal
+
+#define NS_CYCLE_COLLECTION_PARTICIPANT(_class) \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()
+
+template<typename T>
+nsISupports*
+ToSupports(T* aPtr, typename T::NS_CYCLE_COLLECTION_INNERCLASS* aDummy = 0)
+{
+ return T::NS_CYCLE_COLLECTION_INNERCLASS::Upcast(aPtr);
+}
+
+// The default implementation of this class template is empty, because it
+// should never be used: see the partial specializations below.
+template<typename T,
+ bool IsXPCOM = mozilla::IsBaseOf<nsISupports, T>::value>
+struct CycleCollectionNoteChildImpl
+{
+};
+
+template<typename T>
+struct CycleCollectionNoteChildImpl<T, true>
+{
+ static void Run(nsCycleCollectionTraversalCallback& aCallback, T* aChild)
+ {
+ aCallback.NoteXPCOMChild(ToSupports(aChild));
+ }
+};
+
+template<typename T>
+struct CycleCollectionNoteChildImpl<T, false>
+{
+ static void Run(nsCycleCollectionTraversalCallback& aCallback, T* aChild)
+ {
+ aCallback.NoteNativeChild(aChild, NS_CYCLE_COLLECTION_PARTICIPANT(T));
+ }
+};
+
+// We declare CycleCollectionNoteChild in 3-argument and 4-argument variants,
+// rather than using default arguments, so that forward declarations work
+// regardless of header inclusion order.
+template<typename T>
+inline void
+CycleCollectionNoteChild(nsCycleCollectionTraversalCallback& aCallback,
+ T* aChild, const char* aName, uint32_t aFlags)
+{
+ CycleCollectionNoteEdgeName(aCallback, aName, aFlags);
+ CycleCollectionNoteChildImpl<T>::Run(aCallback, aChild);
+}
+
+template<typename T>
+inline void
+CycleCollectionNoteChild(nsCycleCollectionTraversalCallback& aCallback,
+ T* aChild, const char* aName)
+{
+ CycleCollectionNoteChild(aCallback, aChild, aName, 0);
+}
+
+#endif // nsCycleCollectionNoteChild_h__
diff --git a/xpcom/glue/nsCycleCollectionNoteRootCallback.h b/xpcom/glue/nsCycleCollectionNoteRootCallback.h
new file mode 100644
index 000000000..42c43f301
--- /dev/null
+++ b/xpcom/glue/nsCycleCollectionNoteRootCallback.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsCycleCollectionNoteRootCallback_h__
+#define nsCycleCollectionNoteRootCallback_h__
+
+class nsCycleCollectionParticipant;
+class nsISupports;
+
+class nsCycleCollectionNoteRootCallback
+{
+public:
+ NS_IMETHOD_(void) NoteXPCOMRoot(nsISupports* aRoot) = 0;
+ NS_IMETHOD_(void) NoteJSRoot(JSObject* aRoot) = 0;
+ NS_IMETHOD_(void) NoteNativeRoot(void* aRoot,
+ nsCycleCollectionParticipant* aParticipant) = 0;
+
+ NS_IMETHOD_(void) NoteWeakMapping(JSObject* aMap, JS::GCCellPtr aKey,
+ JSObject* aKeyDelegate, JS::GCCellPtr aVal) = 0;
+
+ bool WantAllTraces() const { return mWantAllTraces; }
+protected:
+ nsCycleCollectionNoteRootCallback() : mWantAllTraces(false) {}
+
+ bool mWantAllTraces;
+};
+
+#endif // nsCycleCollectionNoteRootCallback_h__
diff --git a/xpcom/glue/nsCycleCollectionParticipant.cpp b/xpcom/glue/nsCycleCollectionParticipant.cpp
new file mode 100644
index 000000000..973ef2ff5
--- /dev/null
+++ b/xpcom/glue/nsCycleCollectionParticipant.cpp
@@ -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/. */
+
+#include "nsCycleCollectionParticipant.h"
+#include "nsCOMPtr.h"
+
+NS_IMETHODIMP_(void)
+nsXPCOMCycleCollectionParticipant::Root(void* aPtr)
+{
+ nsISupports* s = static_cast<nsISupports*>(aPtr);
+ NS_ADDREF(s);
+}
+
+NS_IMETHODIMP_(void)
+nsXPCOMCycleCollectionParticipant::Unroot(void* aPtr)
+{
+ nsISupports* s = static_cast<nsISupports*>(aPtr);
+ NS_RELEASE(s);
+}
+
+// We define a default trace function because some participants don't need
+// to trace anything, so it is okay for them not to define one.
+NS_IMETHODIMP_(void)
+nsXPCOMCycleCollectionParticipant::Trace(void* aPtr, const TraceCallbacks& aCb,
+ void* aClosure)
+{
+}
+
+bool
+nsXPCOMCycleCollectionParticipant::CheckForRightISupports(nsISupports* aSupports)
+{
+ nsISupports* foo;
+ aSupports->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
+ reinterpret_cast<void**>(&foo));
+ return aSupports == foo;
+}
diff --git a/xpcom/glue/nsCycleCollectionParticipant.h b/xpcom/glue/nsCycleCollectionParticipant.h
new file mode 100644
index 000000000..2dfbb6750
--- /dev/null
+++ b/xpcom/glue/nsCycleCollectionParticipant.h
@@ -0,0 +1,852 @@
+/* -*- 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 nsCycleCollectionParticipant_h__
+#define nsCycleCollectionParticipant_h__
+
+#include "mozilla/MacroArgs.h"
+#include "mozilla/MacroForEach.h"
+#include "nsCycleCollectionNoteChild.h"
+#include "js/RootingAPI.h"
+
+#define NS_XPCOMCYCLECOLLECTIONPARTICIPANT_IID \
+{ \
+ 0x9674489b, \
+ 0x1f6f, \
+ 0x4550, \
+ { 0xa7, 0x30, 0xcc, 0xae, 0xdd, 0x10, 0x4c, 0xf9 } \
+}
+
+/**
+ * Special IID to get at the base nsISupports for a class. Usually this is the
+ * canonical nsISupports pointer, but in the case of tearoffs for example it is
+ * the base nsISupports pointer of the tearoff. This allow the cycle collector
+ * to have separate nsCycleCollectionParticipant's for tearoffs or aggregated
+ * classes.
+ */
+#define NS_CYCLECOLLECTIONISUPPORTS_IID \
+{ \
+ 0xc61eac14, \
+ 0x5f7a, \
+ 0x4481, \
+ { 0x96, 0x5e, 0x7e, 0xaa, 0x6e, 0xff, 0xa8, 0x5f } \
+}
+
+/**
+ * Just holds the IID so NS_GET_IID works.
+ */
+class nsCycleCollectionISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_CYCLECOLLECTIONISUPPORTS_IID)
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsCycleCollectionISupports,
+ NS_CYCLECOLLECTIONISUPPORTS_IID)
+
+namespace JS {
+template<class T> class Heap;
+} /* namespace JS */
+
+/*
+ * A struct defining pure virtual methods which are called when tracing cycle
+ * collection paticipants. The appropriate method is called depending on the
+ * type of JS GC thing.
+ */
+struct TraceCallbacks
+{
+ virtual void Trace(JS::Heap<JS::Value>* aPtr, const char* aName,
+ void* aClosure) const = 0;
+ virtual void Trace(JS::Heap<jsid>* aPtr, const char* aName,
+ void* aClosure) const = 0;
+ virtual void Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
+ void* aClosure) const = 0;
+ virtual void Trace(JSObject** aPtr, const char* aName,
+ void* aClosure) const = 0;
+ virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
+ void* aClosure) const = 0;
+ virtual void Trace(JS::Heap<JSString*>* aPtr, const char* aName,
+ void* aClosure) const = 0;
+ virtual void Trace(JS::Heap<JSScript*>* aPtr, const char* aName,
+ void* aClosure) const = 0;
+ virtual void Trace(JS::Heap<JSFunction*>* aPtr, const char* aName,
+ void* aClosure) const = 0;
+};
+
+/*
+ * An implementation of TraceCallbacks that calls a single function for all JS
+ * GC thing types encountered. Implemented in nsCycleCollectorTraceJSHelpers.cpp.
+ */
+struct TraceCallbackFunc : public TraceCallbacks
+{
+ typedef void (*Func)(JS::GCCellPtr aPtr, const char* aName, void* aClosure);
+
+ explicit TraceCallbackFunc(Func aCb) : mCallback(aCb) {}
+
+ virtual void Trace(JS::Heap<JS::Value>* aPtr, const char* aName,
+ void* aClosure) const override;
+ virtual void Trace(JS::Heap<jsid>* aPtr, const char* aName,
+ void* aClosure) const override;
+ virtual void Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
+ void* aClosure) const override;
+ virtual void Trace(JSObject** aPtr, const char* aName,
+ void* aClosure) const override;
+ virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
+ void* aClosure) const override;
+ virtual void Trace(JS::Heap<JSString*>* aPtr, const char* aName,
+ void* aClosure) const override;
+ virtual void Trace(JS::Heap<JSScript*>* aPtr, const char* aName,
+ void* aClosure) const override;
+ virtual void Trace(JS::Heap<JSFunction*>* aPtr, const char* aName,
+ void* aClosure) const override;
+
+private:
+ Func mCallback;
+};
+
+/**
+ * Participant implementation classes
+ */
+class NS_NO_VTABLE nsCycleCollectionParticipant
+{
+public:
+ constexpr nsCycleCollectionParticipant() : mMightSkip(false) {}
+ constexpr explicit nsCycleCollectionParticipant(bool aSkip) : mMightSkip(aSkip) {}
+
+ NS_IMETHOD Traverse(void* aPtr, nsCycleCollectionTraversalCallback& aCb) = 0;
+
+ NS_IMETHOD_(void) Root(void* aPtr) = 0;
+ NS_IMETHOD_(void) Unlink(void* aPtr) = 0;
+ NS_IMETHOD_(void) Unroot(void* aPtr) = 0;
+ NS_IMETHOD_(const char*) ClassName() = 0;
+
+ NS_IMETHOD_(void) Trace(void* aPtr, const TraceCallbacks& aCb,
+ void* aClosure) {}
+
+ // If CanSkip returns true, p is removed from the purple buffer during
+ // a call to nsCycleCollector_forgetSkippable().
+ // Note, calling CanSkip may remove objects from the purple buffer!
+ // If aRemovingAllowed is true, p can be removed from the purple buffer.
+ bool CanSkip(void* aPtr, bool aRemovingAllowed)
+ {
+ return mMightSkip ? CanSkipReal(aPtr, aRemovingAllowed) : false;
+ }
+
+ // If CanSkipInCC returns true, p is skipped when selecting roots for the
+ // cycle collector graph.
+ // Note, calling CanSkipInCC may remove other objects from the purple buffer!
+ bool CanSkipInCC(void* aPtr)
+ {
+ return mMightSkip ? CanSkipInCCReal(aPtr) : false;
+ }
+
+ // If CanSkipThis returns true, p is not added to the graph.
+ // This method is called during cycle collection, so don't
+ // change the state of any objects!
+ bool CanSkipThis(void* aPtr)
+ {
+ return mMightSkip ? CanSkipThisReal(aPtr) : false;
+ }
+
+ NS_IMETHOD_(void) DeleteCycleCollectable(void* aPtr) = 0;
+
+protected:
+ NS_IMETHOD_(bool) CanSkipReal(void* aPtr, bool aRemovingAllowed)
+ {
+ NS_ASSERTION(false, "Forgot to implement CanSkipReal?");
+ return false;
+ }
+ NS_IMETHOD_(bool) CanSkipInCCReal(void* aPtr)
+ {
+ NS_ASSERTION(false, "Forgot to implement CanSkipInCCReal?");
+ return false;
+ }
+ NS_IMETHOD_(bool) CanSkipThisReal(void* aPtr)
+ {
+ NS_ASSERTION(false, "Forgot to implement CanSkipThisReal?");
+ return false;
+ }
+
+private:
+ const bool mMightSkip;
+};
+
+class NS_NO_VTABLE nsScriptObjectTracer : public nsCycleCollectionParticipant
+{
+public:
+ constexpr nsScriptObjectTracer()
+ : nsCycleCollectionParticipant(false)
+ {
+ }
+ constexpr explicit nsScriptObjectTracer(bool aSkip)
+ : nsCycleCollectionParticipant(aSkip)
+ {
+ }
+
+ NS_IMETHOD_(void) Trace(void* aPtr, const TraceCallbacks& aCb,
+ void* aClosure) override = 0;
+
+ // Implemented in nsCycleCollectorTraceJSHelpers.cpp.
+ static void NoteJSChild(JS::GCCellPtr aGCThing, const char* aName,
+ void* aClosure);
+};
+
+class NS_NO_VTABLE nsXPCOMCycleCollectionParticipant : public nsScriptObjectTracer
+{
+public:
+ constexpr nsXPCOMCycleCollectionParticipant()
+ : nsScriptObjectTracer(false)
+ {
+ }
+ constexpr explicit nsXPCOMCycleCollectionParticipant(bool aSkip)
+ : nsScriptObjectTracer(aSkip)
+ {
+ }
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_XPCOMCYCLECOLLECTIONPARTICIPANT_IID)
+
+ NS_IMETHOD_(void) Root(void* aPtr) override;
+ NS_IMETHOD_(void) Unroot(void* aPtr) override;
+
+ NS_IMETHOD_(void) Trace(void* aPtr, const TraceCallbacks& aCb,
+ void* aClosure) override;
+
+ static bool CheckForRightISupports(nsISupports* aSupports);
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsXPCOMCycleCollectionParticipant,
+ NS_XPCOMCYCLECOLLECTIONPARTICIPANT_IID)
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for implementing a QI to nsXPCOMCycleCollectionParticipant
+///////////////////////////////////////////////////////////////////////////////
+
+#define NS_CYCLE_COLLECTION_CLASSNAME(_class) \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS
+
+#define NS_IMPL_QUERY_CYCLE_COLLECTION(_class) \
+ if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) { \
+ *aInstancePtr = NS_CYCLE_COLLECTION_PARTICIPANT(_class); \
+ return NS_OK; \
+ } else
+
+#define NS_IMPL_QUERY_CYCLE_COLLECTION_ISUPPORTS(_class) \
+ if ( aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) ) { \
+ *aInstancePtr = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this); \
+ return NS_OK; \
+ } else
+
+#define NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(_class) \
+ NS_IMPL_QUERY_CYCLE_COLLECTION(_class)
+
+#define NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_ISUPPORTS(_class) \
+ NS_IMPL_QUERY_CYCLE_COLLECTION_ISUPPORTS(_class)
+
+#define NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(_class) \
+ NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(_class) \
+ NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_ISUPPORTS(_class)
+
+#define NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(_class) \
+ NS_INTERFACE_MAP_BEGIN(_class) \
+ NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(_class)
+
+#define NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(_class) \
+ NS_INTERFACE_MAP_BEGIN(_class) \
+ NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(_class)
+
+#define NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(_class) \
+ if (rv == NS_OK) return rv; \
+ nsISupports* foundInterface; \
+ NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(_class)
+
+#define NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(_class) \
+ NS_IMETHODIMP _class::QueryInterface(REFNSIID aIID, void** aInstancePtr) \
+ { \
+ NS_PRECONDITION(aInstancePtr, "null out param"); \
+ \
+ if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) { \
+ *aInstancePtr = NS_CYCLE_COLLECTION_PARTICIPANT(_class); \
+ return NS_OK; \
+ } \
+ nsresult rv;
+
+#define NS_CYCLE_COLLECTION_UPCAST(obj, clazz) \
+ NS_CYCLE_COLLECTION_CLASSNAME(clazz)::Upcast(obj)
+
+#ifdef DEBUG
+#define NS_CHECK_FOR_RIGHT_PARTICIPANT(_ptr) _ptr->CheckForRightParticipant()
+#else
+#define NS_CHECK_FOR_RIGHT_PARTICIPANT(_ptr)
+#endif
+
+// The default implementation of this class template is empty, because it
+// should never be used: see the partial specializations below.
+template<typename T,
+ bool IsXPCOM = mozilla::IsBaseOf<nsISupports, T>::value>
+struct DowncastCCParticipantImpl
+{
+};
+
+// Specialization for XPCOM CC participants
+template<typename T>
+struct DowncastCCParticipantImpl<T, true>
+{
+ static T* Run(void* aPtr)
+ {
+ nsISupports* s = static_cast<nsISupports*>(aPtr);
+ MOZ_ASSERT(NS_CYCLE_COLLECTION_CLASSNAME(T)::CheckForRightISupports(s),
+ "not the nsISupports pointer we expect");
+ T* rval = NS_CYCLE_COLLECTION_CLASSNAME(T)::Downcast(s);
+ NS_CHECK_FOR_RIGHT_PARTICIPANT(rval);
+ return rval;
+ }
+};
+
+// Specialization for native CC participants
+template<typename T>
+struct DowncastCCParticipantImpl<T, false>
+{
+ static T* Run(void* aPtr) { return static_cast<T*>(aPtr); }
+};
+
+template<typename T>
+T*
+DowncastCCParticipant(void* aPtr)
+{
+ return DowncastCCParticipantImpl<T>::Run(aPtr);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for implementing CanSkip methods
+///////////////////////////////////////////////////////////////////////////////
+
+#define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(_class) \
+ NS_IMETHODIMP_(bool) \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class)::CanSkipReal(void *p, \
+ bool aRemovingAllowed) \
+ { \
+ _class *tmp = DowncastCCParticipant<_class >(p);
+
+#define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END \
+ (void)tmp; \
+ return false; \
+ }
+
+#define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(_class) \
+ NS_IMETHODIMP_(bool) \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class)::CanSkipInCCReal(void *p) \
+ { \
+ _class *tmp = DowncastCCParticipant<_class >(p);
+
+#define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END \
+ (void)tmp; \
+ return false; \
+ }
+
+#define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(_class) \
+ NS_IMETHODIMP_(bool) \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class)::CanSkipThisReal(void *p) \
+ { \
+ _class *tmp = DowncastCCParticipant<_class >(p);
+
+#define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END \
+ (void)tmp; \
+ return false; \
+ }
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for implementing nsCycleCollectionParticipant::Unlink
+//
+// You need to use NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED if you want
+// the base class Unlink version to be called before your own implementation.
+// You can use NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED if you want the
+// base class Unlink to get called after your own implementation. You should
+// never use them together.
+///////////////////////////////////////////////////////////////////////////////
+
+#define NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class) \
+ NS_IMETHODIMP_(void) \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class)::Unlink(void *p) \
+ { \
+ _class *tmp = DowncastCCParticipant<_class >(p);
+
+#define NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(_class, _base_class) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class) \
+ nsISupports *s = static_cast<nsISupports*>(p); \
+ NS_CYCLE_COLLECTION_CLASSNAME(_base_class)::Unlink(s);
+
+#define NS_IMPL_CYCLE_COLLECTION_UNLINK_HELPER(_field) \
+ ImplCycleCollectionUnlink(tmp->_field);
+
+#define NS_IMPL_CYCLE_COLLECTION_UNLINK(...) \
+ MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \
+ MOZ_FOR_EACH(NS_IMPL_CYCLE_COLLECTION_UNLINK_HELPER, (), (__VA_ARGS__))
+
+#define NS_IMPL_CYCLE_COLLECTION_UNLINK_END \
+ (void)tmp; \
+ }
+
+#define NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(_base_class) \
+ nsISupports *s = static_cast<nsISupports*>(p); \
+ NS_CYCLE_COLLECTION_CLASSNAME(_base_class)::Unlink(s); \
+ (void)tmp; \
+ }
+
+#define NS_IMPL_CYCLE_COLLECTION_UNLINK_0(_class) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for implementing nsCycleCollectionParticipant::Traverse
+///////////////////////////////////////////////////////////////////////////////
+
+#define NS_IMPL_CYCLE_COLLECTION_DESCRIBE(_class, _refcnt) \
+ cb.DescribeRefCountedNode(_refcnt, #_class);
+
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(_class) \
+ NS_IMETHODIMP \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class)::Traverse \
+ (void *p, nsCycleCollectionTraversalCallback &cb) \
+ { \
+ _class *tmp = DowncastCCParticipant<_class >(p);
+
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class) \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(_class) \
+ NS_IMPL_CYCLE_COLLECTION_DESCRIBE(_class, tmp->mRefCnt.get())
+
+// Base class' CC participant should return NS_SUCCESS_INTERRUPTED_TRAVERSE
+// from Traverse if it wants derived classes to not traverse anything from
+// their CC participant.
+
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(_class, _base_class) \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(_class) \
+ nsISupports *s = static_cast<nsISupports*>(p); \
+ if (NS_CYCLE_COLLECTION_CLASSNAME(_base_class)::Traverse(s, cb) \
+ == NS_SUCCESS_INTERRUPTED_TRAVERSE) { \
+ return NS_SUCCESS_INTERRUPTED_TRAVERSE; \
+ }
+
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_HELPER(_field) \
+ ImplCycleCollectionTraverse(cb, tmp->_field, #_field, 0);
+
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE(...) \
+ MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \
+ MOZ_FOR_EACH(NS_IMPL_CYCLE_COLLECTION_TRAVERSE_HELPER, (), (__VA_ARGS__))
+
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(_field) \
+ CycleCollectionNoteChild(cb, tmp->_field, #_field);
+
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS \
+ { \
+ TraceCallbackFunc noteJsChild(&nsScriptObjectTracer::NoteJSChild); \
+ Trace(p, noteJsChild, &cb); \
+ }
+
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END \
+ (void)tmp; \
+ return NS_OK; \
+ }
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for implementing nsScriptObjectTracer::Trace
+///////////////////////////////////////////////////////////////////////////////
+
+#define NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(_class) \
+ void \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class)::Trace(void *p, \
+ const TraceCallbacks &aCallbacks, \
+ void *aClosure) \
+ { \
+ _class *tmp = DowncastCCParticipant<_class >(p);
+
+#define NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(_class, _base_class) \
+ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(_class) \
+ nsISupports *s = static_cast<nsISupports*>(p); \
+ NS_CYCLE_COLLECTION_CLASSNAME(_base_class)::Trace(s, aCallbacks, aClosure);
+
+#define NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(_field) \
+ aCallbacks.Trace(&tmp->_field, #_field, aClosure);
+
+// NB: The (void)tmp; hack in the TRACE_END macro exists to support
+// implementations that don't need to do anything in their Trace method.
+// Without this hack, some compilers warn about the unused tmp local.
+#define NS_IMPL_CYCLE_COLLECTION_TRACE_END \
+ (void)tmp; \
+ }
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for implementing a concrete nsCycleCollectionParticipant
+///////////////////////////////////////////////////////////////////////////////
+
+// If a class defines a participant, then QIing an instance of that class to
+// nsXPCOMCycleCollectionParticipant should produce that participant.
+#ifdef DEBUG
+#define NS_CHECK_FOR_RIGHT_PARTICIPANT_BASE \
+ virtual void CheckForRightParticipant()
+#define NS_CHECK_FOR_RIGHT_PARTICIPANT_DERIVED \
+ virtual void CheckForRightParticipant() override
+#define NS_CHECK_FOR_RIGHT_PARTICIPANT_BODY(_class) \
+ { \
+ nsXPCOMCycleCollectionParticipant *p; \
+ CallQueryInterface(this, &p); \
+ MOZ_ASSERT(p == &NS_CYCLE_COLLECTION_INNERNAME, \
+ #_class " should QI to its own CC participant"); \
+ }
+#define NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class) \
+ NS_CHECK_FOR_RIGHT_PARTICIPANT_BASE \
+ NS_CHECK_FOR_RIGHT_PARTICIPANT_BODY(_class)
+#define NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL_INHERITED(_class) \
+ NS_CHECK_FOR_RIGHT_PARTICIPANT_DERIVED \
+ NS_CHECK_FOR_RIGHT_PARTICIPANT_BODY(_class)
+#else
+#define NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class)
+#define NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL_INHERITED(_class)
+#endif
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(_class) \
+ NS_IMETHOD_(const char*) ClassName() override { return #_class; };
+
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS_BODY_NO_UNLINK(_class, _base) \
+public: \
+ NS_IMETHOD Traverse(void *p, nsCycleCollectionTraversalCallback &cb) \
+ override; \
+ NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(_class) \
+ NS_IMETHOD_(void) DeleteCycleCollectable(void *p) override \
+ { \
+ DowncastCCParticipant<_class>(p)->DeleteCycleCollectable(); \
+ } \
+ static _class* Downcast(nsISupports* s) \
+ { \
+ return static_cast<_class*>(static_cast<_base*>(s)); \
+ } \
+ static nsISupports* Upcast(_class *p) \
+ { \
+ return NS_ISUPPORTS_CAST(_base*, p); \
+ } \
+ template<typename T> \
+ friend nsISupports* \
+ ToSupports(T* p, NS_CYCLE_COLLECTION_INNERCLASS* dummy);
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base) \
+ NS_DECL_CYCLE_COLLECTION_CLASS_BODY_NO_UNLINK(_class, _base) \
+ NS_IMETHOD_(void) Unlink(void *p) override;
+
+#define NS_PARTICIPANT_AS(type, participant) \
+ const_cast<type*>(reinterpret_cast<const type*>(participant))
+
+#define NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \
+ static constexpr nsXPCOMCycleCollectionParticipant* GetParticipant() \
+ { \
+ return &_class::NS_CYCLE_COLLECTION_INNERNAME; \
+ }
+
+/**
+ * We use this macro to force that classes that inherit from a ccable class and
+ * declare their own participant declare themselves as inherited cc classes.
+ * To avoid possibly unnecessary vtables we only do this checking in debug
+ * builds.
+ */
+#ifdef DEBUG
+#define NOT_INHERITED_CANT_OVERRIDE virtual void BaseCycleCollectable() final {}
+#else
+#define NOT_INHERITED_CANT_OVERRIDE
+#endif
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(_class, _base) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public nsXPCOMCycleCollectionParticipant \
+{ \
+ NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base) \
+ NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \
+}; \
+NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class) \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME; \
+ NOT_INHERITED_CANT_OVERRIDE
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS(_class) \
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(_class, _class)
+
+// Cycle collector helper for ambiguous classes that can sometimes be skipped.
+#define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS_AMBIGUOUS(_class, _base) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public nsXPCOMCycleCollectionParticipant \
+{ \
+public: \
+ constexpr NS_CYCLE_COLLECTION_INNERCLASS () \
+ : nsXPCOMCycleCollectionParticipant(true) {} \
+private: \
+ NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base) \
+ NS_IMETHOD_(bool) CanSkipReal(void *p, bool aRemovingAllowed) override; \
+ NS_IMETHOD_(bool) CanSkipInCCReal(void *p) override; \
+ NS_IMETHOD_(bool) CanSkipThisReal(void *p) override; \
+ NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \
+}; \
+NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class) \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME; \
+NOT_INHERITED_CANT_OVERRIDE
+
+#define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS(_class) \
+ NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS_AMBIGUOUS(_class, _class)
+
+#define NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(_class, _base) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public nsXPCOMCycleCollectionParticipant \
+{ \
+ NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base) \
+ NS_IMETHOD_(void) Trace(void *p, const TraceCallbacks &cb, void *closure) override; \
+ NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \
+}; \
+NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class) \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME; \
+NOT_INHERITED_CANT_OVERRIDE
+
+#define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(_class, _base) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public nsXPCOMCycleCollectionParticipant \
+{ \
+public: \
+ constexpr NS_CYCLE_COLLECTION_INNERCLASS () \
+ : nsXPCOMCycleCollectionParticipant(true) {} \
+private: \
+ NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base) \
+ NS_IMETHOD_(void) Trace(void *p, const TraceCallbacks &cb, void *closure) override; \
+ NS_IMETHOD_(bool) CanSkipReal(void *p, bool aRemovingAllowed) override; \
+ NS_IMETHOD_(bool) CanSkipInCCReal(void *p) override; \
+ NS_IMETHOD_(bool) CanSkipThisReal(void *p) override; \
+ NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \
+}; \
+NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class) \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME; \
+NOT_INHERITED_CANT_OVERRIDE
+
+#define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(_class) \
+ NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(_class, _class)
+
+#define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_INHERITED(_class, \
+ _base_class) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public NS_CYCLE_COLLECTION_CLASSNAME(_base_class) \
+{ \
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY(_class, _base_class) \
+ NS_IMETHOD_(void) Trace(void *p, const TraceCallbacks &cb, void *closure) override; \
+ NS_IMETHOD_(bool) CanSkipReal(void *p, bool aRemovingAllowed) override; \
+ NS_IMETHOD_(bool) CanSkipInCCReal(void *p) override; \
+ NS_IMETHOD_(bool) CanSkipThisReal(void *p) override; \
+ NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \
+}; \
+NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL_INHERITED(_class) \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
+
+#define NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(_class) \
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(_class, _class)
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY_NO_UNLINK(_class, \
+ _base_class) \
+public: \
+ NS_IMETHOD Traverse(void *p, nsCycleCollectionTraversalCallback &cb) \
+ override; \
+ NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(_class) \
+ static _class* Downcast(nsISupports* s) \
+ { \
+ return static_cast<_class*>(static_cast<_base_class*>( \
+ NS_CYCLE_COLLECTION_CLASSNAME(_base_class)::Downcast(s))); \
+ }
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY(_class, _base_class) \
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY_NO_UNLINK(_class, _base_class) \
+ NS_IMETHOD_(void) Unlink(void *p) override;
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(_class, _base_class) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public NS_CYCLE_COLLECTION_CLASSNAME(_base_class) \
+{ \
+public: \
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY(_class, _base_class) \
+ NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \
+}; \
+NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL_INHERITED(_class) \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(_class, \
+ _base_class) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public NS_CYCLE_COLLECTION_CLASSNAME(_base_class) \
+{ \
+public: \
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY_NO_UNLINK(_class, _base_class) \
+ NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \
+}; \
+NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL_INHERITED(_class) \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
+
+#define NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(_class, \
+ _base_class) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public NS_CYCLE_COLLECTION_CLASSNAME(_base_class) \
+{ \
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY(_class, _base_class) \
+ NS_IMETHOD_(void) Trace(void *p, const TraceCallbacks &cb, void *closure) \
+ override; \
+ NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class) \
+}; \
+NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL_INHERITED(_class) \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
+
+// Cycle collector participant declarations.
+
+#define NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY(_class) \
+ public: \
+ NS_IMETHOD_(void) Root(void *n) override; \
+ NS_IMETHOD_(void) Unlink(void *n) override; \
+ NS_IMETHOD_(void) Unroot(void *n) override; \
+ NS_IMETHOD Traverse(void *n, nsCycleCollectionTraversalCallback &cb) \
+ override; \
+ NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(_class) \
+ NS_IMETHOD_(void) DeleteCycleCollectable(void *n) override \
+ { \
+ DowncastCCParticipant<_class>(n)->DeleteCycleCollectable(); \
+ } \
+ static _class* Downcast(void* s) \
+ { \
+ return DowncastCCParticipant<_class>(s); \
+ } \
+ static void* Upcast(_class *p) \
+ { \
+ return static_cast<void*>(p); \
+ }
+
+#define NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(_class) \
+ void DeleteCycleCollectable(void) \
+ { \
+ delete this; \
+ } \
+ class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public nsCycleCollectionParticipant \
+ { \
+ NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY(_class) \
+ static constexpr nsCycleCollectionParticipant* GetParticipant() \
+ { \
+ return &_class::NS_CYCLE_COLLECTION_INNERNAME; \
+ } \
+ }; \
+ static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
+
+#define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_NATIVE_CLASS(_class) \
+ void DeleteCycleCollectable(void) \
+ { \
+ delete this; \
+ } \
+ class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public nsCycleCollectionParticipant \
+ { \
+ public: \
+ constexpr NS_CYCLE_COLLECTION_INNERCLASS () \
+ : nsCycleCollectionParticipant(true) {} \
+ private: \
+ NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY(_class) \
+ NS_IMETHOD_(bool) CanSkipReal(void *p, bool aRemovingAllowed) override; \
+ NS_IMETHOD_(bool) CanSkipInCCReal(void *p) override; \
+ NS_IMETHOD_(bool) CanSkipThisReal(void *p) override; \
+ static nsCycleCollectionParticipant* GetParticipant() \
+ { \
+ return &_class::NS_CYCLE_COLLECTION_INNERNAME; \
+ } \
+ }; \
+ static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
+
+#define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_NATIVE_CLASS_WITH_CUSTOM_DELETE(_class) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public nsCycleCollectionParticipant \
+{ \
+public: \
+ constexpr NS_CYCLE_COLLECTION_INNERCLASS () \
+ : nsCycleCollectionParticipant(true) {} \
+private: \
+ NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY(_class) \
+ NS_IMETHOD_(bool) CanSkipReal(void *p, bool aRemovingAllowed) override; \
+ NS_IMETHOD_(bool) CanSkipInCCReal(void *p) override; \
+ NS_IMETHOD_(bool) CanSkipThisReal(void *p) override; \
+ static nsCycleCollectionParticipant* GetParticipant() \
+ { \
+ return &_class::NS_CYCLE_COLLECTION_INNERNAME; \
+ } \
+}; \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
+
+#define NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(_class) \
+ void DeleteCycleCollectable(void) \
+ { \
+ delete this; \
+ } \
+ class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public nsScriptObjectTracer \
+ { \
+ NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY(_class) \
+ NS_IMETHOD_(void) Trace(void *p, const TraceCallbacks &cb, void *closure) \
+ override; \
+ static constexpr nsScriptObjectTracer* GetParticipant() \
+ { \
+ return &_class::NS_CYCLE_COLLECTION_INNERNAME; \
+ } \
+ }; \
+ static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
+
+#define NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(_class, _root_function) \
+ NS_IMETHODIMP_(void) \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class)::Root(void *p) \
+ { \
+ _class *tmp = static_cast<_class*>(p); \
+ tmp->_root_function(); \
+ }
+
+#define NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(_class, _unroot_function) \
+ NS_IMETHODIMP_(void) \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class)::Unroot(void *p) \
+ { \
+ _class *tmp = static_cast<_class*>(p); \
+ tmp->_unroot_function(); \
+ }
+
+#define NS_IMPL_CYCLE_COLLECTION_CLASS(_class) \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS _class::NS_CYCLE_COLLECTION_INNERNAME;
+
+// NB: This is not something you usually want to use. It is here to allow
+// adding things to the CC graph to help debugging via CC logs, but it does not
+// traverse or unlink anything, so it is useless for anything else.
+#define NS_IMPL_CYCLE_COLLECTION_0(_class) \
+ NS_IMPL_CYCLE_COLLECTION_CLASS(_class) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_END \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class) \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+#define NS_IMPL_CYCLE_COLLECTION(_class, ...) \
+ NS_IMPL_CYCLE_COLLECTION_CLASS(_class) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(__VA_ARGS__) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_END \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class) \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__) \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+// If you are looking for NS_IMPL_CYCLE_COLLECTION_INHERITED_0(_class, _base)
+// you should instead not declare any cycle collected stuff in _class, so it
+// will just inherit the CC declarations from _base.
+
+#define NS_IMPL_CYCLE_COLLECTION_INHERITED(_class, _base, ...) \
+ NS_IMPL_CYCLE_COLLECTION_CLASS(_class) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(_class, _base) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(__VA_ARGS__) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_END \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(_class, _base) \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__) \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+#define NS_CYCLE_COLLECTION_NOTE_EDGE_NAME CycleCollectionNoteEdgeName
+
+#endif // nsCycleCollectionParticipant_h__
diff --git a/xpcom/glue/nsCycleCollectionTraversalCallback.h b/xpcom/glue/nsCycleCollectionTraversalCallback.h
new file mode 100644
index 000000000..9e314af9b
--- /dev/null
+++ b/xpcom/glue/nsCycleCollectionTraversalCallback.h
@@ -0,0 +1,62 @@
+/* -*- 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 nsCycleCollectionTraversalCallback_h__
+#define nsCycleCollectionTraversalCallback_h__
+
+#include "jspubtd.h"
+#include "js/HeapAPI.h"
+#include "nsISupports.h"
+
+class nsCycleCollectionParticipant;
+
+class NS_NO_VTABLE nsCycleCollectionTraversalCallback
+{
+public:
+ // You must call DescribeRefCountedNode() with an accurate
+ // refcount, otherwise cycle collection will fail, and probably crash.
+ // If the callback cares about objname, it should put
+ // WANT_DEBUG_INFO in mFlags.
+ NS_IMETHOD_(void) DescribeRefCountedNode(nsrefcnt aRefcount,
+ const char* aObjName) = 0;
+ // Note, aCompartmentAddress is 0 if it is unknown.
+ NS_IMETHOD_(void) DescribeGCedNode(bool aIsMarked,
+ const char* aObjName,
+ uint64_t aCompartmentAddress = 0) = 0;
+
+ NS_IMETHOD_(void) NoteXPCOMChild(nsISupports* aChild) = 0;
+ NS_IMETHOD_(void) NoteJSChild(const JS::GCCellPtr& aThing) = 0;
+ NS_IMETHOD_(void) NoteNativeChild(void* aChild,
+ nsCycleCollectionParticipant* aHelper) = 0;
+
+ // Give a name to the edge associated with the next call to
+ // NoteXPCOMChild, NoteJSObject, NoteJSScript, or NoteNativeChild.
+ // Callbacks who care about this should set WANT_DEBUG_INFO in the
+ // flags.
+ NS_IMETHOD_(void) NoteNextEdgeName(const char* aName) = 0;
+
+ enum
+ {
+ // Values for flags:
+
+ // Caller should call NoteNextEdgeName and pass useful objName
+ // to DescribeRefCountedNode and DescribeGCedNode.
+ WANT_DEBUG_INFO = (1 << 0),
+
+ // Caller should not skip objects that we know will be
+ // uncollectable.
+ WANT_ALL_TRACES = (1 << 1)
+ };
+ uint32_t Flags() const { return mFlags; }
+ bool WantDebugInfo() const { return (mFlags & WANT_DEBUG_INFO) != 0; }
+ bool WantAllTraces() const { return (mFlags & WANT_ALL_TRACES) != 0; }
+protected:
+ nsCycleCollectionTraversalCallback() : mFlags(0) {}
+
+ uint32_t mFlags;
+};
+
+#endif // nsCycleCollectionTraversalCallback_h__
diff --git a/xpcom/glue/nsDataHashtable.h b/xpcom/glue/nsDataHashtable.h
new file mode 100644
index 000000000..19c0728b4
--- /dev/null
+++ b/xpcom/glue/nsDataHashtable.h
@@ -0,0 +1,58 @@
+/* -*- 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 nsDataHashtable_h__
+#define nsDataHashtable_h__
+
+#include "nsHashKeys.h"
+#include "nsBaseHashtable.h"
+#include "mozilla/Maybe.h"
+
+/**
+ * templated hashtable class maps keys to simple datatypes.
+ * See nsBaseHashtable for complete declaration
+ * @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h
+ * for a complete specification.
+ * @param DataType the simple datatype being wrapped
+ * @see nsInterfaceHashtable, nsClassHashtable
+ */
+template<class KeyClass, class DataType>
+class nsDataHashtable
+ : public nsBaseHashtable<KeyClass, DataType, DataType>
+{
+private:
+ typedef nsBaseHashtable<KeyClass, DataType, DataType> BaseClass;
+
+public:
+ using typename BaseClass::KeyType;
+ using typename BaseClass::EntryType;
+
+ nsDataHashtable() {}
+ explicit nsDataHashtable(uint32_t aInitLength)
+ : BaseClass(aInitLength)
+ {
+ }
+
+ /**
+ * Retrieve the value for a key and remove the corresponding entry at
+ * the same time.
+ *
+ * @param aKey the key to retrieve and remove
+ * @return the found value, or Nothing if no entry was found with the
+ * given key.
+ */
+ mozilla::Maybe<DataType> GetAndRemove(KeyType aKey)
+ {
+ mozilla::Maybe<DataType> value;
+ if (EntryType* ent = this->GetEntry(aKey)) {
+ value.emplace(mozilla::Move(ent->mData));
+ this->RemoveEntry(ent);
+ }
+ return value;
+ }
+};
+
+#endif // nsDataHashtable_h__
diff --git a/xpcom/glue/nsDebug.h b/xpcom/glue/nsDebug.h
new file mode 100644
index 000000000..7365f9ce3
--- /dev/null
+++ b/xpcom/glue/nsDebug.h
@@ -0,0 +1,460 @@
+/* -*- 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 nsDebug_h___
+#define nsDebug_h___
+
+#include "nscore.h"
+#include "nsError.h"
+
+#include "nsXPCOM.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Likely.h"
+#include <stdarg.h>
+
+#ifdef DEBUG
+#include "prprf.h"
+#endif
+
+/**
+ * Warn if the given condition is true. The condition is evaluated in both
+ * release and debug builds, and the result is an expression which can be
+ * used in subsequent expressions, such as:
+ *
+ * if (NS_WARN_IF(NS_FAILED(rv)) {
+ * return rv;
+ * }
+ *
+ * This explicit warning and return is preferred to the NS_ENSURE_* macros
+ * which hide the warning and the return control flow.
+ *
+ * This macro can also be used outside of conditions just to issue a warning,
+ * like so:
+ *
+ * Unused << NS_WARN_IF(NS_FAILED(FnWithSideEffects());
+ *
+ * (The |Unused <<| is necessary because of the MOZ_MUST_USE annotation.)
+ *
+ * However, note that the argument to this macro is evaluated in all builds. If
+ * you just want a warning assertion, it is better to use NS_WARNING_ASSERTION
+ * (which evaluates the condition only in debug builds) like so:
+ *
+ * NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "operation failed");
+ *
+ * @note This is C++-only
+ */
+#ifdef __cplusplus
+#ifdef DEBUG
+inline MOZ_MUST_USE bool NS_warn_if_impl(bool aCondition, const char* aExpr,
+ const char* aFile, int32_t aLine)
+{
+ if (MOZ_UNLIKELY(aCondition)) {
+ NS_DebugBreak(NS_DEBUG_WARNING, nullptr, aExpr, aFile, aLine);
+ }
+ return aCondition;
+}
+#define NS_WARN_IF(condition) \
+ NS_warn_if_impl(condition, #condition, __FILE__, __LINE__)
+#else
+#define NS_WARN_IF(condition) (bool)(condition)
+#endif
+#endif
+
+/**
+ * Test an assertion for truth. If the expression is not true then
+ * emit a warning.
+ *
+ * Program execution continues past the usage of this macro.
+ *
+ * Note also that the non-debug version of this macro does <b>not</b>
+ * evaluate the message argument.
+ */
+#ifdef DEBUG
+#define NS_WARNING_ASSERTION(_expr, _msg) \
+ do { \
+ if (!(_expr)) { \
+ NS_DebugBreak(NS_DEBUG_WARNING, _msg, #_expr, __FILE__, __LINE__); \
+ } \
+ } while(0)
+#else
+#define NS_WARNING_ASSERTION(_expr, _msg) do { /* nothing */ } while(0)
+#endif
+
+/**
+ * Test an assertion for truth. If the expression is not true then
+ * trigger a program failure.
+ *
+ * Note that the non-debug version of this macro does <b>not</b>
+ * evaluate the message argument.
+ */
+#ifdef DEBUG
+inline void MOZ_PretendNoReturn()
+ MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS {}
+#define NS_ASSERTION(expr, str) \
+ do { \
+ if (!(expr)) { \
+ NS_DebugBreak(NS_DEBUG_ASSERTION, str, #expr, __FILE__, __LINE__); \
+ MOZ_PretendNoReturn(); \
+ } \
+ } while(0)
+#else
+#define NS_ASSERTION(expr, str) do { /* nothing */ } while(0)
+#endif
+
+/**
+ * NS_PRECONDITION/POSTCONDITION are synonyms for NS_ASSERTION.
+ */
+#define NS_PRECONDITION(expr, str) NS_ASSERTION(expr, str)
+#define NS_POSTCONDITION(expr, str) NS_ASSERTION(expr, str)
+
+/**
+ * This macros triggers a program failure if executed. It indicates that
+ * an attempt was made to execute some unimplemented functionality.
+ */
+#ifdef DEBUG
+#define NS_NOTYETIMPLEMENTED(str) \
+ do { \
+ NS_DebugBreak(NS_DEBUG_ASSERTION, str, "NotYetImplemented", __FILE__, __LINE__); \
+ MOZ_PretendNoReturn(); \
+ } while(0)
+#else
+#define NS_NOTYETIMPLEMENTED(str) do { /* nothing */ } while(0)
+#endif
+
+/**
+ * This macros triggers a program failure if executed. It indicates that
+ * an attempt was made to execute a codepath which should not be reachable.
+ */
+#ifdef DEBUG
+#define NS_NOTREACHED(str) \
+ do { \
+ NS_DebugBreak(NS_DEBUG_ASSERTION, str, "Not Reached", __FILE__, __LINE__); \
+ MOZ_PretendNoReturn(); \
+ } while(0)
+#else
+#define NS_NOTREACHED(str) do { /* nothing */ } while(0)
+#endif
+
+/**
+ * Log an error message.
+ */
+#ifdef DEBUG
+#define NS_ERROR(str) \
+ do { \
+ NS_DebugBreak(NS_DEBUG_ASSERTION, str, "Error", __FILE__, __LINE__); \
+ MOZ_PretendNoReturn(); \
+ } while(0)
+#else
+#define NS_ERROR(str) do { /* nothing */ } while(0)
+#endif
+
+/**
+ * Log a warning message.
+ */
+#ifdef DEBUG
+#define NS_WARNING(str) \
+ NS_DebugBreak(NS_DEBUG_WARNING, str, nullptr, __FILE__, __LINE__)
+#else
+#define NS_WARNING(str) do { /* nothing */ } while(0)
+#endif
+
+/**
+ * Trigger an debug-only abort.
+ *
+ * @see NS_RUNTIMEABORT for release-mode asserts.
+ */
+#ifdef DEBUG
+#define NS_ABORT() \
+ do { \
+ NS_DebugBreak(NS_DEBUG_ABORT, nullptr, nullptr, __FILE__, __LINE__); \
+ MOZ_PretendNoReturn(); \
+ } while(0)
+#else
+#define NS_ABORT() do { /* nothing */ } while(0)
+#endif
+
+/**
+ * Trigger a debugger breakpoint, only in debug builds.
+ */
+#ifdef DEBUG
+#define NS_BREAK() \
+ do { \
+ NS_DebugBreak(NS_DEBUG_BREAK, nullptr, nullptr, __FILE__, __LINE__); \
+ MOZ_PretendNoReturn(); \
+ } while(0)
+#else
+#define NS_BREAK() do { /* nothing */ } while(0)
+#endif
+
+/******************************************************************************
+** Macros for static assertions. These are used by the sixgill tool.
+** When the tool is not running these macros are no-ops.
+******************************************************************************/
+
+/* Avoid name collision if included with other headers defining annotations. */
+#ifndef HAVE_STATIC_ANNOTATIONS
+#define HAVE_STATIC_ANNOTATIONS
+
+#ifdef XGILL_PLUGIN
+
+#define STATIC_PRECONDITION(COND) __attribute__((precondition(#COND)))
+#define STATIC_PRECONDITION_ASSUME(COND) __attribute__((precondition_assume(#COND)))
+#define STATIC_POSTCONDITION(COND) __attribute__((postcondition(#COND)))
+#define STATIC_POSTCONDITION_ASSUME(COND) __attribute__((postcondition_assume(#COND)))
+#define STATIC_INVARIANT(COND) __attribute__((invariant(#COND)))
+#define STATIC_INVARIANT_ASSUME(COND) __attribute__((invariant_assume(#COND)))
+
+/* Used to make identifiers for assert/assume annotations in a function. */
+#define STATIC_PASTE2(X,Y) X ## Y
+#define STATIC_PASTE1(X,Y) STATIC_PASTE2(X,Y)
+
+#define STATIC_ASSUME(COND) \
+ do { \
+ __attribute__((assume_static(#COND), unused)) \
+ int STATIC_PASTE1(assume_static_, __COUNTER__); \
+ } while(0)
+
+#define STATIC_ASSERT_RUNTIME(COND) \
+ do { \
+ __attribute__((assert_static_runtime(#COND), unused)) \
+ int STATIC_PASTE1(assert_static_runtime_, __COUNTER__); \
+ } while(0)
+
+#else /* XGILL_PLUGIN */
+
+#define STATIC_PRECONDITION(COND) /* nothing */
+#define STATIC_PRECONDITION_ASSUME(COND) /* nothing */
+#define STATIC_POSTCONDITION(COND) /* nothing */
+#define STATIC_POSTCONDITION_ASSUME(COND) /* nothing */
+#define STATIC_INVARIANT(COND) /* nothing */
+#define STATIC_INVARIANT_ASSUME(COND) /* nothing */
+
+#define STATIC_ASSUME(COND) do { /* nothing */ } while(0)
+#define STATIC_ASSERT_RUNTIME(COND) do { /* nothing */ } while(0)
+
+#endif /* XGILL_PLUGIN */
+
+#define STATIC_SKIP_INFERENCE STATIC_INVARIANT(skip_inference())
+
+#endif /* HAVE_STATIC_ANNOTATIONS */
+
+/******************************************************************************
+** Macros for terminating execution when an unrecoverable condition is
+** reached. These need to be compiled regardless of the DEBUG flag.
+******************************************************************************/
+
+/**
+ * Terminate execution <i>immediately</i>, and if possible on the current
+ * platform, in such a way that execution can't be continued by other
+ * code (e.g., by intercepting a signal).
+ */
+#define NS_RUNTIMEABORT(msg) \
+ NS_DebugBreak(NS_DEBUG_ABORT, msg, nullptr, __FILE__, __LINE__)
+
+
+/* Macros for checking the trueness of an expression passed in within an
+ * interface implementation. These need to be compiled regardless of the
+ * DEBUG flag. New code should use NS_WARN_IF(condition) instead!
+ * @status deprecated
+ */
+
+#define NS_ENSURE_TRUE(x, ret) \
+ do { \
+ if (MOZ_UNLIKELY(!(x))) { \
+ NS_WARNING("NS_ENSURE_TRUE(" #x ") failed"); \
+ return ret; \
+ } \
+ } while(0)
+
+#define NS_ENSURE_FALSE(x, ret) \
+ NS_ENSURE_TRUE(!(x), ret)
+
+#define NS_ENSURE_TRUE_VOID(x) \
+ do { \
+ if (MOZ_UNLIKELY(!(x))) { \
+ NS_WARNING("NS_ENSURE_TRUE(" #x ") failed"); \
+ return; \
+ } \
+ } while(0)
+
+#define NS_ENSURE_FALSE_VOID(x) \
+ NS_ENSURE_TRUE_VOID(!(x))
+
+/******************************************************************************
+** Macros for checking results
+******************************************************************************/
+
+#if defined(DEBUG) && !defined(XPCOM_GLUE_AVOID_NSPR)
+
+#define NS_ENSURE_SUCCESS_BODY(res, ret) \
+ char *msg = PR_smprintf("NS_ENSURE_SUCCESS(%s, %s) failed with " \
+ "result 0x%X", #res, #ret, __rv); \
+ NS_WARNING(msg); \
+ PR_smprintf_free(msg);
+
+#define NS_ENSURE_SUCCESS_BODY_VOID(res) \
+ char *msg = PR_smprintf("NS_ENSURE_SUCCESS_VOID(%s) failed with " \
+ "result 0x%X", #res, __rv); \
+ NS_WARNING(msg); \
+ PR_smprintf_free(msg);
+
+#else
+
+#define NS_ENSURE_SUCCESS_BODY(res, ret) \
+ NS_WARNING("NS_ENSURE_SUCCESS(" #res ", " #ret ") failed");
+
+#define NS_ENSURE_SUCCESS_BODY_VOID(res) \
+ NS_WARNING("NS_ENSURE_SUCCESS_VOID(" #res ") failed");
+
+#endif
+
+#define NS_ENSURE_SUCCESS(res, ret) \
+ do { \
+ nsresult __rv = res; /* Don't evaluate |res| more than once */ \
+ if (NS_FAILED(__rv)) { \
+ NS_ENSURE_SUCCESS_BODY(res, ret) \
+ return ret; \
+ } \
+ } while(0)
+
+#define NS_ENSURE_SUCCESS_VOID(res) \
+ do { \
+ nsresult __rv = res; \
+ if (NS_FAILED(__rv)) { \
+ NS_ENSURE_SUCCESS_BODY_VOID(res) \
+ return; \
+ } \
+ } while(0)
+
+/******************************************************************************
+** Macros for checking state and arguments upon entering interface boundaries
+******************************************************************************/
+
+#define NS_ENSURE_ARG(arg) \
+ NS_ENSURE_TRUE(arg, NS_ERROR_INVALID_ARG)
+
+#define NS_ENSURE_ARG_POINTER(arg) \
+ NS_ENSURE_TRUE(arg, NS_ERROR_INVALID_POINTER)
+
+#define NS_ENSURE_ARG_MIN(arg, min) \
+ NS_ENSURE_TRUE((arg) >= min, NS_ERROR_INVALID_ARG)
+
+#define NS_ENSURE_ARG_MAX(arg, max) \
+ NS_ENSURE_TRUE((arg) <= max, NS_ERROR_INVALID_ARG)
+
+#define NS_ENSURE_ARG_RANGE(arg, min, max) \
+ NS_ENSURE_TRUE(((arg) >= min) && ((arg) <= max), NS_ERROR_INVALID_ARG)
+
+#define NS_ENSURE_STATE(state) \
+ NS_ENSURE_TRUE(state, NS_ERROR_UNEXPECTED)
+
+#define NS_ENSURE_NO_AGGREGATION(outer) \
+ NS_ENSURE_FALSE(outer, NS_ERROR_NO_AGGREGATION)
+
+/*****************************************************************************/
+
+#if (defined(DEBUG) || (defined(NIGHTLY_BUILD) && !defined(MOZ_PROFILING))) && !defined(XPCOM_GLUE_AVOID_NSPR)
+ #define MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED 1
+#endif
+
+#ifdef XPCOM_GLUE
+ #define NS_CheckThreadSafe(owningThread, msg)
+#else
+ #define NS_CheckThreadSafe(owningThread, msg) \
+ if (MOZ_UNLIKELY(owningThread != PR_GetCurrentThread())) { \
+ MOZ_CRASH(msg); \
+ }
+#endif
+
+#ifdef MOZILLA_INTERNAL_API
+void NS_ABORT_OOM(size_t aSize);
+#else
+inline void NS_ABORT_OOM(size_t)
+{
+ MOZ_CRASH();
+}
+#endif
+
+typedef void (*StderrCallback)(const char* aFmt, va_list aArgs);
+/* When compiling the XPCOM Glue on Windows, we pretend that it's going to
+ * be linked with a static CRT (-MT) even when it's not. This means that we
+ * cannot link to data exports from the CRT, only function exports. So,
+ * instead of referencing "stderr" directly, use fdopen.
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * printf_stderr(...) is much like fprintf(stderr, ...), except that:
+ * - it calls the callback set through set_stderr_callback
+ * - on Android and Firefox OS, *instead* of printing to stderr, it
+ * prints to logcat. (Newlines in the string lead to multiple lines
+ * of logcat, but each function call implicitly completes a line even
+ * if the string does not end with a newline.)
+ * - on Windows, if a debugger is present, it calls OutputDebugString
+ * in *addition* to writing to stderr
+ */
+void printf_stderr(const char* aFmt, ...) MOZ_FORMAT_PRINTF(1, 2);
+
+/**
+ * Same as printf_stderr, but taking va_list instead of varargs
+ */
+void vprintf_stderr(const char* aFmt, va_list aArgs);
+
+/**
+ * fprintf_stderr is like fprintf, except that if its file argument
+ * is stderr, it invokes printf_stderr instead.
+ *
+ * This is useful for general debugging code that logs information to a
+ * file, but that you would like to be useful on Android and Firefox OS.
+ * If you use fprintf_stderr instead of fprintf in such debugging code,
+ * then callers can pass stderr to get logging that works on Android and
+ * Firefox OS (and also the other side-effects of using printf_stderr).
+ *
+ * Code that is structured this way needs to be careful not to split a
+ * line of output across multiple calls to fprintf_stderr, since doing
+ * so will cause it to appear in multiple lines in logcat output.
+ * (Producing multiple lines at once is fine.)
+ */
+void fprintf_stderr(FILE* aFile, const char* aFmt, ...) MOZ_FORMAT_PRINTF(2, 3);
+
+// used by the profiler to log stderr in the profiler for more
+// advanced performance debugging and display/layers visualization.
+void set_stderr_callback(StderrCallback aCallback);
+
+#if defined(ANDROID) && !defined(RELEASE_OR_BETA)
+// Call this if you want a copy of stderr logging sent to a file. This is
+// useful to get around logcat overflow problems on android devices, which use
+// a circular logcat buffer and can intermittently drop messages if there's too
+// much spew.
+//
+// This is intended for local debugging only, DO NOT USE IN PRODUCTION CODE.
+// (This is ifndef RELEASE_OR_BETA to catch uses of it that accidentally get
+// checked in). Using this will also prevent the profiler from getting a copy of
+// the stderr messages which it uses for various visualization features.
+//
+// This function can be called from any thread, but if it is called multiple
+// times all invocations must be on the same thread. Invocations after the
+// first one are ignored, so you can safely put it inside a loop, for example.
+// Once this is called there is no way to turn it off; all stderr output from
+// that point forward will go to the file. Note that the output is subject to
+// buffering so make sure you have enough output to flush the messages you care
+// about before you terminate the process.
+//
+// The file passed in should be writable, so on Android devices a path like
+// "/data/local/tmp/blah" is a good one to use as it is world-writable and will
+// work even in B2G child processes which have reduced privileges. Note that the
+// actual file created will have the PID appended to the path you pass in, so
+// that on B2G the output from each process goes to a separate file.
+void copy_stderr_to_file(const char* aFile);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* nsDebug_h___ */
diff --git a/xpcom/glue/nsDeque.cpp b/xpcom/glue/nsDeque.cpp
new file mode 100644
index 000000000..f9eb18b40
--- /dev/null
+++ b/xpcom/glue/nsDeque.cpp
@@ -0,0 +1,361 @@
+/* -*- 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 "nsDeque.h"
+#include "nsISupportsImpl.h"
+#include <string.h>
+#ifdef DEBUG_rickg
+#include <stdio.h>
+#endif
+
+#include "mozilla/CheckedInt.h"
+
+#define modulus(x,y) ((x)%(y))
+
+/**
+ * Standard constructor
+ * @param deallocator, called by Erase and ~nsDeque
+ */
+nsDeque::nsDeque(nsDequeFunctor* aDeallocator)
+{
+ MOZ_COUNT_CTOR(nsDeque);
+ mDeallocator = aDeallocator;
+ mOrigin = mSize = 0;
+ mData = mBuffer; // don't allocate space until you must
+ mCapacity = sizeof(mBuffer) / sizeof(mBuffer[0]);
+ memset(mData, 0, mCapacity * sizeof(mBuffer[0]));
+}
+
+/**
+ * Destructor
+ */
+nsDeque::~nsDeque()
+{
+ MOZ_COUNT_DTOR(nsDeque);
+
+#ifdef DEBUG_rickg
+ char buffer[30];
+ printf("Capacity: %i\n", mCapacity);
+
+ static int mCaps[15] = {0};
+ switch (mCapacity) {
+ case 4: mCaps[0]++; break;
+ case 8: mCaps[1]++; break;
+ case 16: mCaps[2]++; break;
+ case 32: mCaps[3]++; break;
+ case 64: mCaps[4]++; break;
+ case 128: mCaps[5]++; break;
+ case 256: mCaps[6]++; break;
+ case 512: mCaps[7]++; break;
+ case 1024: mCaps[8]++; break;
+ case 2048: mCaps[9]++; break;
+ case 4096: mCaps[10]++; break;
+ default:
+ break;
+ }
+#endif
+
+ Erase();
+ if (mData && mData != mBuffer) {
+ free(mData);
+ }
+ mData = 0;
+ SetDeallocator(0);
+}
+
+size_t
+nsDeque::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ size_t size = 0;
+ if (mData != mBuffer) {
+ size += aMallocSizeOf(mData);
+ }
+
+ if (mDeallocator) {
+ size += aMallocSizeOf(mDeallocator);
+ }
+
+ return size;
+}
+
+size_t
+nsDeque::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
+/**
+ * Set the functor to be called by Erase()
+ * The deque owns the functor.
+ *
+ * @param aDeallocator functor object for use by Erase()
+ */
+void
+nsDeque::SetDeallocator(nsDequeFunctor* aDeallocator)
+{
+ delete mDeallocator;
+ mDeallocator = aDeallocator;
+}
+
+/**
+ * Remove all items from container without destroying them.
+ */
+void
+nsDeque::Empty()
+{
+ if (mSize && mData) {
+ memset(mData, 0, mCapacity * sizeof(*mData));
+ }
+ mSize = 0;
+ mOrigin = 0;
+}
+
+/**
+ * Remove and delete all items from container
+ */
+void
+nsDeque::Erase()
+{
+ if (mDeallocator && mSize) {
+ ForEach(*mDeallocator);
+ }
+ Empty();
+}
+
+/**
+ * This method quadruples the size of the deque
+ * Elements in the deque are resequenced so that elements
+ * in the deque are stored sequentially
+ *
+ * @return whether growing succeeded
+ */
+bool
+nsDeque::GrowCapacity()
+{
+ mozilla::CheckedInt<size_t> newCapacity = mCapacity;
+ newCapacity *= 4;
+
+ NS_ASSERTION(newCapacity.isValid(), "Overflow");
+ if (!newCapacity.isValid()) {
+ return false;
+ }
+
+ // Sanity check the new byte size.
+ mozilla::CheckedInt<size_t> newByteSize = newCapacity;
+ newByteSize *= sizeof(void*);
+
+ NS_ASSERTION(newByteSize.isValid(), "Overflow");
+ if (!newByteSize.isValid()) {
+ return false;
+ }
+
+ void** temp = (void**)malloc(newByteSize.value());
+ if (!temp) {
+ return false;
+ }
+
+ //Here's the interesting part: You can't just move the elements
+ //directly (in situ) from the old buffer to the new one.
+ //Since capacity has changed, the old origin doesn't make
+ //sense anymore. It's better to resequence the elements now.
+
+ memcpy(temp, mData + mOrigin, sizeof(void*) * (mCapacity - mOrigin));
+ memcpy(temp + (mCapacity - mOrigin), mData, sizeof(void*) * mOrigin);
+
+ if (mData != mBuffer) {
+ free(mData);
+ }
+
+ mCapacity = newCapacity.value();
+ mOrigin = 0; //now realign the origin...
+ mData = temp;
+
+ return true;
+}
+
+/**
+ * This method adds an item to the end of the deque.
+ * This operation has the potential to cause the
+ * underlying buffer to resize.
+ *
+ * @param aItem: new item to be added to deque
+ */
+bool
+nsDeque::Push(void* aItem, const fallible_t&)
+{
+ if (mSize == mCapacity && !GrowCapacity()) {
+ return false;
+ }
+ mData[modulus(mOrigin + mSize, mCapacity)] = aItem;
+ mSize++;
+ return true;
+}
+
+/**
+ * This method adds an item to the front of the deque.
+ * This operation has the potential to cause the
+ * underlying buffer to resize.
+ *
+ * --Commments for GrowCapacity() case
+ * We've grown and shifted which means that the old
+ * final element in the deque is now the first element
+ * in the deque. This is temporary.
+ * We haven't inserted the new element at the front.
+ *
+ * To continue with the idea of having the front at zero
+ * after a grow, we move the old final item (which through
+ * the voodoo of mOrigin-- is now the first) to its final
+ * position which is conveniently the old length.
+ *
+ * Note that this case only happens when the deque is full.
+ * [And that pieces of this magic only work if the deque is full.]
+ * picture:
+ * [ABCDEFGH] @[mOrigin:3]:D.
+ * Task: PushFront("Z")
+ * shift mOrigin so, @[mOrigin:2]:C
+ * stretch and rearrange: (mOrigin:0)
+ * [CDEFGHAB ________ ________ ________]
+ * copy: (The second C is currently out of bounds)
+ * [CDEFGHAB C_______ ________ ________]
+ * later we will insert Z:
+ * [ZDEFGHAB C_______ ________ ________]
+ * and increment size: 9. (C is no longer out of bounds)
+ * --
+ * @param aItem: new item to be added to deque
+ */
+bool
+nsDeque::PushFront(void* aItem, const fallible_t&)
+{
+
+ if (mOrigin == 0) {
+ mOrigin = mCapacity - 1;
+ } else {
+ mOrigin--;
+ }
+
+ if (mSize == mCapacity) {
+ if (!GrowCapacity()) {
+ return false;
+ }
+ /* Comments explaining this are above*/
+ mData[mSize] = mData[mOrigin];
+ }
+ mData[mOrigin] = aItem;
+ mSize++;
+ return true;
+}
+
+/**
+ * Remove and return the last item in the container.
+ *
+ * @return ptr to last item in container
+ */
+void*
+nsDeque::Pop()
+{
+ void* result = 0;
+ if (mSize > 0) {
+ --mSize;
+ size_t offset = modulus(mSize + mOrigin, mCapacity);
+ result = mData[offset];
+ mData[offset] = 0;
+ if (!mSize) {
+ mOrigin = 0;
+ }
+ }
+ return result;
+}
+
+/**
+ * This method gets called you want to remove and return
+ * the first member in the container.
+ *
+ * @return last item in container
+ */
+void*
+nsDeque::PopFront()
+{
+ void* result = 0;
+ if (mSize > 0) {
+ NS_ASSERTION(mOrigin < mCapacity, "Error: Bad origin");
+ result = mData[mOrigin];
+ mData[mOrigin++] = 0; //zero it out for debugging purposes.
+ mSize--;
+ // Cycle around if we pop off the end
+ // and reset origin if when we pop the last element
+ if (mCapacity == mOrigin || !mSize) {
+ mOrigin = 0;
+ }
+ }
+ return result;
+}
+
+/**
+ * This method gets called you want to peek at the bottom
+ * member without removing it.
+ *
+ * @return last item in container
+ */
+void*
+nsDeque::Peek() const
+{
+ void* result = 0;
+ if (mSize > 0) {
+ result = mData[modulus(mSize - 1 + mOrigin, mCapacity)];
+ }
+ return result;
+}
+
+/**
+ * This method gets called you want to peek at the topmost
+ * member without removing it.
+ *
+ * @return last item in container
+ */
+void*
+nsDeque::PeekFront() const
+{
+ void* result = 0;
+ if (mSize > 0) {
+ result = mData[mOrigin];
+ }
+ return result;
+}
+
+/**
+ * Call this to retrieve the ith element from this container.
+ * Keep in mind that accessing the underlying elements is
+ * done in a relative fashion. Object 0 is not necessarily
+ * the first element (the first element is at mOrigin).
+ *
+ * @param aIndex : 0 relative offset of item you want
+ * @return void* or null
+ */
+void*
+nsDeque::ObjectAt(size_t aIndex) const
+{
+ void* result = 0;
+ if (aIndex < mSize) {
+ result = mData[modulus(mOrigin + aIndex, mCapacity)];
+ }
+ return result;
+}
+
+/**
+ * Call this method when you want to iterate all the
+ * members of the container, passing a functor along
+ * to call your code.
+ *
+ * @param aFunctor object to call for each member
+ * @return *this
+ */
+void
+nsDeque::ForEach(nsDequeFunctor& aFunctor) const
+{
+ for (size_t i = 0; i < mSize; ++i) {
+ aFunctor(ObjectAt(i));
+ }
+}
diff --git a/xpcom/glue/nsDeque.h b/xpcom/glue/nsDeque.h
new file mode 100644
index 000000000..ace7607d3
--- /dev/null
+++ b/xpcom/glue/nsDeque.h
@@ -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/. */
+
+/**
+ * MODULE NOTES:
+ *
+ * The Deque is a very small, very efficient container object
+ * than can hold elements of type void*, offering the following features:
+ * Its interface supports pushing and popping of elements.
+ * It can iterate (via an interator class) its elements.
+ * When full, it can efficiently resize dynamically.
+ *
+ *
+ * NOTE: The only bit of trickery here is that this deque is
+ * built upon a ring-buffer. Like all ring buffers, the first
+ * element may not be at index[0]. The mOrigin member determines
+ * where the first child is. This point is quietly hidden from
+ * customers of this class.
+ *
+ */
+
+#ifndef _NSDEQUE
+#define _NSDEQUE
+
+#include "nscore.h"
+#include "nsDebug.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/fallible.h"
+#include "mozilla/MemoryReporting.h"
+
+/**
+ * The nsDequeFunctor class is used when you want to create
+ * callbacks between the deque and your generic code.
+ * Use these objects in a call to ForEach();
+ *
+ */
+
+class nsDequeFunctor
+{
+public:
+ virtual void* operator()(void* aObject) = 0;
+ virtual ~nsDequeFunctor() {}
+};
+
+/******************************************************
+ * Here comes the nsDeque class itself...
+ ******************************************************/
+
+/**
+ * The deque (double-ended queue) class is a common container type,
+ * whose behavior mimics a line in your favorite checkout stand.
+ * Classic CS describes the common behavior of a queue as FIFO.
+ * A deque allows insertion and removal at both ends of
+ * the container.
+ *
+ * The deque stores pointers to items.
+ */
+
+class nsDequeIterator;
+
+class nsDeque
+{
+ typedef mozilla::fallible_t fallible_t;
+public:
+ explicit nsDeque(nsDequeFunctor* aDeallocator = nullptr);
+ ~nsDeque();
+
+ /**
+ * Returns the number of elements currently stored in
+ * this deque.
+ *
+ * @return number of elements currently in the deque
+ */
+ inline size_t GetSize() const { return mSize; }
+
+ /**
+ * Appends new member at the end of the deque.
+ *
+ * @param item to store in deque
+ */
+ void Push(void* aItem)
+ {
+ if (!Push(aItem, mozilla::fallible)) {
+ NS_ABORT_OOM(mSize * sizeof(void*));
+ }
+ }
+
+ MOZ_MUST_USE bool Push(void* aItem, const fallible_t&);
+
+ /**
+ * Inserts new member at the front of the deque.
+ *
+ * @param item to store in deque
+ */
+ void PushFront(void* aItem)
+ {
+ if (!PushFront(aItem, mozilla::fallible)) {
+ NS_ABORT_OOM(mSize * sizeof(void*));
+ }
+ }
+
+ MOZ_MUST_USE bool PushFront(void* aItem, const fallible_t&);
+
+ /**
+ * Remove and return the last item in the container.
+ *
+ * @return the item that was the last item in container
+ */
+ void* Pop();
+
+ /**
+ * Remove and return the first item in the container.
+ *
+ * @return the item that was first item in container
+ */
+ void* PopFront();
+
+ /**
+ * Retrieve the bottom item without removing it.
+ *
+ * @return the first item in container
+ */
+
+ void* Peek() const;
+ /**
+ * Return topmost item without removing it.
+ *
+ * @return the first item in container
+ */
+ void* PeekFront() const;
+
+ /**
+ * Retrieve a member from the deque without removing it.
+ *
+ * @param index of desired item
+ * @return element in list
+ */
+ void* ObjectAt(size_t aIndex) const;
+
+ /**
+ * Remove and delete all items from container.
+ * Deletes are handled by the deallocator nsDequeFunctor
+ * which is specified at deque construction.
+ */
+ void Erase();
+
+ /**
+ * Call this method when you want to iterate all the
+ * members of the container, passing a functor along
+ * to call your code.
+ *
+ * @param aFunctor object to call for each member
+ */
+ void ForEach(nsDequeFunctor& aFunctor) const;
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+protected:
+ size_t mSize;
+ size_t mCapacity;
+ size_t mOrigin;
+ nsDequeFunctor* mDeallocator;
+ void* mBuffer[8];
+ void** mData;
+
+private:
+
+ /**
+ * Copy constructor (PRIVATE)
+ *
+ * @param aOther another deque
+ */
+ nsDeque(const nsDeque& aOther);
+
+ /**
+ * Deque assignment operator (PRIVATE)
+ *
+ * @param aOther another deque
+ * @return *this
+ */
+ nsDeque& operator=(const nsDeque& aOther);
+
+ bool GrowCapacity();
+ void SetDeallocator(nsDequeFunctor* aDeallocator);
+
+ /**
+ * Remove all items from container without destroying them.
+ */
+ void Empty();
+};
+#endif
diff --git a/xpcom/glue/nsEnumeratorUtils.cpp b/xpcom/glue/nsEnumeratorUtils.cpp
new file mode 100644
index 000000000..d1843a78a
--- /dev/null
+++ b/xpcom/glue/nsEnumeratorUtils.cpp
@@ -0,0 +1,291 @@
+/* -*- 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/Attributes.h"
+
+#include "nsEnumeratorUtils.h"
+
+#include "nsISimpleEnumerator.h"
+#include "nsIStringEnumerator.h"
+
+#include "nsCOMPtr.h"
+#include "mozilla/RefPtr.h"
+
+class EmptyEnumeratorImpl
+ : public nsISimpleEnumerator
+ , public nsIUTF8StringEnumerator
+ , public nsIStringEnumerator
+{
+public:
+ EmptyEnumeratorImpl() {}
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS_INHERITED // not really inherited, but no mRefCnt
+
+ // nsISimpleEnumerator
+ NS_DECL_NSISIMPLEENUMERATOR
+ NS_DECL_NSIUTF8STRINGENUMERATOR
+ // can't use NS_DECL_NSISTRINGENUMERATOR because they share the
+ // HasMore() signature
+ NS_IMETHOD GetNext(nsAString& aResult) override;
+
+ static EmptyEnumeratorImpl* GetInstance()
+ {
+ static const EmptyEnumeratorImpl kInstance;
+ return const_cast<EmptyEnumeratorImpl*>(&kInstance);
+ }
+};
+
+// nsISupports interface
+NS_IMETHODIMP_(MozExternalRefCountType)
+EmptyEnumeratorImpl::AddRef(void)
+{
+ return 2;
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+EmptyEnumeratorImpl::Release(void)
+{
+ return 1;
+}
+
+NS_IMPL_QUERY_INTERFACE(EmptyEnumeratorImpl, nsISimpleEnumerator,
+ nsIUTF8StringEnumerator, nsIStringEnumerator)
+
+// nsISimpleEnumerator interface
+NS_IMETHODIMP
+EmptyEnumeratorImpl::HasMoreElements(bool* aResult)
+{
+ *aResult = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+EmptyEnumeratorImpl::HasMore(bool* aResult)
+{
+ *aResult = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+EmptyEnumeratorImpl::GetNext(nsISupports** aResult)
+{
+ return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+EmptyEnumeratorImpl::GetNext(nsACString& aResult)
+{
+ return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+EmptyEnumeratorImpl::GetNext(nsAString& aResult)
+{
+ return NS_ERROR_UNEXPECTED;
+}
+
+nsresult
+NS_NewEmptyEnumerator(nsISimpleEnumerator** aResult)
+{
+ *aResult = EmptyEnumeratorImpl::GetInstance();
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+class nsSingletonEnumerator final : public nsISimpleEnumerator
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator methods
+ NS_IMETHOD HasMoreElements(bool* aResult) override;
+ NS_IMETHOD GetNext(nsISupports** aResult) override;
+
+ explicit nsSingletonEnumerator(nsISupports* aValue);
+
+private:
+ ~nsSingletonEnumerator();
+
+protected:
+ nsCOMPtr<nsISupports> mValue;
+ bool mConsumed;
+};
+
+nsSingletonEnumerator::nsSingletonEnumerator(nsISupports* aValue)
+ : mValue(aValue)
+{
+ mConsumed = (mValue ? false : true);
+}
+
+nsSingletonEnumerator::~nsSingletonEnumerator()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsSingletonEnumerator, nsISimpleEnumerator)
+
+NS_IMETHODIMP
+nsSingletonEnumerator::HasMoreElements(bool* aResult)
+{
+ NS_PRECONDITION(aResult != 0, "null ptr");
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ *aResult = !mConsumed;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsSingletonEnumerator::GetNext(nsISupports** aResult)
+{
+ NS_PRECONDITION(aResult != 0, "null ptr");
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (mConsumed) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ mConsumed = true;
+
+ *aResult = mValue;
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+nsresult
+NS_NewSingletonEnumerator(nsISimpleEnumerator** aResult,
+ nsISupports* aSingleton)
+{
+ RefPtr<nsSingletonEnumerator> enumer = new nsSingletonEnumerator(aSingleton);
+ enumer.forget(aResult);
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+class nsUnionEnumerator final : public nsISimpleEnumerator
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator methods
+ NS_IMETHOD HasMoreElements(bool* aResult) override;
+ NS_IMETHOD GetNext(nsISupports** aResult) override;
+
+ nsUnionEnumerator(nsISimpleEnumerator* aFirstEnumerator,
+ nsISimpleEnumerator* aSecondEnumerator);
+
+private:
+ ~nsUnionEnumerator();
+
+protected:
+ nsCOMPtr<nsISimpleEnumerator> mFirstEnumerator, mSecondEnumerator;
+ bool mConsumed;
+ bool mAtSecond;
+};
+
+nsUnionEnumerator::nsUnionEnumerator(nsISimpleEnumerator* aFirstEnumerator,
+ nsISimpleEnumerator* aSecondEnumerator)
+ : mFirstEnumerator(aFirstEnumerator)
+ , mSecondEnumerator(aSecondEnumerator)
+ , mConsumed(false)
+ , mAtSecond(false)
+{
+}
+
+nsUnionEnumerator::~nsUnionEnumerator()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsUnionEnumerator, nsISimpleEnumerator)
+
+NS_IMETHODIMP
+nsUnionEnumerator::HasMoreElements(bool* aResult)
+{
+ NS_PRECONDITION(aResult != 0, "null ptr");
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ nsresult rv;
+
+ if (mConsumed) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ if (!mAtSecond) {
+ rv = mFirstEnumerator->HasMoreElements(aResult);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (*aResult) {
+ return NS_OK;
+ }
+
+ mAtSecond = true;
+ }
+
+ rv = mSecondEnumerator->HasMoreElements(aResult);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (*aResult) {
+ return NS_OK;
+ }
+
+ *aResult = false;
+ mConsumed = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUnionEnumerator::GetNext(nsISupports** aResult)
+{
+ NS_PRECONDITION(aResult != 0, "null ptr");
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (mConsumed) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (!mAtSecond) {
+ return mFirstEnumerator->GetNext(aResult);
+ }
+
+ return mSecondEnumerator->GetNext(aResult);
+}
+
+nsresult
+NS_NewUnionEnumerator(nsISimpleEnumerator** aResult,
+ nsISimpleEnumerator* aFirstEnumerator,
+ nsISimpleEnumerator* aSecondEnumerator)
+{
+ *aResult = nullptr;
+ if (!aFirstEnumerator) {
+ *aResult = aSecondEnumerator;
+ } else if (!aSecondEnumerator) {
+ *aResult = aFirstEnumerator;
+ } else {
+ nsUnionEnumerator* enumer = new nsUnionEnumerator(aFirstEnumerator,
+ aSecondEnumerator);
+ if (!enumer) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ *aResult = enumer;
+ }
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
diff --git a/xpcom/glue/nsEnumeratorUtils.h b/xpcom/glue/nsEnumeratorUtils.h
new file mode 100644
index 000000000..f7a0db099
--- /dev/null
+++ b/xpcom/glue/nsEnumeratorUtils.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsEnumeratorUtils_h__
+#define nsEnumeratorUtils_h__
+
+#include "nscore.h"
+
+class nsISupports;
+class nsISimpleEnumerator;
+
+nsresult NS_NewEmptyEnumerator(nsISimpleEnumerator** aResult);
+
+nsresult NS_NewSingletonEnumerator(nsISimpleEnumerator** aResult,
+ nsISupports* aSingleton);
+
+nsresult NS_NewUnionEnumerator(nsISimpleEnumerator** aResult,
+ nsISimpleEnumerator* aFirstEnumerator,
+ nsISimpleEnumerator* aSecondEnumerator);
+
+#endif /* nsEnumeratorUtils_h__ */
diff --git a/xpcom/glue/nsHashKeys.h b/xpcom/glue/nsHashKeys.h
new file mode 100644
index 000000000..9c688691f
--- /dev/null
+++ b/xpcom/glue/nsHashKeys.h
@@ -0,0 +1,660 @@
+/* -*- 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 nsTHashKeys_h__
+#define nsTHashKeys_h__
+
+#include "nsID.h"
+#include "nsISupports.h"
+#include "nsIHashable.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "PLDHashTable.h"
+#include <new>
+
+#include "nsStringGlue.h"
+#include "nsCRTGlue.h"
+#include "nsUnicharUtils.h"
+#include "nsPointerHashKeys.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "mozilla/HashFunctions.h"
+#include "mozilla/Move.h"
+
+namespace mozilla {
+
+// These are defined analogously to the HashString overloads in mfbt.
+
+inline uint32_t
+HashString(const nsAString& aStr)
+{
+ return HashString(aStr.BeginReading(), aStr.Length());
+}
+
+inline uint32_t
+HashString(const nsACString& aStr)
+{
+ return HashString(aStr.BeginReading(), aStr.Length());
+}
+
+} // namespace mozilla
+
+/** @file nsHashKeys.h
+ * standard HashKey classes for nsBaseHashtable and relatives. Each of these
+ * classes follows the nsTHashtable::EntryType specification
+ *
+ * Lightweight keytypes provided here:
+ * nsStringHashKey
+ * nsCStringHashKey
+ * nsUint32HashKey
+ * nsUint64HashKey
+ * nsFloatHashKey
+ * nsPtrHashKey
+ * nsClearingPtrHashKey
+ * nsVoidPtrHashKey
+ * nsClearingVoidPtrHashKey
+ * nsISupportsHashKey
+ * nsIDHashKey
+ * nsDepCharHashKey
+ * nsCharPtrHashKey
+ * nsUnicharPtrHashKey
+ * nsHashableHashKey
+ * nsGenericHashKey
+ */
+
+/**
+ * hashkey wrapper using nsAString KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+class nsStringHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const nsAString& KeyType;
+ typedef const nsAString* KeyTypePointer;
+
+ explicit nsStringHashKey(KeyTypePointer aStr) : mStr(*aStr) {}
+ nsStringHashKey(const nsStringHashKey& aToCopy) : mStr(aToCopy.mStr) {}
+ ~nsStringHashKey() {}
+
+ KeyType GetKey() const { return mStr; }
+ bool KeyEquals(const KeyTypePointer aKey) const
+ {
+ return mStr.Equals(*aKey);
+ }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(const KeyTypePointer aKey)
+ {
+ return mozilla::HashString(*aKey);
+ }
+
+#ifdef MOZILLA_INTERNAL_API
+ // To avoid double-counting, only measure the string if it is unshared.
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+#endif
+
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ const nsString mStr;
+};
+
+#ifdef MOZILLA_INTERNAL_API
+
+/**
+ * hashkey wrapper using nsAString KeyType
+ *
+ * This is internal-API only because nsCaseInsensitiveStringComparator is
+ * internal-only.
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+class nsStringCaseInsensitiveHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const nsAString& KeyType;
+ typedef const nsAString* KeyTypePointer;
+
+ explicit nsStringCaseInsensitiveHashKey(KeyTypePointer aStr)
+ : mStr(*aStr)
+ {
+ // take it easy just deal HashKey
+ }
+ nsStringCaseInsensitiveHashKey(const nsStringCaseInsensitiveHashKey& aToCopy)
+ : mStr(aToCopy.mStr)
+ {
+ }
+ ~nsStringCaseInsensitiveHashKey() {}
+
+ KeyType GetKey() const { return mStr; }
+ bool KeyEquals(const KeyTypePointer aKey) const
+ {
+ return mStr.Equals(*aKey, nsCaseInsensitiveStringComparator());
+ }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(const KeyTypePointer aKey)
+ {
+ nsAutoString tmKey(*aKey);
+ ToLowerCase(tmKey);
+ return mozilla::HashString(tmKey);
+ }
+ enum { ALLOW_MEMMOVE = true };
+
+ // To avoid double-counting, only measure the string if it is unshared.
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+
+private:
+ const nsString mStr;
+};
+
+#endif
+
+/**
+ * hashkey wrapper using nsACString KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+class nsCStringHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const nsACString& KeyType;
+ typedef const nsACString* KeyTypePointer;
+
+ explicit nsCStringHashKey(const nsACString* aStr) : mStr(*aStr) {}
+ nsCStringHashKey(const nsCStringHashKey& aToCopy) : mStr(aToCopy.mStr) {}
+ ~nsCStringHashKey() {}
+
+ KeyType GetKey() const { return mStr; }
+ bool KeyEquals(KeyTypePointer aKey) const { return mStr.Equals(*aKey); }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return mozilla::HashString(*aKey);
+ }
+
+#ifdef MOZILLA_INTERNAL_API
+ // To avoid double-counting, only measure the string if it is unshared.
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+#endif
+
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ const nsCString mStr;
+};
+
+/**
+ * hashkey wrapper using uint32_t KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+class nsUint32HashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const uint32_t& KeyType;
+ typedef const uint32_t* KeyTypePointer;
+
+ explicit nsUint32HashKey(KeyTypePointer aKey) : mValue(*aKey) {}
+ nsUint32HashKey(const nsUint32HashKey& aToCopy) : mValue(aToCopy.mValue) {}
+ ~nsUint32HashKey() {}
+
+ KeyType GetKey() const { return mValue; }
+ bool KeyEquals(KeyTypePointer aKey) const { return *aKey == mValue; }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey) { return *aKey; }
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ const uint32_t mValue;
+};
+
+/**
+ * hashkey wrapper using uint64_t KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+class nsUint64HashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const uint64_t& KeyType;
+ typedef const uint64_t* KeyTypePointer;
+
+ explicit nsUint64HashKey(KeyTypePointer aKey) : mValue(*aKey) {}
+ nsUint64HashKey(const nsUint64HashKey& aToCopy) : mValue(aToCopy.mValue) {}
+ ~nsUint64HashKey() {}
+
+ KeyType GetKey() const { return mValue; }
+ bool KeyEquals(KeyTypePointer aKey) const { return *aKey == mValue; }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return PLDHashNumber(*aKey);
+ }
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ const uint64_t mValue;
+};
+
+/**
+ * hashkey wrapper using float KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+class nsFloatHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const float& KeyType;
+ typedef const float* KeyTypePointer;
+
+ explicit nsFloatHashKey(KeyTypePointer aKey) : mValue(*aKey) {}
+ nsFloatHashKey(const nsFloatHashKey& aToCopy) : mValue(aToCopy.mValue) {}
+ ~nsFloatHashKey() {}
+
+ KeyType GetKey() const { return mValue; }
+ bool KeyEquals(KeyTypePointer aKey) const { return *aKey == mValue; }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return *reinterpret_cast<const uint32_t*>(aKey);
+ }
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ const float mValue;
+};
+
+/**
+ * hashkey wrapper using nsISupports* KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+class nsISupportsHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef nsISupports* KeyType;
+ typedef const nsISupports* KeyTypePointer;
+
+ explicit nsISupportsHashKey(const nsISupports* aKey)
+ : mSupports(const_cast<nsISupports*>(aKey))
+ {
+ }
+ nsISupportsHashKey(const nsISupportsHashKey& aToCopy)
+ : mSupports(aToCopy.mSupports)
+ {
+ }
+ ~nsISupportsHashKey() {}
+
+ KeyType GetKey() const { return mSupports; }
+ bool KeyEquals(KeyTypePointer aKey) const { return aKey == mSupports; }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return NS_PTR_TO_UINT32(aKey) >> 2;
+ }
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ nsCOMPtr<nsISupports> mSupports;
+};
+
+/**
+ * hashkey wrapper using refcounted * KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+template<class T>
+class nsRefPtrHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef T* KeyType;
+ typedef const T* KeyTypePointer;
+
+ explicit nsRefPtrHashKey(const T* aKey) : mKey(const_cast<T*>(aKey)) {}
+ nsRefPtrHashKey(const nsRefPtrHashKey& aToCopy) : mKey(aToCopy.mKey) {}
+ ~nsRefPtrHashKey() {}
+
+ KeyType GetKey() const { return mKey; }
+ bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return NS_PTR_TO_UINT32(aKey) >> 2;
+ }
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ RefPtr<T> mKey;
+};
+
+template<class T>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ nsRefPtrHashKey<T>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ CycleCollectionNoteChild(aCallback, aField.GetKey(), aName, aFlags);
+}
+
+/**
+ * hashkey wrapper using T* KeyType that sets key to nullptr upon
+ * destruction. Relevant only in cases where a memory pointer-scanner
+ * like valgrind might get confused about stale references.
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+
+template<class T>
+class nsClearingPtrHashKey : public nsPtrHashKey<T>
+{
+public:
+ explicit nsClearingPtrHashKey(const T* aKey) : nsPtrHashKey<T>(aKey) {}
+ nsClearingPtrHashKey(const nsClearingPtrHashKey<T>& aToCopy)
+ : nsPtrHashKey<T>(aToCopy)
+ {
+ }
+ ~nsClearingPtrHashKey() { nsPtrHashKey<T>::mKey = nullptr; }
+};
+
+typedef nsClearingPtrHashKey<const void> nsClearingVoidPtrHashKey;
+
+/**
+ * hashkey wrapper using a function pointer KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+template<class T>
+class nsFuncPtrHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef T& KeyType;
+ typedef const T* KeyTypePointer;
+
+ explicit nsFuncPtrHashKey(const T* aKey) : mKey(*const_cast<T*>(aKey)) {}
+ nsFuncPtrHashKey(const nsFuncPtrHashKey<T>& aToCopy) : mKey(aToCopy.mKey) {}
+ ~nsFuncPtrHashKey() {}
+
+ KeyType GetKey() const { return const_cast<T&>(mKey); }
+ bool KeyEquals(KeyTypePointer aKey) const { return *aKey == mKey; }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return NS_PTR_TO_UINT32(*aKey) >> 2;
+ }
+ enum { ALLOW_MEMMOVE = true };
+
+protected:
+ T mKey;
+};
+
+/**
+ * hashkey wrapper using nsID KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+class nsIDHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const nsID& KeyType;
+ typedef const nsID* KeyTypePointer;
+
+ explicit nsIDHashKey(const nsID* aInID) : mID(*aInID) {}
+ nsIDHashKey(const nsIDHashKey& aToCopy) : mID(aToCopy.mID) {}
+ ~nsIDHashKey() {}
+
+ KeyType GetKey() const { return mID; }
+ bool KeyEquals(KeyTypePointer aKey) const { return aKey->Equals(mID); }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ // Hash the nsID object's raw bytes.
+ return mozilla::HashBytes(aKey, sizeof(KeyType));
+ }
+
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ const nsID mID;
+};
+
+/**
+ * hashkey wrapper for "dependent" const char*; this class does not "own"
+ * its string pointer.
+ *
+ * This class must only be used if the strings have a lifetime longer than
+ * the hashtable they occupy. This normally occurs only for static
+ * strings or strings that have been arena-allocated.
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+class nsDepCharHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const char* KeyType;
+ typedef const char* KeyTypePointer;
+
+ explicit nsDepCharHashKey(const char* aKey) : mKey(aKey) {}
+ nsDepCharHashKey(const nsDepCharHashKey& aToCopy) : mKey(aToCopy.mKey) {}
+ ~nsDepCharHashKey() {}
+
+ const char* GetKey() const { return mKey; }
+ bool KeyEquals(const char* aKey) const { return !strcmp(mKey, aKey); }
+
+ static const char* KeyToPointer(const char* aKey) { return aKey; }
+ static PLDHashNumber HashKey(const char* aKey)
+ {
+ return mozilla::HashString(aKey);
+ }
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ const char* mKey;
+};
+
+/**
+ * hashkey wrapper for const char*; at construction, this class duplicates
+ * a string pointed to by the pointer so that it doesn't matter whether or not
+ * the string lives longer than the hash table.
+ */
+class nsCharPtrHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const char* KeyType;
+ typedef const char* KeyTypePointer;
+
+ explicit nsCharPtrHashKey(const char* aKey) : mKey(strdup(aKey)) {}
+ nsCharPtrHashKey(const nsCharPtrHashKey& aToCopy)
+ : mKey(strdup(aToCopy.mKey))
+ {
+ }
+
+ nsCharPtrHashKey(nsCharPtrHashKey&& aOther)
+ : mKey(aOther.mKey)
+ {
+ aOther.mKey = nullptr;
+ }
+
+ ~nsCharPtrHashKey()
+ {
+ if (mKey) {
+ free(const_cast<char*>(mKey));
+ }
+ }
+
+ const char* GetKey() const { return mKey; }
+ bool KeyEquals(KeyTypePointer aKey) const { return !strcmp(mKey, aKey); }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return mozilla::HashString(aKey);
+ }
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return aMallocSizeOf(mKey);
+ }
+
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ const char* mKey;
+};
+
+/**
+ * hashkey wrapper for const char16_t*; at construction, this class duplicates
+ * a string pointed to by the pointer so that it doesn't matter whether or not
+ * the string lives longer than the hash table.
+ */
+class nsUnicharPtrHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const char16_t* KeyType;
+ typedef const char16_t* KeyTypePointer;
+
+ explicit nsUnicharPtrHashKey(const char16_t* aKey) : mKey(NS_strdup(aKey)) {}
+ nsUnicharPtrHashKey(const nsUnicharPtrHashKey& aToCopy)
+ : mKey(NS_strdup(aToCopy.mKey))
+ {
+ }
+
+ nsUnicharPtrHashKey(nsUnicharPtrHashKey&& aOther)
+ : mKey(aOther.mKey)
+ {
+ aOther.mKey = nullptr;
+ }
+
+ ~nsUnicharPtrHashKey()
+ {
+ if (mKey) {
+ NS_Free(const_cast<char16_t*>(mKey));
+ }
+ }
+
+ const char16_t* GetKey() const { return mKey; }
+ bool KeyEquals(KeyTypePointer aKey) const { return !NS_strcmp(mKey, aKey); }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return mozilla::HashString(aKey);
+ }
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return aMallocSizeOf(mKey);
+ }
+
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ const char16_t* mKey;
+};
+
+/**
+ * Hashtable key class to use with objects that support nsIHashable
+ */
+class nsHashableHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef nsIHashable* KeyType;
+ typedef const nsIHashable* KeyTypePointer;
+
+ explicit nsHashableHashKey(const nsIHashable* aKey)
+ : mKey(const_cast<nsIHashable*>(aKey))
+ {
+ }
+ nsHashableHashKey(const nsHashableHashKey& aToCopy) : mKey(aToCopy.mKey) {}
+ ~nsHashableHashKey() {}
+
+ nsIHashable* GetKey() const { return mKey; }
+
+ bool KeyEquals(const nsIHashable* aKey) const
+ {
+ bool eq;
+ if (NS_SUCCEEDED(mKey->Equals(const_cast<nsIHashable*>(aKey), &eq))) {
+ return eq;
+ }
+ return false;
+ }
+
+ static const nsIHashable* KeyToPointer(nsIHashable* aKey) { return aKey; }
+ static PLDHashNumber HashKey(const nsIHashable* aKey)
+ {
+ uint32_t code = 8888; // magic number if GetHashCode fails :-(
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ const_cast<nsIHashable*>(aKey)->GetHashCode(&code);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "GetHashCode should not throw!");
+ return code;
+ }
+
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ nsCOMPtr<nsIHashable> mKey;
+};
+
+namespace mozilla {
+
+template <typename T>
+PLDHashNumber
+Hash(const T& aValue)
+{
+ return aValue.Hash();
+}
+
+} // namespace mozilla
+
+/**
+ * Hashtable key class to use with objects for which Hash() and operator==()
+ * are defined.
+ */
+template<typename T>
+class nsGenericHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef const T& KeyType;
+ typedef const T* KeyTypePointer;
+
+ explicit nsGenericHashKey(KeyTypePointer aKey) : mKey(*aKey) {}
+ nsGenericHashKey(const nsGenericHashKey<T>& aOther) : mKey(aOther.mKey) {}
+
+ KeyType GetKey() const { return mKey; }
+ bool KeyEquals(KeyTypePointer aKey) const { return *aKey == mKey; }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey) { return ::mozilla::Hash(*aKey); }
+ enum { ALLOW_MEMMOVE = true };
+
+private:
+ T mKey;
+};
+
+#endif // nsTHashKeys_h__
diff --git a/xpcom/glue/nsIClassInfoImpl.h b/xpcom/glue/nsIClassInfoImpl.h
new file mode 100644
index 000000000..44303f2be
--- /dev/null
+++ b/xpcom/glue/nsIClassInfoImpl.h
@@ -0,0 +1,179 @@
+/* -*- 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 nsIClassInfoImpl_h__
+#define nsIClassInfoImpl_h__
+
+#include "mozilla/Alignment.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/MacroArgs.h"
+#include "mozilla/MacroForEach.h"
+#include "nsIClassInfo.h"
+#include "nsISupportsImpl.h"
+
+#include <new>
+
+/**
+ * This header file provides macros which help you make your class implement
+ * nsIClassInfo. Implementing nsIClassInfo is particularly helpful if you have
+ * a C++ class which implements multiple interfaces and which you access from
+ * JavaScript. If that class implements nsIClassInfo, the JavaScript code
+ * won't have to call QueryInterface on instances of the class; all methods
+ * from all interfaces returned by GetInterfaces() will be available
+ * automagically.
+ *
+ * Here's all you need to do. Given a class
+ *
+ * class nsFooBar : public nsIFoo, public nsIBar { };
+ *
+ * you should already have the following nsISupports implementation in its cpp
+ * file:
+ *
+ * NS_IMPL_ISUPPORTS(nsFooBar, nsIFoo, nsIBar).
+ *
+ * Change this to
+ *
+ * NS_IMPL_CLASSINFO(nsFooBar, nullptr, 0, NS_FOOBAR_CID)
+ * NS_IMPL_ISUPPORTS_CI(nsFooBar, nsIFoo, nsIBar)
+ *
+ * If nsFooBar is threadsafe, change the 0 above to nsIClassInfo::THREADSAFE.
+ * If it's a singleton, use nsIClassInfo::SINGLETON. The full list of flags is
+ * in nsIClassInfo.idl.
+ *
+ * The nullptr parameter is there so you can pass a function for converting
+ * from an XPCOM object to a scriptable helper. Unless you're doing
+ * specialized JS work, you can probably leave this as nullptr.
+ *
+ * This file also defines the NS_IMPL_QUERY_INTERFACE_CI macro, which you can
+ * use to replace NS_IMPL_QUERY_INTERFACE, if you use that instead of
+ * NS_IMPL_ISUPPORTS.
+ *
+ * That's it! The rest is gory details.
+ *
+ *
+ * Notice that nsFooBar didn't need to inherit from nsIClassInfo in order to
+ * "implement" it. However, after adding these macros to nsFooBar, you you can
+ * QueryInterface an instance of nsFooBar to nsIClassInfo. How can this be?
+ *
+ * The answer lies in the NS_IMPL_ISUPPORTS_CI macro. It modifies nsFooBar's
+ * QueryInterface implementation such that, if we ask to QI to nsIClassInfo, it
+ * returns a singleton object associated with the class. (That singleton is
+ * defined by NS_IMPL_CLASSINFO.) So all nsFooBar instances will return the
+ * same object when QI'ed to nsIClassInfo. (You can see this in
+ * NS_IMPL_QUERY_CLASSINFO below.)
+ *
+ * This hack breaks XPCOM's rules, since if you take an instance of nsFooBar,
+ * QI it to nsIClassInfo, and then try to QI to nsIFoo, that will fail. On the
+ * upside, implementing nsIClassInfo doesn't add a vtable pointer to instances
+ * of your class.
+ *
+ * In principal, you can also implement nsIClassInfo by inheriting from the
+ * interface. But some code expects that when it QI's an object to
+ * nsIClassInfo, it gets back a singleton which isn't attached to any
+ * particular object. If a class were to implement nsIClassInfo through
+ * inheritance, that code might QI to nsIClassInfo and keep the resulting
+ * object alive, thinking it was only keeping alive the classinfo singleton,
+ * but in fact keeping a whole instance of the class alive. See, e.g., bug
+ * 658632.
+ *
+ * Unless you specifically need to have a different nsIClassInfo instance for
+ * each instance of your class, you should probably just implement nsIClassInfo
+ * as a singleton.
+ */
+
+class GenericClassInfo : public nsIClassInfo
+{
+public:
+ struct ClassInfoData
+ {
+ // This function pointer uses NS_CALLBACK because it's always set to an
+ // NS_IMETHOD function, which uses __stdcall on Win32.
+ typedef NS_CALLBACK(GetInterfacesProc)(uint32_t* aCountP, nsIID*** aArray);
+ GetInterfacesProc getinterfaces;
+
+ // This function pointer doesn't use NS_CALLBACK because it's always set to
+ // a vanilla function.
+ typedef nsresult (*GetScriptableHelperProc)(nsIXPCScriptable** aHelper);
+ GetScriptableHelperProc getscriptablehelper;
+
+ uint32_t flags;
+ nsCID cid;
+ };
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSICLASSINFO
+
+ explicit GenericClassInfo(const ClassInfoData* aData) : mData(aData) {}
+
+private:
+ const ClassInfoData* mData;
+};
+
+#define NS_CLASSINFO_NAME(_class) g##_class##_classInfoGlobal
+#define NS_CI_INTERFACE_GETTER_NAME(_class) _class##_GetInterfacesHelper
+#define NS_DECL_CI_INTERFACE_GETTER(_class) \
+ extern NS_IMETHODIMP NS_CI_INTERFACE_GETTER_NAME(_class) \
+ (uint32_t *, nsIID ***);
+
+#define NS_IMPL_CLASSINFO(_class, _getscriptablehelper, _flags, _cid) \
+ NS_DECL_CI_INTERFACE_GETTER(_class) \
+ static const GenericClassInfo::ClassInfoData k##_class##ClassInfoData = { \
+ NS_CI_INTERFACE_GETTER_NAME(_class), \
+ _getscriptablehelper, \
+ _flags | nsIClassInfo::SINGLETON_CLASSINFO, \
+ _cid, \
+ }; \
+ mozilla::AlignedStorage2<GenericClassInfo> k##_class##ClassInfoDataPlace; \
+ nsIClassInfo* NS_CLASSINFO_NAME(_class) = nullptr;
+
+#define NS_IMPL_QUERY_CLASSINFO(_class) \
+ if ( aIID.Equals(NS_GET_IID(nsIClassInfo)) ) { \
+ if (!NS_CLASSINFO_NAME(_class)) \
+ NS_CLASSINFO_NAME(_class) = new (k##_class##ClassInfoDataPlace.addr()) \
+ GenericClassInfo(&k##_class##ClassInfoData); \
+ foundInterface = NS_CLASSINFO_NAME(_class); \
+ } else
+
+#define NS_CLASSINFO_HELPER_BEGIN(_class, _c) \
+NS_IMETHODIMP \
+NS_CI_INTERFACE_GETTER_NAME(_class)(uint32_t *count, nsIID ***array) \
+{ \
+ *count = _c; \
+ *array = (nsIID **)moz_xmalloc(sizeof (nsIID *) * _c); \
+ uint32_t i = 0;
+
+#define NS_CLASSINFO_HELPER_ENTRY(_interface) \
+ (*array)[i++] = (nsIID*)nsMemory::Clone(&NS_GET_IID(_interface), \
+ sizeof(nsIID));
+
+#define NS_CLASSINFO_HELPER_END \
+ MOZ_ASSERT(i == *count, "Incorrent number of entries"); \
+ return NS_OK; \
+}
+
+#define NS_IMPL_CI_INTERFACE_GETTER(aClass, ...) \
+ MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \
+ NS_CLASSINFO_HELPER_BEGIN(aClass, \
+ MOZ_PASTE_PREFIX_AND_ARG_COUNT(/* No prefix */, \
+ __VA_ARGS__)) \
+ MOZ_FOR_EACH(NS_CLASSINFO_HELPER_ENTRY, (), (__VA_ARGS__)) \
+ NS_CLASSINFO_HELPER_END
+
+#define NS_IMPL_QUERY_INTERFACE_CI(aClass, ...) \
+ MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \
+ NS_INTERFACE_MAP_BEGIN(aClass) \
+ MOZ_FOR_EACH(NS_INTERFACE_MAP_ENTRY, (), (__VA_ARGS__)) \
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, MOZ_ARG_1(__VA_ARGS__)) \
+ NS_IMPL_QUERY_CLASSINFO(aClass) \
+ NS_INTERFACE_MAP_END
+
+#define NS_IMPL_ISUPPORTS_CI(aClass, ...) \
+ NS_IMPL_ADDREF(aClass) \
+ NS_IMPL_RELEASE(aClass) \
+ NS_IMPL_QUERY_INTERFACE_CI(aClass, __VA_ARGS__) \
+ NS_IMPL_CI_INTERFACE_GETTER(aClass, __VA_ARGS__)
+
+#endif // nsIClassInfoImpl_h__
diff --git a/xpcom/glue/nsID.cpp b/xpcom/glue/nsID.cpp
new file mode 100644
index 000000000..0a73bf6c3
--- /dev/null
+++ b/xpcom/glue/nsID.cpp
@@ -0,0 +1,133 @@
+/* -*- 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 "nsID.h"
+#include "nsMemory.h"
+#include "mozilla/Sprintf.h"
+
+void nsID::Clear()
+{
+ m0 = 0;
+ m1 = 0;
+ m2 = 0;
+ for (int i = 0; i < 8; ++i) {
+ m3[i] = 0;
+ }
+}
+
+/**
+ * Multiplies the_int_var with 16 (0x10) and adds the value of the
+ * hexadecimal digit the_char. If it fails it returns false from
+ * the function it's used in.
+ */
+
+#define ADD_HEX_CHAR_TO_INT_OR_RETURN_FALSE(the_char, the_int_var) \
+ the_int_var = (the_int_var << 4) + the_char; \
+ if(the_char >= '0' && the_char <= '9') the_int_var -= '0'; \
+ else if(the_char >= 'a' && the_char <= 'f') the_int_var -= 'a'-10; \
+ else if(the_char >= 'A' && the_char <= 'F') the_int_var -= 'A'-10; \
+ else return false
+
+
+/**
+ * Parses number_of_chars characters from the char_pointer pointer and
+ * puts the number in the dest_variable. The pointer is moved to point
+ * at the first character after the parsed ones. If it fails it returns
+ * false from the function the macro is used in.
+ */
+
+#define PARSE_CHARS_TO_NUM(char_pointer, dest_variable, number_of_chars) \
+ do { int32_t _i=number_of_chars; \
+ dest_variable = 0; \
+ while(_i) { \
+ ADD_HEX_CHAR_TO_INT_OR_RETURN_FALSE(*char_pointer, dest_variable); \
+ char_pointer++; \
+ _i--; \
+ } } while(0)
+
+
+/**
+ * Parses a hyphen from the char_pointer string. If there is no hyphen there
+ * the function returns false from the function it's used in. The
+ * char_pointer is advanced one step.
+ */
+
+#define PARSE_HYPHEN(char_pointer) if (*(char_pointer++) != '-') return false
+
+/*
+ * Turns a {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} string into
+ * an nsID. It can also handle the old format without the { and }.
+ */
+
+bool
+nsID::Parse(const char* aIDStr)
+{
+ /* Optimized for speed */
+ if (!aIDStr) {
+ return false;
+ }
+
+ bool expectFormat1 = (aIDStr[0] == '{');
+ if (expectFormat1) {
+ ++aIDStr;
+ }
+
+ PARSE_CHARS_TO_NUM(aIDStr, m0, 8);
+ PARSE_HYPHEN(aIDStr);
+ PARSE_CHARS_TO_NUM(aIDStr, m1, 4);
+ PARSE_HYPHEN(aIDStr);
+ PARSE_CHARS_TO_NUM(aIDStr, m2, 4);
+ PARSE_HYPHEN(aIDStr);
+ int i;
+ for (i = 0; i < 2; ++i) {
+ PARSE_CHARS_TO_NUM(aIDStr, m3[i], 2);
+ }
+ PARSE_HYPHEN(aIDStr);
+ while (i < 8) {
+ PARSE_CHARS_TO_NUM(aIDStr, m3[i], 2);
+ i++;
+ }
+
+ return expectFormat1 ? *aIDStr == '}' : true;
+}
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+
+static const char gIDFormat[] =
+ "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}";
+
+/*
+ * Returns an allocated string in {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ * format. The string is allocated with NS_Alloc and should be freed by
+ * the caller.
+ */
+
+char*
+nsID::ToString() const
+{
+ char* res = (char*)NS_Alloc(NSID_LENGTH);
+
+ if (res) {
+ snprintf(res, NSID_LENGTH, gIDFormat,
+ m0, (uint32_t)m1, (uint32_t)m2,
+ (uint32_t)m3[0], (uint32_t)m3[1], (uint32_t)m3[2],
+ (uint32_t)m3[3], (uint32_t)m3[4], (uint32_t)m3[5],
+ (uint32_t)m3[6], (uint32_t)m3[7]);
+ }
+ return res;
+}
+
+void
+nsID::ToProvidedString(char (&aDest)[NSID_LENGTH]) const
+{
+ SprintfLiteral(aDest, gIDFormat,
+ m0, (uint32_t)m1, (uint32_t)m2,
+ (uint32_t)m3[0], (uint32_t)m3[1], (uint32_t)m3[2],
+ (uint32_t)m3[3], (uint32_t)m3[4], (uint32_t)m3[5],
+ (uint32_t)m3[6], (uint32_t)m3[7]);
+}
+
+#endif // XPCOM_GLUE_AVOID_NSPR
diff --git a/xpcom/glue/nsID.h b/xpcom/glue/nsID.h
new file mode 100644
index 000000000..c04007d4e
--- /dev/null
+++ b/xpcom/glue/nsID.h
@@ -0,0 +1,179 @@
+/* -*- 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 nsID_h__
+#define nsID_h__
+
+#include <string.h>
+
+#include "nscore.h"
+
+#define NSID_LENGTH 39
+
+/**
+ * A "unique identifier". This is modeled after OSF DCE UUIDs.
+ */
+
+struct nsID
+{
+ /**
+ * @name Identifier values
+ */
+
+ //@{
+ uint32_t m0;
+ uint16_t m1;
+ uint16_t m2;
+ uint8_t m3[8];
+ //@}
+
+ /**
+ * @name Methods
+ */
+
+ //@{
+ /**
+ * Ensures everything is zeroed out.
+ */
+ void Clear();
+
+ /**
+ * Equivalency method. Compares this nsID with another.
+ * @return <b>true</b> if they are the same, <b>false</b> if not.
+ */
+
+ inline bool Equals(const nsID& aOther) const
+ {
+ // Unfortunately memcmp isn't faster than this.
+ return
+ (((uint32_t*)&m0)[0] == ((uint32_t*)&aOther.m0)[0]) &&
+ (((uint32_t*)&m0)[1] == ((uint32_t*)&aOther.m0)[1]) &&
+ (((uint32_t*)&m0)[2] == ((uint32_t*)&aOther.m0)[2]) &&
+ (((uint32_t*)&m0)[3] == ((uint32_t*)&aOther.m0)[3]);
+ }
+
+ inline bool operator==(const nsID& aOther) const
+ {
+ return Equals(aOther);
+ }
+
+ /**
+ * nsID Parsing method. Turns a {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ * string into an nsID
+ */
+ bool Parse(const char* aIDStr);
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+ /**
+ * nsID string encoder. Returns an allocated string in
+ * {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} format. Caller should free string.
+ * YOU SHOULD ONLY USE THIS IF YOU CANNOT USE ToProvidedString() BELOW.
+ */
+ char* ToString() const;
+
+ /**
+ * nsID string encoder. Builds a string in
+ * {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} format, into a char[NSID_LENGTH]
+ * buffer provided by the caller (for instance, on the stack).
+ */
+ void ToProvidedString(char (&aDest)[NSID_LENGTH]) const;
+
+#endif // XPCOM_GLUE_AVOID_NSPR
+
+ //@}
+};
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+/**
+ * A stack helper class to convert a nsID to a string. Useful
+ * for printing nsIDs. For example:
+ * nsID aID = ...;
+ * printf("%s", nsIDToCString(aID).get());
+ */
+class nsIDToCString
+{
+public:
+ explicit nsIDToCString(const nsID& aID)
+ {
+ aID.ToProvidedString(mStringBytes);
+ }
+
+ const char *get() const
+ {
+ return mStringBytes;
+ }
+
+protected:
+ char mStringBytes[NSID_LENGTH];
+};
+#endif
+
+/*
+ * Class IDs
+ */
+
+typedef nsID nsCID;
+
+// Define an CID
+#define NS_DEFINE_CID(_name, _cidspec) \
+ const nsCID _name = _cidspec
+
+#define NS_DEFINE_NAMED_CID(_name) \
+ static const nsCID k##_name = _name
+
+#define REFNSCID const nsCID&
+
+/**
+ * An "interface id" which can be used to uniquely identify a given
+ * interface.
+ */
+
+typedef nsID nsIID;
+
+/**
+ * A macro shorthand for <tt>const nsIID&<tt>
+ */
+
+#define REFNSIID const nsIID&
+
+/**
+ * Define an IID
+ * obsolete - do not use this macro
+ */
+
+#define NS_DEFINE_IID(_name, _iidspec) \
+ const nsIID _name = _iidspec
+
+/**
+ * A macro to build the static const IID accessor method. The Dummy
+ * template parameter only exists so that the kIID symbol will be linked
+ * properly (weak symbol on linux, gnu_linkonce on mac, multiple-definitions
+ * merged on windows). Dummy should always be instantiated as "void".
+ */
+
+#define NS_DECLARE_STATIC_IID_ACCESSOR(the_iid) \
+ template<typename T, typename U> \
+ struct COMTypeInfo;
+
+#define NS_DEFINE_STATIC_IID_ACCESSOR(the_interface, the_iid) \
+ template<typename T> \
+ struct the_interface::COMTypeInfo<the_interface, T> { \
+ static const nsIID kIID NS_HIDDEN; \
+ }; \
+ template<typename T> \
+ const nsIID the_interface::COMTypeInfo<the_interface, T>::kIID NS_HIDDEN = the_iid;
+
+/**
+ * A macro to build the static const CID accessor method
+ */
+
+#define NS_DEFINE_STATIC_CID_ACCESSOR(the_cid) \
+ static const nsID& GetCID() {static const nsID cid = the_cid; return cid;}
+
+#define NS_GET_IID(T) (T::COMTypeInfo<T, void>::kIID)
+#define NS_GET_TEMPLATE_IID(T) (T::template COMTypeInfo<T, void>::kIID)
+
+#endif
diff --git a/xpcom/glue/nsIInterfaceRequestorUtils.cpp b/xpcom/glue/nsIInterfaceRequestorUtils.cpp
new file mode 100644
index 000000000..8a2dfe3f8
--- /dev/null
+++ b/xpcom/glue/nsIInterfaceRequestorUtils.cpp
@@ -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/. */
+
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+
+nsresult
+nsGetInterface::operator()(const nsIID& aIID, void** aInstancePtr) const
+{
+ nsresult status;
+
+ if (mSource) {
+ nsCOMPtr<nsIInterfaceRequestor> factoryPtr = do_QueryInterface(mSource);
+ if (factoryPtr) {
+ status = factoryPtr->GetInterface(aIID, aInstancePtr);
+ } else {
+ status = NS_ERROR_NO_INTERFACE;
+ }
+ } else {
+ status = NS_ERROR_NULL_POINTER;
+ }
+
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
diff --git a/xpcom/glue/nsIInterfaceRequestorUtils.h b/xpcom/glue/nsIInterfaceRequestorUtils.h
new file mode 100644
index 000000000..718cf387b
--- /dev/null
+++ b/xpcom/glue/nsIInterfaceRequestorUtils.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/. */
+
+#ifndef __nsInterfaceRequestorUtils_h
+#define __nsInterfaceRequestorUtils_h
+
+#include "nsCOMPtr.h"
+
+// a type-safe shortcut for calling the |GetInterface()| member function
+// T must inherit from nsIInterfaceRequestor, but the cast may be ambiguous.
+template<class T, class DestinationType>
+inline nsresult
+CallGetInterface(T* aSource, DestinationType** aDestination)
+{
+ NS_PRECONDITION(aSource, "null parameter");
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return aSource->GetInterface(NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+class MOZ_STACK_CLASS nsGetInterface final : public nsCOMPtr_helper
+{
+public:
+ nsGetInterface(nsISupports* aSource, nsresult* aError)
+ : mSource(aSource)
+ , mErrorPtr(aError)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const
+ override;
+
+private:
+ nsISupports* MOZ_NON_OWNING_REF mSource;
+ nsresult* mErrorPtr;
+};
+
+inline const nsGetInterface
+do_GetInterface(nsISupports* aSource, nsresult* aError = 0)
+{
+ return nsGetInterface(aSource, aError);
+}
+
+#endif // __nsInterfaceRequestorUtils_h
+
diff --git a/xpcom/glue/nsINIParser.cpp b/xpcom/glue/nsINIParser.cpp
new file mode 100644
index 000000000..53eae7292
--- /dev/null
+++ b/xpcom/glue/nsINIParser.cpp
@@ -0,0 +1,331 @@
+/* -*- 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/. */
+
+// Moz headers (alphabetical)
+#include "nsCRTGlue.h"
+#include "nsError.h"
+#include "nsIFile.h"
+#include "nsINIParser.h"
+#include "mozilla/FileUtils.h" // AutoFILE
+
+// System headers (alphabetical)
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+#if defined(XP_WIN)
+#define READ_BINARYMODE L"rb"
+#else
+#define READ_BINARYMODE "r"
+#endif
+
+using namespace mozilla;
+
+#ifdef XP_WIN
+inline FILE*
+TS_tfopen(const char* aPath, const wchar_t* aMode)
+{
+ wchar_t wPath[MAX_PATH];
+ MultiByteToWideChar(CP_UTF8, 0, aPath, -1, wPath, MAX_PATH);
+ return _wfopen(wPath, aMode);
+}
+#else
+inline FILE*
+TS_tfopen(const char* aPath, const char* aMode)
+{
+ return fopen(aPath, aMode);
+}
+#endif
+
+// Stack based FILE wrapper to ensure that fclose is called, copied from
+// toolkit/mozapps/update/updater/readstrings.cpp
+
+class AutoFILE
+{
+public:
+ explicit AutoFILE(FILE* aFp = nullptr) : fp_(aFp) {}
+ ~AutoFILE()
+ {
+ if (fp_) {
+ fclose(fp_);
+ }
+ }
+ operator FILE*() { return fp_; }
+ FILE** operator&() { return &fp_; }
+ void operator=(FILE* aFp) { fp_ = aFp; }
+private:
+ FILE* fp_;
+};
+
+nsresult
+nsINIParser::Init(nsIFile* aFile)
+{
+ /* open the file. Don't use OpenANSIFileDesc, because you mustn't
+ pass FILE* across shared library boundaries, which may be using
+ different CRTs */
+
+ AutoFILE fd;
+
+#ifdef XP_WIN
+ nsAutoString path;
+ nsresult rv = aFile->GetPath(path);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ fd = _wfopen(path.get(), READ_BINARYMODE);
+#else
+ nsAutoCString path;
+ aFile->GetNativePath(path);
+
+ fd = fopen(path.get(), READ_BINARYMODE);
+#endif
+
+ if (!fd) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return InitFromFILE(fd);
+}
+
+nsresult
+nsINIParser::Init(const char* aPath)
+{
+ /* open the file */
+ AutoFILE fd(TS_tfopen(aPath, READ_BINARYMODE));
+ if (!fd) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return InitFromFILE(fd);
+}
+
+static const char kNL[] = "\r\n";
+static const char kEquals[] = "=";
+static const char kWhitespace[] = " \t";
+static const char kRBracket[] = "]";
+
+nsresult
+nsINIParser::InitFromFILE(FILE* aFd)
+{
+ /* get file size */
+ if (fseek(aFd, 0, SEEK_END) != 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ long flen = ftell(aFd);
+ /* zero-sized file, or an error */
+ if (flen <= 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ /* malloc an internal buf the size of the file */
+ mFileContents = MakeUnique<char[]>(flen + 2);
+ if (!mFileContents) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ /* read the file in one swoop */
+ if (fseek(aFd, 0, SEEK_SET) != 0) {
+ return NS_BASE_STREAM_OSERROR;
+ }
+
+ int rd = fread(mFileContents.get(), sizeof(char), flen, aFd);
+ if (rd != flen) {
+ return NS_BASE_STREAM_OSERROR;
+ }
+
+ // We write a UTF16 null so that the file is easier to convert to UTF8
+ mFileContents[flen] = mFileContents[flen + 1] = '\0';
+
+ char* buffer = &mFileContents[0];
+
+ if (flen >= 3 &&
+ mFileContents[0] == '\xEF' &&
+ mFileContents[1] == '\xBB' &&
+ mFileContents[2] == '\xBF') {
+ // Someone set us up the Utf-8 BOM
+ // This case is easy, since we assume that BOM-less
+ // files are Utf-8 anyway. Just skip the BOM and process as usual.
+ buffer = &mFileContents[3];
+ }
+
+#ifdef XP_WIN
+ if (flen >= 2 &&
+ mFileContents[0] == '\xFF' &&
+ mFileContents[1] == '\xFE') {
+ // Someone set us up the Utf-16LE BOM
+ buffer = &mFileContents[2];
+ // Get the size required for our Utf8 buffer
+ flen = WideCharToMultiByte(CP_UTF8,
+ 0,
+ reinterpret_cast<LPWSTR>(buffer),
+ -1,
+ nullptr,
+ 0,
+ nullptr,
+ nullptr);
+ if (flen == 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ UniquePtr<char[]> utf8Buffer(new char[flen]);
+ if (WideCharToMultiByte(CP_UTF8, 0, reinterpret_cast<LPWSTR>(buffer), -1,
+ utf8Buffer.get(), flen, nullptr, nullptr) == 0) {
+ return NS_ERROR_FAILURE;
+ }
+ mFileContents = Move(utf8Buffer);
+ buffer = mFileContents.get();
+ }
+#endif
+
+ char* currSection = nullptr;
+
+ // outer loop tokenizes into lines
+ while (char* token = NS_strtok(kNL, &buffer)) {
+ if (token[0] == '#' || token[0] == ';') { // it's a comment
+ continue;
+ }
+
+ token = (char*)NS_strspnp(kWhitespace, token);
+ if (!*token) { // empty line
+ continue;
+ }
+
+ if (token[0] == '[') { // section header!
+ ++token;
+ currSection = token;
+
+ char* rb = NS_strtok(kRBracket, &token);
+ if (!rb || NS_strtok(kWhitespace, &token)) {
+ // there's either an unclosed [Section or a [Section]Moretext!
+ // we could frankly decide that this INI file is malformed right
+ // here and stop, but we won't... keep going, looking for
+ // a well-formed [section] to continue working with
+ currSection = nullptr;
+ }
+
+ continue;
+ }
+
+ if (!currSection) {
+ // If we haven't found a section header (or we found a malformed
+ // section header), don't bother parsing this line.
+ continue;
+ }
+
+ char* key = token;
+ char* e = NS_strtok(kEquals, &token);
+ if (!e || !token) {
+ continue;
+ }
+
+ INIValue* v;
+ if (!mSections.Get(currSection, &v)) {
+ v = new INIValue(key, token);
+ if (!v) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ mSections.Put(currSection, v);
+ continue;
+ }
+
+ // Check whether this key has already been specified; overwrite
+ // if so, or append if not.
+ while (v) {
+ if (!strcmp(key, v->key)) {
+ v->value = token;
+ break;
+ }
+ if (!v->next) {
+ v->next = MakeUnique<INIValue>(key, token);
+ if (!v->next) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ break;
+ }
+ v = v->next.get();
+ }
+ NS_ASSERTION(v, "v should never be null coming out of this loop");
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsINIParser::GetString(const char* aSection, const char* aKey,
+ nsACString& aResult)
+{
+ INIValue* val;
+ mSections.Get(aSection, &val);
+
+ while (val) {
+ if (strcmp(val->key, aKey) == 0) {
+ aResult.Assign(val->value);
+ return NS_OK;
+ }
+
+ val = val->next.get();
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+nsINIParser::GetString(const char* aSection, const char* aKey,
+ char* aResult, uint32_t aResultLen)
+{
+ INIValue* val;
+ mSections.Get(aSection, &val);
+
+ while (val) {
+ if (strcmp(val->key, aKey) == 0) {
+ strncpy(aResult, val->value, aResultLen);
+ aResult[aResultLen - 1] = '\0';
+ if (strlen(val->value) >= aResultLen) {
+ return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
+ }
+
+ return NS_OK;
+ }
+
+ val = val->next.get();
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+nsINIParser::GetSections(INISectionCallback aCB, void* aClosure)
+{
+ for (auto iter = mSections.Iter(); !iter.Done(); iter.Next()) {
+ if (!aCB(iter.Key(), aClosure)) {
+ break;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult
+nsINIParser::GetStrings(const char* aSection,
+ INIStringCallback aCB, void* aClosure)
+{
+ INIValue* val;
+
+ for (mSections.Get(aSection, &val);
+ val;
+ val = val->next.get()) {
+
+ if (!aCB(val->key, val->value, aClosure)) {
+ return NS_OK;
+ }
+ }
+
+ return NS_OK;
+}
diff --git a/xpcom/glue/nsINIParser.h b/xpcom/glue/nsINIParser.h
new file mode 100644
index 000000000..d0f553d49
--- /dev/null
+++ b/xpcom/glue/nsINIParser.h
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This file was shamelessly copied from mozilla/xpinstall/wizard/unix/src2
+
+#ifndef nsINIParser_h__
+#define nsINIParser_h__
+
+#ifdef MOZILLA_INTERNAL_API
+#define nsINIParser nsINIParser_internal
+#endif
+
+#include "nscore.h"
+#include "nsClassHashtable.h"
+#include "mozilla/UniquePtr.h"
+
+#include <stdio.h>
+
+class nsIFile;
+
+class nsINIParser
+{
+public:
+ nsINIParser() {}
+ ~nsINIParser() {}
+
+ /**
+ * Initialize the INIParser with a nsIFile. If this method fails, no
+ * other methods should be called. This method reads and parses the file,
+ * the class does not hold a file handle open. An instance must only be
+ * initialized once.
+ */
+ nsresult Init(nsIFile* aFile);
+
+ /**
+ * Initialize the INIParser with a file path. If this method fails, no
+ * other methods should be called. This method reads and parses the file,
+ * the class does not hold a file handle open. An instance must only
+ * be initialized once.
+ */
+ nsresult Init(const char* aPath);
+
+ /**
+ * Callback for GetSections
+ * @return false to stop enumeration, or true to continue.
+ */
+ typedef bool (*INISectionCallback)(const char* aSection, void* aClosure);
+
+ /**
+ * Enumerate the sections within the INI file.
+ */
+ nsresult GetSections(INISectionCallback aCB, void* aClosure);
+
+ /**
+ * Callback for GetStrings
+ * @return false to stop enumeration, or true to continue
+ */
+ typedef bool (*INIStringCallback)(const char* aString, const char* aValue,
+ void* aClosure);
+
+ /**
+ * Enumerate the strings within a section. If the section does
+ * not exist, this function will silently return.
+ */
+ nsresult GetStrings(const char* aSection,
+ INIStringCallback aCB, void* aClosure);
+
+ /**
+ * Get the value of the specified key in the specified section
+ * of the INI file represented by this instance.
+ *
+ * @param aSection section name
+ * @param aKey key name
+ * @param aResult the value found
+ * @throws NS_ERROR_FAILURE if the specified section/key could not be
+ * found.
+ */
+ nsresult GetString(const char* aSection, const char* aKey,
+ nsACString& aResult);
+
+ /**
+ * Alternate signature of GetString that uses a pre-allocated buffer
+ * instead of a nsACString (for use in the standalone glue before
+ * the glue is initialized).
+ *
+ * @throws NS_ERROR_LOSS_OF_SIGNIFICANT_DATA if the aResult buffer is not
+ * large enough for the data. aResult will be filled with as
+ * much data as possible.
+ *
+ * @see GetString [1]
+ */
+ nsresult GetString(const char* aSection, const char* aKey,
+ char* aResult, uint32_t aResultLen);
+
+private:
+ struct INIValue
+ {
+ INIValue(const char* aKey, const char* aValue)
+ : key(aKey)
+ , value(aValue)
+ {
+ }
+
+ const char* key;
+ const char* value;
+ mozilla::UniquePtr<INIValue> next;
+ };
+
+ nsClassHashtable<nsDepCharHashKey, INIValue> mSections;
+ mozilla::UniquePtr<char[]> mFileContents;
+
+ nsresult InitFromFILE(FILE* aFd);
+};
+
+#endif /* nsINIParser_h__ */
diff --git a/xpcom/glue/nsISupportsImpl.cpp b/xpcom/glue/nsISupportsImpl.cpp
new file mode 100644
index 000000000..c60c0bfa7
--- /dev/null
+++ b/xpcom/glue/nsISupportsImpl.cpp
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupportsImpl.h"
+
+nsresult NS_FASTCALL
+NS_TableDrivenQI(void* aThis, REFNSIID aIID, void** aInstancePtr,
+ const QITableEntry* aEntries)
+{
+ do {
+ if (aIID.Equals(*aEntries->iid)) {
+ nsISupports* r = reinterpret_cast<nsISupports*>(
+ reinterpret_cast<char*>(aThis) + aEntries->offset);
+ NS_ADDREF(r);
+ *aInstancePtr = r;
+ return NS_OK;
+ }
+
+ ++aEntries;
+ } while (aEntries->iid);
+
+ *aInstancePtr = nullptr;
+ return NS_ERROR_NO_INTERFACE;
+}
diff --git a/xpcom/glue/nsISupportsImpl.h b/xpcom/glue/nsISupportsImpl.h
new file mode 100644
index 000000000..26db3c525
--- /dev/null
+++ b/xpcom/glue/nsISupportsImpl.h
@@ -0,0 +1,1090 @@
+/* -*- 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/. */
+// IWYU pragma: private, include "nsISupports.h"
+
+
+#ifndef nsISupportsImpl_h__
+#define nsISupportsImpl_h__
+
+#include "nscore.h"
+#include "nsISupportsBase.h"
+#include "nsISupportsUtils.h"
+
+
+#if !defined(XPCOM_GLUE_AVOID_NSPR)
+#include "prthread.h" /* needed for thread-safety checks */
+#endif // !XPCOM_GLUE_AVOID_NSPR
+
+#include "nsDebug.h"
+#include "nsXPCOM.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Compiler.h"
+#include "mozilla/Likely.h"
+#include "mozilla/MacroArgs.h"
+#include "mozilla/MacroForEach.h"
+#include "mozilla/TypeTraits.h"
+
+#define MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(X) \
+ static_assert(!mozilla::IsDestructible<X>::value, \
+ "Reference-counted class " #X " should not have a public destructor. " \
+ "Make this class's destructor non-public");
+
+inline nsISupports*
+ToSupports(nsISupports* aSupports)
+{
+ return aSupports;
+}
+
+inline nsISupports*
+ToCanonicalSupports(nsISupports* aSupports)
+{
+ return nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Macros to help detect thread-safety:
+
+#ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED
+
+class nsAutoOwningThread
+{
+public:
+ nsAutoOwningThread() { mThread = PR_GetCurrentThread(); }
+ void* GetThread() const { return mThread; }
+
+private:
+ void* mThread;
+};
+
+#define NS_DECL_OWNINGTHREAD nsAutoOwningThread _mOwningThread;
+#define NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class) \
+ NS_CheckThreadSafe(agg->_mOwningThread.GetThread(), #_class " not thread-safe")
+#define NS_ASSERT_OWNINGTHREAD(_class) NS_ASSERT_OWNINGTHREAD_AGGREGATE(this, _class)
+#else // !DEBUG && !(NIGHTLY_BUILD && !MOZ_PROFILING)
+
+#define NS_DECL_OWNINGTHREAD /* nothing */
+#define NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class) ((void)0)
+#define NS_ASSERT_OWNINGTHREAD(_class) ((void)0)
+
+#endif // DEBUG || (NIGHTLY_BUILD && !MOZ_PROFILING)
+
+
+// Macros for reference-count and constructor logging
+
+#if defined(NS_BUILD_REFCNT_LOGGING)
+
+#define NS_LOG_ADDREF(_p, _rc, _type, _size) \
+ NS_LogAddRef((_p), (_rc), (_type), (uint32_t) (_size))
+
+#define NS_LOG_RELEASE(_p, _rc, _type) \
+ NS_LogRelease((_p), (_rc), (_type))
+
+#include "mozilla/TypeTraits.h"
+#define MOZ_ASSERT_CLASSNAME(_type) \
+ static_assert(mozilla::IsClass<_type>::value, \
+ "Token '" #_type "' is not a class type.")
+
+// Note that the following constructor/destructor logging macros are redundant
+// for refcounted objects that log via the NS_LOG_ADDREF/NS_LOG_RELEASE macros.
+// Refcount logging is preferred.
+#define MOZ_COUNT_CTOR(_type) \
+do { \
+ MOZ_ASSERT_CLASSNAME(_type); \
+ NS_LogCtor((void*)this, #_type, sizeof(*this)); \
+} while (0)
+
+#define MOZ_COUNT_CTOR_INHERITED(_type, _base) \
+do { \
+ MOZ_ASSERT_CLASSNAME(_type); \
+ MOZ_ASSERT_CLASSNAME(_base); \
+ NS_LogCtor((void*)this, #_type, sizeof(*this) - sizeof(_base)); \
+} while (0)
+
+#define MOZ_LOG_CTOR(_ptr, _name, _size) \
+do { \
+ NS_LogCtor((void*)_ptr, _name, _size); \
+} while (0)
+
+#define MOZ_COUNT_DTOR(_type) \
+do { \
+ MOZ_ASSERT_CLASSNAME(_type); \
+ NS_LogDtor((void*)this, #_type, sizeof(*this)); \
+} while (0)
+
+#define MOZ_COUNT_DTOR_INHERITED(_type, _base) \
+do { \
+ MOZ_ASSERT_CLASSNAME(_type); \
+ MOZ_ASSERT_CLASSNAME(_base); \
+ NS_LogDtor((void*)this, #_type, sizeof(*this) - sizeof(_base)); \
+} while (0)
+
+#define MOZ_LOG_DTOR(_ptr, _name, _size) \
+do { \
+ NS_LogDtor((void*)_ptr, _name, _size); \
+} while (0)
+
+/* nsCOMPtr.h allows these macros to be defined by clients
+ * These logging functions require dynamic_cast<void*>, so they don't
+ * do anything useful if we don't have dynamic_cast<void*>.
+ * Note: The explicit comparison to nullptr is needed to avoid warnings
+ * when _p is a nullptr itself. */
+#define NSCAP_LOG_ASSIGNMENT(_c, _p) \
+ if (_p != nullptr) \
+ NS_LogCOMPtrAddRef((_c),static_cast<nsISupports*>(_p))
+
+#define NSCAP_LOG_RELEASE(_c, _p) \
+ if (_p) \
+ NS_LogCOMPtrRelease((_c), static_cast<nsISupports*>(_p))
+
+#else /* !NS_BUILD_REFCNT_LOGGING */
+
+#define NS_LOG_ADDREF(_p, _rc, _type, _size)
+#define NS_LOG_RELEASE(_p, _rc, _type)
+#define MOZ_COUNT_CTOR(_type)
+#define MOZ_COUNT_CTOR_INHERITED(_type, _base)
+#define MOZ_LOG_CTOR(_ptr, _name, _size)
+#define MOZ_COUNT_DTOR(_type)
+#define MOZ_COUNT_DTOR_INHERITED(_type, _base)
+#define MOZ_LOG_DTOR(_ptr, _name, _size)
+
+#endif /* NS_BUILD_REFCNT_LOGGING */
+
+
+// Support for ISupports classes which interact with cycle collector.
+
+#define NS_NUMBER_OF_FLAGS_IN_REFCNT 2
+#define NS_IN_PURPLE_BUFFER (1 << 0)
+#define NS_IS_PURPLE (1 << 1)
+#define NS_REFCOUNT_CHANGE (1 << NS_NUMBER_OF_FLAGS_IN_REFCNT)
+#define NS_REFCOUNT_VALUE(_val) (_val >> NS_NUMBER_OF_FLAGS_IN_REFCNT)
+
+class nsCycleCollectingAutoRefCnt
+{
+public:
+ nsCycleCollectingAutoRefCnt() : mRefCntAndFlags(0) {}
+
+ explicit nsCycleCollectingAutoRefCnt(uintptr_t aValue)
+ : mRefCntAndFlags(aValue << NS_NUMBER_OF_FLAGS_IN_REFCNT)
+ {
+ }
+
+ nsCycleCollectingAutoRefCnt(const nsCycleCollectingAutoRefCnt&) = delete;
+ void operator=(const nsCycleCollectingAutoRefCnt&) = delete;
+
+ MOZ_ALWAYS_INLINE uintptr_t incr(nsISupports* aOwner)
+ {
+ return incr(aOwner, nullptr);
+ }
+
+ MOZ_ALWAYS_INLINE uintptr_t incr(void* aOwner,
+ nsCycleCollectionParticipant* aCp)
+ {
+ mRefCntAndFlags += NS_REFCOUNT_CHANGE;
+ mRefCntAndFlags &= ~NS_IS_PURPLE;
+ // For incremental cycle collection, use the purple buffer to track objects
+ // that have been AddRef'd.
+ if (!IsInPurpleBuffer()) {
+ mRefCntAndFlags |= NS_IN_PURPLE_BUFFER;
+ // Refcount isn't zero, so Suspect won't delete anything.
+ MOZ_ASSERT(get() > 0);
+ NS_CycleCollectorSuspect3(aOwner, aCp, this, nullptr);
+ }
+ return NS_REFCOUNT_VALUE(mRefCntAndFlags);
+ }
+
+ MOZ_ALWAYS_INLINE void stabilizeForDeletion()
+ {
+ // Set refcnt to 1 and mark us to be in the purple buffer.
+ // This way decr won't call suspect again.
+ mRefCntAndFlags = NS_REFCOUNT_CHANGE | NS_IN_PURPLE_BUFFER;
+ }
+
+ MOZ_ALWAYS_INLINE uintptr_t decr(nsISupports* aOwner,
+ bool* aShouldDelete = nullptr)
+ {
+ return decr(aOwner, nullptr, aShouldDelete);
+ }
+
+ MOZ_ALWAYS_INLINE uintptr_t decr(void* aOwner,
+ nsCycleCollectionParticipant* aCp,
+ bool* aShouldDelete = nullptr)
+ {
+ MOZ_ASSERT(get() > 0);
+ if (!IsInPurpleBuffer()) {
+ mRefCntAndFlags -= NS_REFCOUNT_CHANGE;
+ mRefCntAndFlags |= (NS_IN_PURPLE_BUFFER | NS_IS_PURPLE);
+ uintptr_t retval = NS_REFCOUNT_VALUE(mRefCntAndFlags);
+ // Suspect may delete 'aOwner' and 'this'!
+ NS_CycleCollectorSuspect3(aOwner, aCp, this, aShouldDelete);
+ return retval;
+ }
+ mRefCntAndFlags -= NS_REFCOUNT_CHANGE;
+ mRefCntAndFlags |= (NS_IN_PURPLE_BUFFER | NS_IS_PURPLE);
+ return NS_REFCOUNT_VALUE(mRefCntAndFlags);
+ }
+
+ MOZ_ALWAYS_INLINE void RemovePurple()
+ {
+ MOZ_ASSERT(IsPurple(), "must be purple");
+ mRefCntAndFlags &= ~NS_IS_PURPLE;
+ }
+
+ MOZ_ALWAYS_INLINE void RemoveFromPurpleBuffer()
+ {
+ MOZ_ASSERT(IsInPurpleBuffer());
+ mRefCntAndFlags &= ~(NS_IS_PURPLE | NS_IN_PURPLE_BUFFER);
+ }
+
+ MOZ_ALWAYS_INLINE bool IsPurple() const
+ {
+ return !!(mRefCntAndFlags & NS_IS_PURPLE);
+ }
+
+ MOZ_ALWAYS_INLINE bool IsInPurpleBuffer() const
+ {
+ return !!(mRefCntAndFlags & NS_IN_PURPLE_BUFFER);
+ }
+
+ MOZ_ALWAYS_INLINE nsrefcnt get() const
+ {
+ return NS_REFCOUNT_VALUE(mRefCntAndFlags);
+ }
+
+ MOZ_ALWAYS_INLINE operator nsrefcnt() const
+ {
+ return get();
+ }
+
+private:
+ uintptr_t mRefCntAndFlags;
+};
+
+class nsAutoRefCnt
+{
+public:
+ nsAutoRefCnt() : mValue(0) {}
+ explicit nsAutoRefCnt(nsrefcnt aValue) : mValue(aValue) {}
+
+ nsAutoRefCnt(const nsAutoRefCnt&) = delete;
+ void operator=(const nsAutoRefCnt&) = delete;
+
+ // only support prefix increment/decrement
+ nsrefcnt operator++() { return ++mValue; }
+ nsrefcnt operator--() { return --mValue; }
+
+ nsrefcnt operator=(nsrefcnt aValue) { return (mValue = aValue); }
+ operator nsrefcnt() const { return mValue; }
+ nsrefcnt get() const { return mValue; }
+
+ static const bool isThreadSafe = false;
+private:
+ nsrefcnt operator++(int) = delete;
+ nsrefcnt operator--(int) = delete;
+ nsrefcnt mValue;
+};
+
+namespace mozilla {
+class ThreadSafeAutoRefCnt
+{
+public:
+ ThreadSafeAutoRefCnt() : mValue(0) {}
+ explicit ThreadSafeAutoRefCnt(nsrefcnt aValue) : mValue(aValue) {}
+
+ ThreadSafeAutoRefCnt(const ThreadSafeAutoRefCnt&) = delete;
+ void operator=(const ThreadSafeAutoRefCnt&) = delete;
+
+ // only support prefix increment/decrement
+ MOZ_ALWAYS_INLINE nsrefcnt operator++() { return ++mValue; }
+ MOZ_ALWAYS_INLINE nsrefcnt operator--() { return --mValue; }
+
+ MOZ_ALWAYS_INLINE nsrefcnt operator=(nsrefcnt aValue)
+ {
+ return (mValue = aValue);
+ }
+ MOZ_ALWAYS_INLINE operator nsrefcnt() const { return mValue; }
+ MOZ_ALWAYS_INLINE nsrefcnt get() const { return mValue; }
+
+ static const bool isThreadSafe = true;
+private:
+ nsrefcnt operator++(int) = delete;
+ nsrefcnt operator--(int) = delete;
+ // In theory, RelaseAcquire consistency (but no weaker) is sufficient for
+ // the counter. Making it weaker could speed up builds on ARM (but not x86),
+ // but could break pre-existing code that assumes sequential consistency.
+ Atomic<nsrefcnt> mValue;
+};
+} // namespace mozilla
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Declare the reference count variable and the implementations of the
+ * AddRef and QueryInterface methods.
+ */
+
+#define NS_DECL_ISUPPORTS \
+public: \
+ NS_IMETHOD QueryInterface(REFNSIID aIID, \
+ void** aInstancePtr) override; \
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override; \
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) override; \
+ typedef mozilla::FalseType HasThreadSafeRefCnt; \
+protected: \
+ nsAutoRefCnt mRefCnt; \
+ NS_DECL_OWNINGTHREAD \
+public:
+
+#define NS_DECL_THREADSAFE_ISUPPORTS \
+public: \
+ NS_IMETHOD QueryInterface(REFNSIID aIID, \
+ void** aInstancePtr) override; \
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override; \
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) override; \
+ typedef mozilla::TrueType HasThreadSafeRefCnt; \
+protected: \
+ ::mozilla::ThreadSafeAutoRefCnt mRefCnt; \
+ NS_DECL_OWNINGTHREAD \
+public:
+
+#define NS_DECL_CYCLE_COLLECTING_ISUPPORTS \
+public: \
+ NS_IMETHOD QueryInterface(REFNSIID aIID, \
+ void** aInstancePtr) override; \
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override; \
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) override; \
+ NS_IMETHOD_(void) DeleteCycleCollectable(void); \
+ typedef mozilla::FalseType HasThreadSafeRefCnt; \
+protected: \
+ nsCycleCollectingAutoRefCnt mRefCnt; \
+ NS_DECL_OWNINGTHREAD \
+public:
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Implementation of AddRef and Release for non-nsISupports (ie "native")
+ * cycle-collected classes that use the purple buffer to avoid leaks.
+ */
+
+#define NS_IMPL_CC_NATIVE_ADDREF_BODY(_class) \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ nsrefcnt count = \
+ mRefCnt.incr(static_cast<void*>(this), \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \
+ NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
+ return count;
+
+#define NS_IMPL_CC_NATIVE_RELEASE_BODY(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ nsrefcnt count = \
+ mRefCnt.decr(static_cast<void*>(this), \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \
+ NS_LOG_RELEASE(this, count, #_class); \
+ return count;
+
+#define NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(_class) \
+NS_METHOD_(MozExternalRefCountType) _class::AddRef(void) \
+{ \
+ NS_IMPL_CC_NATIVE_ADDREF_BODY(_class) \
+}
+
+#define NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE_WITH_LAST_RELEASE(_class, _last) \
+NS_METHOD_(MozExternalRefCountType) _class::Release(void) \
+{ \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ bool shouldDelete = false; \
+ nsrefcnt count = \
+ mRefCnt.decr(static_cast<void*>(this), \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant(), \
+ &shouldDelete); \
+ NS_LOG_RELEASE(this, count, #_class); \
+ if (count == 0) { \
+ mRefCnt.incr(static_cast<void*>(this), \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \
+ _last; \
+ mRefCnt.decr(static_cast<void*>(this), \
+ _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \
+ if (shouldDelete) { \
+ mRefCnt.stabilizeForDeletion(); \
+ DeleteCycleCollectable(); \
+ } \
+ } \
+ return count; \
+}
+
+#define NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(_class) \
+NS_METHOD_(MozExternalRefCountType) _class::Release(void) \
+{ \
+ NS_IMPL_CC_NATIVE_RELEASE_BODY(_class) \
+}
+
+#define NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(_class) \
+public: \
+ NS_METHOD_(MozExternalRefCountType) AddRef(void) { \
+ NS_IMPL_CC_NATIVE_ADDREF_BODY(_class) \
+ } \
+ NS_METHOD_(MozExternalRefCountType) Release(void) { \
+ NS_IMPL_CC_NATIVE_RELEASE_BODY(_class) \
+ } \
+ typedef mozilla::FalseType HasThreadSafeRefCnt; \
+protected: \
+ nsCycleCollectingAutoRefCnt mRefCnt; \
+ NS_DECL_OWNINGTHREAD \
+public:
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Use this macro to declare and implement the AddRef & Release methods for a
+ * given non-XPCOM <i>_class</i>.
+ *
+ * @param _class The name of the class implementing the method
+ * @param _destroy A statement that is executed when the object's
+ * refcount drops to zero.
+ * @param optional override Mark the AddRef & Release methods as overrides.
+ */
+#define NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(_class, _destroy, ...) \
+public: \
+ NS_METHOD_(MozExternalRefCountType) AddRef(void) __VA_ARGS__ { \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ ++mRefCnt; \
+ NS_LOG_ADDREF(this, mRefCnt, #_class, sizeof(*this)); \
+ return mRefCnt; \
+ } \
+ NS_METHOD_(MozExternalRefCountType) Release(void) __VA_ARGS__ { \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ --mRefCnt; \
+ NS_LOG_RELEASE(this, mRefCnt, #_class); \
+ if (mRefCnt == 0) { \
+ mRefCnt = 1; /* stabilize */ \
+ _destroy; \
+ return 0; \
+ } \
+ return mRefCnt; \
+ } \
+ typedef mozilla::FalseType HasThreadSafeRefCnt; \
+protected: \
+ nsAutoRefCnt mRefCnt; \
+ NS_DECL_OWNINGTHREAD \
+public:
+
+/**
+ * Use this macro to declare and implement the AddRef & Release methods for a
+ * given non-XPCOM <i>_class</i>.
+ *
+ * @param _class The name of the class implementing the method
+ * @param optional override Mark the AddRef & Release methods as overrides.
+ */
+#define NS_INLINE_DECL_REFCOUNTING(_class, ...) \
+ NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(_class, delete(this), __VA_ARGS__)
+
+#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING_META(_class, _decl, ...) \
+public: \
+ _decl(MozExternalRefCountType) AddRef(void) __VA_ARGS__ { \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ nsrefcnt count = ++mRefCnt; \
+ NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
+ return (nsrefcnt) count; \
+ } \
+ _decl(MozExternalRefCountType) Release(void) __VA_ARGS__ { \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ nsrefcnt count = --mRefCnt; \
+ NS_LOG_RELEASE(this, count, #_class); \
+ if (count == 0) { \
+ delete (this); \
+ return 0; \
+ } \
+ return count; \
+ } \
+ typedef mozilla::TrueType HasThreadSafeRefCnt; \
+protected: \
+ ::mozilla::ThreadSafeAutoRefCnt mRefCnt; \
+public:
+
+/**
+ * Use this macro to declare and implement the AddRef & Release methods for a
+ * given non-XPCOM <i>_class</i> in a threadsafe manner.
+ *
+ * DOES NOT DO REFCOUNT STABILIZATION!
+ *
+ * @param _class The name of the class implementing the method
+ */
+#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING(_class, ...) \
+NS_INLINE_DECL_THREADSAFE_REFCOUNTING_META(_class, NS_METHOD_, __VA_ARGS__)
+
+/**
+ * Like NS_INLINE_DECL_THREADSAFE_REFCOUNTING with AddRef & Release declared
+ * virtual.
+ */
+#define NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING(_class, ...) \
+NS_INLINE_DECL_THREADSAFE_REFCOUNTING_META(_class, NS_IMETHOD_, __VA_ARGS__)
+
+/**
+ * Use this macro to implement the AddRef method for a given <i>_class</i>
+ * @param _class The name of the class implementing the method
+ */
+#define NS_IMPL_ADDREF(_class) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::AddRef(void) \
+{ \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ if (!mRefCnt.isThreadSafe) \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ nsrefcnt count = ++mRefCnt; \
+ NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
+ return count; \
+}
+
+/**
+ * Use this macro to implement the AddRef method for a given <i>_class</i>
+ * implemented as a wholly owned aggregated object intended to implement
+ * interface(s) for its owner
+ * @param _class The name of the class implementing the method
+ * @param _aggregator the owning/containing object
+ */
+#define NS_IMPL_ADDREF_USING_AGGREGATOR(_class, _aggregator) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::AddRef(void) \
+{ \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ NS_PRECONDITION(_aggregator, "null aggregator"); \
+ return (_aggregator)->AddRef(); \
+}
+
+/**
+ * Use this macro to implement the Release method for a given
+ * <i>_class</i>.
+ * @param _class The name of the class implementing the method
+ * @param _destroy A statement that is executed when the object's
+ * refcount drops to zero.
+ *
+ * For example,
+ *
+ * NS_IMPL_RELEASE_WITH_DESTROY(Foo, Destroy(this))
+ *
+ * will cause
+ *
+ * Destroy(this);
+ *
+ * to be invoked when the object's refcount drops to zero. This
+ * allows for arbitrary teardown activity to occur (e.g., deallocation
+ * of object allocated with placement new).
+ */
+#define NS_IMPL_RELEASE_WITH_DESTROY(_class, _destroy) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::Release(void) \
+{ \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ if (!mRefCnt.isThreadSafe) \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ nsrefcnt count = --mRefCnt; \
+ NS_LOG_RELEASE(this, count, #_class); \
+ if (count == 0) { \
+ mRefCnt = 1; /* stabilize */ \
+ _destroy; \
+ return 0; \
+ } \
+ return count; \
+}
+
+/**
+ * Use this macro to implement the Release method for a given <i>_class</i>
+ * @param _class The name of the class implementing the method
+ *
+ * A note on the 'stabilization' of the refcnt to one. At that point,
+ * the object's refcount will have gone to zero. The object's
+ * destructor may trigger code that attempts to QueryInterface() and
+ * Release() 'this' again. Doing so will temporarily increment and
+ * decrement the refcount. (Only a logic error would make one try to
+ * keep a permanent hold on 'this'.) To prevent re-entering the
+ * destructor, we make sure that no balanced refcounting can return
+ * the refcount to |0|.
+ */
+#define NS_IMPL_RELEASE(_class) \
+ NS_IMPL_RELEASE_WITH_DESTROY(_class, delete (this))
+
+/**
+ * Use this macro to implement the Release method for a given <i>_class</i>
+ * implemented as a wholly owned aggregated object intended to implement
+ * interface(s) for its owner
+ * @param _class The name of the class implementing the method
+ * @param _aggregator the owning/containing object
+ */
+#define NS_IMPL_RELEASE_USING_AGGREGATOR(_class, _aggregator) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::Release(void) \
+{ \
+ NS_PRECONDITION(_aggregator, "null aggregator"); \
+ return (_aggregator)->Release(); \
+}
+
+
+#define NS_IMPL_CYCLE_COLLECTING_ADDREF(_class) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::AddRef(void) \
+{ \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this); \
+ nsrefcnt count = mRefCnt.incr(base); \
+ NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
+ return count; \
+}
+
+#define NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(_class, _destroy) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::Release(void) \
+{ \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this); \
+ nsrefcnt count = mRefCnt.decr(base); \
+ NS_LOG_RELEASE(this, count, #_class); \
+ return count; \
+} \
+NS_IMETHODIMP_(void) _class::DeleteCycleCollectable(void) \
+{ \
+ _destroy; \
+}
+
+#define NS_IMPL_CYCLE_COLLECTING_RELEASE(_class) \
+ NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(_class, delete (this))
+
+// _LAST_RELEASE can be useful when certain resources should be released
+// as soon as we know the object will be deleted.
+#define NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(_class, _last) \
+NS_IMETHODIMP_(MozExternalRefCountType) _class::Release(void) \
+{ \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ bool shouldDelete = false; \
+ nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this); \
+ nsrefcnt count = mRefCnt.decr(base, &shouldDelete); \
+ NS_LOG_RELEASE(this, count, #_class); \
+ if (count == 0) { \
+ mRefCnt.incr(base); \
+ _last; \
+ mRefCnt.decr(base); \
+ if (shouldDelete) { \
+ mRefCnt.stabilizeForDeletion(); \
+ DeleteCycleCollectable(); \
+ } \
+ } \
+ return count; \
+} \
+NS_IMETHODIMP_(void) _class::DeleteCycleCollectable(void) \
+{ \
+ delete this; \
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * There are two ways of implementing QueryInterface, and we use both:
+ *
+ * Table-driven QueryInterface uses a static table of IID->offset mappings
+ * and a shared helper function. Using it tends to reduce codesize and improve
+ * runtime performance (due to processor cache hits).
+ *
+ * Macro-driven QueryInterface generates a QueryInterface function directly
+ * using common macros. This is necessary if special QueryInterface features
+ * are being used (such as tearoffs and conditional interfaces).
+ *
+ * These methods can be combined into a table-driven function call followed
+ * by custom code for tearoffs and conditionals.
+ */
+
+struct QITableEntry
+{
+ const nsIID* iid; // null indicates end of the QITableEntry array
+ int32_t offset;
+};
+
+nsresult NS_FASTCALL
+NS_TableDrivenQI(void* aThis, REFNSIID aIID,
+ void** aInstancePtr, const QITableEntry* aEntries);
+
+/**
+ * Implement table-driven queryinterface
+ */
+
+#define NS_INTERFACE_TABLE_HEAD(_class) \
+NS_IMETHODIMP _class::QueryInterface(REFNSIID aIID, void** aInstancePtr) \
+{ \
+ NS_ASSERTION(aInstancePtr, \
+ "QueryInterface requires a non-NULL destination!"); \
+ nsresult rv = NS_ERROR_FAILURE;
+
+#define NS_INTERFACE_TABLE_BEGIN \
+ static const QITableEntry table[] = {
+
+#define NS_INTERFACE_TABLE_ENTRY(_class, _interface) \
+ { &NS_GET_IID(_interface), \
+ int32_t(reinterpret_cast<char*>( \
+ static_cast<_interface*>((_class*) 0x1000)) - \
+ reinterpret_cast<char*>((_class*) 0x1000)) \
+ },
+
+#define NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, _interface, _implClass) \
+ { &NS_GET_IID(_interface), \
+ int32_t(reinterpret_cast<char*>( \
+ static_cast<_interface*>( \
+ static_cast<_implClass*>( \
+ (_class*) 0x1000))) - \
+ reinterpret_cast<char*>((_class*) 0x1000)) \
+ },
+
+/*
+ * XXX: we want to use mozilla::ArrayLength (or equivalent,
+ * MOZ_ARRAY_LENGTH) in this condition, but some versions of GCC don't
+ * see that the static_assert condition is actually constant in those
+ * cases, even with constexpr support (?).
+ */
+#define NS_INTERFACE_TABLE_END_WITH_PTR(_ptr) \
+ { nullptr, 0 } }; \
+ static_assert((sizeof(table)/sizeof(table[0])) > 1, "need at least 1 interface"); \
+ rv = NS_TableDrivenQI(static_cast<void*>(_ptr), \
+ aIID, aInstancePtr, table);
+
+#define NS_INTERFACE_TABLE_END \
+ NS_INTERFACE_TABLE_END_WITH_PTR(this)
+
+#define NS_INTERFACE_TABLE_TAIL \
+ return rv; \
+}
+
+#define NS_INTERFACE_TABLE_TAIL_INHERITING(_baseclass) \
+ if (NS_SUCCEEDED(rv)) \
+ return rv; \
+ return _baseclass::QueryInterface(aIID, aInstancePtr); \
+}
+
+#define NS_INTERFACE_TABLE_TAIL_USING_AGGREGATOR(_aggregator) \
+ if (NS_SUCCEEDED(rv)) \
+ return rv; \
+ NS_ASSERTION(_aggregator, "null aggregator"); \
+ return _aggregator->QueryInterface(aIID, aInstancePtr) \
+}
+
+/**
+ * This implements query interface with two assumptions: First, the
+ * class in question implements nsISupports and its own interface and
+ * nothing else. Second, the implementation of the class's primary
+ * inheritance chain leads to its own interface.
+ *
+ * @param _class The name of the class implementing the method
+ * @param _classiiddef The name of the #define symbol that defines the IID
+ * for the class (e.g. NS_ISUPPORTS_IID)
+ */
+
+#define NS_IMPL_QUERY_HEAD(_class) \
+NS_IMETHODIMP _class::QueryInterface(REFNSIID aIID, void** aInstancePtr) \
+{ \
+ NS_ASSERTION(aInstancePtr, \
+ "QueryInterface requires a non-NULL destination!"); \
+ nsISupports* foundInterface;
+
+#define NS_IMPL_QUERY_BODY(_interface) \
+ if ( aIID.Equals(NS_GET_IID(_interface)) ) \
+ foundInterface = static_cast<_interface*>(this); \
+ else
+
+#define NS_IMPL_QUERY_BODY_CONDITIONAL(_interface, condition) \
+ if ( (condition) && aIID.Equals(NS_GET_IID(_interface))) \
+ foundInterface = static_cast<_interface*>(this); \
+ else
+
+#define NS_IMPL_QUERY_BODY_AMBIGUOUS(_interface, _implClass) \
+ if ( aIID.Equals(NS_GET_IID(_interface)) ) \
+ foundInterface = static_cast<_interface*>( \
+ static_cast<_implClass*>(this)); \
+ else
+
+#define NS_IMPL_QUERY_BODY_AGGREGATED(_interface, _aggregate) \
+ if ( aIID.Equals(NS_GET_IID(_interface)) ) \
+ foundInterface = static_cast<_interface*>(_aggregate); \
+ else
+
+#define NS_IMPL_QUERY_TAIL_GUTS \
+ foundInterface = 0; \
+ nsresult status; \
+ if ( !foundInterface ) \
+ { \
+ /* nsISupports should be handled by this point. If not, fail. */ \
+ MOZ_ASSERT(!aIID.Equals(NS_GET_IID(nsISupports))); \
+ status = NS_NOINTERFACE; \
+ } \
+ else \
+ { \
+ NS_ADDREF(foundInterface); \
+ status = NS_OK; \
+ } \
+ *aInstancePtr = foundInterface; \
+ return status; \
+}
+
+#define NS_IMPL_QUERY_TAIL_INHERITING(_baseclass) \
+ foundInterface = 0; \
+ nsresult status; \
+ if ( !foundInterface ) \
+ status = _baseclass::QueryInterface(aIID, (void**)&foundInterface); \
+ else \
+ { \
+ NS_ADDREF(foundInterface); \
+ status = NS_OK; \
+ } \
+ *aInstancePtr = foundInterface; \
+ return status; \
+}
+
+#define NS_IMPL_QUERY_TAIL_USING_AGGREGATOR(_aggregator) \
+ foundInterface = 0; \
+ nsresult status; \
+ if ( !foundInterface ) { \
+ NS_ASSERTION(_aggregator, "null aggregator"); \
+ status = _aggregator->QueryInterface(aIID, (void**)&foundInterface); \
+ } else \
+ { \
+ NS_ADDREF(foundInterface); \
+ status = NS_OK; \
+ } \
+ *aInstancePtr = foundInterface; \
+ return status; \
+}
+
+#define NS_IMPL_QUERY_TAIL(_supports_interface) \
+ NS_IMPL_QUERY_BODY_AMBIGUOUS(nsISupports, _supports_interface) \
+ NS_IMPL_QUERY_TAIL_GUTS
+
+
+/*
+ This is the new scheme. Using this notation now will allow us to switch to
+ a table driven mechanism when it's ready. Note the difference between this
+ and the (currently) underlying NS_IMPL_QUERY_INTERFACE mechanism. You must
+ explicitly mention |nsISupports| when using the interface maps.
+*/
+#define NS_INTERFACE_MAP_BEGIN(_implClass) NS_IMPL_QUERY_HEAD(_implClass)
+#define NS_INTERFACE_MAP_ENTRY(_interface) NS_IMPL_QUERY_BODY(_interface)
+#define NS_INTERFACE_MAP_ENTRY_CONDITIONAL(_interface, condition) \
+ NS_IMPL_QUERY_BODY_CONDITIONAL(_interface, condition)
+#define NS_INTERFACE_MAP_ENTRY_AGGREGATED(_interface,_aggregate) \
+ NS_IMPL_QUERY_BODY_AGGREGATED(_interface,_aggregate)
+
+#define NS_INTERFACE_MAP_END NS_IMPL_QUERY_TAIL_GUTS
+#define NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(_interface, _implClass) \
+ NS_IMPL_QUERY_BODY_AMBIGUOUS(_interface, _implClass)
+#define NS_INTERFACE_MAP_END_INHERITING(_baseClass) \
+ NS_IMPL_QUERY_TAIL_INHERITING(_baseClass)
+#define NS_INTERFACE_MAP_END_AGGREGATED(_aggregator) \
+ NS_IMPL_QUERY_TAIL_USING_AGGREGATOR(_aggregator)
+
+#define NS_INTERFACE_TABLE0(_class) \
+ NS_INTERFACE_TABLE_BEGIN \
+ NS_INTERFACE_TABLE_ENTRY(_class, nsISupports) \
+ NS_INTERFACE_TABLE_END
+
+#define NS_INTERFACE_TABLE(aClass, ...) \
+ MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \
+ NS_INTERFACE_TABLE_BEGIN \
+ MOZ_FOR_EACH(NS_INTERFACE_TABLE_ENTRY, (aClass,), (__VA_ARGS__)) \
+ NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(aClass, nsISupports, \
+ MOZ_ARG_1(__VA_ARGS__)) \
+ NS_INTERFACE_TABLE_END
+
+#define NS_IMPL_QUERY_INTERFACE0(_class) \
+ NS_INTERFACE_TABLE_HEAD(_class) \
+ NS_INTERFACE_TABLE0(_class) \
+ NS_INTERFACE_TABLE_TAIL
+
+#define NS_IMPL_QUERY_INTERFACE(aClass, ...) \
+ NS_INTERFACE_TABLE_HEAD(aClass) \
+ NS_INTERFACE_TABLE(aClass, __VA_ARGS__) \
+ NS_INTERFACE_TABLE_TAIL
+
+/**
+ * Declare that you're going to inherit from something that already
+ * implements nsISupports, but also implements an additional interface, thus
+ * causing an ambiguity. In this case you don't need another mRefCnt, you
+ * just need to forward the definitions to the appropriate superclass. E.g.
+ *
+ * class Bar : public Foo, public nsIBar { // both provide nsISupports
+ * public:
+ * NS_DECL_ISUPPORTS_INHERITED
+ * ...other nsIBar and Bar methods...
+ * };
+ */
+#define NS_DECL_ISUPPORTS_INHERITED \
+public: \
+ NS_IMETHOD QueryInterface(REFNSIID aIID, \
+ void** aInstancePtr) override; \
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override; \
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) override; \
+
+/**
+ * These macros can be used in conjunction with NS_DECL_ISUPPORTS_INHERITED
+ * to implement the nsISupports methods, forwarding the invocations to a
+ * superclass that already implements nsISupports.
+ *
+ * Note that I didn't make these inlined because they're virtual methods.
+ */
+
+#define NS_IMPL_ADDREF_INHERITED(Class, Super) \
+NS_IMETHODIMP_(MozExternalRefCountType) Class::AddRef(void) \
+{ \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(Class) \
+ nsrefcnt r = Super::AddRef(); \
+ NS_LOG_ADDREF(this, r, #Class, sizeof(*this)); \
+ return r; \
+}
+
+#define NS_IMPL_RELEASE_INHERITED(Class, Super) \
+NS_IMETHODIMP_(MozExternalRefCountType) Class::Release(void) \
+{ \
+ nsrefcnt r = Super::Release(); \
+ NS_LOG_RELEASE(this, r, #Class); \
+ return r; \
+}
+
+/**
+ * As above but not logging the addref/release; needed if the base
+ * class might be aggregated.
+ */
+#define NS_IMPL_NONLOGGING_ADDREF_INHERITED(Class, Super) \
+NS_IMETHODIMP_(MozExternalRefCountType) Class::AddRef(void) \
+{ \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(Class) \
+ return Super::AddRef(); \
+}
+
+#define NS_IMPL_NONLOGGING_RELEASE_INHERITED(Class, Super) \
+NS_IMETHODIMP_(MozExternalRefCountType) Class::Release(void) \
+{ \
+ return Super::Release(); \
+}
+
+#define NS_INTERFACE_TABLE_INHERITED0(Class) /* Nothing to do here */
+
+#define NS_INTERFACE_TABLE_INHERITED(aClass, ...) \
+ MOZ_STATIC_ASSERT_VALID_ARG_COUNT(__VA_ARGS__); \
+ NS_INTERFACE_TABLE_BEGIN \
+ MOZ_FOR_EACH(NS_INTERFACE_TABLE_ENTRY, (aClass,), (__VA_ARGS__)) \
+ NS_INTERFACE_TABLE_END
+
+#define NS_IMPL_QUERY_INTERFACE_INHERITED(aClass, aSuper, ...) \
+ NS_INTERFACE_TABLE_HEAD(aClass) \
+ NS_INTERFACE_TABLE_INHERITED(aClass, __VA_ARGS__) \
+ NS_INTERFACE_TABLE_TAIL_INHERITING(aSuper)
+
+/**
+ * Convenience macros for implementing all nsISupports methods for
+ * a simple class.
+ * @param _class The name of the class implementing the method
+ * @param _classiiddef The name of the #define symbol that defines the IID
+ * for the class (e.g. NS_ISUPPORTS_IID)
+ */
+
+#define NS_IMPL_ISUPPORTS0(_class) \
+ NS_IMPL_ADDREF(_class) \
+ NS_IMPL_RELEASE(_class) \
+ NS_IMPL_QUERY_INTERFACE0(_class)
+
+#define NS_IMPL_ISUPPORTS(aClass, ...) \
+ NS_IMPL_ADDREF(aClass) \
+ NS_IMPL_RELEASE(aClass) \
+ NS_IMPL_QUERY_INTERFACE(aClass, __VA_ARGS__)
+
+#define NS_IMPL_ISUPPORTS_INHERITED0(aClass, aSuper) \
+ NS_INTERFACE_TABLE_HEAD(aClass) \
+ NS_INTERFACE_TABLE_TAIL_INHERITING(aSuper) \
+ NS_IMPL_ADDREF_INHERITED(aClass, aSuper) \
+ NS_IMPL_RELEASE_INHERITED(aClass, aSuper) \
+
+#define NS_IMPL_ISUPPORTS_INHERITED(aClass, aSuper, ...) \
+ NS_IMPL_QUERY_INTERFACE_INHERITED(aClass, aSuper, __VA_ARGS__) \
+ NS_IMPL_ADDREF_INHERITED(aClass, aSuper) \
+ NS_IMPL_RELEASE_INHERITED(aClass, aSuper)
+
+/*
+ * Macro to glue together a QI that starts with an interface table
+ * and segues into an interface map (e.g. it uses singleton classinfo
+ * or tearoffs).
+ */
+#define NS_INTERFACE_TABLE_TO_MAP_SEGUE \
+ if (rv == NS_OK) return rv; \
+ nsISupports* foundInterface;
+
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ *
+ * Threadsafe implementations of the ISupports convenience macros.
+ *
+ * @note These are not available when linking against the standalone glue,
+ * because the implementation requires PR_ symbols.
+ */
+#define NS_INTERFACE_MAP_END_THREADSAFE NS_IMPL_QUERY_TAIL_GUTS
+
+/**
+ * Macro to generate nsIClassInfo methods for classes which do not have
+ * corresponding nsIFactory implementations.
+ */
+#define NS_IMPL_THREADSAFE_CI(_class) \
+NS_IMETHODIMP \
+_class::GetInterfaces(uint32_t* _count, nsIID*** _array) \
+{ \
+ return NS_CI_INTERFACE_GETTER_NAME(_class)(_count, _array); \
+} \
+ \
+NS_IMETHODIMP \
+_class::GetScriptableHelper(nsIXPCScriptable** _retval) \
+{ \
+ *_retval = nullptr; \
+ return NS_OK; \
+} \
+ \
+NS_IMETHODIMP \
+_class::GetContractID(char** _contractID) \
+{ \
+ *_contractID = nullptr; \
+ return NS_OK; \
+} \
+ \
+NS_IMETHODIMP \
+_class::GetClassDescription(char** _classDescription) \
+{ \
+ *_classDescription = nullptr; \
+ return NS_OK; \
+} \
+ \
+NS_IMETHODIMP \
+_class::GetClassID(nsCID** _classID) \
+{ \
+ *_classID = nullptr; \
+ return NS_OK; \
+} \
+ \
+NS_IMETHODIMP \
+_class::GetFlags(uint32_t* _flags) \
+{ \
+ *_flags = nsIClassInfo::THREADSAFE; \
+ return NS_OK; \
+} \
+ \
+NS_IMETHODIMP \
+_class::GetClassIDNoAlloc(nsCID* _classIDNoAlloc) \
+{ \
+ return NS_ERROR_NOT_AVAILABLE; \
+}
+
+#endif
diff --git a/xpcom/glue/nsISupportsUtils.h b/xpcom/glue/nsISupportsUtils.h
new file mode 100644
index 000000000..4d306d3d1
--- /dev/null
+++ b/xpcom/glue/nsISupportsUtils.h
@@ -0,0 +1,145 @@
+/* -*- 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 nsISupportsUtils_h__
+#define nsISupportsUtils_h__
+
+#include "nscore.h"
+#include "nsISupportsBase.h"
+#include "nsError.h"
+#include "nsDebug.h"
+#include "nsISupportsImpl.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TypeTraits.h"
+
+/**
+ * Macro for adding a reference to an interface.
+ * @param _ptr The interface pointer.
+ */
+#define NS_ADDREF(_ptr) \
+ (_ptr)->AddRef()
+
+/**
+ * Macro for adding a reference to this. This macro should be used
+ * because NS_ADDREF (when tracing) may require an ambiguous cast
+ * from the pointers primary type to nsISupports. This macro sidesteps
+ * that entire problem.
+ */
+#define NS_ADDREF_THIS() \
+ AddRef()
+
+
+// Making this a |inline| |template| allows |aExpr| to be evaluated only once,
+// yet still denies you the ability to |AddRef()| an |nsCOMPtr|.
+template<class T>
+inline void
+ns_if_addref(T aExpr)
+{
+ if (aExpr) {
+ aExpr->AddRef();
+ }
+}
+
+/**
+ * Macro for adding a reference to an interface that checks for nullptr.
+ * @param _expr The interface pointer.
+ */
+#define NS_IF_ADDREF(_expr) ns_if_addref(_expr)
+
+/*
+ * Given these declarations, it explicitly OK and efficient to end a `getter' with:
+ *
+ * NS_IF_ADDREF(*result = mThing);
+ *
+ * even if |mThing| is an |nsCOMPtr|. If |mThing| is an |nsCOMPtr|, however, it is still
+ * _illegal_ to say |NS_IF_ADDREF(mThing)|.
+ */
+
+/**
+ * Macro for releasing a reference to an interface.
+ * @param _ptr The interface pointer.
+ */
+#define NS_RELEASE(_ptr) \
+ do { \
+ (_ptr)->Release(); \
+ (_ptr) = 0; \
+ } while (0)
+
+/**
+ * Macro for releasing a reference to this interface.
+ */
+#define NS_RELEASE_THIS() \
+ Release()
+
+/**
+ * Macro for releasing a reference to an interface, except that this
+ * macro preserves the return value from the underlying Release call.
+ * The interface pointer argument will only be NULLed if the reference count
+ * goes to zero.
+ *
+ * @param _ptr The interface pointer.
+ * @param _rc The reference count.
+ */
+#define NS_RELEASE2(_ptr, _rc) \
+ do { \
+ _rc = (_ptr)->Release(); \
+ if (0 == (_rc)) (_ptr) = 0; \
+ } while (0)
+
+/**
+ * Macro for releasing a reference to an interface that checks for nullptr;
+ * @param _ptr The interface pointer.
+ */
+#define NS_IF_RELEASE(_ptr) \
+ do { \
+ if (_ptr) { \
+ (_ptr)->Release(); \
+ (_ptr) = 0; \
+ } \
+ } while (0)
+
+/*
+ * Often you have to cast an implementation pointer, e.g., |this|, to an
+ * |nsISupports*|, but because you have multiple inheritance, a simple cast
+ * is ambiguous. One could simply say, e.g., (given a base |nsIBase|),
+ * |static_cast<nsIBase*>(this)|; but that disguises the fact that what
+ * you are really doing is disambiguating the |nsISupports|. You could make
+ * that more obvious with a double cast, e.g., |static_cast<nsISupports*>
+ (* static_cast<nsIBase*>(this))|, but that is bulky and harder to read...
+ *
+ * The following macro is clean, short, and obvious. In the example above,
+ * you would use it like this: |NS_ISUPPORTS_CAST(nsIBase*, this)|.
+ */
+
+#define NS_ISUPPORTS_CAST(__unambiguousBase, __expr) \
+ static_cast<nsISupports*>(static_cast<__unambiguousBase>(__expr))
+
+// a type-safe shortcut for calling the |QueryInterface()| member function
+template<class T, class DestinationType>
+inline nsresult
+CallQueryInterface(T* aSource, DestinationType** aDestination)
+{
+ // We permit nsISupports-to-nsISupports here so that one can still obtain
+ // the canonical nsISupports pointer with CallQueryInterface.
+ static_assert(!mozilla::IsSame<T, DestinationType>::value ||
+ mozilla::IsSame<DestinationType, nsISupports>::value,
+ "don't use CallQueryInterface for compile-time-determinable casts");
+
+ NS_PRECONDITION(aSource, "null parameter");
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return aSource->QueryInterface(NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template <class SourceType, class DestinationType>
+inline nsresult
+CallQueryInterface(RefPtr<SourceType>& aSourcePtr, DestinationType** aDestPtr)
+{
+ return CallQueryInterface(aSourcePtr.get(), aDestPtr);
+}
+
+#endif /* __nsISupportsUtils_h */
diff --git a/xpcom/glue/nsIWeakReferenceUtils.h b/xpcom/glue/nsIWeakReferenceUtils.h
new file mode 100644
index 000000000..1c84e00df
--- /dev/null
+++ b/xpcom/glue/nsIWeakReferenceUtils.h
@@ -0,0 +1,102 @@
+/* -*- 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 nsIWeakReferenceUtils_h__
+#define nsIWeakReferenceUtils_h__
+
+#include "nsCOMPtr.h"
+#include "nsIWeakReference.h"
+
+typedef nsCOMPtr<nsIWeakReference> nsWeakPtr;
+
+/**
+ *
+ */
+
+// a type-safe shortcut for calling the |QueryReferent()| member function
+// T must inherit from nsIWeakReference, but the cast may be ambiguous.
+template<class T, class DestinationType>
+inline nsresult
+CallQueryReferent(T* aSource, DestinationType** aDestination)
+{
+ NS_PRECONDITION(aSource, "null parameter");
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return aSource->QueryReferent(NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+
+class MOZ_STACK_CLASS nsQueryReferent final : public nsCOMPtr_helper
+{
+public:
+ nsQueryReferent(nsIWeakReference* aWeakPtr, nsresult* aError)
+ : mWeakPtr(aWeakPtr)
+ , mErrorPtr(aError)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID& aIID, void**) const
+ override;
+
+private:
+ nsIWeakReference* MOZ_NON_OWNING_REF mWeakPtr;
+ nsresult* mErrorPtr;
+};
+
+inline const nsQueryReferent
+do_QueryReferent(nsIWeakReference* aRawPtr, nsresult* aError = 0)
+{
+ return nsQueryReferent(aRawPtr, aError);
+}
+
+
+/**
+ * Deprecated, use |do_GetWeakReference| instead.
+ */
+extern nsIWeakReference* NS_GetWeakReference(nsISupports*,
+ nsresult* aResult = 0);
+
+/**
+ * |do_GetWeakReference| is a convenience function that bundles up all the work needed
+ * to get a weak reference to an arbitrary object, i.e., the |QueryInterface|, test, and
+ * call through to |GetWeakReference|, and put it into your |nsCOMPtr|.
+ * It is specifically designed to cooperate with |nsCOMPtr| (or |nsWeakPtr|) like so:
+ * |nsWeakPtr myWeakPtr = do_GetWeakReference(aPtr);|.
+ */
+inline already_AddRefed<nsIWeakReference>
+do_GetWeakReference(nsISupports* aRawPtr, nsresult* aError = 0)
+{
+ return dont_AddRef(NS_GetWeakReference(aRawPtr, aError));
+}
+
+inline void
+do_GetWeakReference(nsIWeakReference* aRawPtr, nsresult* aError = 0)
+{
+ // This signature exists solely to _stop_ you from doing a bad thing.
+ // Saying |do_GetWeakReference()| on a weak reference itself,
+ // is very likely to be a programmer error.
+}
+
+template<class T>
+inline void
+do_GetWeakReference(already_AddRefed<T>&)
+{
+ // This signature exists solely to _stop_ you from doing the bad thing.
+ // Saying |do_GetWeakReference()| on a pointer that is not otherwise owned by
+ // someone else is an automatic leak. See <http://bugzilla.mozilla.org/show_bug.cgi?id=8221>.
+}
+
+template<class T>
+inline void
+do_GetWeakReference(already_AddRefed<T>&, nsresult*)
+{
+ // This signature exists solely to _stop_ you from doing the bad thing.
+ // Saying |do_GetWeakReference()| on a pointer that is not otherwise owned by
+ // someone else is an automatic leak. See <http://bugzilla.mozilla.org/show_bug.cgi?id=8221>.
+}
+
+#endif
diff --git a/xpcom/glue/nsInterfaceHashtable.h b/xpcom/glue/nsInterfaceHashtable.h
new file mode 100644
index 000000000..14368af63
--- /dev/null
+++ b/xpcom/glue/nsInterfaceHashtable.h
@@ -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/. */
+
+#ifndef nsInterfaceHashtable_h__
+#define nsInterfaceHashtable_h__
+
+#include "nsBaseHashtable.h"
+#include "nsHashKeys.h"
+#include "nsCOMPtr.h"
+
+/**
+ * templated hashtable class maps keys to interface pointers.
+ * See nsBaseHashtable for complete declaration.
+ * @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h
+ * for a complete specification.
+ * @param Interface the interface-type being wrapped
+ * @see nsDataHashtable, nsClassHashtable
+ */
+template<class KeyClass, class Interface>
+class nsInterfaceHashtable
+ : public nsBaseHashtable<KeyClass, nsCOMPtr<Interface>, Interface*>
+{
+public:
+ typedef typename KeyClass::KeyType KeyType;
+ typedef Interface* UserDataType;
+ typedef nsBaseHashtable<KeyClass, nsCOMPtr<Interface>, Interface*> base_type;
+
+ nsInterfaceHashtable() {}
+ explicit nsInterfaceHashtable(uint32_t aInitLength)
+ : nsBaseHashtable<KeyClass, nsCOMPtr<Interface>, Interface*>(aInitLength)
+ {
+ }
+
+ /**
+ * @copydoc nsBaseHashtable::Get
+ * @param aData This is an XPCOM getter, so aData is already_addrefed.
+ * If the key doesn't exist, aData will be set to nullptr.
+ */
+ bool Get(KeyType aKey, UserDataType* aData) const;
+
+ /**
+ * @copydoc nsBaseHashtable::Get
+ */
+ already_AddRefed<Interface> Get(KeyType aKey) const;
+
+ /**
+ * Gets a weak reference to the hashtable entry.
+ * @param aFound If not nullptr, will be set to true if the entry is found,
+ * to false otherwise.
+ * @return The entry, or nullptr if not found. Do not release this pointer!
+ */
+ Interface* GetWeak(KeyType aKey, bool* aFound = nullptr) const;
+};
+
+template<typename K, typename T>
+inline void
+ImplCycleCollectionUnlink(nsInterfaceHashtable<K, T>& aField)
+{
+ aField.Clear();
+}
+
+template<typename K, typename T>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ const nsInterfaceHashtable<K, T>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ for (auto iter = aField.ConstIter(); !iter.Done(); iter.Next()) {
+ CycleCollectionNoteChild(aCallback, iter.UserData(), aName, aFlags);
+ }
+}
+
+//
+// nsInterfaceHashtable definitions
+//
+
+template<class KeyClass, class Interface>
+bool
+nsInterfaceHashtable<KeyClass, Interface>::Get(KeyType aKey,
+ UserDataType* aInterface) const
+{
+ typename base_type::EntryType* ent = this->GetEntry(aKey);
+
+ if (ent) {
+ if (aInterface) {
+ *aInterface = ent->mData;
+
+ NS_IF_ADDREF(*aInterface);
+ }
+
+ return true;
+ }
+
+ // if the key doesn't exist, set *aInterface to null
+ // so that it is a valid XPCOM getter
+ if (aInterface) {
+ *aInterface = nullptr;
+ }
+
+ return false;
+}
+
+template<class KeyClass, class Interface>
+already_AddRefed<Interface>
+nsInterfaceHashtable<KeyClass, Interface>::Get(KeyType aKey) const
+{
+ typename base_type::EntryType* ent = this->GetEntry(aKey);
+ if (!ent) {
+ return nullptr;
+ }
+
+ nsCOMPtr<Interface> copy = ent->mData;
+ return copy.forget();
+}
+
+template<class KeyClass, class Interface>
+Interface*
+nsInterfaceHashtable<KeyClass, Interface>::GetWeak(KeyType aKey,
+ bool* aFound) const
+{
+ typename base_type::EntryType* ent = this->GetEntry(aKey);
+
+ if (ent) {
+ if (aFound) {
+ *aFound = true;
+ }
+
+ return ent->mData;
+ }
+
+ // Key does not exist, return nullptr and set aFound to false
+ if (aFound) {
+ *aFound = false;
+ }
+ return nullptr;
+}
+
+#endif // nsInterfaceHashtable_h__
diff --git a/xpcom/glue/nsJSThingHashtable.h b/xpcom/glue/nsJSThingHashtable.h
new file mode 100644
index 000000000..8c1b1447d
--- /dev/null
+++ b/xpcom/glue/nsJSThingHashtable.h
@@ -0,0 +1,61 @@
+/* -*- 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 nsJSThingHashtable_h__
+#define nsJSThingHashtable_h__
+
+#include "nsHashKeys.h"
+#include "nsBaseHashtable.h"
+
+namespace JS {
+template<class T>
+class Heap;
+} /* namespace JS */
+
+/**
+ * A wrapper for hash keys that sets ALLOW_MEMMOVE to false.
+ *
+ * This is used in the implementation of nsJSThingHashtable and is not intended
+ * to be used directly.
+ *
+ * It is necessary for hash tables containing JS::Heap<T> values as these must
+ * be copied rather than memmoved.
+ */
+template<class T>
+class nsHashKeyDisallowMemmove : public T
+{
+public:
+ explicit nsHashKeyDisallowMemmove(const typename T::KeyTypePointer aKey) : T(aKey) {}
+ enum { ALLOW_MEMMOVE = false };
+};
+
+
+/**
+ * Templated hashtable class for use on the heap where the values are JS GC things.
+ *
+ * Storing JS GC thing pointers on the heap requires wrapping them in a
+ * JS::Heap<T>, and this class takes care of that while presenting an interface
+ * in terms of the wrapped type T.
+ *
+ * For example, to store a hashtable mapping strings to JSObject pointers, you
+ * can declare a data member like this:
+ *
+ * nsJSThingHashtable<nsStringHashKey, JSObject*> mStringToObjectMap;
+ *
+ * See nsBaseHashtable for complete declaration
+ * @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h
+ * for a complete specification.
+ * @param DataType the datatype being wrapped, must be a JS GC thing.
+ * @see nsInterfaceHashtable, nsClassHashtable
+ */
+template<class KeyClass, class DataType>
+class nsJSThingHashtable
+ : public nsBaseHashtable<nsHashKeyDisallowMemmove<KeyClass>,
+ JS::Heap<DataType>, DataType>
+{
+};
+
+#endif // nsJSThingHashtable_h__
diff --git a/xpcom/glue/nsMemory.cpp b/xpcom/glue/nsMemory.cpp
new file mode 100644
index 000000000..3bf7c1f0f
--- /dev/null
+++ b/xpcom/glue/nsMemory.cpp
@@ -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/. */
+
+#include "nsXPCOM.h"
+#include "nsMemory.h"
+#include "nsIMemory.h"
+#include "nsXPCOMPrivate.h"
+#include "nsDebug.h"
+#include "nsISupportsUtils.h"
+#include "nsCOMPtr.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// nsMemory static helper routines
+
+nsresult
+nsMemory::HeapMinimize(bool aImmediate)
+{
+ nsCOMPtr<nsIMemory> mem;
+ nsresult rv = NS_GetMemoryManager(getter_AddRefs(mem));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return mem->HeapMinimize(aImmediate);
+}
+
+void*
+nsMemory::Clone(const void* aPtr, size_t aSize)
+{
+ void* newPtr = NS_Alloc(aSize);
+ if (newPtr) {
+ memcpy(newPtr, aPtr, aSize);
+ }
+ return newPtr;
+}
+
+nsIMemory*
+nsMemory::GetGlobalMemoryService()
+{
+ nsIMemory* mem;
+ nsresult rv = NS_GetMemoryManager(&mem);
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ return mem;
+}
+
+//----------------------------------------------------------------------
+
diff --git a/xpcom/glue/nsMemory.h b/xpcom/glue/nsMemory.h
new file mode 100644
index 000000000..6f19b8117
--- /dev/null
+++ b/xpcom/glue/nsMemory.h
@@ -0,0 +1,136 @@
+/* -*- 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 nsMemory_h__
+#define nsMemory_h__
+
+#include "nsXPCOM.h"
+
+class nsIMemory;
+
+#define NS_MEMORY_CONTRACTID "@mozilla.org/xpcom/memory-service;1"
+#define NS_MEMORY_CID \
+{ /* 30a04e40-38e7-11d4-8cf5-0060b0fc14a3 */ \
+ 0x30a04e40, \
+ 0x38e7, \
+ 0x11d4, \
+ {0x8c, 0xf5, 0x00, 0x60, 0xb0, 0xfc, 0x14, 0xa3} \
+}
+
+
+/**
+ * Static helper routines to manage memory. These routines allow easy access
+ * to xpcom's built-in (global) nsIMemory implementation, without needing
+ * to go through the service manager to get it. However this requires clients
+ * to link with the xpcom DLL.
+ *
+ * This class is not threadsafe and is intented for use only on the main
+ * thread.
+ */
+class nsMemory
+{
+public:
+ static nsresult HeapMinimize(bool aImmediate);
+ static void* Clone(const void* aPtr, size_t aSize);
+ static nsIMemory* GetGlobalMemoryService(); // AddRefs
+};
+
+/**
+ * Macro to free all elements of an XPCOM array of a given size using
+ * freeFunc, then frees the array itself using free().
+ *
+ * Note that this macro (and its wrappers) can be used to deallocate a
+ * partially- or completely-built array while unwinding an error
+ * condition inside the XPCOM routine that was going to return the
+ * array. For this to work on a partially-built array, your code
+ * needs to be building the array from index 0 upwards, and simply
+ * pass the number of elements that have already been built (and thus
+ * need to be freed) as |size|.
+ *
+ * Thanks to <alecf@netscape.com> for suggesting this form, which
+ * allows the macro to be used with NS_RELEASE / NS_RELEASE_IF in
+ * addition to free.
+ *
+ * @param size Number of elements in the array. If not a constant, this
+ * should be a int32_t. Note that this means this macro
+ * will not work if size >= 2^31.
+ * @param array The array to be freed.
+ * @param freeFunc The function or macro to be used to free it.
+ * For arrays of nsISupports (or any class derived
+ * from it), NS_IF_RELEASE (or NS_RELEASE) should be
+ * passed as freeFunc. For most (all?) other pointer
+ * types (including XPCOM strings and wstrings),
+ * free should be used.
+ */
+#define NS_FREE_XPCOM_POINTER_ARRAY(size, array, freeFunc) \
+ PR_BEGIN_MACRO \
+ int32_t iter_ = int32_t(size); \
+ while (--iter_ >= 0) \
+ freeFunc((array)[iter_]); \
+ NS_Free((array)); \
+ PR_END_MACRO
+
+// convenience macros for commonly used calls. mmmmm. syntactic sugar.
+
+/**
+ * Macro to free arrays of non-refcounted objects allocated by the
+ * shared allocator (nsMemory) such as strings and wstrings. A
+ * convenience wrapper around NS_FREE_XPCOM_POINTER_ARRAY.
+ *
+ * @param size Number of elements in the array. If not a constant, this
+ * should be a int32_t. Note that this means this macro
+ * will not work if size >= 2^31.
+ * @param array The array to be freed.
+ */
+#define NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(size, array) \
+ NS_FREE_XPCOM_POINTER_ARRAY((size), (array), NS_Free)
+
+/**
+ * Macro to free an array of pointers to nsISupports (or classes
+ * derived from it). A convenience wrapper around
+ * NS_FREE_XPCOM_POINTER_ARRAY.
+ *
+ * Note that if you know that none of your nsISupports pointers are
+ * going to be 0, you can gain a bit of speed by calling
+ * NS_FREE_XPCOM_POINTER_ARRAY directly and using NS_RELEASE as your
+ * free function.
+ *
+ * @param size Number of elements in the array. If not a constant, this
+ * should be a int32_t. Note that this means this macro
+ * will not work if size >= 2^31.
+ * @param array The array to be freed.
+ */
+#define NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(size, array) \
+ NS_FREE_XPCOM_POINTER_ARRAY((size), (array), NS_IF_RELEASE)
+
+/**
+ * A macro, NS_ALIGNMENT_OF(t_) that determines the alignment
+ * requirements of a type.
+ */
+namespace mozilla {
+template<class T>
+struct AlignmentTestStruct
+{
+ char c;
+ T t;
+};
+} // namespace mozilla
+
+#define NS_ALIGNMENT_OF(t_) \
+ (sizeof(mozilla::AlignmentTestStruct<t_>) - sizeof(t_))
+
+/**
+ * An enumeration type used to represent a method of assignment.
+ */
+enum nsAssignmentType
+{
+ NS_ASSIGNMENT_COPY, // copy by value
+ NS_ASSIGNMENT_DEPEND, // copy by reference
+ NS_ASSIGNMENT_ADOPT // copy by reference (take ownership of resource)
+};
+
+#endif // nsMemory_h__
+
diff --git a/xpcom/glue/nsPointerHashKeys.h b/xpcom/glue/nsPointerHashKeys.h
new file mode 100644
index 000000000..a89843101
--- /dev/null
+++ b/xpcom/glue/nsPointerHashKeys.h
@@ -0,0 +1,48 @@
+/* -*- 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/. */
+
+/* Definitions for nsPtrHashKey<T> and nsVoidPtrHashKey. */
+
+#ifndef nsPointerHashKeys_h
+#define nsPointerHashKeys_h
+
+#include "nscore.h"
+
+#include "mozilla/Attributes.h"
+
+/**
+ * hashkey wrapper using T* KeyType
+ *
+ * @see nsTHashtable::EntryType for specification
+ */
+template<class T>
+class nsPtrHashKey : public PLDHashEntryHdr
+{
+public:
+ typedef T* KeyType;
+ typedef const T* KeyTypePointer;
+
+ explicit nsPtrHashKey(const T* aKey) : mKey(const_cast<T*>(aKey)) {}
+ nsPtrHashKey(const nsPtrHashKey<T>& aToCopy) : mKey(aToCopy.mKey) {}
+ ~nsPtrHashKey() {}
+
+ KeyType GetKey() const { return mKey; }
+ bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return NS_PTR_TO_UINT32(aKey) >> 2;
+ }
+ enum { ALLOW_MEMMOVE = true };
+
+protected:
+ T* MOZ_NON_OWNING_REF mKey;
+};
+
+typedef nsPtrHashKey<const void> nsVoidPtrHashKey;
+
+#endif // nsPointerHashKeys_h
diff --git a/xpcom/glue/nsProxyRelease.cpp b/xpcom/glue/nsProxyRelease.cpp
new file mode 100644
index 000000000..1a8150cc6
--- /dev/null
+++ b/xpcom/glue/nsProxyRelease.cpp
@@ -0,0 +1,21 @@
+/* -*- 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 "nsProxyRelease.h"
+#include "nsThreadUtils.h"
+
+namespace detail {
+
+/* static */ void
+ProxyReleaseChooser<true>::ProxyReleaseISupports(nsIEventTarget* aTarget,
+ nsISupports* aDoomed,
+ bool aAlwaysProxy)
+{
+ ::detail::ProxyRelease<nsISupports>(aTarget, dont_AddRef(aDoomed),
+ aAlwaysProxy);
+}
+
+} // namespace detail
diff --git a/xpcom/glue/nsProxyRelease.h b/xpcom/glue/nsProxyRelease.h
new file mode 100644
index 000000000..d99f970b9
--- /dev/null
+++ b/xpcom/glue/nsProxyRelease.h
@@ -0,0 +1,353 @@
+/* -*- 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 nsProxyRelease_h__
+#define nsProxyRelease_h__
+
+#include "nsIEventTarget.h"
+#include "nsIThread.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "MainThreadUtils.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Likely.h"
+#include "mozilla/Move.h"
+#include "mozilla/TypeTraits.h"
+#include "mozilla/Unused.h"
+
+#ifdef XPCOM_GLUE_AVOID_NSPR
+#error NS_ProxyRelease implementation depends on NSPR.
+#endif
+
+namespace detail {
+
+template<typename T>
+class ProxyReleaseEvent : public mozilla::Runnable
+{
+public:
+ explicit ProxyReleaseEvent(already_AddRefed<T> aDoomed)
+ : mDoomed(aDoomed.take()) {}
+
+ NS_IMETHOD Run() override
+ {
+ NS_IF_RELEASE(mDoomed);
+ return NS_OK;
+ }
+
+private:
+ T* MOZ_OWNING_REF mDoomed;
+};
+
+template<typename T>
+void
+ProxyRelease(nsIEventTarget* aTarget, already_AddRefed<T> aDoomed, bool aAlwaysProxy)
+{
+ // Auto-managing release of the pointer.
+ RefPtr<T> doomed = aDoomed;
+ nsresult rv;
+
+ if (!doomed || !aTarget) {
+ return;
+ }
+
+ if (!aAlwaysProxy) {
+ bool onCurrentThread = false;
+ rv = aTarget->IsOnCurrentThread(&onCurrentThread);
+ if (NS_SUCCEEDED(rv) && onCurrentThread) {
+ return;
+ }
+ }
+
+ nsCOMPtr<nsIRunnable> ev = new ProxyReleaseEvent<T>(doomed.forget());
+
+ rv = aTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("failed to post proxy release event, leaking!");
+ // It is better to leak the aDoomed object than risk crashing as
+ // a result of deleting it on the wrong thread.
+ }
+}
+
+template<bool nsISupportsBased>
+struct ProxyReleaseChooser
+{
+ template<typename T>
+ static void ProxyRelease(nsIEventTarget* aTarget,
+ already_AddRefed<T> aDoomed,
+ bool aAlwaysProxy)
+ {
+ ::detail::ProxyRelease(aTarget, mozilla::Move(aDoomed), aAlwaysProxy);
+ }
+};
+
+template<>
+struct ProxyReleaseChooser<true>
+{
+ // We need an intermediate step for handling classes with ambiguous
+ // inheritance to nsISupports.
+ template<typename T>
+ static void ProxyRelease(nsIEventTarget* aTarget,
+ already_AddRefed<T> aDoomed,
+ bool aAlwaysProxy)
+ {
+ ProxyReleaseISupports(aTarget, ToSupports(aDoomed.take()), aAlwaysProxy);
+ }
+
+ static void ProxyReleaseISupports(nsIEventTarget* aTarget,
+ nsISupports* aDoomed,
+ bool aAlwaysProxy);
+};
+
+} // namespace detail
+
+/**
+ * Ensures that the delete of a smart pointer occurs on the target thread.
+ *
+ * @param aTarget
+ * the target thread where the doomed object should be released.
+ * @param aDoomed
+ * the doomed object; the object to be released on the target thread.
+ * @param aAlwaysProxy
+ * normally, if NS_ProxyRelease is called on the target thread, then the
+ * doomed object will be released directly. However, if this parameter is
+ * true, then an event will always be posted to the target thread for
+ * asynchronous release.
+ */
+template<class T>
+inline NS_HIDDEN_(void)
+NS_ProxyRelease(nsIEventTarget* aTarget, already_AddRefed<T> aDoomed,
+ bool aAlwaysProxy = false)
+{
+ ::detail::ProxyReleaseChooser<mozilla::IsBaseOf<nsISupports, T>::value>
+ ::ProxyRelease(aTarget, mozilla::Move(aDoomed), aAlwaysProxy);
+}
+
+/**
+ * Ensures that the delete of a smart pointer occurs on the main thread.
+ *
+ * @param aDoomed
+ * the doomed object; the object to be released on the main thread.
+ * @param aAlwaysProxy
+ * normally, if NS_ReleaseOnMainThread is called on the main thread,
+ * then the doomed object will be released directly. However, if this
+ * parameter is true, then an event will always be posted to the main
+ * thread for asynchronous release.
+ */
+template<class T>
+inline NS_HIDDEN_(void)
+NS_ReleaseOnMainThread(already_AddRefed<T> aDoomed,
+ bool aAlwaysProxy = false)
+{
+ // NS_ProxyRelease treats a null event target as "the current thread". So a
+ // handle on the main thread is only necessary when we're not already on the
+ // main thread or the release must happen asynchronously.
+ nsCOMPtr<nsIThread> mainThread;
+ if (!NS_IsMainThread() || aAlwaysProxy) {
+ nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
+
+ if (NS_FAILED(rv)) {
+ MOZ_ASSERT_UNREACHABLE("Could not get main thread; leaking an object!");
+ mozilla::Unused << aDoomed.take();
+ return;
+ }
+ }
+
+ NS_ProxyRelease(mainThread, mozilla::Move(aDoomed), aAlwaysProxy);
+}
+
+/**
+ * Class to safely handle main-thread-only pointers off the main thread.
+ *
+ * Classes like XPCWrappedJS are main-thread-only, which means that it is
+ * forbidden to call methods on instances of these classes off the main thread.
+ * For various reasons (see bug 771074), this restriction recently began to
+ * apply to AddRef/Release as well.
+ *
+ * This presents a problem for consumers that wish to hold a callback alive
+ * on non-main-thread code. A common example of this is the proxy callback
+ * pattern, where non-main-thread code holds a strong-reference to the callback
+ * object, and dispatches new Runnables (also with a strong reference) to the
+ * main thread in order to execute the callback. This involves several AddRef
+ * and Release calls on the other thread, which is (now) verboten.
+ *
+ * The basic idea of this class is to introduce a layer of indirection.
+ * nsMainThreadPtrHolder is a threadsafe reference-counted class that internally
+ * maintains one strong reference to the main-thread-only object. It must be
+ * instantiated on the main thread (so that the AddRef of the underlying object
+ * happens on the main thread), but consumers may subsequently pass references
+ * to the holder anywhere they please. These references are meant to be opaque
+ * when accessed off-main-thread (assertions enforce this).
+ *
+ * The semantics of RefPtr<nsMainThreadPtrHolder<T> > would be cumbersome, so
+ * we also introduce nsMainThreadPtrHandle<T>, which is conceptually identical
+ * to the above (though it includes various convenience methods). The basic
+ * pattern is as follows.
+ *
+ * // On the main thread:
+ * nsCOMPtr<nsIFooCallback> callback = ...;
+ * nsMainThreadPtrHandle<nsIFooCallback> callbackHandle =
+ * new nsMainThreadPtrHolder<nsIFooCallback>(callback);
+ * // Pass callbackHandle to structs/classes that might be accessed on other
+ * // threads.
+ *
+ * All structs and classes that might be accessed on other threads should store
+ * an nsMainThreadPtrHandle<T> rather than an nsCOMPtr<T>.
+ */
+template<class T>
+class nsMainThreadPtrHolder final
+{
+public:
+ // We can only acquire a pointer on the main thread. We to fail fast for
+ // threading bugs, so by default we assert if our pointer is used or acquired
+ // off-main-thread. But some consumers need to use the same pointer for
+ // multiple classes, some of which are main-thread-only and some of which
+ // aren't. So we allow them to explicitly disable this strict checking.
+ explicit nsMainThreadPtrHolder(T* aPtr, bool aStrict = true)
+ : mRawPtr(nullptr)
+ , mStrict(aStrict)
+ {
+ // We can only AddRef our pointer on the main thread, which means that the
+ // holder must be constructed on the main thread.
+ MOZ_ASSERT(!mStrict || NS_IsMainThread());
+ NS_IF_ADDREF(mRawPtr = aPtr);
+ }
+ explicit nsMainThreadPtrHolder(already_AddRefed<T> aPtr, bool aString = true)
+ : mRawPtr(aPtr.take())
+ , mStrict(aString)
+ {
+ // Since we don't need to AddRef the pointer, this constructor is safe to
+ // call on any thread.
+ }
+
+private:
+ // We can be released on any thread.
+ ~nsMainThreadPtrHolder()
+ {
+ if (NS_IsMainThread()) {
+ NS_IF_RELEASE(mRawPtr);
+ } else if (mRawPtr) {
+ NS_ReleaseOnMainThread(dont_AddRef(mRawPtr));
+ }
+ }
+
+public:
+ T* get()
+ {
+ // Nobody should be touching the raw pointer off-main-thread.
+ if (mStrict && MOZ_UNLIKELY(!NS_IsMainThread())) {
+ NS_ERROR("Can't dereference nsMainThreadPtrHolder off main thread");
+ MOZ_CRASH();
+ }
+ return mRawPtr;
+ }
+
+ bool operator==(const nsMainThreadPtrHolder<T>& aOther) const
+ {
+ return mRawPtr == aOther.mRawPtr;
+ }
+ bool operator!() const
+ {
+ return !mRawPtr;
+ }
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsMainThreadPtrHolder<T>)
+
+private:
+ // Our wrapped pointer.
+ T* mRawPtr;
+
+ // Whether to strictly enforce thread invariants in this class.
+ bool mStrict;
+
+ // Copy constructor and operator= not implemented. Once constructed, the
+ // holder is immutable.
+ T& operator=(nsMainThreadPtrHolder& aOther);
+ nsMainThreadPtrHolder(const nsMainThreadPtrHolder& aOther);
+};
+
+template<class T>
+class nsMainThreadPtrHandle
+{
+ RefPtr<nsMainThreadPtrHolder<T>> mPtr;
+
+public:
+ nsMainThreadPtrHandle() : mPtr(nullptr) {}
+ MOZ_IMPLICIT nsMainThreadPtrHandle(decltype(nullptr)) : mPtr(nullptr) {}
+ explicit nsMainThreadPtrHandle(nsMainThreadPtrHolder<T>* aHolder)
+ : mPtr(aHolder)
+ {
+ }
+ explicit nsMainThreadPtrHandle(
+ already_AddRefed<nsMainThreadPtrHolder<T>> aHolder)
+ : mPtr(aHolder)
+ {
+ }
+ nsMainThreadPtrHandle(const nsMainThreadPtrHandle& aOther)
+ : mPtr(aOther.mPtr)
+ {
+ }
+ nsMainThreadPtrHandle& operator=(const nsMainThreadPtrHandle& aOther)
+ {
+ mPtr = aOther.mPtr;
+ return *this;
+ }
+ nsMainThreadPtrHandle& operator=(nsMainThreadPtrHolder<T>* aHolder)
+ {
+ mPtr = aHolder;
+ return *this;
+ }
+
+ // These all call through to nsMainThreadPtrHolder, and thus implicitly
+ // assert that we're on the main thread. Off-main-thread consumers must treat
+ // these handles as opaque.
+ T* get()
+ {
+ if (mPtr) {
+ return mPtr.get()->get();
+ }
+ return nullptr;
+ }
+ const T* get() const
+ {
+ if (mPtr) {
+ return mPtr.get()->get();
+ }
+ return nullptr;
+ }
+
+ operator T*() { return get(); }
+ T* operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN { return get(); }
+
+ // These are safe to call on other threads with appropriate external locking.
+ bool operator==(const nsMainThreadPtrHandle<T>& aOther) const
+ {
+ if (!mPtr || !aOther.mPtr) {
+ return mPtr == aOther.mPtr;
+ }
+ return *mPtr == *aOther.mPtr;
+ }
+ bool operator!=(const nsMainThreadPtrHandle<T>& aOther) const
+ {
+ return !operator==(aOther);
+ }
+ bool operator==(decltype(nullptr)) const { return mPtr == nullptr; }
+ bool operator!=(decltype(nullptr)) const { return mPtr != nullptr; }
+ bool operator!() const {
+ return !mPtr || !*mPtr;
+ }
+};
+
+namespace mozilla {
+
+template<typename T>
+using PtrHolder = nsMainThreadPtrHolder<T>;
+
+template<typename T>
+using PtrHandle = nsMainThreadPtrHandle<T>;
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/glue/nsQuickSort.cpp b/xpcom/glue/nsQuickSort.cpp
new file mode 100644
index 000000000..f409b875e
--- /dev/null
+++ b/xpcom/glue/nsQuickSort.cpp
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* We need this because Solaris' version of qsort is broken and
+ * causes array bounds reads.
+ */
+
+#include <stdlib.h>
+#include "nsAlgorithm.h"
+#include "nsQuickSort.h"
+
+extern "C" {
+
+#if !defined(DEBUG) && (defined(__cplusplus) || defined(__gcc))
+# ifndef INLINE
+# define INLINE inline
+# endif
+#else
+# define INLINE
+#endif
+
+typedef int cmp_t(const void *, const void *, void *);
+static INLINE char *med3(char *, char *, char *, cmp_t *, void *);
+static INLINE void swapfunc(char *, char *, int, int);
+
+/*
+ * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
+ */
+#define swapcode(TYPE, parmi, parmj, n) { \
+ long i = (n) / sizeof (TYPE); \
+ TYPE *pi = (TYPE *) (parmi); \
+ TYPE *pj = (TYPE *) (parmj); \
+ do { \
+ TYPE t = *pi; \
+ *pi++ = *pj; \
+ *pj++ = t; \
+ } while (--i > 0); \
+}
+
+#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
+ es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
+
+static INLINE void
+swapfunc(char *a, char *b, int n, int swaptype)
+{
+ if(swaptype <= 1)
+ swapcode(long, a, b, n)
+ else
+ swapcode(char, a, b, n)
+}
+
+#define swap(a, b) \
+ if (swaptype == 0) { \
+ long t = *(long *)(a); \
+ *(long *)(a) = *(long *)(b); \
+ *(long *)(b) = t; \
+ } else \
+ swapfunc((char *)a, (char*)b, (int)es, swaptype)
+
+#define vecswap(a, b, n) if ((n) > 0) swapfunc((char *)a, (char *)b, (int)n, swaptype)
+
+static INLINE char *
+med3(char *a, char *b, char *c, cmp_t* cmp, void *data)
+{
+ return cmp(a, b, data) < 0 ?
+ (cmp(b, c, data) < 0 ? b : (cmp(a, c, data) < 0 ? c : a ))
+ :(cmp(b, c, data) > 0 ? b : (cmp(a, c, data) < 0 ? a : c ));
+}
+
+void NS_QuickSort (
+ void *a,
+ unsigned int n,
+ unsigned int es,
+ cmp_t *cmp,
+ void *data
+ )
+{
+ char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
+ int d, r, swaptype;
+
+loop: SWAPINIT(a, es);
+ /* Use insertion sort when input is small */
+ if (n < 7) {
+ for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es)
+ for (pl = pm; pl > (char *)a && cmp(pl - es, pl, data) > 0;
+ pl -= es)
+ swap(pl, pl - es);
+ return;
+ }
+ /* Choose pivot */
+ pm = (char *)a + (n / 2) * es;
+ if (n > 7) {
+ pl = (char *)a;
+ pn = (char *)a + (n - 1) * es;
+ if (n > 40) {
+ d = (n / 8) * es;
+ pl = med3(pl, pl + d, pl + 2 * d, cmp, data);
+ pm = med3(pm - d, pm, pm + d, cmp, data);
+ pn = med3(pn - 2 * d, pn - d, pn, cmp, data);
+ }
+ pm = med3(pl, pm, pn, cmp, data);
+ }
+ swap(a, pm);
+ pa = pb = (char *)a + es;
+
+ pc = pd = (char *)a + (n - 1) * es;
+ /* loop invariants:
+ * [a, pa) = pivot
+ * [pa, pb) < pivot
+ * [pb, pc + es) unprocessed
+ * [pc + es, pd + es) > pivot
+ * [pd + es, pn) = pivot
+ */
+ for (;;) {
+ while (pb <= pc && (r = cmp(pb, a, data)) <= 0) {
+ if (r == 0) {
+ swap(pa, pb);
+ pa += es;
+ }
+ pb += es;
+ }
+ while (pb <= pc && (r = cmp(pc, a, data)) >= 0) {
+ if (r == 0) {
+ swap(pc, pd);
+ pd -= es;
+ }
+ pc -= es;
+ }
+ if (pb > pc)
+ break;
+ swap(pb, pc);
+ pb += es;
+ pc -= es;
+ }
+ /* Move pivot values */
+ pn = (char *)a + n * es;
+ r = XPCOM_MIN(pa - (char *)a, pb - pa);
+ vecswap(a, pb - r, r);
+ r = XPCOM_MIN<size_t>(pd - pc, pn - pd - es);
+ vecswap(pb, pn - r, r);
+ /* Recursively process partitioned items */
+ if ((r = pb - pa) > (int)es)
+ NS_QuickSort(a, r / es, es, cmp, data);
+ if ((r = pd - pc) > (int)es) {
+ /* Iterate rather than recurse to save stack space */
+ a = pn - r;
+ n = r / es;
+ goto loop;
+ }
+/* NS_QuickSort(pn - r, r / es, es, cmp, data);*/
+}
+
+}
+
+#undef INLINE
+#undef swapcode
+#undef SWAPINIT
+#undef swap
+#undef vecswap
diff --git a/xpcom/glue/nsQuickSort.h b/xpcom/glue/nsQuickSort.h
new file mode 100644
index 000000000..e8d8ed870
--- /dev/null
+++ b/xpcom/glue/nsQuickSort.h
@@ -0,0 +1,41 @@
+/* -*- 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/. */
+
+
+/* We need this because Solaris' version of qsort is broken and
+ * causes array bounds reads.
+ */
+
+#ifndef nsQuickSort_h___
+#define nsQuickSort_h___
+
+#include "nscore.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Parameters:
+ * 1. the array to sort
+ * 2. the number of elements in the array
+ * 3. the size of each array element
+ * 4. comparison function taking two elements and parameter #5 and
+ * returning an integer:
+ * + less than zero if the first element should be before the second
+ * + 0 if the order of the elements does not matter
+ * + greater than zero if the second element should be before the first
+ * 5. extra data to pass to comparison function
+ */
+void NS_QuickSort(void*, unsigned int, unsigned int,
+ int (*)(const void*, const void*, void*),
+ void*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* nsQuickSort_h___ */
diff --git a/xpcom/glue/nsRefPtrHashtable.h b/xpcom/glue/nsRefPtrHashtable.h
new file mode 100644
index 000000000..0ff1e0181
--- /dev/null
+++ b/xpcom/glue/nsRefPtrHashtable.h
@@ -0,0 +1,191 @@
+/* -*- 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 nsRefPtrHashtable_h__
+#define nsRefPtrHashtable_h__
+
+#include "nsBaseHashtable.h"
+#include "nsHashKeys.h"
+#include "nsAutoPtr.h"
+
+/**
+ * templated hashtable class maps keys to reference pointers.
+ * See nsBaseHashtable for complete declaration.
+ * @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h
+ * for a complete specification.
+ * @param PtrType the reference-type being wrapped
+ * @see nsDataHashtable, nsClassHashtable
+ */
+template<class KeyClass, class PtrType>
+class nsRefPtrHashtable
+ : public nsBaseHashtable<KeyClass, RefPtr<PtrType>, PtrType*>
+{
+public:
+ typedef typename KeyClass::KeyType KeyType;
+ typedef PtrType* UserDataType;
+ typedef nsBaseHashtable<KeyClass, RefPtr<PtrType>, PtrType*> base_type;
+
+ nsRefPtrHashtable() {}
+ explicit nsRefPtrHashtable(uint32_t aInitLength)
+ : nsBaseHashtable<KeyClass, RefPtr<PtrType>, PtrType*>(aInitLength)
+ {
+ }
+
+ /**
+ * @copydoc nsBaseHashtable::Get
+ * @param aData This is an XPCOM getter, so aData is already_addrefed.
+ * If the key doesn't exist, aData will be set to nullptr.
+ */
+ bool Get(KeyType aKey, UserDataType* aData) const;
+
+ /**
+ * Gets a weak reference to the hashtable entry.
+ * @param aFound If not nullptr, will be set to true if the entry is found,
+ * to false otherwise.
+ * @return The entry, or nullptr if not found. Do not release this pointer!
+ */
+ PtrType* GetWeak(KeyType aKey, bool* aFound = nullptr) const;
+
+ // Overload Put, rather than overriding it.
+ using base_type::Put;
+
+ void Put(KeyType aKey, already_AddRefed<PtrType> aData);
+
+ MOZ_MUST_USE bool Put(KeyType aKey, already_AddRefed<PtrType> aData,
+ const mozilla::fallible_t&);
+
+ // Overload Remove, rather than overriding it.
+ using base_type::Remove;
+
+ /**
+ * Remove the data for the associated key, swapping the current value into
+ * pData, thereby avoiding calls to AddRef and Release.
+ * @param aKey the key to remove from the hashtable
+ * @param aData This is an XPCOM getter, so aData is already_addrefed.
+ * If the key doesn't exist, aData will be set to nullptr. Must be non-null.
+ */
+ bool Remove(KeyType aKey, UserDataType* aData);
+};
+
+template<typename K, typename T>
+inline void
+ImplCycleCollectionUnlink(nsRefPtrHashtable<K, T>& aField)
+{
+ aField.Clear();
+}
+
+template<typename K, typename T>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ nsRefPtrHashtable<K, T>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ for (auto iter = aField.ConstIter(); !iter.Done(); iter.Next()) {
+ CycleCollectionNoteChild(aCallback, iter.UserData(), aName, aFlags);
+ }
+}
+
+//
+// nsRefPtrHashtable definitions
+//
+
+template<class KeyClass, class PtrType>
+bool
+nsRefPtrHashtable<KeyClass, PtrType>::Get(KeyType aKey,
+ UserDataType* aRefPtr) const
+{
+ typename base_type::EntryType* ent = this->GetEntry(aKey);
+
+ if (ent) {
+ if (aRefPtr) {
+ *aRefPtr = ent->mData;
+
+ NS_IF_ADDREF(*aRefPtr);
+ }
+
+ return true;
+ }
+
+ // if the key doesn't exist, set *aRefPtr to null
+ // so that it is a valid XPCOM getter
+ if (aRefPtr) {
+ *aRefPtr = nullptr;
+ }
+
+ return false;
+}
+
+template<class KeyClass, class PtrType>
+PtrType*
+nsRefPtrHashtable<KeyClass, PtrType>::GetWeak(KeyType aKey, bool* aFound) const
+{
+ typename base_type::EntryType* ent = this->GetEntry(aKey);
+
+ if (ent) {
+ if (aFound) {
+ *aFound = true;
+ }
+
+ return ent->mData;
+ }
+
+ // Key does not exist, return nullptr and set aFound to false
+ if (aFound) {
+ *aFound = false;
+ }
+
+ return nullptr;
+}
+
+template<class KeyClass, class PtrType>
+void
+nsRefPtrHashtable<KeyClass, PtrType>::Put(KeyType aKey,
+ already_AddRefed<PtrType> aData)
+{
+ if (!Put(aKey, mozilla::Move(aData), mozilla::fallible)) {
+ NS_ABORT_OOM(this->mTable.EntrySize() * this->mTable.EntryCount());
+ }
+}
+
+template<class KeyClass, class PtrType>
+bool
+nsRefPtrHashtable<KeyClass, PtrType>::Put(KeyType aKey,
+ already_AddRefed<PtrType> aData,
+ const mozilla::fallible_t&)
+{
+ typename base_type::EntryType* ent = this->PutEntry(aKey);
+
+ if (!ent) {
+ return false;
+ }
+
+ ent->mData = aData;
+
+ return true;
+}
+
+template<class KeyClass, class PtrType>
+bool
+nsRefPtrHashtable<KeyClass, PtrType>::Remove(KeyType aKey,
+ UserDataType* aRefPtr)
+{
+ MOZ_ASSERT(aRefPtr);
+ typename base_type::EntryType* ent = this->GetEntry(aKey);
+
+ if (ent) {
+ ent->mData.forget(aRefPtr);
+ this->Remove(aKey);
+ return true;
+ }
+
+ // If the key doesn't exist, set *aRefPtr to null
+ // so that it is a valid XPCOM getter.
+ *aRefPtr = nullptr;
+ return false;
+}
+
+#endif // nsRefPtrHashtable_h__
diff --git a/xpcom/glue/nsServiceManagerUtils.h b/xpcom/glue/nsServiceManagerUtils.h
new file mode 100644
index 000000000..d1ea408a2
--- /dev/null
+++ b/xpcom/glue/nsServiceManagerUtils.h
@@ -0,0 +1,94 @@
+/* -*- 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 nsServiceManagerUtils_h__
+#define nsServiceManagerUtils_h__
+
+#include "nsIServiceManager.h"
+#include "nsCOMPtr.h"
+
+inline const nsGetServiceByCID
+do_GetService(const nsCID& aCID)
+{
+ return nsGetServiceByCID(aCID);
+}
+
+inline const nsGetServiceByCIDWithError
+do_GetService(const nsCID& aCID, nsresult* aError)
+{
+ return nsGetServiceByCIDWithError(aCID, aError);
+}
+
+inline const nsGetServiceByContractID
+do_GetService(const char* aContractID)
+{
+ return nsGetServiceByContractID(aContractID);
+}
+
+inline const nsGetServiceByContractIDWithError
+do_GetService(const char* aContractID, nsresult* aError)
+{
+ return nsGetServiceByContractIDWithError(aContractID, aError);
+}
+
+class MOZ_STACK_CLASS nsGetServiceFromCategory final : public nsCOMPtr_helper
+{
+public:
+ nsGetServiceFromCategory(const char* aCategory, const char* aEntry,
+ nsresult* aErrorPtr)
+ : mCategory(aCategory)
+ , mEntry(aEntry)
+ , mErrorPtr(aErrorPtr)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const
+ override;
+protected:
+ const char* mCategory;
+ const char* mEntry;
+ nsresult* mErrorPtr;
+};
+
+inline const nsGetServiceFromCategory
+do_GetServiceFromCategory(const char* aCategory, const char* aEntry,
+ nsresult* aError = 0)
+{
+ return nsGetServiceFromCategory(aCategory, aEntry, aError);
+}
+
+nsresult CallGetService(const nsCID& aClass, const nsIID& aIID, void** aResult);
+
+nsresult CallGetService(const char* aContractID, const nsIID& aIID,
+ void** aResult);
+
+// type-safe shortcuts for calling |GetService|
+template<class DestinationType>
+inline nsresult
+CallGetService(const nsCID& aClass,
+ DestinationType** aDestination)
+{
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return CallGetService(aClass,
+ NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template<class DestinationType>
+inline nsresult
+CallGetService(const char* aContractID,
+ DestinationType** aDestination)
+{
+ NS_PRECONDITION(aContractID, "null parameter");
+ NS_PRECONDITION(aDestination, "null parameter");
+
+ return CallGetService(aContractID,
+ NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+#endif
diff --git a/xpcom/glue/nsStringAPI.cpp b/xpcom/glue/nsStringAPI.cpp
new file mode 100644
index 000000000..e5114a149
--- /dev/null
+++ b/xpcom/glue/nsStringAPI.cpp
@@ -0,0 +1,1304 @@
+/* -*- 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 "nscore.h"
+#include "nsCRTGlue.h"
+#include "prprf.h"
+#include "nsStringAPI.h"
+#include "nsXPCOMStrings.h"
+#include "nsDebug.h"
+
+#include "mozilla/Sprintf.h"
+
+#include <stdio.h>
+
+// nsAString
+
+uint32_t
+nsAString::BeginReading(const char_type** aBegin, const char_type** aEnd) const
+{
+ uint32_t len = NS_StringGetData(*this, aBegin);
+ if (aEnd) {
+ *aEnd = *aBegin + len;
+ }
+
+ return len;
+}
+
+const nsAString::char_type*
+nsAString::BeginReading() const
+{
+ const char_type* data;
+ NS_StringGetData(*this, &data);
+ return data;
+}
+
+const nsAString::char_type*
+nsAString::EndReading() const
+{
+ const char_type* data;
+ uint32_t len = NS_StringGetData(*this, &data);
+ return data + len;
+}
+
+uint32_t
+nsAString::BeginWriting(char_type** aBegin, char_type** aEnd, uint32_t aNewSize)
+{
+ uint32_t len = NS_StringGetMutableData(*this, aNewSize, aBegin);
+ if (aEnd) {
+ *aEnd = *aBegin + len;
+ }
+
+ return len;
+}
+
+nsAString::char_type*
+nsAString::BeginWriting(uint32_t aLen)
+{
+ char_type* data;
+ NS_StringGetMutableData(*this, aLen, &data);
+ return data;
+}
+
+nsAString::char_type*
+nsAString::EndWriting()
+{
+ char_type* data;
+ uint32_t len = NS_StringGetMutableData(*this, UINT32_MAX, &data);
+ return data + len;
+}
+
+bool
+nsAString::SetLength(uint32_t aLen)
+{
+ char_type* data;
+ NS_StringGetMutableData(*this, aLen, &data);
+ return data != nullptr;
+}
+
+void
+nsAString::AssignLiteral(const char* aStr)
+{
+ uint32_t len = strlen(aStr);
+ char16_t* buf = BeginWriting(len);
+ if (!buf) {
+ return;
+ }
+
+ for (; *aStr; ++aStr, ++buf) {
+ *buf = *aStr;
+ }
+}
+
+void
+nsAString::AppendLiteral(const char* aASCIIStr)
+{
+ uint32_t appendLen = strlen(aASCIIStr);
+
+ uint32_t thisLen = Length();
+ char16_t* begin;
+ char16_t* end;
+ BeginWriting(&begin, &end, appendLen + thisLen);
+ if (!begin) {
+ return;
+ }
+
+ for (begin += thisLen; begin < end; ++begin, ++aASCIIStr) {
+ *begin = *aASCIIStr;
+ }
+}
+
+void
+nsAString::StripChars(const char* aSet)
+{
+ nsString copy(*this);
+
+ const char_type* source;
+ const char_type* sourceEnd;
+ copy.BeginReading(&source, &sourceEnd);
+
+ char_type* dest;
+ BeginWriting(&dest);
+ if (!dest) {
+ return;
+ }
+
+ char_type* curDest = dest;
+
+ for (; source < sourceEnd; ++source) {
+ const char* test;
+ for (test = aSet; *test; ++test) {
+ if (*source == char_type(*test)) {
+ break;
+ }
+ }
+
+ if (!*test) {
+ // not stripped, copy this char
+ *curDest = *source;
+ ++curDest;
+ }
+ }
+
+ SetLength(curDest - dest);
+}
+
+void
+nsAString::Trim(const char* aSet, bool aLeading, bool aTrailing)
+{
+ NS_ASSERTION(aLeading || aTrailing, "Ineffective Trim");
+
+ const char16_t* start;
+ const char16_t* end;
+ uint32_t cutLen;
+
+ if (aLeading) {
+ BeginReading(&start, &end);
+ for (cutLen = 0; start < end; ++start, ++cutLen) {
+ const char* test;
+ for (test = aSet; *test; ++test) {
+ if (*test == *start) {
+ break;
+ }
+ }
+ if (!*test) {
+ break;
+ }
+ }
+ if (cutLen) {
+ NS_StringCutData(*this, 0, cutLen);
+ }
+ }
+ if (aTrailing) {
+ uint32_t len = BeginReading(&start, &end);
+ --end;
+ for (cutLen = 0; end >= start; --end, ++cutLen) {
+ const char* test;
+ for (test = aSet; *test; ++test) {
+ if (*test == *end) {
+ break;
+ }
+ }
+ if (!*test) {
+ break;
+ }
+ }
+ if (cutLen) {
+ NS_StringCutData(*this, len - cutLen, cutLen);
+ }
+ }
+}
+
+int32_t
+nsAString::DefaultComparator(const char_type* aStrA, const char_type* aStrB,
+ uint32_t aLen)
+{
+ for (const char_type* end = aStrA + aLen; aStrA < end; ++aStrA, ++aStrB) {
+ if (*aStrA == *aStrB) {
+ continue;
+ }
+
+ return *aStrA < *aStrB ? -1 : 1;
+ }
+
+ return 0;
+}
+
+int32_t
+nsAString::Compare(const char_type* aOther, ComparatorFunc aComparator) const
+{
+ const char_type* cself;
+ uint32_t selflen = NS_StringGetData(*this, &cself);
+ uint32_t otherlen = NS_strlen(aOther);
+ uint32_t comparelen = selflen <= otherlen ? selflen : otherlen;
+
+ int32_t result = aComparator(cself, aOther, comparelen);
+ if (result == 0) {
+ if (selflen < otherlen) {
+ return -1;
+ } else if (selflen > otherlen) {
+ return 1;
+ }
+ }
+ return result;
+}
+
+int32_t
+nsAString::Compare(const self_type& aOther, ComparatorFunc aComparator) const
+{
+ const char_type* cself;
+ const char_type* cother;
+ uint32_t selflen = NS_StringGetData(*this, &cself);
+ uint32_t otherlen = NS_StringGetData(aOther, &cother);
+ uint32_t comparelen = selflen <= otherlen ? selflen : otherlen;
+
+ int32_t result = aComparator(cself, cother, comparelen);
+ if (result == 0) {
+ if (selflen < otherlen) {
+ return -1;
+ } else if (selflen > otherlen) {
+ return 1;
+ }
+ }
+ return result;
+}
+
+bool
+nsAString::Equals(const char_type* aOther, ComparatorFunc aComparator) const
+{
+ const char_type* cself;
+ uint32_t selflen = NS_StringGetData(*this, &cself);
+ uint32_t otherlen = NS_strlen(aOther);
+
+ if (selflen != otherlen) {
+ return false;
+ }
+
+ return aComparator(cself, aOther, selflen) == 0;
+}
+
+bool
+nsAString::Equals(const self_type& aOther, ComparatorFunc aComparator) const
+{
+ const char_type* cself;
+ const char_type* cother;
+ uint32_t selflen = NS_StringGetData(*this, &cself);
+ uint32_t otherlen = NS_StringGetData(aOther, &cother);
+
+ if (selflen != otherlen) {
+ return false;
+ }
+
+ return aComparator(cself, cother, selflen) == 0;
+}
+
+bool
+nsAString::EqualsLiteral(const char* aASCIIString) const
+{
+ const char16_t* begin;
+ const char16_t* end;
+ BeginReading(&begin, &end);
+
+ for (; begin < end; ++begin, ++aASCIIString) {
+ if (!*aASCIIString || !NS_IsAscii(*begin) ||
+ (char)*begin != *aASCIIString) {
+ return false;
+ }
+ }
+
+ return *aASCIIString == '\0';
+}
+
+bool
+nsAString::LowerCaseEqualsLiteral(const char* aASCIIString) const
+{
+ const char16_t* begin;
+ const char16_t* end;
+ BeginReading(&begin, &end);
+
+ for (; begin < end; ++begin, ++aASCIIString) {
+ if (!*aASCIIString || !NS_IsAscii(*begin) ||
+ NS_ToLower((char)*begin) != *aASCIIString) {
+ return false;
+ }
+ }
+
+ return *aASCIIString == '\0';
+}
+
+int32_t
+nsAString::Find(const self_type& aStr, uint32_t aOffset,
+ ComparatorFunc aComparator) const
+{
+ const char_type* begin;
+ const char_type* end;
+ uint32_t selflen = BeginReading(&begin, &end);
+
+ if (aOffset > selflen) {
+ return -1;
+ }
+
+ const char_type* other;
+ uint32_t otherlen = aStr.BeginReading(&other);
+
+ if (otherlen > selflen - aOffset) {
+ return -1;
+ }
+
+ // We want to stop searching otherlen characters before the end of the string
+ end -= otherlen;
+
+ for (const char_type* cur = begin + aOffset; cur <= end; ++cur) {
+ if (!aComparator(cur, other, otherlen)) {
+ return cur - begin;
+ }
+ }
+ return -1;
+}
+
+static bool
+ns_strnmatch(const char16_t* aStr, const char* aSubstring, uint32_t aLen)
+{
+ for (; aLen; ++aStr, ++aSubstring, --aLen) {
+ if (!NS_IsAscii(*aStr)) {
+ return false;
+ }
+
+ if ((char)*aStr != *aSubstring) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool
+ns_strnimatch(const char16_t* aStr, const char* aSubstring, uint32_t aLen)
+{
+ for (; aLen; ++aStr, ++aSubstring, --aLen) {
+ if (!NS_IsAscii(*aStr)) {
+ return false;
+ }
+
+ if (NS_ToLower((char)*aStr) != NS_ToLower(*aSubstring)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int32_t
+nsAString::Find(const char* aStr, uint32_t aOffset, bool aIgnoreCase) const
+{
+ bool (*match)(const char16_t*, const char*, uint32_t) =
+ aIgnoreCase ? ns_strnimatch : ns_strnmatch;
+
+ const char_type* begin;
+ const char_type* end;
+ uint32_t selflen = BeginReading(&begin, &end);
+
+ if (aOffset > selflen) {
+ return -1;
+ }
+
+ uint32_t otherlen = strlen(aStr);
+
+ if (otherlen > selflen - aOffset) {
+ return -1;
+ }
+
+ // We want to stop searching otherlen characters before the end of the string
+ end -= otherlen;
+
+ for (const char_type* cur = begin + aOffset; cur <= end; ++cur) {
+ if (match(cur, aStr, otherlen)) {
+ return cur - begin;
+ }
+ }
+ return -1;
+}
+
+int32_t
+nsAString::RFind(const self_type& aStr, int32_t aOffset,
+ ComparatorFunc aComparator) const
+{
+ const char_type* begin;
+ const char_type* end;
+ uint32_t selflen = BeginReading(&begin, &end);
+
+ const char_type* other;
+ uint32_t otherlen = aStr.BeginReading(&other);
+
+ if (selflen < otherlen) {
+ return -1;
+ }
+
+ if (aOffset < 0 || uint32_t(aOffset) > (selflen - otherlen)) {
+ end -= otherlen;
+ } else {
+ end = begin + aOffset;
+ }
+
+ for (const char_type* cur = end; cur >= begin; --cur) {
+ if (!aComparator(cur, other, otherlen)) {
+ return cur - begin;
+ }
+ }
+ return -1;
+}
+
+int32_t
+nsAString::RFind(const char* aStr, int32_t aOffset, bool aIgnoreCase) const
+{
+ bool (*match)(const char16_t*, const char*, uint32_t) =
+ aIgnoreCase ? ns_strnimatch : ns_strnmatch;
+
+ const char_type* begin;
+ const char_type* end;
+ uint32_t selflen = BeginReading(&begin, &end);
+ uint32_t otherlen = strlen(aStr);
+
+ if (selflen < otherlen) {
+ return -1;
+ }
+
+ if (aOffset < 0 || uint32_t(aOffset) > (selflen - otherlen)) {
+ end -= otherlen;
+ } else {
+ end = begin + aOffset;
+ }
+
+ for (const char_type* cur = end; cur >= begin; --cur) {
+ if (match(cur, aStr, otherlen)) {
+ return cur - begin;
+ }
+ }
+ return -1;
+}
+
+int32_t
+nsAString::FindChar(char_type aChar, uint32_t aOffset) const
+{
+ const char_type* start;
+ const char_type* end;
+ uint32_t len = BeginReading(&start, &end);
+ if (aOffset > len) {
+ return -1;
+ }
+
+ const char_type* cur;
+
+ for (cur = start + aOffset; cur < end; ++cur) {
+ if (*cur == aChar) {
+ return cur - start;
+ }
+ }
+
+ return -1;
+}
+
+int32_t
+nsAString::RFindChar(char_type aChar) const
+{
+ const char16_t* start;
+ const char16_t* end;
+ BeginReading(&start, &end);
+
+ do {
+ --end;
+
+ if (*end == aChar) {
+ return end - start;
+ }
+
+ } while (end >= start);
+
+ return -1;
+}
+
+void
+nsAString::AppendInt(int aInt, int32_t aRadix)
+{
+ const char* fmt;
+ switch (aRadix) {
+ case 8:
+ fmt = "%o";
+ break;
+
+ case 10:
+ fmt = "%d";
+ break;
+
+ case 16:
+ fmt = "%x";
+ break;
+
+ default:
+ NS_ERROR("Unrecognized radix");
+ fmt = "";
+ }
+
+ char buf[20];
+ int len = SprintfLiteral(buf, fmt, aInt);
+ Append(NS_ConvertASCIItoUTF16(buf, len));
+}
+
+// Strings
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+int32_t
+nsAString::ToInteger(nsresult* aErrorCode, uint32_t aRadix) const
+{
+ NS_ConvertUTF16toUTF8 narrow(*this);
+
+ const char* fmt;
+ switch (aRadix) {
+ case 10:
+ fmt = "%i";
+ break;
+
+ case 16:
+ fmt = "%x";
+ break;
+
+ default:
+ NS_ERROR("Unrecognized radix!");
+ *aErrorCode = NS_ERROR_INVALID_ARG;
+ return 0;
+ }
+
+ int32_t result = 0;
+ if (PR_sscanf(narrow.get(), fmt, &result) == 1) {
+ *aErrorCode = NS_OK;
+ } else {
+ *aErrorCode = NS_ERROR_FAILURE;
+ }
+
+ return result;
+}
+
+int64_t
+nsAString::ToInteger64(nsresult* aErrorCode, uint32_t aRadix) const
+{
+ NS_ConvertUTF16toUTF8 narrow(*this);
+
+ const char* fmt;
+ switch (aRadix) {
+ case 10:
+ fmt = "%lli";
+ break;
+
+ case 16:
+ fmt = "%llx";
+ break;
+
+ default:
+ NS_ERROR("Unrecognized radix!");
+ *aErrorCode = NS_ERROR_INVALID_ARG;
+ return 0;
+ }
+
+ int64_t result = 0;
+ if (PR_sscanf(narrow.get(), fmt, &result) == 1) {
+ *aErrorCode = NS_OK;
+ } else {
+ *aErrorCode = NS_ERROR_FAILURE;
+ }
+
+ return result;
+}
+#endif // XPCOM_GLUE_AVOID_NSPR
+
+// nsACString
+
+uint32_t
+nsACString::BeginReading(const char_type** aBegin, const char_type** aEnd) const
+{
+ uint32_t len = NS_CStringGetData(*this, aBegin);
+ if (aEnd) {
+ *aEnd = *aBegin + len;
+ }
+
+ return len;
+}
+
+const nsACString::char_type*
+nsACString::BeginReading() const
+{
+ const char_type* data;
+ NS_CStringGetData(*this, &data);
+ return data;
+}
+
+const nsACString::char_type*
+nsACString::EndReading() const
+{
+ const char_type* data;
+ uint32_t len = NS_CStringGetData(*this, &data);
+ return data + len;
+}
+
+uint32_t
+nsACString::BeginWriting(char_type** aBegin, char_type** aEnd,
+ uint32_t aNewSize)
+{
+ uint32_t len = NS_CStringGetMutableData(*this, aNewSize, aBegin);
+ if (aEnd) {
+ *aEnd = *aBegin + len;
+ }
+
+ return len;
+}
+
+nsACString::char_type*
+nsACString::BeginWriting(uint32_t aLen)
+{
+ char_type* data;
+ NS_CStringGetMutableData(*this, aLen, &data);
+ return data;
+}
+
+nsACString::char_type*
+nsACString::EndWriting()
+{
+ char_type* data;
+ uint32_t len = NS_CStringGetMutableData(*this, UINT32_MAX, &data);
+ return data + len;
+}
+
+bool
+nsACString::SetLength(uint32_t aLen)
+{
+ char_type* data;
+ NS_CStringGetMutableData(*this, aLen, &data);
+ return data != nullptr;
+}
+
+void
+nsACString::StripChars(const char* aSet)
+{
+ nsCString copy(*this);
+
+ const char_type* source;
+ const char_type* sourceEnd;
+ copy.BeginReading(&source, &sourceEnd);
+
+ char_type* dest;
+ BeginWriting(&dest);
+ if (!dest) {
+ return;
+ }
+
+ char_type* curDest = dest;
+
+ for (; source < sourceEnd; ++source) {
+ const char* test;
+ for (test = aSet; *test; ++test) {
+ if (*source == char_type(*test)) {
+ break;
+ }
+ }
+
+ if (!*test) {
+ // not stripped, copy this char
+ *curDest = *source;
+ ++curDest;
+ }
+ }
+
+ SetLength(curDest - dest);
+}
+
+void
+nsACString::Trim(const char* aSet, bool aLeading, bool aTrailing)
+{
+ NS_ASSERTION(aLeading || aTrailing, "Ineffective Trim");
+
+ const char* start;
+ const char* end;
+ uint32_t cutLen;
+
+ if (aLeading) {
+ BeginReading(&start, &end);
+ for (cutLen = 0; start < end; ++start, ++cutLen) {
+ const char* test;
+ for (test = aSet; *test; ++test) {
+ if (*test == *start) {
+ break;
+ }
+ }
+ if (!*test) {
+ break;
+ }
+ }
+ if (cutLen) {
+ NS_CStringCutData(*this, 0, cutLen);
+ }
+ }
+ if (aTrailing) {
+ uint32_t len = BeginReading(&start, &end);
+ --end;
+ for (cutLen = 0; end >= start; --end, ++cutLen) {
+ const char* test;
+ for (test = aSet; *test; ++test) {
+ if (*test == *end) {
+ break;
+ }
+ }
+ if (!*test) {
+ break;
+ }
+ }
+ if (cutLen) {
+ NS_CStringCutData(*this, len - cutLen, cutLen);
+ }
+ }
+}
+
+int32_t
+nsACString::DefaultComparator(const char_type* aStrA, const char_type* aStrB,
+ uint32_t aLen)
+{
+ return memcmp(aStrA, aStrB, aLen);
+}
+
+int32_t
+nsACString::Compare(const char_type* aOther, ComparatorFunc aComparator) const
+{
+ const char_type* cself;
+ uint32_t selflen = NS_CStringGetData(*this, &cself);
+ uint32_t otherlen = strlen(aOther);
+ uint32_t comparelen = selflen <= otherlen ? selflen : otherlen;
+
+ int32_t result = aComparator(cself, aOther, comparelen);
+ if (result == 0) {
+ if (selflen < otherlen) {
+ return -1;
+ } else if (selflen > otherlen) {
+ return 1;
+ }
+ }
+ return result;
+}
+
+int32_t
+nsACString::Compare(const self_type& aOther, ComparatorFunc aComparator) const
+{
+ const char_type* cself;
+ const char_type* cother;
+ uint32_t selflen = NS_CStringGetData(*this, &cself);
+ uint32_t otherlen = NS_CStringGetData(aOther, &cother);
+ uint32_t comparelen = selflen <= otherlen ? selflen : otherlen;
+
+ int32_t result = aComparator(cself, cother, comparelen);
+ if (result == 0) {
+ if (selflen < otherlen) {
+ return -1;
+ } else if (selflen > otherlen) {
+ return 1;
+ }
+ }
+ return result;
+}
+
+bool
+nsACString::Equals(const char_type* aOther, ComparatorFunc aComparator) const
+{
+ const char_type* cself;
+ uint32_t selflen = NS_CStringGetData(*this, &cself);
+ uint32_t otherlen = strlen(aOther);
+
+ if (selflen != otherlen) {
+ return false;
+ }
+
+ return aComparator(cself, aOther, selflen) == 0;
+}
+
+bool
+nsACString::Equals(const self_type& aOther, ComparatorFunc aComparator) const
+{
+ const char_type* cself;
+ const char_type* cother;
+ uint32_t selflen = NS_CStringGetData(*this, &cself);
+ uint32_t otherlen = NS_CStringGetData(aOther, &cother);
+
+ if (selflen != otherlen) {
+ return false;
+ }
+
+ return aComparator(cself, cother, selflen) == 0;
+}
+
+int32_t
+nsACString::Find(const self_type& aStr, uint32_t aOffset,
+ ComparatorFunc aComparator) const
+{
+ const char_type* begin;
+ const char_type* end;
+ uint32_t selflen = BeginReading(&begin, &end);
+
+ if (aOffset > selflen) {
+ return -1;
+ }
+
+ const char_type* other;
+ uint32_t otherlen = aStr.BeginReading(&other);
+
+ if (otherlen > selflen - aOffset) {
+ return -1;
+ }
+
+ // We want to stop searching otherlen characters before the end of the string
+ end -= otherlen;
+
+ for (const char_type* cur = begin + aOffset; cur <= end; ++cur) {
+ if (!aComparator(cur, other, otherlen)) {
+ return cur - begin;
+ }
+ }
+ return -1;
+}
+
+int32_t
+nsACString::Find(const char_type* aStr, ComparatorFunc aComparator) const
+{
+ return Find(aStr, strlen(aStr), aComparator);
+}
+
+int32_t
+nsACString::Find(const char_type* aStr, uint32_t aLen,
+ ComparatorFunc aComparator) const
+{
+ const char_type* begin;
+ const char_type* end;
+ uint32_t selflen = BeginReading(&begin, &end);
+
+ if (aLen == 0) {
+ NS_WARNING("Searching for zero-length string.");
+ return -1;
+ }
+
+ if (aLen > selflen) {
+ return -1;
+ }
+
+ // We want to stop searching otherlen characters before the end of the string
+ end -= aLen;
+
+ for (const char_type* cur = begin; cur <= end; ++cur) {
+ if (!aComparator(cur, aStr, aLen)) {
+ return cur - begin;
+ }
+ }
+ return -1;
+}
+
+int32_t
+nsACString::RFind(const self_type& aStr, int32_t aOffset,
+ ComparatorFunc aComparator) const
+{
+ const char_type* begin;
+ const char_type* end;
+ uint32_t selflen = BeginReading(&begin, &end);
+
+ const char_type* other;
+ uint32_t otherlen = aStr.BeginReading(&other);
+
+ if (selflen < otherlen) {
+ return -1;
+ }
+
+ if (aOffset < 0 || uint32_t(aOffset) > (selflen - otherlen)) {
+ end -= otherlen;
+ } else {
+ end = begin + aOffset;
+ }
+
+ for (const char_type* cur = end; cur >= begin; --cur) {
+ if (!aComparator(cur, other, otherlen)) {
+ return cur - begin;
+ }
+ }
+ return -1;
+}
+
+int32_t
+nsACString::RFind(const char_type* aStr, ComparatorFunc aComparator) const
+{
+ return RFind(aStr, strlen(aStr), aComparator);
+}
+
+int32_t
+nsACString::RFind(const char_type* aStr, int32_t aLen,
+ ComparatorFunc aComparator) const
+{
+ const char_type* begin;
+ const char_type* end;
+ uint32_t selflen = BeginReading(&begin, &end);
+
+ if (aLen <= 0) {
+ NS_WARNING("Searching for zero-length string.");
+ return -1;
+ }
+
+ if (uint32_t(aLen) > selflen) {
+ return -1;
+ }
+
+ // We want to start searching otherlen characters before the end of the string
+ end -= aLen;
+
+ for (const char_type* cur = end; cur >= begin; --cur) {
+ if (!aComparator(cur, aStr, aLen)) {
+ return cur - begin;
+ }
+ }
+ return -1;
+}
+
+int32_t
+nsACString::FindChar(char_type aChar, uint32_t aOffset) const
+{
+ const char_type* start;
+ const char_type* end;
+ uint32_t len = BeginReading(&start, &end);
+ if (aOffset > len) {
+ return -1;
+ }
+
+ const char_type* cur;
+
+ for (cur = start + aOffset; cur < end; ++cur) {
+ if (*cur == aChar) {
+ return cur - start;
+ }
+ }
+
+ return -1;
+}
+
+int32_t
+nsACString::RFindChar(char_type aChar) const
+{
+ const char* start;
+ const char* end;
+ BeginReading(&start, &end);
+
+ for (; end >= start; --end) {
+ if (*end == aChar) {
+ return end - start;
+ }
+ }
+
+ return -1;
+}
+
+void
+nsACString::AppendInt(int aInt, int32_t aRadix)
+{
+ const char* fmt;
+ switch (aRadix) {
+ case 8:
+ fmt = "%o";
+ break;
+
+ case 10:
+ fmt = "%d";
+ break;
+
+ case 16:
+ fmt = "%x";
+ break;
+
+ default:
+ NS_ERROR("Unrecognized radix");
+ fmt = "";
+ }
+
+ char buf[20];
+ int len = SprintfLiteral(buf, fmt, aInt);
+ Append(buf, len);
+}
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+int32_t
+nsACString::ToInteger(nsresult* aErrorCode, uint32_t aRadix) const
+{
+ const char* fmt;
+ switch (aRadix) {
+ case 10:
+ fmt = "%i";
+ break;
+
+ case 16:
+ fmt = "%x";
+ break;
+
+ default:
+ NS_ERROR("Unrecognized radix!");
+ *aErrorCode = NS_ERROR_INVALID_ARG;
+ return 0;
+ }
+
+ int32_t result = 0;
+ if (PR_sscanf(nsCString(*this).get(), fmt, &result) == 1) {
+ *aErrorCode = NS_OK;
+ } else {
+ *aErrorCode = NS_ERROR_FAILURE;
+ }
+
+ return result;
+}
+
+int64_t
+nsACString::ToInteger64(nsresult* aErrorCode, uint32_t aRadix) const
+{
+ const char* fmt;
+ switch (aRadix) {
+ case 10:
+ fmt = "%lli";
+ break;
+
+ case 16:
+ fmt = "%llx";
+ break;
+
+ default:
+ NS_ERROR("Unrecognized radix!");
+ *aErrorCode = NS_ERROR_INVALID_ARG;
+ return 0;
+ }
+
+ int64_t result = 0;
+ if (PR_sscanf(nsCString(*this).get(), fmt, &result) == 1) {
+ *aErrorCode = NS_OK;
+ } else {
+ *aErrorCode = NS_ERROR_FAILURE;
+ }
+
+ return result;
+}
+#endif // XPCOM_GLUE_AVOID_NSPR
+
+// Substrings
+
+nsDependentSubstring::nsDependentSubstring(const abstract_string_type& aStr,
+ uint32_t aStartPos)
+{
+ const char16_t* data;
+ uint32_t len = NS_StringGetData(aStr, &data);
+
+ if (aStartPos > len) {
+ aStartPos = len;
+ }
+
+ NS_StringContainerInit2(*this, data + aStartPos, len - aStartPos,
+ NS_STRING_CONTAINER_INIT_DEPEND |
+ NS_STRING_CONTAINER_INIT_SUBSTRING);
+}
+
+nsDependentSubstring::nsDependentSubstring(const abstract_string_type& aStr,
+ uint32_t aStartPos,
+ uint32_t aLength)
+{
+ const char16_t* data;
+ uint32_t len = NS_StringGetData(aStr, &data);
+
+ if (aStartPos > len) {
+ aStartPos = len;
+ }
+
+ if (aStartPos + aLength > len) {
+ aLength = len - aStartPos;
+ }
+
+ NS_StringContainerInit2(*this, data + aStartPos, aLength,
+ NS_STRING_CONTAINER_INIT_DEPEND |
+ NS_STRING_CONTAINER_INIT_SUBSTRING);
+}
+
+nsDependentCSubstring::nsDependentCSubstring(const abstract_string_type& aStr,
+ uint32_t aStartPos)
+{
+ const char* data;
+ uint32_t len = NS_CStringGetData(aStr, &data);
+
+ if (aStartPos > len) {
+ aStartPos = len;
+ }
+
+ NS_CStringContainerInit2(*this, data + aStartPos, len - aStartPos,
+ NS_CSTRING_CONTAINER_INIT_DEPEND |
+ NS_CSTRING_CONTAINER_INIT_SUBSTRING);
+}
+
+nsDependentCSubstring::nsDependentCSubstring(const abstract_string_type& aStr,
+ uint32_t aStartPos,
+ uint32_t aLength)
+{
+ const char* data;
+ uint32_t len = NS_CStringGetData(aStr, &data);
+
+ if (aStartPos > len) {
+ aStartPos = len;
+ }
+
+ if (aStartPos + aLength > len) {
+ aLength = len - aStartPos;
+ }
+
+ NS_CStringContainerInit2(*this, data + aStartPos, aLength,
+ NS_CSTRING_CONTAINER_INIT_DEPEND |
+ NS_CSTRING_CONTAINER_INIT_SUBSTRING);
+}
+
+// Utils
+
+char*
+ToNewUTF8String(const nsAString& aSource)
+{
+ nsCString temp;
+ CopyUTF16toUTF8(aSource, temp);
+ return NS_CStringCloneData(temp);
+}
+
+void
+CompressWhitespace(nsAString& aString)
+{
+ char16_t* start;
+ uint32_t len = NS_StringGetMutableData(aString, UINT32_MAX, &start);
+ char16_t* end = start + len;
+ char16_t* from = start;
+ char16_t* to = start;
+
+ // Skip any leading whitespace
+ while (from < end && NS_IsAsciiWhitespace(*from)) {
+ from++;
+ }
+
+ while (from < end) {
+ char16_t theChar = *from++;
+
+ if (NS_IsAsciiWhitespace(theChar)) {
+ // We found a whitespace char, so skip over any more
+ while (from < end && NS_IsAsciiWhitespace(*from)) {
+ from++;
+ }
+
+ // Turn all whitespace into spaces
+ theChar = ' ';
+ }
+
+ *to++ = theChar;
+ }
+
+ // Drop any trailing space
+ if (to > start && to[-1] == ' ') {
+ to--;
+ }
+
+ // Re-terminate the string
+ *to = '\0';
+
+ // Set the new length
+ aString.SetLength(to - start);
+}
+
+uint32_t
+ToLowerCase(nsACString& aStr)
+{
+ char* begin;
+ char* end;
+ uint32_t len = aStr.BeginWriting(&begin, &end);
+
+ for (; begin < end; ++begin) {
+ *begin = NS_ToLower(*begin);
+ }
+
+ return len;
+}
+
+uint32_t
+ToUpperCase(nsACString& aStr)
+{
+ char* begin;
+ char* end;
+ uint32_t len = aStr.BeginWriting(&begin, &end);
+
+ for (; begin < end; ++begin) {
+ *begin = NS_ToUpper(*begin);
+ }
+
+ return len;
+}
+
+uint32_t
+ToLowerCase(const nsACString& aSrc, nsACString& aDest)
+{
+ const char* begin;
+ const char* end;
+ uint32_t len = aSrc.BeginReading(&begin, &end);
+
+ char* dest;
+ NS_CStringGetMutableData(aDest, len, &dest);
+
+ for (; begin < end; ++begin, ++dest) {
+ *dest = NS_ToLower(*begin);
+ }
+
+ return len;
+}
+
+uint32_t
+ToUpperCase(const nsACString& aSrc, nsACString& aDest)
+{
+ const char* begin;
+ const char* end;
+ uint32_t len = aSrc.BeginReading(&begin, &end);
+
+ char* dest;
+ NS_CStringGetMutableData(aDest, len, &dest);
+
+ for (; begin < end; ++begin, ++dest) {
+ *dest = NS_ToUpper(*begin);
+ }
+
+ return len;
+}
+
+int32_t
+CaseInsensitiveCompare(const char* aStrA, const char* aStrB,
+ uint32_t aLen)
+{
+ for (const char* aend = aStrA + aLen; aStrA < aend; ++aStrA, ++aStrB) {
+ char la = NS_ToLower(*aStrA);
+ char lb = NS_ToLower(*aStrB);
+
+ if (la == lb) {
+ continue;
+ }
+
+ return la < lb ? -1 : 1;
+ }
+
+ return 0;
+}
+
+bool
+ParseString(const nsACString& aSource, char aDelimiter,
+ nsTArray<nsCString>& aArray)
+{
+ int32_t start = 0;
+ int32_t end = aSource.Length();
+
+ uint32_t oldLength = aArray.Length();
+
+ for (;;) {
+ int32_t delimiter = aSource.FindChar(aDelimiter, start);
+ if (delimiter < 0) {
+ delimiter = end;
+ }
+
+ if (delimiter != start) {
+ if (!aArray.AppendElement(Substring(aSource, start, delimiter - start))) {
+ aArray.RemoveElementsAt(oldLength, aArray.Length() - oldLength);
+ return false;
+ }
+ }
+
+ if (delimiter == end) {
+ break;
+ }
+ start = ++delimiter;
+ if (start == end) {
+ break;
+ }
+ }
+
+ return true;
+}
diff --git a/xpcom/glue/nsStringAPI.h b/xpcom/glue/nsStringAPI.h
new file mode 100644
index 000000000..d5e368695
--- /dev/null
+++ b/xpcom/glue/nsStringAPI.h
@@ -0,0 +1,1596 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This header provides wrapper classes around the frozen string API
+ * which are roughly equivalent to the internal string classes.
+ */
+
+#ifdef MOZILLA_INTERNAL_API
+#error nsStringAPI.h is only usable from non-MOZILLA_INTERNAL_API code!
+#endif
+
+#ifndef nsStringAPI_h__
+#define nsStringAPI_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Char16.h"
+
+#include "nsXPCOMStrings.h"
+#include "nsISupportsImpl.h"
+#include "mozilla/Logging.h"
+#include "nsTArray.h"
+
+/**
+ * Comparison function for use with nsACString::Equals
+ */
+NS_HIDDEN_(int32_t) CaseInsensitiveCompare(const char* aStrA, const char* aStrB,
+ uint32_t aLength);
+
+class nsAString
+{
+public:
+ typedef char16_t char_type;
+ typedef nsAString self_type;
+ typedef uint32_t size_type;
+ typedef uint32_t index_type;
+
+ /**
+ * Returns the length, beginning, and end of a string in one operation.
+ */
+ NS_HIDDEN_(uint32_t) BeginReading(const char_type** aBegin,
+ const char_type** aEnd = nullptr) const;
+
+ NS_HIDDEN_(const char_type*) BeginReading() const;
+ NS_HIDDEN_(const char_type*) EndReading() const;
+
+ NS_HIDDEN_(char_type) CharAt(uint32_t aPos) const
+ {
+ NS_ASSERTION(aPos < Length(), "Out of bounds");
+ return BeginReading()[aPos];
+ }
+ NS_HIDDEN_(char_type) operator [](uint32_t aPos) const
+ {
+ return CharAt(aPos);
+ }
+ NS_HIDDEN_(char_type) First() const
+ {
+ return CharAt(0);
+ }
+ NS_HIDDEN_(char_type) Last() const
+ {
+ const char_type* data;
+ uint32_t dataLen = NS_StringGetData(*this, &data);
+ return data[dataLen - 1];
+ }
+
+ /**
+ * Get the length, begin writing, and optionally set the length of a
+ * string all in one operation.
+ *
+ * @param newSize Size the string to this length. Pass UINT32_MAX
+ * to leave the length unchanged.
+ * @return The new length of the string, or 0 if resizing failed.
+ */
+ NS_HIDDEN_(uint32_t) BeginWriting(char_type** aBegin,
+ char_type** aEnd = nullptr,
+ uint32_t aNewSize = UINT32_MAX);
+
+ NS_HIDDEN_(char_type*) BeginWriting(uint32_t = UINT32_MAX);
+ NS_HIDDEN_(char_type*) EndWriting();
+
+ NS_HIDDEN_(bool) SetLength(uint32_t aLen);
+
+ NS_HIDDEN_(size_type) Length() const
+ {
+ const char_type* data;
+ return NS_StringGetData(*this, &data);
+ }
+
+ NS_HIDDEN_(bool) IsEmpty() const { return Length() == 0; }
+
+ NS_HIDDEN_(void) SetIsVoid(bool aVal) { NS_StringSetIsVoid(*this, aVal); }
+ NS_HIDDEN_(bool) IsVoid() const { return NS_StringGetIsVoid(*this); }
+
+ NS_HIDDEN_(void) Assign(const self_type& aString)
+ {
+ NS_StringCopy(*this, aString);
+ }
+ NS_HIDDEN_(void) Assign(const char_type* aData, size_type aLength = UINT32_MAX)
+ {
+ NS_StringSetData(*this, aData, aLength);
+ }
+ NS_HIDDEN_(void) Assign(char_type aChar)
+ {
+ NS_StringSetData(*this, &aChar, 1);
+ }
+#ifdef MOZ_USE_CHAR16_WRAPPER
+ NS_HIDDEN_(void) Assign(char16ptr_t aData, size_type aLength = UINT32_MAX)
+ {
+ NS_StringSetData(*this, aData, aLength);
+ }
+#endif
+
+ NS_HIDDEN_(void) AssignLiteral(const char* aStr);
+ NS_HIDDEN_(void) AssignASCII(const char* aStr)
+ {
+ AssignLiteral(aStr);
+ }
+
+ NS_HIDDEN_(self_type&) operator=(const self_type& aString)
+ {
+ Assign(aString);
+ return *this;
+ }
+ NS_HIDDEN_(self_type&) operator=(const char_type* aPtr)
+ {
+ Assign(aPtr);
+ return *this;
+ }
+ NS_HIDDEN_(self_type&) operator=(char_type aChar)
+ {
+ Assign(aChar);
+ return *this;
+ }
+#ifdef MOZ_USE_CHAR16_WRAPPER
+ NS_HIDDEN_(self_type&) operator=(char16ptr_t aPtr)
+ {
+ Assign(aPtr);
+ return *this;
+ }
+#endif
+
+ NS_HIDDEN_(void) Replace(index_type aCutStart, size_type aCutLength,
+ const char_type* aData,
+ size_type aLength = size_type(-1))
+ {
+ NS_StringSetDataRange(*this, aCutStart, aCutLength, aData, aLength);
+ }
+ NS_HIDDEN_(void) Replace(index_type aCutStart, size_type aCutLength,
+ char_type aChar)
+ {
+ Replace(aCutStart, aCutLength, &aChar, 1);
+ }
+ NS_HIDDEN_(void) Replace(index_type aCutStart, size_type aCutLength,
+ const self_type& aReadable)
+ {
+ const char_type* data;
+ uint32_t dataLen = NS_StringGetData(aReadable, &data);
+ NS_StringSetDataRange(*this, aCutStart, aCutLength, data, dataLen);
+ }
+ NS_HIDDEN_(void) SetCharAt(char_type aChar, index_type aPos)
+ {
+ Replace(aPos, 1, &aChar, 1);
+ }
+
+ NS_HIDDEN_(void) Append(char_type aChar)
+ {
+ Replace(size_type(-1), 0, aChar);
+ }
+ NS_HIDDEN_(void) Append(const char_type* aData,
+ size_type aLength = size_type(-1))
+ {
+ Replace(size_type(-1), 0, aData, aLength);
+ }
+#ifdef MOZ_USE_CHAR16_WRAPPER
+ NS_HIDDEN_(void) Append(char16ptr_t aData, size_type aLength = size_type(-1))
+ {
+ Append(static_cast<const char16_t*>(aData), aLength);
+ }
+#endif
+ NS_HIDDEN_(void) Append(const self_type& aReadable)
+ {
+ Replace(size_type(-1), 0, aReadable);
+ }
+ NS_HIDDEN_(void) AppendLiteral(const char* aASCIIStr);
+ NS_HIDDEN_(void) AppendASCII(const char* aASCIIStr)
+ {
+ AppendLiteral(aASCIIStr);
+ }
+
+ NS_HIDDEN_(self_type&) operator+=(char_type aChar)
+ {
+ Append(aChar);
+ return *this;
+ }
+ NS_HIDDEN_(self_type&) operator+=(const char_type* aData)
+ {
+ Append(aData);
+ return *this;
+ }
+ NS_HIDDEN_(self_type&) operator+=(const self_type& aReadable)
+ {
+ Append(aReadable);
+ return *this;
+ }
+
+ NS_HIDDEN_(void) Insert(char_type aChar, index_type aPos)
+ {
+ Replace(aPos, 0, aChar);
+ }
+ NS_HIDDEN_(void) Insert(const char_type* aData, index_type aPos,
+ size_type aLength = size_type(-1))
+ {
+ Replace(aPos, 0, aData, aLength);
+ }
+ NS_HIDDEN_(void) Insert(const self_type& aReadable, index_type aPos)
+ {
+ Replace(aPos, 0, aReadable);
+ }
+
+ NS_HIDDEN_(void) Cut(index_type aCutStart, size_type aCutLength)
+ {
+ Replace(aCutStart, aCutLength, nullptr, 0);
+ }
+
+ NS_HIDDEN_(void) Truncate(size_type aNewLength = 0)
+ {
+ NS_ASSERTION(aNewLength <= Length(), "Truncate cannot make string longer");
+ SetLength(aNewLength);
+ }
+
+ /**
+ * Remove all occurences of characters in aSet from the string.
+ */
+ NS_HIDDEN_(void) StripChars(const char* aSet);
+
+ /**
+ * Strip whitespace characters from the string.
+ */
+ NS_HIDDEN_(void) StripWhitespace() { StripChars("\b\t\r\n "); }
+
+ NS_HIDDEN_(void) Trim(const char* aSet, bool aLeading = true,
+ bool aTrailing = true);
+
+ /**
+ * Compare strings of characters. Return 0 if the characters are equal,
+ */
+ typedef int32_t (*ComparatorFunc)(const char_type* aStrA,
+ const char_type* aStrB,
+ uint32_t aLength);
+
+ static NS_HIDDEN_(int32_t) DefaultComparator(const char_type* aStrA,
+ const char_type* aStrB,
+ uint32_t aLength);
+
+ NS_HIDDEN_(int32_t) Compare(const char_type* aOther,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ NS_HIDDEN_(int32_t) Compare(const self_type& aOther,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ NS_HIDDEN_(bool) Equals(const char_type* aOther,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ NS_HIDDEN_(bool) Equals(const self_type& aOther,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ NS_HIDDEN_(bool) operator<(const self_type& aOther) const
+ {
+ return Compare(aOther) < 0;
+ }
+ NS_HIDDEN_(bool) operator<(const char_type* aOther) const
+ {
+ return Compare(aOther) < 0;
+ }
+
+ NS_HIDDEN_(bool) operator<=(const self_type& aOther) const
+ {
+ return Compare(aOther) <= 0;
+ }
+ NS_HIDDEN_(bool) operator<=(const char_type* aOther) const
+ {
+ return Compare(aOther) <= 0;
+ }
+
+ NS_HIDDEN_(bool) operator==(const self_type& aOther) const
+ {
+ return Equals(aOther);
+ }
+ NS_HIDDEN_(bool) operator==(const char_type* aOther) const
+ {
+ return Equals(aOther);
+ }
+#ifdef MOZ_USE_CHAR16_WRAPPER
+ NS_HIDDEN_(bool) operator==(char16ptr_t aOther) const
+ {
+ return Equals(aOther);
+ }
+#endif
+
+ NS_HIDDEN_(bool) operator>=(const self_type& aOther) const
+ {
+ return Compare(aOther) >= 0;
+ }
+ NS_HIDDEN_(bool) operator>=(const char_type* aOther) const
+ {
+ return Compare(aOther) >= 0;
+ }
+
+ NS_HIDDEN_(bool) operator>(const self_type& aOther) const
+ {
+ return Compare(aOther) > 0;
+ }
+ NS_HIDDEN_(bool) operator>(const char_type* aOther) const
+ {
+ return Compare(aOther) > 0;
+ }
+
+ NS_HIDDEN_(bool) operator!=(const self_type& aOther) const
+ {
+ return !Equals(aOther);
+ }
+ NS_HIDDEN_(bool) operator!=(const char_type* aOther) const
+ {
+ return !Equals(aOther);
+ }
+
+ NS_HIDDEN_(bool) EqualsLiteral(const char* aASCIIString) const;
+ NS_HIDDEN_(bool) EqualsASCII(const char* aASCIIString) const
+ {
+ return EqualsLiteral(aASCIIString);
+ }
+
+ /**
+ * Case-insensitive match this string to a lowercase ASCII string.
+ */
+ NS_HIDDEN_(bool) LowerCaseEqualsLiteral(const char* aASCIIString) const;
+
+ /**
+ * Find the first occurrence of aStr in this string.
+ *
+ * @return the offset of aStr, or -1 if not found
+ */
+ NS_HIDDEN_(int32_t) Find(const self_type& aStr,
+ ComparatorFunc aComparator = DefaultComparator) const
+ {
+ return Find(aStr, 0, aComparator);
+ }
+
+ /**
+ * Find the first occurrence of aStr in this string, beginning at aOffset.
+ *
+ * @return the offset of aStr, or -1 if not found
+ */
+ NS_HIDDEN_(int32_t) Find(const self_type& aStr, uint32_t aOffset,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ /**
+ * Find an ASCII string within this string.
+ *
+ * @return the offset of aStr, or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) Find(const char* aStr, bool aIgnoreCase = false) const
+ {
+ return Find(aStr, 0, aIgnoreCase);
+ }
+
+ NS_HIDDEN_(int32_t) Find(const char* aStr, uint32_t aOffset,
+ bool aIgnoreCase = false) const;
+
+ /**
+ * Find the last occurrence of aStr in this string.
+ *
+ * @return The offset of aStr from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFind(const self_type& aStr,
+ ComparatorFunc aComparator = DefaultComparator) const
+ {
+ return RFind(aStr, -1, aComparator);
+ }
+
+ /**
+ * Find the last occurrence of aStr in this string, beginning at aOffset.
+ *
+ * @param aOffset the offset from the beginning of the string to begin
+ * searching. If aOffset < 0, search from end of this string.
+ * @return The offset of aStr from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFind(const self_type& aStr, int32_t aOffset,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ /**
+ * Find the last occurrence of an ASCII string within this string.
+ *
+ * @return The offset of aStr from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFind(const char* aStr, bool aIgnoreCase = false) const
+ {
+ return RFind(aStr, -1, aIgnoreCase);
+ }
+
+ /**
+ * Find the last occurrence of an ASCII string beginning at aOffset.
+ *
+ * @param aOffset the offset from the beginning of the string to begin
+ * searching. If aOffset < 0, search from end of this string.
+ * @return The offset of aStr from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFind(const char* aStr, int32_t aOffset,
+ bool aIgnoreCase) const;
+
+ /**
+ * Search for the offset of the first occurrence of a character in a
+ * string.
+ *
+ * @param aOffset the offset from the beginning of the string to begin
+ * searching
+ * @return The offset of the character from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) FindChar(char_type aChar, uint32_t aOffset = 0) const;
+
+ /**
+ * Search for the offset of the last occurrence of a character in a
+ * string.
+ *
+ * @return The offset of the character from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFindChar(char_type aChar) const;
+
+ /**
+ * Append a string representation of a number.
+ */
+ NS_HIDDEN_(void) AppendInt(int aInt, int32_t aRadix = 10);
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+ /**
+ * Convert this string to an integer.
+ *
+ * @param aErrorCode pointer to contain result code.
+ * @param aRadix must be 10 or 16
+ */
+ NS_HIDDEN_(int32_t) ToInteger(nsresult* aErrorCode,
+ uint32_t aRadix = 10) const;
+ /**
+ * Convert this string to a 64-bit integer.
+ *
+ * @param aErrorCode pointer to contain result code.
+ * @param aRadix must be 10 or 16
+ */
+ NS_HIDDEN_(int64_t) ToInteger64(nsresult* aErrorCode,
+ uint32_t aRadix = 10) const;
+#endif // XPCOM_GLUE_AVOID_NSPR
+
+protected:
+ // Prevent people from allocating a nsAString directly.
+ ~nsAString() {}
+};
+
+class nsACString
+{
+public:
+ typedef char char_type;
+ typedef nsACString self_type;
+ typedef uint32_t size_type;
+ typedef uint32_t index_type;
+
+ /**
+ * Returns the length, beginning, and end of a string in one operation.
+ */
+ NS_HIDDEN_(uint32_t) BeginReading(const char_type** aBegin,
+ const char_type** aEnd = nullptr) const;
+
+ NS_HIDDEN_(const char_type*) BeginReading() const;
+ NS_HIDDEN_(const char_type*) EndReading() const;
+
+ NS_HIDDEN_(char_type) CharAt(uint32_t aPos) const
+ {
+ NS_ASSERTION(aPos < Length(), "Out of bounds");
+ return BeginReading()[aPos];
+ }
+ NS_HIDDEN_(char_type) operator [](uint32_t aPos) const
+ {
+ return CharAt(aPos);
+ }
+ NS_HIDDEN_(char_type) First() const
+ {
+ return CharAt(0);
+ }
+ NS_HIDDEN_(char_type) Last() const
+ {
+ const char_type* data;
+ uint32_t dataLen = NS_CStringGetData(*this, &data);
+ return data[dataLen - 1];
+ }
+
+ /**
+ * Get the length, begin writing, and optionally set the length of a
+ * string all in one operation.
+ *
+ * @param newSize Size the string to this length. Pass UINT32_MAX
+ * to leave the length unchanged.
+ * @return The new length of the string, or 0 if resizing failed.
+ */
+ NS_HIDDEN_(uint32_t) BeginWriting(char_type** aBegin,
+ char_type** aEnd = nullptr,
+ uint32_t aNewSize = UINT32_MAX);
+
+ NS_HIDDEN_(char_type*) BeginWriting(uint32_t aLen = UINT32_MAX);
+ NS_HIDDEN_(char_type*) EndWriting();
+
+ NS_HIDDEN_(bool) SetLength(uint32_t aLen);
+
+ NS_HIDDEN_(size_type) Length() const
+ {
+ const char_type* data;
+ return NS_CStringGetData(*this, &data);
+ }
+
+ NS_HIDDEN_(bool) IsEmpty() const { return Length() == 0; }
+
+ NS_HIDDEN_(void) SetIsVoid(bool aVal) { NS_CStringSetIsVoid(*this, aVal); }
+ NS_HIDDEN_(bool) IsVoid() const { return NS_CStringGetIsVoid(*this); }
+
+ NS_HIDDEN_(void) Assign(const self_type& aString)
+ {
+ NS_CStringCopy(*this, aString);
+ }
+ NS_HIDDEN_(void) Assign(const char_type* aData, size_type aLength = UINT32_MAX)
+ {
+ NS_CStringSetData(*this, aData, aLength);
+ }
+ NS_HIDDEN_(void) Assign(char_type aChar)
+ {
+ NS_CStringSetData(*this, &aChar, 1);
+ }
+ NS_HIDDEN_(void) AssignLiteral(const char_type* aData)
+ {
+ Assign(aData);
+ }
+ NS_HIDDEN_(void) AssignASCII(const char_type* aData)
+ {
+ Assign(aData);
+ }
+
+ NS_HIDDEN_(self_type&) operator=(const self_type& aString)
+ {
+ Assign(aString);
+ return *this;
+ }
+ NS_HIDDEN_(self_type&) operator=(const char_type* aPtr)
+ {
+ Assign(aPtr);
+ return *this;
+ }
+ NS_HIDDEN_(self_type&) operator=(char_type aChar)
+ {
+ Assign(aChar);
+ return *this;
+ }
+
+ NS_HIDDEN_(void) Replace(index_type aCutStart, size_type aCutLength,
+ const char_type* aData,
+ size_type aLength = size_type(-1))
+ {
+ NS_CStringSetDataRange(*this, aCutStart, aCutLength, aData, aLength);
+ }
+ NS_HIDDEN_(void) Replace(index_type aCutStart, size_type aCutLength,
+ char_type aChar)
+ {
+ Replace(aCutStart, aCutLength, &aChar, 1);
+ }
+ NS_HIDDEN_(void) Replace(index_type aCutStart, size_type aCutLength,
+ const self_type& aReadable)
+ {
+ const char_type* data;
+ uint32_t dataLen = NS_CStringGetData(aReadable, &data);
+ NS_CStringSetDataRange(*this, aCutStart, aCutLength, data, dataLen);
+ }
+ NS_HIDDEN_(void) SetCharAt(char_type aChar, index_type aPos)
+ {
+ Replace(aPos, 1, &aChar, 1);
+ }
+
+ NS_HIDDEN_(void) Append(char_type aChar)
+ {
+ Replace(size_type(-1), 0, aChar);
+ }
+ NS_HIDDEN_(void) Append(const char_type* aData,
+ size_type aLength = size_type(-1))
+ {
+ Replace(size_type(-1), 0, aData, aLength);
+ }
+ NS_HIDDEN_(void) Append(const self_type& aReadable)
+ {
+ Replace(size_type(-1), 0, aReadable);
+ }
+ NS_HIDDEN_(void) AppendLiteral(const char* aASCIIStr)
+ {
+ Append(aASCIIStr);
+ }
+ NS_HIDDEN_(void) AppendASCII(const char* aASCIIStr)
+ {
+ Append(aASCIIStr);
+ }
+
+ NS_HIDDEN_(self_type&) operator+=(char_type aChar)
+ {
+ Append(aChar);
+ return *this;
+ }
+ NS_HIDDEN_(self_type&) operator+=(const char_type* aData)
+ {
+ Append(aData);
+ return *this;
+ }
+ NS_HIDDEN_(self_type&) operator+=(const self_type& aReadable)
+ {
+ Append(aReadable);
+ return *this;
+ }
+
+ NS_HIDDEN_(void) Insert(char_type aChar, index_type aPos)
+ {
+ Replace(aPos, 0, aChar);
+ }
+ NS_HIDDEN_(void) Insert(const char_type* aData, index_type aPos,
+ size_type aLength = size_type(-1))
+ {
+ Replace(aPos, 0, aData, aLength);
+ }
+ NS_HIDDEN_(void) Insert(const self_type& aReadable, index_type aPos)
+ {
+ Replace(aPos, 0, aReadable);
+ }
+
+ NS_HIDDEN_(void) Cut(index_type aCutStart, size_type aCutLength)
+ {
+ Replace(aCutStart, aCutLength, nullptr, 0);
+ }
+
+ NS_HIDDEN_(void) Truncate(size_type aNewLength = 0)
+ {
+ NS_ASSERTION(aNewLength <= Length(), "Truncate cannot make string longer");
+ SetLength(aNewLength);
+ }
+
+ /**
+ * Remove all occurences of characters in aSet from the string.
+ */
+ NS_HIDDEN_(void) StripChars(const char* aSet);
+
+ /**
+ * Strip whitespace characters from the string.
+ */
+ NS_HIDDEN_(void) StripWhitespace() { StripChars("\b\t\r\n "); }
+
+ NS_HIDDEN_(void) Trim(const char* aSet, bool aLeading = true,
+ bool aTrailing = true);
+
+ /**
+ * Compare strings of characters. Return 0 if the characters are equal,
+ */
+ typedef int32_t (*ComparatorFunc)(const char_type* a,
+ const char_type* b,
+ uint32_t length);
+
+ static NS_HIDDEN_(int32_t) DefaultComparator(const char_type* aStrA,
+ const char_type* aStrB,
+ uint32_t aLength);
+
+ NS_HIDDEN_(int32_t) Compare(const char_type* aOther,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ NS_HIDDEN_(int32_t) Compare(const self_type& aOther,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ NS_HIDDEN_(bool) Equals(const char_type* aOther,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ NS_HIDDEN_(bool) Equals(const self_type& aOther,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ NS_HIDDEN_(bool) operator<(const self_type& aOther) const
+ {
+ return Compare(aOther) < 0;
+ }
+ NS_HIDDEN_(bool) operator<(const char_type* aOther) const
+ {
+ return Compare(aOther) < 0;
+ }
+
+ NS_HIDDEN_(bool) operator<=(const self_type& aOther) const
+ {
+ return Compare(aOther) <= 0;
+ }
+ NS_HIDDEN_(bool) operator<=(const char_type* aOther) const
+ {
+ return Compare(aOther) <= 0;
+ }
+
+ NS_HIDDEN_(bool) operator==(const self_type& aOther) const
+ {
+ return Equals(aOther);
+ }
+ NS_HIDDEN_(bool) operator==(const char_type* aOther) const
+ {
+ return Equals(aOther);
+ }
+
+ NS_HIDDEN_(bool) operator>=(const self_type& aOther) const
+ {
+ return Compare(aOther) >= 0;
+ }
+ NS_HIDDEN_(bool) operator>=(const char_type* aOther) const
+ {
+ return Compare(aOther) >= 0;
+ }
+
+ NS_HIDDEN_(bool) operator>(const self_type& aOther) const
+ {
+ return Compare(aOther) > 0;
+ }
+ NS_HIDDEN_(bool) operator>(const char_type* aOther) const
+ {
+ return Compare(aOther) > 0;
+ }
+
+ NS_HIDDEN_(bool) operator!=(const self_type& aOther) const
+ {
+ return !Equals(aOther);
+ }
+ NS_HIDDEN_(bool) operator!=(const char_type* aOther) const
+ {
+ return !Equals(aOther);
+ }
+
+ NS_HIDDEN_(bool) EqualsLiteral(const char_type* aOther) const
+ {
+ return Equals(aOther);
+ }
+ NS_HIDDEN_(bool) EqualsASCII(const char_type* aOther) const
+ {
+ return Equals(aOther);
+ }
+
+ /**
+ * Case-insensitive match this string to a lowercase ASCII string.
+ */
+ NS_HIDDEN_(bool) LowerCaseEqualsLiteral(const char* aASCIIString) const
+ {
+ return Equals(aASCIIString, CaseInsensitiveCompare);
+ }
+
+ /**
+ * Find the first occurrence of aStr in this string.
+ *
+ * @return the offset of aStr, or -1 if not found
+ */
+ NS_HIDDEN_(int32_t) Find(const self_type& aStr,
+ ComparatorFunc aComparator = DefaultComparator) const
+ {
+ return Find(aStr, 0, aComparator);
+ }
+
+ /**
+ * Find the first occurrence of aStr in this string, beginning at aOffset.
+ *
+ * @return the offset of aStr, or -1 if not found
+ */
+ NS_HIDDEN_(int32_t) Find(const self_type& aStr, uint32_t aOffset,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ /**
+ * Find the first occurrence of aStr in this string.
+ *
+ * @return the offset of aStr, or -1 if not found
+ */
+ NS_HIDDEN_(int32_t) Find(const char_type* aStr,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ NS_HIDDEN_(int32_t) Find(const char_type* aStr, uint32_t aLen,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ /**
+ * Find the last occurrence of aStr in this string.
+ *
+ * @return The offset of the character from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFind(const self_type& aStr,
+ ComparatorFunc aComparator = DefaultComparator) const
+ {
+ return RFind(aStr, -1, aComparator);
+ }
+
+ /**
+ * Find the last occurrence of aStr in this string, beginning at aOffset.
+ *
+ * @param aOffset the offset from the beginning of the string to begin
+ * searching. If aOffset < 0, search from end of this string.
+ * @return The offset of aStr from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFind(const self_type& aStr, int32_t aOffset,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ /**
+ * Find the last occurrence of aStr in this string.
+ *
+ * @return The offset of aStr from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFind(const char_type* aStr,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ /**
+ * Find the last occurrence of an ASCII string in this string,
+ * beginning at aOffset.
+ *
+ * @param aLen is the length of aStr
+ * @return The offset of aStr from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFind(const char_type* aStr, int32_t aLen,
+ ComparatorFunc aComparator = DefaultComparator) const;
+
+ /**
+ * Search for the offset of the first occurrence of a character in a
+ * string.
+ *
+ * @param aOffset the offset from the beginning of the string to begin
+ * searching
+ * @return The offset of the character from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) FindChar(char_type aChar, uint32_t aOffset = 0) const;
+
+ /**
+ * Search for the offset of the last occurrence of a character in a
+ * string.
+ *
+ * @return The offset of the character from the beginning of the string,
+ * or -1 if not found.
+ */
+ NS_HIDDEN_(int32_t) RFindChar(char_type aChar) const;
+
+ /**
+ * Append a string representation of a number.
+ */
+ NS_HIDDEN_(void) AppendInt(int aInt, int32_t aRadix = 10);
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+ /**
+ * Convert this string to an integer.
+ *
+ * @param aErrorCode pointer to contain result code.
+ * @param aRadix must be 10 or 16
+ */
+ NS_HIDDEN_(int32_t) ToInteger(nsresult* aErrorCode,
+ uint32_t aRadix = 10) const;
+ /**
+ * Convert this string to a 64-bit integer.
+ *
+ * @param aErrorCode pointer to contain result code.
+ * @param aRadix must be 10 or 16
+ */
+ NS_HIDDEN_(int64_t) ToInteger64(nsresult* aErrorCode,
+ uint32_t aRadix = 10) const;
+#endif // XPCOM_GLUE_AVOID_NSPR
+
+protected:
+ // Prevent people from allocating a nsAString directly.
+ ~nsACString() {}
+};
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * Below we define nsStringContainer and nsCStringContainer. These classes
+ * have unspecified structure. In most cases, your code should use
+ * nsString/nsCString instead of these classes; if you prefer C-style
+ * programming, then look no further.
+ */
+
+class nsStringContainer
+ : public nsAString
+ , private nsStringContainer_base
+{
+};
+
+class nsCStringContainer
+ : public nsACString
+ , private nsStringContainer_base
+{
+};
+
+/**
+ * The following classes are C++ helper classes that make the frozen string
+ * API easier to use.
+ */
+
+/**
+ * Rename symbols to avoid conflicting with internal versions.
+ */
+#define nsString nsString_external
+#define nsCString nsCString_external
+#define nsDependentString nsDependentString_external
+#define nsDependentCString nsDependentCString_external
+#define NS_ConvertASCIItoUTF16 NS_ConvertASCIItoUTF16_external
+#define NS_ConvertUTF8toUTF16 NS_ConvertUTF8toUTF16_external
+#define NS_ConvertUTF16toUTF8 NS_ConvertUTF16toUTF8_external
+#define NS_LossyConvertUTF16toASCII NS_LossyConvertUTF16toASCII_external
+#define nsGetterCopies nsGetterCopies_external
+#define nsCGetterCopies nsCGetterCopies_external
+#define nsDependentSubstring nsDependentSubstring_external
+#define nsDependentCSubstring nsDependentCSubstring_external
+
+/**
+ * basic strings
+ */
+
+class nsString : public nsStringContainer
+{
+public:
+ typedef nsString self_type;
+ typedef nsAString abstract_string_type;
+
+ nsString()
+ {
+ NS_StringContainerInit(*this);
+ }
+
+ nsString(const self_type& aString)
+ {
+ NS_StringContainerInit(*this);
+ NS_StringCopy(*this, aString);
+ }
+
+ explicit nsString(const abstract_string_type& aReadable)
+ {
+ NS_StringContainerInit(*this);
+ NS_StringCopy(*this, aReadable);
+ }
+
+ explicit nsString(const char_type* aData, size_type aLength = UINT32_MAX)
+ {
+ NS_StringContainerInit2(*this, aData, aLength, 0);
+ }
+
+#ifdef MOZ_USE_CHAR16_WRAPPER
+ explicit nsString(char16ptr_t aData, size_type aLength = UINT32_MAX)
+ : nsString(static_cast<const char16_t*>(aData), aLength)
+ {
+ }
+#endif
+
+ ~nsString()
+ {
+ NS_StringContainerFinish(*this);
+ }
+
+ char16ptr_t get() const
+ {
+ return char16ptr_t(BeginReading());
+ }
+
+ self_type& operator=(const self_type& aString)
+ {
+ Assign(aString);
+ return *this;
+ }
+ self_type& operator=(const abstract_string_type& aReadable)
+ {
+ Assign(aReadable);
+ return *this;
+ }
+ self_type& operator=(const char_type* aPtr)
+ {
+ Assign(aPtr);
+ return *this;
+ }
+ self_type& operator=(char_type aChar)
+ {
+ Assign(aChar);
+ return *this;
+ }
+
+ void Adopt(const char_type* aData, size_type aLength = UINT32_MAX)
+ {
+ NS_StringContainerFinish(*this);
+ NS_StringContainerInit2(*this, aData, aLength,
+ NS_STRING_CONTAINER_INIT_ADOPT);
+ }
+
+protected:
+ nsString(const char_type* aData, size_type aLength, uint32_t aFlags)
+ {
+ NS_StringContainerInit2(*this, aData, aLength, aFlags);
+ }
+};
+
+class nsCString : public nsCStringContainer
+{
+public:
+ typedef nsCString self_type;
+ typedef nsACString abstract_string_type;
+
+ nsCString()
+ {
+ NS_CStringContainerInit(*this);
+ }
+
+ nsCString(const self_type& aString)
+ {
+ NS_CStringContainerInit(*this);
+ NS_CStringCopy(*this, aString);
+ }
+
+ explicit nsCString(const abstract_string_type& aReadable)
+ {
+ NS_CStringContainerInit(*this);
+ NS_CStringCopy(*this, aReadable);
+ }
+
+ explicit nsCString(const char_type* aData, size_type aLength = UINT32_MAX)
+ {
+ NS_CStringContainerInit(*this);
+ NS_CStringSetData(*this, aData, aLength);
+ }
+
+ ~nsCString()
+ {
+ NS_CStringContainerFinish(*this);
+ }
+
+ const char_type* get() const
+ {
+ return BeginReading();
+ }
+
+ self_type& operator=(const self_type& aString)
+ {
+ Assign(aString);
+ return *this;
+ }
+ self_type& operator=(const abstract_string_type& aReadable)
+ {
+ Assign(aReadable);
+ return *this;
+ }
+ self_type& operator=(const char_type* aPtr)
+ {
+ Assign(aPtr);
+ return *this;
+ }
+ self_type& operator=(char_type aChar)
+ {
+ Assign(aChar);
+ return *this;
+ }
+
+ void Adopt(const char_type* aData, size_type aLength = UINT32_MAX)
+ {
+ NS_CStringContainerFinish(*this);
+ NS_CStringContainerInit2(*this, aData, aLength,
+ NS_CSTRING_CONTAINER_INIT_ADOPT);
+ }
+
+protected:
+ nsCString(const char_type* aData, size_type aLength, uint32_t aFlags)
+ {
+ NS_CStringContainerInit2(*this, aData, aLength, aFlags);
+ }
+};
+
+
+/**
+ * dependent strings
+ */
+
+class nsDependentString : public nsString
+{
+public:
+ typedef nsDependentString self_type;
+
+ nsDependentString() {}
+
+ explicit nsDependentString(const char_type* aData,
+ size_type aLength = UINT32_MAX)
+ : nsString(aData, aLength, NS_CSTRING_CONTAINER_INIT_DEPEND)
+ {
+ }
+
+#ifdef MOZ_USE_CHAR16_WRAPPER
+ explicit nsDependentString(char16ptr_t aData, size_type aLength = UINT32_MAX)
+ : nsDependentString(static_cast<const char16_t*>(aData), aLength)
+ {
+ }
+#endif
+
+ void Rebind(const char_type* aData, size_type aLength = UINT32_MAX)
+ {
+ NS_StringContainerFinish(*this);
+ NS_StringContainerInit2(*this, aData, aLength,
+ NS_STRING_CONTAINER_INIT_DEPEND);
+ }
+
+private:
+ self_type& operator=(const self_type& aString) = delete;
+};
+
+class nsDependentCString : public nsCString
+{
+public:
+ typedef nsDependentCString self_type;
+
+ nsDependentCString() {}
+
+ explicit nsDependentCString(const char_type* aData,
+ size_type aLength = UINT32_MAX)
+ : nsCString(aData, aLength, NS_CSTRING_CONTAINER_INIT_DEPEND)
+ {
+ }
+
+ void Rebind(const char_type* aData, size_type aLength = UINT32_MAX)
+ {
+ NS_CStringContainerFinish(*this);
+ NS_CStringContainerInit2(*this, aData, aLength,
+ NS_CSTRING_CONTAINER_INIT_DEPEND);
+ }
+
+private:
+ self_type& operator=(const self_type& aString) = delete;
+};
+
+
+/**
+ * conversion classes
+ */
+
+inline void
+CopyUTF16toUTF8(const nsAString& aSource, nsACString& aDest)
+{
+ NS_UTF16ToCString(aSource, NS_CSTRING_ENCODING_UTF8, aDest);
+}
+
+inline void
+CopyUTF8toUTF16(const nsACString& aSource, nsAString& aDest)
+{
+ NS_CStringToUTF16(aSource, NS_CSTRING_ENCODING_UTF8, aDest);
+}
+
+inline void
+LossyCopyUTF16toASCII(const nsAString& aSource, nsACString& aDest)
+{
+ NS_UTF16ToCString(aSource, NS_CSTRING_ENCODING_ASCII, aDest);
+}
+
+inline void
+CopyASCIItoUTF16(const nsACString& aSource, nsAString& aDest)
+{
+ NS_CStringToUTF16(aSource, NS_CSTRING_ENCODING_ASCII, aDest);
+}
+
+char*
+ToNewUTF8String(const nsAString& aSource);
+
+class NS_ConvertASCIItoUTF16 : public nsString
+{
+public:
+ typedef NS_ConvertASCIItoUTF16 self_type;
+
+ explicit NS_ConvertASCIItoUTF16(const nsACString& aStr)
+ {
+ NS_CStringToUTF16(aStr, NS_CSTRING_ENCODING_ASCII, *this);
+ }
+
+ explicit NS_ConvertASCIItoUTF16(const char* aData,
+ uint32_t aLength = UINT32_MAX)
+ {
+ NS_CStringToUTF16(nsDependentCString(aData, aLength),
+ NS_CSTRING_ENCODING_ASCII, *this);
+ }
+
+private:
+ self_type& operator=(const self_type& aString) = delete;
+};
+
+class NS_ConvertUTF8toUTF16 : public nsString
+{
+public:
+ typedef NS_ConvertUTF8toUTF16 self_type;
+
+ explicit NS_ConvertUTF8toUTF16(const nsACString& aStr)
+ {
+ NS_CStringToUTF16(aStr, NS_CSTRING_ENCODING_UTF8, *this);
+ }
+
+ explicit NS_ConvertUTF8toUTF16(const char* aData,
+ uint32_t aLength = UINT32_MAX)
+ {
+ NS_CStringToUTF16(nsDependentCString(aData, aLength),
+ NS_CSTRING_ENCODING_UTF8, *this);
+ }
+
+private:
+ self_type& operator=(const self_type& aString) = delete;
+};
+
+class NS_ConvertUTF16toUTF8 : public nsCString
+{
+public:
+ typedef NS_ConvertUTF16toUTF8 self_type;
+
+ explicit NS_ConvertUTF16toUTF8(const nsAString& aStr)
+ {
+ NS_UTF16ToCString(aStr, NS_CSTRING_ENCODING_UTF8, *this);
+ }
+
+ explicit NS_ConvertUTF16toUTF8(const char16ptr_t aData,
+ uint32_t aLength = UINT32_MAX)
+ {
+ NS_UTF16ToCString(nsDependentString(aData, aLength),
+ NS_CSTRING_ENCODING_UTF8, *this);
+ }
+
+private:
+ self_type& operator=(const self_type& aString) = delete;
+};
+
+class NS_LossyConvertUTF16toASCII : public nsCString
+{
+public:
+ typedef NS_LossyConvertUTF16toASCII self_type;
+
+ explicit NS_LossyConvertUTF16toASCII(const nsAString& aStr)
+ {
+ NS_UTF16ToCString(aStr, NS_CSTRING_ENCODING_ASCII, *this);
+ }
+
+ explicit NS_LossyConvertUTF16toASCII(const char16ptr_t aData,
+ uint32_t aLength = UINT32_MAX)
+ {
+ NS_UTF16ToCString(nsDependentString(aData, aLength),
+ NS_CSTRING_ENCODING_ASCII, *this);
+ }
+
+private:
+ self_type& operator=(const self_type& aString) = delete;
+};
+
+
+/**
+ * literal strings
+ */
+static_assert(sizeof(char16_t) == 2, "size of char16_t must be 2");
+static_assert(char16_t(-1) > char16_t(0), "char16_t must be unsigned");
+
+#define NS_MULTILINE_LITERAL_STRING(s) \
+ nsDependentString(reinterpret_cast<const nsAString::char_type*>(s), \
+ uint32_t((sizeof(s) / 2) - 1))
+#define NS_MULTILINE_LITERAL_STRING_INIT(n, s) \
+ n(reinterpret_cast<const nsAString::char_type*>(s), \
+ uint32_t((sizeof(s) / 2) - 1))
+#define NS_NAMED_MULTILINE_LITERAL_STRING(n,s) \
+ const nsDependentString n(reinterpret_cast<const nsAString::char_type*>(s), \
+ uint32_t((sizeof(s) / 2) - 1))
+typedef nsDependentString nsLiteralString;
+
+#define NS_LITERAL_STRING(s) \
+ static_cast<const nsString&>(NS_MULTILINE_LITERAL_STRING(u"" s))
+#define NS_LITERAL_STRING_INIT(n, s) \
+ NS_MULTILINE_LITERAL_STRING_INIT(n, (u"" s))
+#define NS_NAMED_LITERAL_STRING(n, s) \
+ NS_NAMED_MULTILINE_LITERAL_STRING(n, (u"" s))
+
+#define NS_LITERAL_CSTRING(s) \
+ static_cast<const nsDependentCString&>(nsDependentCString(s, uint32_t(sizeof(s) - 1)))
+#define NS_LITERAL_CSTRING_INIT(n, s) \
+ n(s, uint32_t(sizeof(s)-1))
+#define NS_NAMED_LITERAL_CSTRING(n, s) \
+ const nsDependentCString n(s, uint32_t(sizeof(s)-1))
+
+typedef nsDependentCString nsLiteralCString;
+
+
+/**
+ * getter_Copies support
+ *
+ * NS_IMETHOD GetBlah(char16_t**);
+ *
+ * void some_function()
+ * {
+ * nsString blah;
+ * GetBlah(getter_Copies(blah));
+ * // ...
+ * }
+ */
+
+class nsGetterCopies
+{
+public:
+ typedef char16_t char_type;
+
+ explicit nsGetterCopies(nsString& aStr)
+ : mString(aStr)
+ , mData(nullptr)
+ {
+ }
+
+ ~nsGetterCopies() { mString.Adopt(mData); }
+
+ operator char_type**() { return &mData; }
+
+private:
+ nsString& mString;
+ char_type* mData;
+};
+
+inline nsGetterCopies
+getter_Copies(nsString& aString)
+{
+ return nsGetterCopies(aString);
+}
+
+class nsCGetterCopies
+{
+public:
+ typedef char char_type;
+
+ explicit nsCGetterCopies(nsCString& aStr)
+ : mString(aStr)
+ , mData(nullptr)
+ {
+ }
+
+ ~nsCGetterCopies() { mString.Adopt(mData); }
+
+ operator char_type**() { return &mData; }
+
+private:
+ nsCString& mString;
+ char_type* mData;
+};
+
+inline nsCGetterCopies
+getter_Copies(nsCString& aString)
+{
+ return nsCGetterCopies(aString);
+}
+
+
+/**
+* substrings
+*/
+
+class nsDependentSubstring : public nsStringContainer
+{
+public:
+ typedef nsDependentSubstring self_type;
+ typedef nsAString abstract_string_type;
+
+ ~nsDependentSubstring() { NS_StringContainerFinish(*this); }
+ nsDependentSubstring() { NS_StringContainerInit(*this); }
+
+ nsDependentSubstring(const char_type* aStart, uint32_t aLength)
+ {
+ NS_StringContainerInit2(*this, aStart, aLength,
+ NS_STRING_CONTAINER_INIT_DEPEND |
+ NS_STRING_CONTAINER_INIT_SUBSTRING);
+ }
+
+ nsDependentSubstring(const abstract_string_type& aStr,
+ uint32_t aStartPos);
+ nsDependentSubstring(const abstract_string_type& aStr,
+ uint32_t aStartPos, uint32_t aLength);
+
+ void Rebind(const char_type* aStart, uint32_t aLength)
+ {
+ NS_StringContainerFinish(*this);
+ NS_StringContainerInit2(*this, aStart, aLength,
+ NS_STRING_CONTAINER_INIT_DEPEND |
+ NS_STRING_CONTAINER_INIT_SUBSTRING);
+ }
+
+private:
+ self_type& operator=(const self_type& aString) = delete;
+};
+
+class nsDependentCSubstring : public nsCStringContainer
+{
+public:
+ typedef nsDependentCSubstring self_type;
+ typedef nsACString abstract_string_type;
+
+ ~nsDependentCSubstring() { NS_CStringContainerFinish(*this); }
+ nsDependentCSubstring() { NS_CStringContainerInit(*this); }
+
+ nsDependentCSubstring(const char_type* aStart, uint32_t aLength)
+ {
+ NS_CStringContainerInit2(*this, aStart, aLength,
+ NS_CSTRING_CONTAINER_INIT_DEPEND |
+ NS_CSTRING_CONTAINER_INIT_SUBSTRING);
+ }
+
+ nsDependentCSubstring(const abstract_string_type& aStr,
+ uint32_t aStartPos);
+ nsDependentCSubstring(const abstract_string_type& aStr,
+ uint32_t aStartPos, uint32_t aLength);
+
+ void Rebind(const char_type* aStart, uint32_t aLength)
+ {
+ NS_CStringContainerFinish(*this);
+ NS_CStringContainerInit2(*this, aStart, aLength,
+ NS_CSTRING_CONTAINER_INIT_DEPEND |
+ NS_CSTRING_CONTAINER_INIT_SUBSTRING);
+ }
+
+private:
+ self_type& operator=(const self_type& aString) = delete;
+};
+
+
+/**
+ * Various nsDependentC?Substring constructor functions
+ */
+
+// char16_t
+inline const nsDependentSubstring
+Substring(const nsAString& aStr, uint32_t aStartPos)
+{
+ return nsDependentSubstring(aStr, aStartPos);
+}
+
+inline const nsDependentSubstring
+Substring(const nsAString& aStr, uint32_t aStartPos, uint32_t aLength)
+{
+ return nsDependentSubstring(aStr, aStartPos, aLength);
+}
+
+inline const nsDependentSubstring
+Substring(const char16_t* aStart, const char16_t* aEnd)
+{
+ MOZ_ASSERT(uint32_t(aEnd - aStart) == uintptr_t(aEnd - aStart),
+ "string too long");
+ return nsDependentSubstring(aStart, uint32_t(aEnd - aStart));
+}
+
+inline const nsDependentSubstring
+Substring(const char16_t* aStart, uint32_t aLength)
+{
+ return nsDependentSubstring(aStart, aLength);
+}
+
+inline const nsDependentSubstring
+StringHead(const nsAString& aStr, uint32_t aCount)
+{
+ return nsDependentSubstring(aStr, 0, aCount);
+}
+
+inline const nsDependentSubstring
+StringTail(const nsAString& aStr, uint32_t aCount)
+{
+ return nsDependentSubstring(aStr, aStr.Length() - aCount, aCount);
+}
+
+// char
+inline const nsDependentCSubstring
+Substring(const nsACString& aStr, uint32_t aStartPos)
+{
+ return nsDependentCSubstring(aStr, aStartPos);
+}
+
+inline const nsDependentCSubstring
+Substring(const nsACString& aStr, uint32_t aStartPos, uint32_t aLength)
+{
+ return nsDependentCSubstring(aStr, aStartPos, aLength);
+}
+
+inline const nsDependentCSubstring
+Substring(const char* aStart, const char* aEnd)
+{
+ MOZ_ASSERT(uint32_t(aEnd - aStart) == uintptr_t(aEnd - aStart),
+ "string too long");
+ return nsDependentCSubstring(aStart, uint32_t(aEnd - aStart));
+}
+
+inline const nsDependentCSubstring
+Substring(const char* aStart, uint32_t aLength)
+{
+ return nsDependentCSubstring(aStart, aLength);
+}
+
+inline const nsDependentCSubstring
+StringHead(const nsACString& aStr, uint32_t aCount)
+{
+ return nsDependentCSubstring(aStr, 0, aCount);
+}
+
+inline const nsDependentCSubstring
+StringTail(const nsACString& aStr, uint32_t aCount)
+{
+ return nsDependentCSubstring(aStr, aStr.Length() - aCount, aCount);
+}
+
+
+inline bool
+StringBeginsWith(const nsAString& aSource, const nsAString& aSubstring,
+ nsAString::ComparatorFunc aComparator = nsAString::DefaultComparator)
+{
+ return aSubstring.Length() <= aSource.Length() &&
+ StringHead(aSource, aSubstring.Length()).Equals(aSubstring, aComparator);
+}
+
+inline bool
+StringEndsWith(const nsAString& aSource, const nsAString& aSubstring,
+ nsAString::ComparatorFunc aComparator = nsAString::DefaultComparator)
+{
+ return aSubstring.Length() <= aSource.Length() &&
+ StringTail(aSource, aSubstring.Length()).Equals(aSubstring, aComparator);
+}
+
+inline bool
+StringBeginsWith(const nsACString& aSource, const nsACString& aSubstring,
+ nsACString::ComparatorFunc aComparator = nsACString::DefaultComparator)
+{
+ return aSubstring.Length() <= aSource.Length() &&
+ StringHead(aSource, aSubstring.Length()).Equals(aSubstring, aComparator);
+}
+
+inline bool
+StringEndsWith(const nsACString& aSource, const nsACString& aSubstring,
+ nsACString::ComparatorFunc aComparator = nsACString::DefaultComparator)
+{
+ return aSubstring.Length() <= aSource.Length() &&
+ StringTail(aSource, aSubstring.Length()).Equals(aSubstring, aComparator);
+}
+
+/**
+ * Trim whitespace from the beginning and end of a string; then compress
+ * remaining runs of whitespace characters to a single space.
+ */
+NS_HIDDEN_(void) CompressWhitespace(nsAString& aString);
+
+#define EmptyCString() nsCString()
+#define EmptyString() nsString()
+
+/**
+ * Convert an ASCII string to all upper/lowercase (a-z,A-Z only). As a bonus,
+ * returns the string length.
+ */
+NS_HIDDEN_(uint32_t) ToLowerCase(nsACString& aStr);
+
+NS_HIDDEN_(uint32_t) ToUpperCase(nsACString& aStr);
+
+NS_HIDDEN_(uint32_t) ToLowerCase(const nsACString& aSrc, nsACString& aDest);
+
+NS_HIDDEN_(uint32_t) ToUpperCase(const nsACString& aSrc, nsACString& aDest);
+
+/**
+ * The following declarations are *deprecated*, and are included here only
+ * to make porting from existing code that doesn't use the frozen string API
+ * easier. They may disappear in the future.
+ */
+
+inline char*
+ToNewCString(const nsACString& aStr)
+{
+ return NS_CStringCloneData(aStr);
+}
+
+inline char16_t*
+ToNewUnicode(const nsAString& aStr)
+{
+ return NS_StringCloneData(aStr);
+}
+
+typedef nsString PromiseFlatString;
+typedef nsCString PromiseFlatCString;
+
+typedef nsCString nsAutoCString;
+typedef nsString nsAutoString;
+
+NS_HIDDEN_(bool) ParseString(const nsACString& aAstring, char aDelimiter,
+ nsTArray<nsCString>& aArray);
+
+#endif // nsStringAPI_h__
diff --git a/xpcom/glue/nsStringGlue.h b/xpcom/glue/nsStringGlue.h
new file mode 100644
index 000000000..a444eb8a1
--- /dev/null
+++ b/xpcom/glue/nsStringGlue.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// IWYU pragma: private, include "nsString.h"
+
+/**
+ * @file nsStringGlue.h
+ * This header exists solely to #include the proper internal/frozen string
+ * headers, depending on whether MOZILLA_INTERNAL_API is defined.
+ */
+
+#ifndef nsStringGlue_h__
+#define nsStringGlue_h__
+
+#ifdef MOZILLA_INTERNAL_API
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#else
+#include "nsStringAPI.h"
+#endif
+
+#endif // nsStringGlue_h__
diff --git a/xpcom/glue/nsTArray-inl.h b/xpcom/glue/nsTArray-inl.h
new file mode 100644
index 000000000..af57c9866
--- /dev/null
+++ b/xpcom/glue/nsTArray-inl.h
@@ -0,0 +1,463 @@
+/* -*- 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 nsTArray_h__
+# error "Don't include this file directly"
+#endif
+
+template<class Alloc, class Copy>
+nsTArray_base<Alloc, Copy>::nsTArray_base()
+ : mHdr(EmptyHdr())
+{
+ MOZ_COUNT_CTOR(nsTArray_base);
+}
+
+template<class Alloc, class Copy>
+nsTArray_base<Alloc, Copy>::~nsTArray_base()
+{
+ if (mHdr != EmptyHdr() && !UsesAutoArrayBuffer()) {
+ Alloc::Free(mHdr);
+ }
+ MOZ_COUNT_DTOR(nsTArray_base);
+}
+
+template<class Alloc, class Copy>
+const nsTArrayHeader*
+nsTArray_base<Alloc, Copy>::GetAutoArrayBufferUnsafe(size_t aElemAlign) const
+{
+ // Assuming |this| points to an nsAutoArray, we want to get a pointer to
+ // mAutoBuf. So just cast |this| to nsAutoArray* and read &mAutoBuf!
+
+ const void* autoBuf =
+ &reinterpret_cast<const AutoTArray<nsTArray<uint32_t>, 1>*>(this)->mAutoBuf;
+
+ // If we're on a 32-bit system and aElemAlign is 8, we need to adjust our
+ // pointer to take into account the extra alignment in the auto array.
+
+ static_assert(sizeof(void*) != 4 ||
+ (MOZ_ALIGNOF(mozilla::AlignedElem<8>) == 8 &&
+ sizeof(AutoTArray<mozilla::AlignedElem<8>, 1>) ==
+ sizeof(void*) + sizeof(nsTArrayHeader) +
+ 4 + sizeof(mozilla::AlignedElem<8>)),
+ "auto array padding wasn't what we expected");
+
+ // We don't support alignments greater than 8 bytes.
+ MOZ_ASSERT(aElemAlign <= 4 || aElemAlign == 8,
+ "unsupported alignment.");
+ if (sizeof(void*) == 4 && aElemAlign == 8) {
+ autoBuf = reinterpret_cast<const char*>(autoBuf) + 4;
+ }
+
+ return reinterpret_cast<const Header*>(autoBuf);
+}
+
+template<class Alloc, class Copy>
+bool
+nsTArray_base<Alloc, Copy>::UsesAutoArrayBuffer() const
+{
+ if (!mHdr->mIsAutoArray) {
+ return false;
+ }
+
+ // This is nuts. If we were sane, we'd pass aElemAlign as a parameter to
+ // this function. Unfortunately this function is called in nsTArray_base's
+ // destructor, at which point we don't know elem_type's alignment.
+ //
+ // We'll fall on our face and return true when we should say false if
+ //
+ // * we're not using our auto buffer,
+ // * aElemAlign == 4, and
+ // * mHdr == GetAutoArrayBuffer(8).
+ //
+ // This could happen if |*this| lives on the heap and malloc allocated our
+ // buffer on the heap adjacent to |*this|.
+ //
+ // However, we can show that this can't happen. If |this| is an auto array
+ // (as we ensured at the beginning of the method), GetAutoArrayBuffer(8)
+ // always points to memory owned by |*this|, because (as we assert below)
+ //
+ // * GetAutoArrayBuffer(8) is at most 4 bytes past GetAutoArrayBuffer(4), and
+ // * sizeof(nsTArrayHeader) > 4.
+ //
+ // Since AutoTArray always contains an nsTArrayHeader,
+ // GetAutoArrayBuffer(8) will always point inside the auto array object,
+ // even if it doesn't point at the beginning of the header.
+ //
+ // Note that this means that we can't store elements with alignment 16 in an
+ // nsTArray, because GetAutoArrayBuffer(16) could lie outside the memory
+ // owned by this AutoTArray. We statically assert that elem_type's
+ // alignment is 8 bytes or less in AutoTArray.
+
+ static_assert(sizeof(nsTArrayHeader) > 4,
+ "see comment above");
+
+#ifdef DEBUG
+ ptrdiff_t diff = reinterpret_cast<const char*>(GetAutoArrayBuffer(8)) -
+ reinterpret_cast<const char*>(GetAutoArrayBuffer(4));
+ MOZ_ASSERT(diff >= 0 && diff <= 4,
+ "GetAutoArrayBuffer doesn't do what we expect.");
+#endif
+
+ return mHdr == GetAutoArrayBuffer(4) || mHdr == GetAutoArrayBuffer(8);
+}
+
+// defined in nsTArray.cpp
+bool IsTwiceTheRequiredBytesRepresentableAsUint32(size_t aCapacity,
+ size_t aElemSize);
+
+template<class Alloc, class Copy>
+template<typename ActualAlloc>
+typename ActualAlloc::ResultTypeProxy
+nsTArray_base<Alloc, Copy>::EnsureCapacity(size_type aCapacity,
+ size_type aElemSize)
+{
+ // This should be the most common case so test this first
+ if (aCapacity <= mHdr->mCapacity) {
+ return ActualAlloc::SuccessResult();
+ }
+
+ // If the requested memory allocation exceeds size_type(-1)/2, then
+ // our doubling algorithm may not be able to allocate it.
+ // Additionally, if it exceeds uint32_t(-1) then we couldn't fit in the
+ // Header::mCapacity member. Just bail out in cases like that. We don't want
+ // to be allocating 2 GB+ arrays anyway.
+ if (!IsTwiceTheRequiredBytesRepresentableAsUint32(aCapacity, aElemSize)) {
+ ActualAlloc::SizeTooBig((size_t)aCapacity * aElemSize);
+ return ActualAlloc::FailureResult();
+ }
+
+ size_t reqSize = sizeof(Header) + aCapacity * aElemSize;
+
+ if (mHdr == EmptyHdr()) {
+ // Malloc() new data
+ Header* header = static_cast<Header*>(ActualAlloc::Malloc(reqSize));
+ if (!header) {
+ return ActualAlloc::FailureResult();
+ }
+ header->mLength = 0;
+ header->mCapacity = aCapacity;
+ header->mIsAutoArray = 0;
+ mHdr = header;
+
+ return ActualAlloc::SuccessResult();
+ }
+
+ // We increase our capacity so that the allocated buffer grows exponentially,
+ // which gives us amortized O(1) appending. Below the threshold, we use
+ // powers-of-two. Above the threshold, we grow by at least 1.125, rounding up
+ // to the nearest MiB.
+ const size_t slowGrowthThreshold = 8 * 1024 * 1024;
+
+ size_t bytesToAlloc;
+ if (reqSize >= slowGrowthThreshold) {
+ size_t currSize = sizeof(Header) + Capacity() * aElemSize;
+ size_t minNewSize = currSize + (currSize >> 3); // multiply by 1.125
+ bytesToAlloc = reqSize > minNewSize ? reqSize : minNewSize;
+
+ // Round up to the next multiple of MiB.
+ const size_t MiB = 1 << 20;
+ bytesToAlloc = MiB * ((bytesToAlloc + MiB - 1) / MiB);
+ } else {
+ // Round up to the next power of two.
+ bytesToAlloc = mozilla::RoundUpPow2(reqSize);
+ }
+
+ Header* header;
+ if (UsesAutoArrayBuffer() || !Copy::allowRealloc) {
+ // Malloc() and copy
+ header = static_cast<Header*>(ActualAlloc::Malloc(bytesToAlloc));
+ if (!header) {
+ return ActualAlloc::FailureResult();
+ }
+
+ Copy::MoveNonOverlappingRegionWithHeader(header, mHdr, Length(), aElemSize);
+
+ if (!UsesAutoArrayBuffer()) {
+ ActualAlloc::Free(mHdr);
+ }
+ } else {
+ // Realloc() existing data
+ header = static_cast<Header*>(ActualAlloc::Realloc(mHdr, bytesToAlloc));
+ if (!header) {
+ return ActualAlloc::FailureResult();
+ }
+ }
+
+ // How many elements can we fit in bytesToAlloc?
+ size_t newCapacity = (bytesToAlloc - sizeof(Header)) / aElemSize;
+ MOZ_ASSERT(newCapacity >= aCapacity, "Didn't enlarge the array enough!");
+ header->mCapacity = newCapacity;
+
+ mHdr = header;
+
+ return ActualAlloc::SuccessResult();
+}
+
+// We don't need use Alloc template parameter specified here because failure to
+// shrink the capacity will leave the array unchanged.
+template<class Alloc, class Copy>
+void
+nsTArray_base<Alloc, Copy>::ShrinkCapacity(size_type aElemSize,
+ size_t aElemAlign)
+{
+ if (mHdr == EmptyHdr() || UsesAutoArrayBuffer()) {
+ return;
+ }
+
+ if (mHdr->mLength >= mHdr->mCapacity) { // should never be greater than...
+ return;
+ }
+
+ size_type length = Length();
+
+ if (IsAutoArray() && GetAutoArrayBuffer(aElemAlign)->mCapacity >= length) {
+ Header* header = GetAutoArrayBuffer(aElemAlign);
+
+ // Move the data, but don't copy the header to avoid overwriting mCapacity.
+ header->mLength = length;
+ Copy::MoveNonOverlappingRegion(header + 1, mHdr + 1, length, aElemSize);
+
+ nsTArrayFallibleAllocator::Free(mHdr);
+ mHdr = header;
+ return;
+ }
+
+ if (length == 0) {
+ MOZ_ASSERT(!IsAutoArray(), "autoarray should have fit 0 elements");
+ nsTArrayFallibleAllocator::Free(mHdr);
+ mHdr = EmptyHdr();
+ return;
+ }
+
+ size_type size = sizeof(Header) + length * aElemSize;
+ void* ptr = nsTArrayFallibleAllocator::Realloc(mHdr, size);
+ if (!ptr) {
+ return;
+ }
+ mHdr = static_cast<Header*>(ptr);
+ mHdr->mCapacity = length;
+}
+
+template<class Alloc, class Copy>
+template<typename ActualAlloc>
+void
+nsTArray_base<Alloc, Copy>::ShiftData(index_type aStart,
+ size_type aOldLen, size_type aNewLen,
+ size_type aElemSize, size_t aElemAlign)
+{
+ if (aOldLen == aNewLen) {
+ return;
+ }
+
+ // Determine how many elements need to be shifted
+ size_type num = mHdr->mLength - (aStart + aOldLen);
+
+ // Compute the resulting length of the array
+ mHdr->mLength += aNewLen - aOldLen;
+ if (mHdr->mLength == 0) {
+ ShrinkCapacity(aElemSize, aElemAlign);
+ } else {
+ // Maybe nothing needs to be shifted
+ if (num == 0) {
+ return;
+ }
+ // Perform shift (change units to bytes first)
+ aStart *= aElemSize;
+ aNewLen *= aElemSize;
+ aOldLen *= aElemSize;
+ char* baseAddr = reinterpret_cast<char*>(mHdr + 1) + aStart;
+ Copy::MoveOverlappingRegion(baseAddr + aNewLen, baseAddr + aOldLen, num, aElemSize);
+ }
+}
+
+template<class Alloc, class Copy>
+template<typename ActualAlloc>
+bool
+nsTArray_base<Alloc, Copy>::InsertSlotsAt(index_type aIndex, size_type aCount,
+ size_type aElemSize,
+ size_t aElemAlign)
+{
+ MOZ_ASSERT(aIndex <= Length(), "Bogus insertion index");
+ size_type newLen = Length() + aCount;
+
+ EnsureCapacity<ActualAlloc>(newLen, aElemSize);
+
+ // Check for out of memory conditions
+ if (Capacity() < newLen) {
+ return false;
+ }
+
+ // Move the existing elements as needed. Note that this will
+ // change our mLength, so no need to call IncrementLength.
+ ShiftData<ActualAlloc>(aIndex, 0, aCount, aElemSize, aElemAlign);
+
+ return true;
+}
+
+// nsTArray_base::IsAutoArrayRestorer is an RAII class which takes
+// |nsTArray_base &array| in its constructor. When it's destructed, it ensures
+// that
+//
+// * array.mIsAutoArray has the same value as it did when we started, and
+// * if array has an auto buffer and mHdr would otherwise point to sEmptyHdr,
+// array.mHdr points to array's auto buffer.
+
+template<class Alloc, class Copy>
+nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::IsAutoArrayRestorer(
+ nsTArray_base<Alloc, Copy>& aArray,
+ size_t aElemAlign)
+ : mArray(aArray)
+ , mElemAlign(aElemAlign)
+ , mIsAuto(aArray.IsAutoArray())
+{
+}
+
+template<class Alloc, class Copy>
+nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::~IsAutoArrayRestorer()
+{
+ // Careful: We don't want to set mIsAutoArray = 1 on sEmptyHdr.
+ if (mIsAuto && mArray.mHdr == mArray.EmptyHdr()) {
+ // Call GetAutoArrayBufferUnsafe() because GetAutoArrayBuffer() asserts
+ // that mHdr->mIsAutoArray is true, which surely isn't the case here.
+ mArray.mHdr = mArray.GetAutoArrayBufferUnsafe(mElemAlign);
+ mArray.mHdr->mLength = 0;
+ } else if (mArray.mHdr != mArray.EmptyHdr()) {
+ mArray.mHdr->mIsAutoArray = mIsAuto;
+ }
+}
+
+template<class Alloc, class Copy>
+template<typename ActualAlloc, class Allocator>
+typename ActualAlloc::ResultTypeProxy
+nsTArray_base<Alloc, Copy>::SwapArrayElements(nsTArray_base<Allocator,
+ Copy>& aOther,
+ size_type aElemSize,
+ size_t aElemAlign)
+{
+
+ // EnsureNotUsingAutoArrayBuffer will set mHdr = sEmptyHdr even if we have an
+ // auto buffer. We need to point mHdr back to our auto buffer before we
+ // return, otherwise we'll forget that we have an auto buffer at all!
+ // IsAutoArrayRestorer takes care of this for us.
+
+ IsAutoArrayRestorer ourAutoRestorer(*this, aElemAlign);
+ typename nsTArray_base<Allocator, Copy>::IsAutoArrayRestorer
+ otherAutoRestorer(aOther, aElemAlign);
+
+ // If neither array uses an auto buffer which is big enough to store the
+ // other array's elements, then ensure that both arrays use malloc'ed storage
+ // and swap their mHdr pointers.
+ if ((!UsesAutoArrayBuffer() || Capacity() < aOther.Length()) &&
+ (!aOther.UsesAutoArrayBuffer() || aOther.Capacity() < Length())) {
+
+ if (!EnsureNotUsingAutoArrayBuffer<ActualAlloc>(aElemSize) ||
+ !aOther.template EnsureNotUsingAutoArrayBuffer<ActualAlloc>(aElemSize)) {
+ return ActualAlloc::FailureResult();
+ }
+
+ Header* temp = mHdr;
+ mHdr = aOther.mHdr;
+ aOther.mHdr = temp;
+
+ return ActualAlloc::SuccessResult();
+ }
+
+ // Swap the two arrays by copying, since at least one is using an auto
+ // buffer which is large enough to hold all of the aOther's elements. We'll
+ // copy the shorter array into temporary storage.
+ //
+ // (We could do better than this in some circumstances. Suppose we're
+ // swapping arrays X and Y. X has space for 2 elements in its auto buffer,
+ // but currently has length 4, so it's using malloc'ed storage. Y has length
+ // 2. When we swap X and Y, we don't need to use a temporary buffer; we can
+ // write Y straight into X's auto buffer, write X's malloc'ed buffer on top
+ // of Y, and then switch X to using its auto buffer.)
+
+ if (!ActualAlloc::Successful(EnsureCapacity<ActualAlloc>(aOther.Length(), aElemSize)) ||
+ !Allocator::Successful(aOther.template EnsureCapacity<Allocator>(Length(), aElemSize))) {
+ return ActualAlloc::FailureResult();
+ }
+
+ // The EnsureCapacity calls above shouldn't have caused *both* arrays to
+ // switch from their auto buffers to malloc'ed space.
+ MOZ_ASSERT(UsesAutoArrayBuffer() || aOther.UsesAutoArrayBuffer(),
+ "One of the arrays should be using its auto buffer.");
+
+ size_type smallerLength = XPCOM_MIN(Length(), aOther.Length());
+ size_type largerLength = XPCOM_MAX(Length(), aOther.Length());
+ void* smallerElements;
+ void* largerElements;
+ if (Length() <= aOther.Length()) {
+ smallerElements = Hdr() + 1;
+ largerElements = aOther.Hdr() + 1;
+ } else {
+ smallerElements = aOther.Hdr() + 1;
+ largerElements = Hdr() + 1;
+ }
+
+ // Allocate temporary storage for the smaller of the two arrays. We want to
+ // allocate this space on the stack, if it's not too large. Sounds like a
+ // job for AutoTArray! (One of the two arrays we're swapping is using an
+ // auto buffer, so we're likely not allocating a lot of space here. But one
+ // could, in theory, allocate a huge AutoTArray on the heap.)
+ AutoTArray<nsTArray_Impl<uint8_t, ActualAlloc>, 64> temp;
+ if (!ActualAlloc::Successful(temp.template EnsureCapacity<ActualAlloc>(smallerLength,
+ aElemSize))) {
+ return ActualAlloc::FailureResult();
+ }
+
+ Copy::MoveNonOverlappingRegion(temp.Elements(), smallerElements, smallerLength, aElemSize);
+ Copy::MoveNonOverlappingRegion(smallerElements, largerElements, largerLength, aElemSize);
+ Copy::MoveNonOverlappingRegion(largerElements, temp.Elements(), smallerLength, aElemSize);
+
+ // Swap the arrays' lengths.
+ MOZ_ASSERT((aOther.Length() == 0 || mHdr != EmptyHdr()) &&
+ (Length() == 0 || aOther.mHdr != EmptyHdr()),
+ "Don't set sEmptyHdr's length.");
+ size_type tempLength = Length();
+
+ // Avoid writing to EmptyHdr, since it can trigger false
+ // positives with TSan.
+ if (mHdr != EmptyHdr()) {
+ mHdr->mLength = aOther.Length();
+ }
+ if (aOther.mHdr != EmptyHdr()) {
+ aOther.mHdr->mLength = tempLength;
+ }
+
+ return ActualAlloc::SuccessResult();
+}
+
+template<class Alloc, class Copy>
+template<typename ActualAlloc>
+bool
+nsTArray_base<Alloc, Copy>::EnsureNotUsingAutoArrayBuffer(size_type aElemSize)
+{
+ if (UsesAutoArrayBuffer()) {
+
+ // If you call this on a 0-length array, we'll set that array's mHdr to
+ // sEmptyHdr, in flagrant violation of the AutoTArray invariants. It's
+ // up to you to set it back! (If you don't, the AutoTArray will forget
+ // that it has an auto buffer.)
+ if (Length() == 0) {
+ mHdr = EmptyHdr();
+ return true;
+ }
+
+ size_type size = sizeof(Header) + Length() * aElemSize;
+
+ Header* header = static_cast<Header*>(ActualAlloc::Malloc(size));
+ if (!header) {
+ return false;
+ }
+
+ Copy::MoveNonOverlappingRegionWithHeader(header, mHdr, Length(), aElemSize);
+ header->mCapacity = Length();
+ mHdr = header;
+ }
+
+ return true;
+}
diff --git a/xpcom/glue/nsTArray.cpp b/xpcom/glue/nsTArray.cpp
new file mode 100644
index 000000000..fd8422ec7
--- /dev/null
+++ b/xpcom/glue/nsTArray.cpp
@@ -0,0 +1,29 @@
+/* -*- 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 <string.h>
+#include "nsTArray.h"
+#include "nsXPCOM.h"
+#include "nsDebug.h"
+#include "mozilla/CheckedInt.h"
+#include "mozilla/IntegerPrintfMacros.h"
+
+nsTArrayHeader nsTArrayHeader::sEmptyHdr = { 0, 0, 0 };
+
+bool
+IsTwiceTheRequiredBytesRepresentableAsUint32(size_t aCapacity, size_t aElemSize)
+{
+ using mozilla::CheckedUint32;
+ return ((CheckedUint32(aCapacity) * aElemSize) * 2).isValid();
+}
+
+MOZ_NORETURN MOZ_COLD void
+InvalidArrayIndex_CRASH(size_t aIndex, size_t aLength)
+{
+ MOZ_CRASH_UNSAFE_PRINTF(
+ "ElementAt(aIndex = %" PRIu64 ", aLength = %" PRIu64 ")",
+ static_cast<uint64_t>(aIndex), static_cast<uint64_t>(aLength));
+}
diff --git a/xpcom/glue/nsTArray.h b/xpcom/glue/nsTArray.h
new file mode 100644
index 000000000..ca74a41f7
--- /dev/null
+++ b/xpcom/glue/nsTArray.h
@@ -0,0 +1,2371 @@
+/* -*- 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 nsTArray_h__
+#define nsTArray_h__
+
+#include "nsTArrayForwardDeclare.h"
+#include "mozilla/Alignment.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/BinarySearch.h"
+#include "mozilla/fallible.h"
+#include "mozilla/Function.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Move.h"
+#include "mozilla/ReverseIterator.h"
+#include "mozilla/TypeTraits.h"
+
+#include <string.h>
+
+#include "nsCycleCollectionNoteChild.h"
+#include "nsAlgorithm.h"
+#include "nscore.h"
+#include "nsQuickSort.h"
+#include "nsDebug.h"
+#include "nsISupportsImpl.h"
+#include "nsRegionFwd.h"
+#include <initializer_list>
+#include <new>
+
+namespace JS {
+template<class T>
+class Heap;
+class ObjectPtr;
+} /* namespace JS */
+
+class nsRegion;
+namespace mozilla {
+namespace layers {
+struct TileClient;
+} // namespace layers
+} // namespace mozilla
+
+namespace mozilla {
+struct SerializedStructuredCloneBuffer;
+} // namespace mozilla
+
+namespace mozilla {
+namespace dom {
+namespace ipc {
+class StructuredCloneData;
+} // namespace ipc
+} // namespace dom
+} // namespace mozilla
+
+namespace mozilla {
+namespace dom {
+class ClonedMessageData;
+class MessagePortMessage;
+namespace indexedDB {
+struct StructuredCloneReadInfo;
+class SerializedStructuredCloneReadInfo;
+class ObjectStoreCursorResponse;
+} // namespace indexedDB
+} // namespace dom
+} // namespace mozilla
+
+class JSStructuredCloneData;
+
+//
+// nsTArray is a resizable array class, like std::vector.
+//
+// Unlike std::vector, which follows C++'s construction/destruction rules,
+// nsTArray assumes that your "T" can be memmoved()'ed safely.
+//
+// The public classes defined in this header are
+//
+// nsTArray<T>,
+// FallibleTArray<T>,
+// AutoTArray<T, N>, and
+//
+// nsTArray and AutoTArray are infallible by default. To opt-in to fallible
+// behaviour, use the `mozilla::fallible` parameter and check the return value.
+//
+// If you just want to declare the nsTArray types (e.g., if you're in a header
+// file and don't need the full nsTArray definitions) consider including
+// nsTArrayForwardDeclare.h instead of nsTArray.h.
+//
+// The template parameter (i.e., T in nsTArray<T>) specifies the type of the
+// elements and has the following requirements:
+//
+// T MUST be safely memmove()'able.
+// T MUST define a copy-constructor.
+// T MAY define operator< for sorting.
+// T MAY define operator== for searching.
+//
+// (Note that the memmove requirement may be relaxed for certain types - see
+// nsTArray_CopyChooser below.)
+//
+// For methods taking a Comparator instance, the Comparator must be a class
+// defining the following methods:
+//
+// class Comparator {
+// public:
+// /** @return True if the elements are equals; false otherwise. */
+// bool Equals(const elem_type& a, const Item& b) const;
+//
+// /** @return True if (a < b); false otherwise. */
+// bool LessThan(const elem_type& a, const Item& b) const;
+// };
+//
+// The Equals method is used for searching, and the LessThan method is used for
+// searching and sorting. The |Item| type above can be arbitrary, but must
+// match the Item type passed to the sort or search function.
+//
+
+
+//
+// nsTArrayFallibleResult and nsTArrayInfallibleResult types are proxy types
+// which are used because you cannot use a templated type which is bound to
+// void as an argument to a void function. In order to work around that, we
+// encode either a void or a boolean inside these proxy objects, and pass them
+// to the aforementioned function instead, and then use the type information to
+// decide what to do in the function.
+//
+// Note that public nsTArray methods should never return a proxy type. Such
+// types are only meant to be used in the internal nsTArray helper methods.
+// Public methods returning non-proxy types cannot be called from other
+// nsTArray members.
+//
+struct nsTArrayFallibleResult
+{
+ // Note: allows implicit conversions from and to bool
+ MOZ_IMPLICIT nsTArrayFallibleResult(bool aResult) : mResult(aResult) {}
+
+ MOZ_IMPLICIT operator bool() { return mResult; }
+
+private:
+ bool mResult;
+};
+
+struct nsTArrayInfallibleResult
+{
+};
+
+//
+// nsTArray*Allocators must all use the same |free()|, to allow swap()'ing
+// between fallible and infallible variants.
+//
+
+struct nsTArrayFallibleAllocatorBase
+{
+ typedef bool ResultType;
+ typedef nsTArrayFallibleResult ResultTypeProxy;
+
+ static ResultType Result(ResultTypeProxy aResult) { return aResult; }
+ static bool Successful(ResultTypeProxy aResult) { return aResult; }
+ static ResultTypeProxy SuccessResult() { return true; }
+ static ResultTypeProxy FailureResult() { return false; }
+ static ResultType ConvertBoolToResultType(bool aValue) { return aValue; }
+};
+
+struct nsTArrayInfallibleAllocatorBase
+{
+ typedef void ResultType;
+ typedef nsTArrayInfallibleResult ResultTypeProxy;
+
+ static ResultType Result(ResultTypeProxy aResult) {}
+ static bool Successful(ResultTypeProxy) { return true; }
+ static ResultTypeProxy SuccessResult() { return ResultTypeProxy(); }
+
+ static ResultTypeProxy FailureResult()
+ {
+ NS_RUNTIMEABORT("Infallible nsTArray should never fail");
+ return ResultTypeProxy();
+ }
+
+ static ResultType ConvertBoolToResultType(bool aValue)
+ {
+ if (!aValue) {
+ NS_RUNTIMEABORT("infallible nsTArray should never convert false to ResultType");
+ }
+ }
+};
+
+struct nsTArrayFallibleAllocator : nsTArrayFallibleAllocatorBase
+{
+ static void* Malloc(size_t aSize) { return malloc(aSize); }
+ static void* Realloc(void* aPtr, size_t aSize)
+ {
+ return realloc(aPtr, aSize);
+ }
+
+ static void Free(void* aPtr) { free(aPtr); }
+ static void SizeTooBig(size_t) {}
+};
+
+#if defined(MOZALLOC_HAVE_XMALLOC)
+#include "mozilla/mozalloc_abort.h"
+
+struct nsTArrayInfallibleAllocator : nsTArrayInfallibleAllocatorBase
+{
+ static void* Malloc(size_t aSize) { return moz_xmalloc(aSize); }
+ static void* Realloc(void* aPtr, size_t aSize)
+ {
+ return moz_xrealloc(aPtr, aSize);
+ }
+
+ static void Free(void* aPtr) { free(aPtr); }
+ static void SizeTooBig(size_t aSize) { NS_ABORT_OOM(aSize); }
+};
+
+#else
+#include <stdlib.h>
+
+struct nsTArrayInfallibleAllocator : nsTArrayInfallibleAllocatorBase
+{
+ static void* Malloc(size_t aSize)
+ {
+ void* ptr = malloc(aSize);
+ if (MOZ_UNLIKELY(!ptr)) {
+ NS_ABORT_OOM(aSize);
+ }
+ return ptr;
+ }
+
+ static void* Realloc(void* aPtr, size_t aSize)
+ {
+ void* newptr = realloc(aPtr, aSize);
+ if (MOZ_UNLIKELY(!newptr && aSize)) {
+ NS_ABORT_OOM(aSize);
+ }
+ return newptr;
+ }
+
+ static void Free(void* aPtr) { free(aPtr); }
+ static void SizeTooBig(size_t aSize) { NS_ABORT_OOM(aSize); }
+};
+
+#endif
+
+// nsTArray_base stores elements into the space allocated beyond
+// sizeof(*this). This is done to minimize the size of the nsTArray
+// object when it is empty.
+struct nsTArrayHeader
+{
+ static nsTArrayHeader sEmptyHdr;
+
+ uint32_t mLength;
+ uint32_t mCapacity : 31;
+ uint32_t mIsAutoArray : 1;
+};
+
+// This class provides a SafeElementAt method to nsTArray<T*> which does
+// not take a second default value parameter.
+template<class E, class Derived>
+struct nsTArray_SafeElementAtHelper
+{
+ typedef E* elem_type;
+ typedef size_t index_type;
+
+ // No implementation is provided for these two methods, and that is on
+ // purpose, since we don't support these functions on non-pointer type
+ // instantiations.
+ elem_type& SafeElementAt(index_type aIndex);
+ const elem_type& SafeElementAt(index_type aIndex) const;
+};
+
+template<class E, class Derived>
+struct nsTArray_SafeElementAtHelper<E*, Derived>
+{
+ typedef E* elem_type;
+ //typedef const E* const_elem_type; XXX: see below
+ typedef size_t index_type;
+
+ elem_type SafeElementAt(index_type aIndex)
+ {
+ return static_cast<Derived*>(this)->SafeElementAt(aIndex, nullptr);
+ }
+
+ // XXX: Probably should return const_elem_type, but callsites must be fixed.
+ // Also, the use of const_elem_type for nsTArray<xpcGCCallback> in
+ // xpcprivate.h causes build failures on Windows because xpcGCCallback is a
+ // function pointer and MSVC doesn't like qualifying it with |const|.
+ elem_type SafeElementAt(index_type aIndex) const
+ {
+ return static_cast<const Derived*>(this)->SafeElementAt(aIndex, nullptr);
+ }
+};
+
+// E is the base type that the smart pointer is templated over; the
+// smart pointer can act as E*.
+template<class E, class Derived>
+struct nsTArray_SafeElementAtSmartPtrHelper
+{
+ typedef E* elem_type;
+ typedef const E* const_elem_type;
+ typedef size_t index_type;
+
+ elem_type SafeElementAt(index_type aIndex)
+ {
+ return static_cast<Derived*>(this)->SafeElementAt(aIndex, nullptr);
+ }
+
+ // XXX: Probably should return const_elem_type, but callsites must be fixed.
+ elem_type SafeElementAt(index_type aIndex) const
+ {
+ return static_cast<const Derived*>(this)->SafeElementAt(aIndex, nullptr);
+ }
+};
+
+template<class T> class nsCOMPtr;
+
+template<class E, class Derived>
+struct nsTArray_SafeElementAtHelper<nsCOMPtr<E>, Derived>
+ : public nsTArray_SafeElementAtSmartPtrHelper<E, Derived>
+{
+};
+
+template<class E, class Derived>
+struct nsTArray_SafeElementAtHelper<RefPtr<E>, Derived>
+ : public nsTArray_SafeElementAtSmartPtrHelper<E, Derived>
+{
+};
+
+namespace mozilla {
+template<class T> class OwningNonNull;
+} // namespace mozilla
+
+template<class E, class Derived>
+struct nsTArray_SafeElementAtHelper<mozilla::OwningNonNull<E>, Derived>
+{
+ typedef E* elem_type;
+ typedef const E* const_elem_type;
+ typedef size_t index_type;
+
+ elem_type SafeElementAt(index_type aIndex)
+ {
+ if (aIndex < static_cast<Derived*>(this)->Length()) {
+ return static_cast<Derived*>(this)->ElementAt(aIndex);
+ }
+ return nullptr;
+ }
+
+ // XXX: Probably should return const_elem_type, but callsites must be fixed.
+ elem_type SafeElementAt(index_type aIndex) const
+ {
+ if (aIndex < static_cast<const Derived*>(this)->Length()) {
+ return static_cast<const Derived*>(this)->ElementAt(aIndex);
+ }
+ return nullptr;
+ }
+};
+
+// Servo bindings.
+extern "C" void Gecko_EnsureTArrayCapacity(void* aArray,
+ size_t aCapacity,
+ size_t aElementSize);
+extern "C" void Gecko_ClearPODTArray(void* aArray,
+ size_t aElementSize,
+ size_t aElementAlign);
+
+MOZ_NORETURN MOZ_COLD void
+InvalidArrayIndex_CRASH(size_t aIndex, size_t aLength);
+
+//
+// This class serves as a base class for nsTArray. It shouldn't be used
+// directly. It holds common implementation code that does not depend on the
+// element type of the nsTArray.
+//
+template<class Alloc, class Copy>
+class nsTArray_base
+{
+ // Allow swapping elements with |nsTArray_base|s created using a
+ // different allocator. This is kosher because all allocators use
+ // the same free().
+ template<class Allocator, class Copier>
+ friend class nsTArray_base;
+ friend void Gecko_EnsureTArrayCapacity(void* aArray, size_t aCapacity,
+ size_t aElemSize);
+ friend void Gecko_ClearPODTArray(void* aTArray, size_t aElementSize,
+ size_t aElementAlign);
+
+protected:
+ typedef nsTArrayHeader Header;
+
+public:
+ typedef size_t size_type;
+ typedef size_t index_type;
+
+ // @return The number of elements in the array.
+ size_type Length() const { return mHdr->mLength; }
+
+ // @return True if the array is empty or false otherwise.
+ bool IsEmpty() const { return Length() == 0; }
+
+ // @return The number of elements that can fit in the array without forcing
+ // the array to be re-allocated. The length of an array is always less
+ // than or equal to its capacity.
+ size_type Capacity() const { return mHdr->mCapacity; }
+
+#ifdef DEBUG
+ void* DebugGetHeader() const { return mHdr; }
+#endif
+
+protected:
+ nsTArray_base();
+
+ ~nsTArray_base();
+
+ // Resize the storage if necessary to achieve the requested capacity.
+ // @param aCapacity The requested number of array elements.
+ // @param aElemSize The size of an array element.
+ // @return False if insufficient memory is available; true otherwise.
+ template<typename ActualAlloc>
+ typename ActualAlloc::ResultTypeProxy EnsureCapacity(size_type aCapacity,
+ size_type aElemSize);
+
+ // Tries to resize the storage to the minimum required amount. If this fails,
+ // the array is left as-is.
+ // @param aElemSize The size of an array element.
+ // @param aElemAlign The alignment in bytes of an array element.
+ void ShrinkCapacity(size_type aElemSize, size_t aElemAlign);
+
+ // This method may be called to resize a "gap" in the array by shifting
+ // elements around. It updates mLength appropriately. If the resulting
+ // array has zero elements, then the array's memory is free'd.
+ // @param aStart The starting index of the gap.
+ // @param aOldLen The current length of the gap.
+ // @param aNewLen The desired length of the gap.
+ // @param aElemSize The size of an array element.
+ // @param aElemAlign The alignment in bytes of an array element.
+ template<typename ActualAlloc>
+ void ShiftData(index_type aStart, size_type aOldLen, size_type aNewLen,
+ size_type aElemSize, size_t aElemAlign);
+
+ // This method increments the length member of the array's header.
+ // Note that mHdr may actually be sEmptyHdr in the case where a
+ // zero-length array is inserted into our array. But then aNum should
+ // always be 0.
+ void IncrementLength(size_t aNum)
+ {
+ if (mHdr == EmptyHdr()) {
+ if (MOZ_UNLIKELY(aNum != 0)) {
+ // Writing a non-zero length to the empty header would be extremely bad.
+ MOZ_CRASH();
+ }
+ } else {
+ mHdr->mLength += aNum;
+ }
+ }
+
+ // This method inserts blank slots into the array.
+ // @param aIndex the place to insert the new elements. This must be no
+ // greater than the current length of the array.
+ // @param aCount the number of slots to insert
+ // @param aElementSize the size of an array element.
+ // @param aElemAlign the alignment in bytes of an array element.
+ template<typename ActualAlloc>
+ bool InsertSlotsAt(index_type aIndex, size_type aCount,
+ size_type aElementSize, size_t aElemAlign);
+
+ template<typename ActualAlloc, class Allocator>
+ typename ActualAlloc::ResultTypeProxy
+ SwapArrayElements(nsTArray_base<Allocator, Copy>& aOther,
+ size_type aElemSize,
+ size_t aElemAlign);
+
+ // This is an RAII class used in SwapArrayElements.
+ class IsAutoArrayRestorer
+ {
+ public:
+ IsAutoArrayRestorer(nsTArray_base<Alloc, Copy>& aArray, size_t aElemAlign);
+ ~IsAutoArrayRestorer();
+
+ private:
+ nsTArray_base<Alloc, Copy>& mArray;
+ size_t mElemAlign;
+ bool mIsAuto;
+ };
+
+ // Helper function for SwapArrayElements. Ensures that if the array
+ // is an AutoTArray that it doesn't use the built-in buffer.
+ template<typename ActualAlloc>
+ bool EnsureNotUsingAutoArrayBuffer(size_type aElemSize);
+
+ // Returns true if this nsTArray is an AutoTArray with a built-in buffer.
+ bool IsAutoArray() const { return mHdr->mIsAutoArray; }
+
+ // Returns a Header for the built-in buffer of this AutoTArray.
+ Header* GetAutoArrayBuffer(size_t aElemAlign)
+ {
+ MOZ_ASSERT(IsAutoArray(), "Should be an auto array to call this");
+ return GetAutoArrayBufferUnsafe(aElemAlign);
+ }
+ const Header* GetAutoArrayBuffer(size_t aElemAlign) const
+ {
+ MOZ_ASSERT(IsAutoArray(), "Should be an auto array to call this");
+ return GetAutoArrayBufferUnsafe(aElemAlign);
+ }
+
+ // Returns a Header for the built-in buffer of this AutoTArray, but doesn't
+ // assert that we are an AutoTArray.
+ Header* GetAutoArrayBufferUnsafe(size_t aElemAlign)
+ {
+ return const_cast<Header*>(static_cast<const nsTArray_base<Alloc, Copy>*>(
+ this)->GetAutoArrayBufferUnsafe(aElemAlign));
+ }
+ const Header* GetAutoArrayBufferUnsafe(size_t aElemAlign) const;
+
+ // Returns true if this is an AutoTArray and it currently uses the
+ // built-in buffer to store its elements.
+ bool UsesAutoArrayBuffer() const;
+
+ // The array's elements (prefixed with a Header). This pointer is never
+ // null. If the array is empty, then this will point to sEmptyHdr.
+ Header* mHdr;
+
+ Header* Hdr() const { return mHdr; }
+ Header** PtrToHdr() { return &mHdr; }
+ static Header* EmptyHdr() { return &Header::sEmptyHdr; }
+};
+
+//
+// This class defines convenience functions for element specific operations.
+// Specialize this template if necessary.
+//
+template<class E>
+class nsTArrayElementTraits
+{
+public:
+ // Invoke the default constructor in place.
+ static inline void Construct(E* aE)
+ {
+ // Do NOT call "E()"! That triggers C++ "default initialization"
+ // which zeroes out POD ("plain old data") types such as regular
+ // ints. We don't want that because it can be a performance issue
+ // and people don't expect it; nsTArray should work like a regular
+ // C/C++ array in this respect.
+ new (static_cast<void*>(aE)) E;
+ }
+ // Invoke the copy-constructor in place.
+ template<class A>
+ static inline void Construct(E* aE, A&& aArg)
+ {
+ typedef typename mozilla::RemoveCV<E>::Type E_NoCV;
+ typedef typename mozilla::RemoveCV<A>::Type A_NoCV;
+ static_assert(!mozilla::IsSame<E_NoCV*, A_NoCV>::value,
+ "For safety, we disallow constructing nsTArray<E> elements "
+ "from E* pointers. See bug 960591.");
+ new (static_cast<void*>(aE)) E(mozilla::Forward<A>(aArg));
+ }
+ // Invoke the destructor in place.
+ static inline void Destruct(E* aE) { aE->~E(); }
+};
+
+// The default comparator used by nsTArray
+template<class A, class B>
+class nsDefaultComparator
+{
+public:
+ bool Equals(const A& aA, const B& aB) const { return aA == aB; }
+ bool LessThan(const A& aA, const B& aB) const { return aA < aB; }
+};
+
+template<bool IsPod, bool IsSameType>
+struct AssignRangeAlgorithm
+{
+ template<class Item, class ElemType, class IndexType, class SizeType>
+ static void implementation(ElemType* aElements, IndexType aStart,
+ SizeType aCount, const Item* aValues)
+ {
+ ElemType* iter = aElements + aStart;
+ ElemType* end = iter + aCount;
+ for (; iter != end; ++iter, ++aValues) {
+ nsTArrayElementTraits<ElemType>::Construct(iter, *aValues);
+ }
+ }
+};
+
+template<>
+struct AssignRangeAlgorithm<true, true>
+{
+ template<class Item, class ElemType, class IndexType, class SizeType>
+ static void implementation(ElemType* aElements, IndexType aStart,
+ SizeType aCount, const Item* aValues)
+ {
+ memcpy(aElements + aStart, aValues, aCount * sizeof(ElemType));
+ }
+};
+
+//
+// Normally elements are copied with memcpy and memmove, but for some element
+// types that is problematic. The nsTArray_CopyChooser template class can be
+// specialized to ensure that copying calls constructors and destructors
+// instead, as is done below for JS::Heap<E> elements.
+//
+
+//
+// A class that defines how to copy elements using memcpy/memmove.
+//
+struct nsTArray_CopyWithMemutils
+{
+ const static bool allowRealloc = true;
+
+ static void MoveNonOverlappingRegionWithHeader(void* aDest, const void* aSrc,
+ size_t aCount, size_t aElemSize)
+ {
+ memcpy(aDest, aSrc, sizeof(nsTArrayHeader) + aCount * aElemSize);
+ }
+
+ static void MoveOverlappingRegion(void* aDest, void* aSrc, size_t aCount,
+ size_t aElemSize)
+ {
+ memmove(aDest, aSrc, aCount * aElemSize);
+ }
+
+ static void MoveNonOverlappingRegion(void* aDest, void* aSrc, size_t aCount,
+ size_t aElemSize)
+ {
+ memcpy(aDest, aSrc, aCount * aElemSize);
+ }
+};
+
+//
+// A template class that defines how to copy elements calling their constructors
+// and destructors appropriately.
+//
+template<class ElemType>
+struct nsTArray_CopyWithConstructors
+{
+ typedef nsTArrayElementTraits<ElemType> traits;
+
+ const static bool allowRealloc = false;
+
+ static void MoveNonOverlappingRegionWithHeader(void* aDest, void* aSrc, size_t aCount,
+ size_t aElemSize)
+ {
+ nsTArrayHeader* destHeader = static_cast<nsTArrayHeader*>(aDest);
+ nsTArrayHeader* srcHeader = static_cast<nsTArrayHeader*>(aSrc);
+ *destHeader = *srcHeader;
+ MoveNonOverlappingRegion(static_cast<uint8_t*>(aDest) + sizeof(nsTArrayHeader),
+ static_cast<uint8_t*>(aSrc) + sizeof(nsTArrayHeader),
+ aCount, aElemSize);
+ }
+
+ // These functions are defined by analogy with memmove and memcpy.
+ // What they actually do is slightly different: MoveOverlappingRegion
+ // checks to see which direction the movement needs to take place,
+ // whether from back-to-front of the range to be moved or from
+ // front-to-back. MoveNonOverlappingRegion assumes that moving
+ // front-to-back is always valid. So they're really more like
+ // std::move{_backward,} in that respect. We keep these names because
+ // we think they read slightly better, and MoveNonOverlappingRegion is
+ // only ever called on overlapping regions from MoveOverlappingRegion.
+ static void MoveOverlappingRegion(void* aDest, void* aSrc, size_t aCount,
+ size_t aElemSize)
+ {
+ ElemType* destElem = static_cast<ElemType*>(aDest);
+ ElemType* srcElem = static_cast<ElemType*>(aSrc);
+ ElemType* destElemEnd = destElem + aCount;
+ ElemType* srcElemEnd = srcElem + aCount;
+ if (destElem == srcElem) {
+ return; // In practice, we don't do this.
+ }
+
+ // Figure out whether to copy back-to-front or front-to-back.
+ if (srcElemEnd > destElem && srcElemEnd < destElemEnd) {
+ while (destElemEnd != destElem) {
+ --destElemEnd;
+ --srcElemEnd;
+ traits::Construct(destElemEnd, mozilla::Move(*srcElemEnd));
+ traits::Destruct(srcElemEnd);
+ }
+ } else {
+ MoveNonOverlappingRegion(aDest, aSrc, aCount, aElemSize);
+ }
+ }
+
+ static void MoveNonOverlappingRegion(void* aDest, void* aSrc, size_t aCount,
+ size_t aElemSize)
+ {
+ ElemType* destElem = static_cast<ElemType*>(aDest);
+ ElemType* srcElem = static_cast<ElemType*>(aSrc);
+ ElemType* destElemEnd = destElem + aCount;
+#ifdef DEBUG
+ ElemType* srcElemEnd = srcElem + aCount;
+ MOZ_ASSERT(srcElemEnd <= destElem || srcElemEnd > destElemEnd);
+#endif
+ while (destElem != destElemEnd) {
+ traits::Construct(destElem, mozilla::Move(*srcElem));
+ traits::Destruct(srcElem);
+ ++destElem;
+ ++srcElem;
+ }
+ }
+};
+
+//
+// The default behaviour is to use memcpy/memmove for everything.
+//
+template<class E>
+struct MOZ_NEEDS_MEMMOVABLE_TYPE nsTArray_CopyChooser
+{
+ using Type = nsTArray_CopyWithMemutils;
+};
+
+//
+// Some classes require constructors/destructors to be called, so they are
+// specialized here.
+//
+#define DECLARE_USE_COPY_CONSTRUCTORS(T) \
+ template<> \
+ struct nsTArray_CopyChooser<T> \
+ { \
+ using Type = nsTArray_CopyWithConstructors<T>; \
+ };
+
+#define DECLARE_USE_COPY_CONSTRUCTORS_FOR_TEMPLATE(T) \
+ template<typename S> \
+ struct nsTArray_CopyChooser<T<S>> \
+ { \
+ using Type = nsTArray_CopyWithConstructors<T<S>>; \
+ };
+
+DECLARE_USE_COPY_CONSTRUCTORS_FOR_TEMPLATE(JS::Heap)
+
+DECLARE_USE_COPY_CONSTRUCTORS(nsRegion)
+DECLARE_USE_COPY_CONSTRUCTORS(nsIntRegion)
+DECLARE_USE_COPY_CONSTRUCTORS(mozilla::layers::TileClient)
+DECLARE_USE_COPY_CONSTRUCTORS(mozilla::SerializedStructuredCloneBuffer)
+DECLARE_USE_COPY_CONSTRUCTORS(mozilla::dom::ipc::StructuredCloneData)
+DECLARE_USE_COPY_CONSTRUCTORS(mozilla::dom::ClonedMessageData)
+DECLARE_USE_COPY_CONSTRUCTORS(mozilla::dom::indexedDB::StructuredCloneReadInfo);
+DECLARE_USE_COPY_CONSTRUCTORS(mozilla::dom::indexedDB::ObjectStoreCursorResponse)
+DECLARE_USE_COPY_CONSTRUCTORS(mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo);
+DECLARE_USE_COPY_CONSTRUCTORS(JSStructuredCloneData)
+DECLARE_USE_COPY_CONSTRUCTORS(mozilla::dom::MessagePortMessage)
+DECLARE_USE_COPY_CONSTRUCTORS(JS::ObjectPtr)
+
+
+//
+// Base class for nsTArray_Impl that is templated on element type and derived
+// nsTArray_Impl class, to allow extra conversions to be added for specific
+// types.
+//
+template<class E, class Derived>
+struct nsTArray_TypedBase : public nsTArray_SafeElementAtHelper<E, Derived>
+{
+};
+
+//
+// Specialization of nsTArray_TypedBase for arrays containing JS::Heap<E>
+// elements.
+//
+// These conversions are safe because JS::Heap<E> and E share the same
+// representation, and since the result of the conversions are const references
+// we won't miss any barriers.
+//
+// The static_cast is necessary to obtain the correct address for the derived
+// class since we are a base class used in multiple inheritance.
+//
+template<class E, class Derived>
+struct nsTArray_TypedBase<JS::Heap<E>, Derived>
+ : public nsTArray_SafeElementAtHelper<JS::Heap<E>, Derived>
+{
+ operator const nsTArray<E>&()
+ {
+ static_assert(sizeof(E) == sizeof(JS::Heap<E>),
+ "JS::Heap<E> must be binary compatible with E.");
+ Derived* self = static_cast<Derived*>(this);
+ return *reinterpret_cast<nsTArray<E> *>(self);
+ }
+
+ operator const FallibleTArray<E>&()
+ {
+ Derived* self = static_cast<Derived*>(this);
+ return *reinterpret_cast<FallibleTArray<E> *>(self);
+ }
+};
+
+namespace detail {
+
+template<class Item, class Comparator>
+struct ItemComparatorEq
+{
+ const Item& mItem;
+ const Comparator& mComp;
+ ItemComparatorEq(const Item& aItem, const Comparator& aComp)
+ : mItem(aItem)
+ , mComp(aComp)
+ {}
+ template<class T>
+ int operator()(const T& aElement) const {
+ if (mComp.Equals(aElement, mItem)) {
+ return 0;
+ }
+
+ return mComp.LessThan(aElement, mItem) ? 1 : -1;
+ }
+};
+
+template<class Item, class Comparator>
+struct ItemComparatorFirstElementGT
+{
+ const Item& mItem;
+ const Comparator& mComp;
+ ItemComparatorFirstElementGT(const Item& aItem, const Comparator& aComp)
+ : mItem(aItem)
+ , mComp(aComp)
+ {}
+ template<class T>
+ int operator()(const T& aElement) const {
+ if (mComp.LessThan(aElement, mItem) ||
+ mComp.Equals(aElement, mItem)) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+};
+
+} // namespace detail
+
+//
+// nsTArray_Impl contains most of the guts supporting nsTArray, FallibleTArray,
+// AutoTArray.
+//
+// The only situation in which you might need to use nsTArray_Impl in your code
+// is if you're writing code which mutates a TArray which may or may not be
+// infallible.
+//
+// Code which merely reads from a TArray which may or may not be infallible can
+// simply cast the TArray to |const nsTArray&|; both fallible and infallible
+// TArrays can be cast to |const nsTArray&|.
+//
+template<class E, class Alloc>
+class nsTArray_Impl
+ : public nsTArray_base<Alloc, typename nsTArray_CopyChooser<E>::Type>
+ , public nsTArray_TypedBase<E, nsTArray_Impl<E, Alloc>>
+{
+private:
+ typedef nsTArrayFallibleAllocator FallibleAlloc;
+ typedef nsTArrayInfallibleAllocator InfallibleAlloc;
+
+public:
+ typedef typename nsTArray_CopyChooser<E>::Type copy_type;
+ typedef nsTArray_base<Alloc, copy_type> base_type;
+ typedef typename base_type::size_type size_type;
+ typedef typename base_type::index_type index_type;
+ typedef E elem_type;
+ typedef nsTArray_Impl<E, Alloc> self_type;
+ typedef nsTArrayElementTraits<E> elem_traits;
+ typedef nsTArray_SafeElementAtHelper<E, self_type> safeelementat_helper_type;
+ typedef elem_type* iterator;
+ typedef const elem_type* const_iterator;
+ typedef mozilla::ReverseIterator<elem_type*> reverse_iterator;
+ typedef mozilla::ReverseIterator<const elem_type*> const_reverse_iterator;
+
+ using safeelementat_helper_type::SafeElementAt;
+ using base_type::EmptyHdr;
+
+ // A special value that is used to indicate an invalid or unknown index
+ // into the array.
+ static const index_type NoIndex = index_type(-1);
+
+ using base_type::Length;
+
+ //
+ // Finalization method
+ //
+
+ ~nsTArray_Impl() { Clear(); }
+
+ //
+ // Initialization methods
+ //
+
+ nsTArray_Impl() {}
+
+ // Initialize this array and pre-allocate some number of elements.
+ explicit nsTArray_Impl(size_type aCapacity) { SetCapacity(aCapacity); }
+
+ // Initialize this array with an r-value.
+ // Allow different types of allocators, since the allocator doesn't matter.
+ template<typename Allocator>
+ explicit nsTArray_Impl(nsTArray_Impl<E, Allocator>&& aOther)
+ {
+ SwapElements(aOther);
+ }
+
+ // The array's copy-constructor performs a 'deep' copy of the given array.
+ // @param aOther The array object to copy.
+ //
+ // It's very important that we declare this method as taking |const
+ // self_type&| as opposed to taking |const nsTArray_Impl<E, OtherAlloc>| for
+ // an arbitrary OtherAlloc.
+ //
+ // If we don't declare a constructor taking |const self_type&|, C++ generates
+ // a copy-constructor for this class which merely copies the object's
+ // members, which is obviously wrong.
+ //
+ // You can pass an nsTArray_Impl<E, OtherAlloc> to this method because
+ // nsTArray_Impl<E, X> can be cast to const nsTArray_Impl<E, Y>&. So the
+ // effect on the API is the same as if we'd declared this method as taking
+ // |const nsTArray_Impl<E, OtherAlloc>&|.
+ explicit nsTArray_Impl(const self_type& aOther) { AppendElements(aOther); }
+
+ explicit nsTArray_Impl(std::initializer_list<E> aIL) { AppendElements(aIL.begin(), aIL.size()); }
+ // Allow converting to a const array with a different kind of allocator,
+ // Since the allocator doesn't matter for const arrays
+ template<typename Allocator>
+ operator const nsTArray_Impl<E, Allocator>&() const
+ {
+ return *reinterpret_cast<const nsTArray_Impl<E, Allocator>*>(this);
+ }
+ // And we have to do this for our subclasses too
+ operator const nsTArray<E>&() const
+ {
+ return *reinterpret_cast<const InfallibleTArray<E>*>(this);
+ }
+ operator const FallibleTArray<E>&() const
+ {
+ return *reinterpret_cast<const FallibleTArray<E>*>(this);
+ }
+
+ // The array's assignment operator performs a 'deep' copy of the given
+ // array. It is optimized to reuse existing storage if possible.
+ // @param aOther The array object to copy.
+ self_type& operator=(const self_type& aOther)
+ {
+ if (this != &aOther) {
+ ReplaceElementsAt(0, Length(), aOther.Elements(), aOther.Length());
+ }
+ return *this;
+ }
+
+ // The array's move assignment operator steals the underlying data from
+ // the other array.
+ // @param other The array object to move from.
+ self_type& operator=(self_type&& aOther)
+ {
+ if (this != &aOther) {
+ Clear();
+ SwapElements(aOther);
+ }
+ return *this;
+ }
+
+ // Return true if this array has the same length and the same
+ // elements as |aOther|.
+ template<typename Allocator>
+ bool operator==(const nsTArray_Impl<E, Allocator>& aOther) const
+ {
+ size_type len = Length();
+ if (len != aOther.Length()) {
+ return false;
+ }
+
+ // XXX std::equal would be as fast or faster here
+ for (index_type i = 0; i < len; ++i) {
+ if (!(operator[](i) == aOther[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ // Return true if this array does not have the same length and the same
+ // elements as |aOther|.
+ bool operator!=(const self_type& aOther) const { return !operator==(aOther); }
+
+ template<typename Allocator>
+ self_type& operator=(const nsTArray_Impl<E, Allocator>& aOther)
+ {
+ ReplaceElementsAt(0, Length(), aOther.Elements(), aOther.Length());
+ return *this;
+ }
+
+ template<typename Allocator>
+ self_type& operator=(nsTArray_Impl<E, Allocator>&& aOther)
+ {
+ Clear();
+ SwapElements(aOther);
+ return *this;
+ }
+
+ // @return The amount of memory used by this nsTArray_Impl, excluding
+ // sizeof(*this). If you want to measure anything hanging off the array, you
+ // must iterate over the elements and measure them individually; hence the
+ // "Shallow" prefix.
+ size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ if (this->UsesAutoArrayBuffer() || Hdr() == EmptyHdr()) {
+ return 0;
+ }
+ return aMallocSizeOf(this->Hdr());
+ }
+
+ // @return The amount of memory used by this nsTArray_Impl, including
+ // sizeof(*this). If you want to measure anything hanging off the array, you
+ // must iterate over the elements and measure them individually; hence the
+ // "Shallow" prefix.
+ size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return aMallocSizeOf(this) + ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ //
+ // Accessor methods
+ //
+
+ // This method provides direct access to the array elements.
+ // @return A pointer to the first element of the array. If the array is
+ // empty, then this pointer must not be dereferenced.
+ elem_type* Elements() { return reinterpret_cast<elem_type*>(Hdr() + 1); }
+
+ // This method provides direct, readonly access to the array elements.
+ // @return A pointer to the first element of the array. If the array is
+ // empty, then this pointer must not be dereferenced.
+ const elem_type* Elements() const
+ {
+ return reinterpret_cast<const elem_type*>(Hdr() + 1);
+ }
+
+ // This method provides direct access to an element of the array. The given
+ // index must be within the array bounds.
+ // @param aIndex The index of an element in the array.
+ // @return A reference to the i'th element of the array.
+ elem_type& ElementAt(index_type aIndex)
+ {
+ if (MOZ_UNLIKELY(aIndex >= Length())) {
+ InvalidArrayIndex_CRASH(aIndex, Length());
+ }
+ return Elements()[aIndex];
+ }
+
+ // This method provides direct, readonly access to an element of the array
+ // The given index must be within the array bounds.
+ // @param aIndex The index of an element in the array.
+ // @return A const reference to the i'th element of the array.
+ const elem_type& ElementAt(index_type aIndex) const
+ {
+ if (MOZ_UNLIKELY(aIndex >= Length())) {
+ InvalidArrayIndex_CRASH(aIndex, Length());
+ }
+ return Elements()[aIndex];
+ }
+
+ // This method provides direct access to an element of the array in a bounds
+ // safe manner. If the requested index is out of bounds the provided default
+ // value is returned.
+ // @param aIndex The index of an element in the array.
+ // @param aDef The value to return if the index is out of bounds.
+ elem_type& SafeElementAt(index_type aIndex, elem_type& aDef)
+ {
+ return aIndex < Length() ? Elements()[aIndex] : aDef;
+ }
+
+ // This method provides direct access to an element of the array in a bounds
+ // safe manner. If the requested index is out of bounds the provided default
+ // value is returned.
+ // @param aIndex The index of an element in the array.
+ // @param aDef The value to return if the index is out of bounds.
+ const elem_type& SafeElementAt(index_type aIndex, const elem_type& aDef) const
+ {
+ return aIndex < Length() ? Elements()[aIndex] : aDef;
+ }
+
+ // Shorthand for ElementAt(aIndex)
+ elem_type& operator[](index_type aIndex) { return ElementAt(aIndex); }
+
+ // Shorthand for ElementAt(aIndex)
+ const elem_type& operator[](index_type aIndex) const { return ElementAt(aIndex); }
+
+ // Shorthand for ElementAt(length - 1)
+ elem_type& LastElement() { return ElementAt(Length() - 1); }
+
+ // Shorthand for ElementAt(length - 1)
+ const elem_type& LastElement() const { return ElementAt(Length() - 1); }
+
+ // Shorthand for SafeElementAt(length - 1, def)
+ elem_type& SafeLastElement(elem_type& aDef)
+ {
+ return SafeElementAt(Length() - 1, aDef);
+ }
+
+ // Shorthand for SafeElementAt(length - 1, def)
+ const elem_type& SafeLastElement(const elem_type& aDef) const
+ {
+ return SafeElementAt(Length() - 1, aDef);
+ }
+
+ // Methods for range-based for loops.
+ iterator begin() { return Elements(); }
+ const_iterator begin() const { return Elements(); }
+ const_iterator cbegin() const { return begin(); }
+ iterator end() { return Elements() + Length(); }
+ const_iterator end() const { return Elements() + Length(); }
+ const_iterator cend() const { return end(); }
+
+ // Methods for reverse iterating.
+ reverse_iterator rbegin() { return reverse_iterator(end()); }
+ const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
+ const_reverse_iterator crbegin() const { return rbegin(); }
+ reverse_iterator rend() { return reverse_iterator(begin()); }
+ const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
+ const_reverse_iterator crend() const { return rend(); }
+
+ //
+ // Search methods
+ //
+
+ // This method searches for the first element in this array that is equal
+ // to the given element.
+ // @param aItem The item to search for.
+ // @param aComp The Comparator used to determine element equality.
+ // @return true if the element was found.
+ template<class Item, class Comparator>
+ bool Contains(const Item& aItem, const Comparator& aComp) const
+ {
+ return IndexOf(aItem, 0, aComp) != NoIndex;
+ }
+
+ // This method searches for the first element in this array that is equal
+ // to the given element. This method assumes that 'operator==' is defined
+ // for elem_type.
+ // @param aItem The item to search for.
+ // @return true if the element was found.
+ template<class Item>
+ bool Contains(const Item& aItem) const
+ {
+ return IndexOf(aItem) != NoIndex;
+ }
+
+ // This method searches for the offset of the first element in this
+ // array that is equal to the given element.
+ // @param aItem The item to search for.
+ // @param aStart The index to start from.
+ // @param aComp The Comparator used to determine element equality.
+ // @return The index of the found element or NoIndex if not found.
+ template<class Item, class Comparator>
+ index_type IndexOf(const Item& aItem, index_type aStart,
+ const Comparator& aComp) const
+ {
+ const elem_type* iter = Elements() + aStart;
+ const elem_type* iend = Elements() + Length();
+ for (; iter != iend; ++iter) {
+ if (aComp.Equals(*iter, aItem)) {
+ return index_type(iter - Elements());
+ }
+ }
+ return NoIndex;
+ }
+
+ // This method searches for the offset of the first element in this
+ // array that is equal to the given element. This method assumes
+ // that 'operator==' is defined for elem_type.
+ // @param aItem The item to search for.
+ // @param aStart The index to start from.
+ // @return The index of the found element or NoIndex if not found.
+ template<class Item>
+ index_type IndexOf(const Item& aItem, index_type aStart = 0) const
+ {
+ return IndexOf(aItem, aStart, nsDefaultComparator<elem_type, Item>());
+ }
+
+ // This method searches for the offset of the last element in this
+ // array that is equal to the given element.
+ // @param aItem The item to search for.
+ // @param aStart The index to start from. If greater than or equal to the
+ // length of the array, then the entire array is searched.
+ // @param aComp The Comparator used to determine element equality.
+ // @return The index of the found element or NoIndex if not found.
+ template<class Item, class Comparator>
+ index_type LastIndexOf(const Item& aItem, index_type aStart,
+ const Comparator& aComp) const
+ {
+ size_type endOffset = aStart >= Length() ? Length() : aStart + 1;
+ const elem_type* iend = Elements() - 1;
+ const elem_type* iter = iend + endOffset;
+ for (; iter != iend; --iter) {
+ if (aComp.Equals(*iter, aItem)) {
+ return index_type(iter - Elements());
+ }
+ }
+ return NoIndex;
+ }
+
+ // This method searches for the offset of the last element in this
+ // array that is equal to the given element. This method assumes
+ // that 'operator==' is defined for elem_type.
+ // @param aItem The item to search for.
+ // @param aStart The index to start from. If greater than or equal to the
+ // length of the array, then the entire array is searched.
+ // @return The index of the found element or NoIndex if not found.
+ template<class Item>
+ index_type LastIndexOf(const Item& aItem,
+ index_type aStart = NoIndex) const
+ {
+ return LastIndexOf(aItem, aStart, nsDefaultComparator<elem_type, Item>());
+ }
+
+ // This method searches for the offset for the element in this array
+ // that is equal to the given element. The array is assumed to be sorted.
+ // If there is more than one equivalent element, there is no guarantee
+ // on which one will be returned.
+ // @param aItem The item to search for.
+ // @param aComp The Comparator used.
+ // @return The index of the found element or NoIndex if not found.
+ template<class Item, class Comparator>
+ index_type BinaryIndexOf(const Item& aItem, const Comparator& aComp) const
+ {
+ using mozilla::BinarySearchIf;
+ typedef ::detail::ItemComparatorEq<Item, Comparator> Cmp;
+
+ size_t index;
+ bool found = BinarySearchIf(*this, 0, Length(), Cmp(aItem, aComp), &index);
+ return found ? index : NoIndex;
+ }
+
+ // This method searches for the offset for the element in this array
+ // that is equal to the given element. The array is assumed to be sorted.
+ // This method assumes that 'operator==' and 'operator<' are defined.
+ // @param aItem The item to search for.
+ // @return The index of the found element or NoIndex if not found.
+ template<class Item>
+ index_type BinaryIndexOf(const Item& aItem) const
+ {
+ return BinaryIndexOf(aItem, nsDefaultComparator<elem_type, Item>());
+ }
+
+ //
+ // Mutation methods
+ //
+
+ template<class Allocator, typename ActualAlloc = Alloc>
+ typename ActualAlloc::ResultType Assign(
+ const nsTArray_Impl<E, Allocator>& aOther)
+ {
+ return ActualAlloc::ConvertBoolToResultType(
+ !!ReplaceElementsAt<E, ActualAlloc>(0, Length(),
+ aOther.Elements(), aOther.Length()));
+ }
+
+ template<class Allocator>
+ MOZ_MUST_USE
+ bool Assign(const nsTArray_Impl<E, Allocator>& aOther,
+ const mozilla::fallible_t&)
+ {
+ return Assign<Allocator, FallibleAlloc>(aOther);
+ }
+
+ template<class Allocator>
+ void Assign(nsTArray_Impl<E, Allocator>&& aOther)
+ {
+ Clear();
+ SwapElements(aOther);
+ }
+
+ // This method call the destructor on each element of the array, empties it,
+ // but does not shrink the array's capacity.
+ // See also SetLengthAndRetainStorage.
+ // Make sure to call Compact() if needed to avoid keeping a huge array
+ // around.
+ void ClearAndRetainStorage()
+ {
+ if (base_type::mHdr == EmptyHdr()) {
+ return;
+ }
+
+ DestructRange(0, Length());
+ base_type::mHdr->mLength = 0;
+ }
+
+ // This method modifies the length of the array, but unlike SetLength
+ // it doesn't deallocate/reallocate the current internal storage.
+ // The new length MUST be shorter than or equal to the current capacity.
+ // If the new length is larger than the existing length of the array,
+ // then new elements will be constructed using elem_type's default
+ // constructor. If shorter, elements will be destructed and removed.
+ // See also ClearAndRetainStorage.
+ // @param aNewLen The desired length of this array.
+ void SetLengthAndRetainStorage(size_type aNewLen)
+ {
+ MOZ_ASSERT(aNewLen <= base_type::Capacity());
+ size_type oldLen = Length();
+ if (aNewLen > oldLen) {
+ InsertElementsAt(oldLen, aNewLen - oldLen);
+ return;
+ }
+ if (aNewLen < oldLen) {
+ DestructRange(aNewLen, oldLen - aNewLen);
+ base_type::mHdr->mLength = aNewLen;
+ }
+ }
+
+ // This method replaces a range of elements in this array.
+ // @param aStart The starting index of the elements to replace.
+ // @param aCount The number of elements to replace. This may be zero to
+ // insert elements without removing any existing elements.
+ // @param aArray The values to copy into this array. Must be non-null,
+ // and these elements must not already exist in the array
+ // being modified.
+ // @param aArrayLen The number of values to copy into this array.
+ // @return A pointer to the new elements in the array, or null if
+ // the operation failed due to insufficient memory.
+protected:
+ template<class Item, typename ActualAlloc = Alloc>
+ elem_type* ReplaceElementsAt(index_type aStart, size_type aCount,
+ const Item* aArray, size_type aArrayLen);
+
+public:
+
+ template<class Item>
+ MOZ_MUST_USE
+ elem_type* ReplaceElementsAt(index_type aStart, size_type aCount,
+ const Item* aArray, size_type aArrayLen,
+ const mozilla::fallible_t&)
+ {
+ return ReplaceElementsAt<Item, FallibleAlloc>(aStart, aCount,
+ aArray, aArrayLen);
+ }
+
+ // A variation on the ReplaceElementsAt method defined above.
+protected:
+ template<class Item, typename ActualAlloc = Alloc>
+ elem_type* ReplaceElementsAt(index_type aStart, size_type aCount,
+ const nsTArray<Item>& aArray)
+ {
+ return ReplaceElementsAt<Item, ActualAlloc>(
+ aStart, aCount, aArray.Elements(), aArray.Length());
+ }
+public:
+
+ template<class Item>
+ MOZ_MUST_USE
+ elem_type* ReplaceElementsAt(index_type aStart, size_type aCount,
+ const nsTArray<Item>& aArray,
+ const mozilla::fallible_t&)
+ {
+ return ReplaceElementsAt<Item, FallibleAlloc>(aStart, aCount, aArray);
+ }
+
+ // A variation on the ReplaceElementsAt method defined above.
+protected:
+ template<class Item, typename ActualAlloc = Alloc>
+ elem_type* ReplaceElementsAt(index_type aStart, size_type aCount,
+ const Item& aItem)
+ {
+ return ReplaceElementsAt<Item, ActualAlloc>(aStart, aCount, &aItem, 1);
+ }
+public:
+
+ template<class Item>
+ MOZ_MUST_USE
+ elem_type* ReplaceElementsAt(index_type aStart, size_type aCount,
+ const Item& aItem, const mozilla::fallible_t&)
+ {
+ return ReplaceElementsAt<Item, FallibleAlloc>(aStart, aCount, aItem);
+ }
+
+ // A variation on the ReplaceElementsAt method defined above.
+ template<class Item>
+ elem_type* ReplaceElementAt(index_type aIndex, const Item& aItem)
+ {
+ return ReplaceElementsAt(aIndex, 1, &aItem, 1);
+ }
+
+ // A variation on the ReplaceElementsAt method defined above.
+protected:
+ template<class Item, typename ActualAlloc = Alloc>
+ elem_type* InsertElementsAt(index_type aIndex, const Item* aArray,
+ size_type aArrayLen)
+ {
+ return ReplaceElementsAt<Item, ActualAlloc>(aIndex, 0, aArray, aArrayLen);
+ }
+public:
+
+ template<class Item>
+ MOZ_MUST_USE
+ elem_type* InsertElementsAt(index_type aIndex, const Item* aArray,
+ size_type aArrayLen, const mozilla::fallible_t&)
+ {
+ return InsertElementsAt<Item, FallibleAlloc>(aIndex, aArray, aArrayLen);
+ }
+
+ // A variation on the ReplaceElementsAt method defined above.
+protected:
+ template<class Item, class Allocator, typename ActualAlloc = Alloc>
+ elem_type* InsertElementsAt(index_type aIndex,
+ const nsTArray_Impl<Item, Allocator>& aArray)
+ {
+ return ReplaceElementsAt<Item, ActualAlloc>(
+ aIndex, 0, aArray.Elements(), aArray.Length());
+ }
+public:
+
+ template<class Item, class Allocator>
+ MOZ_MUST_USE
+ elem_type* InsertElementsAt(index_type aIndex,
+ const nsTArray_Impl<Item, Allocator>& aArray,
+ const mozilla::fallible_t&)
+ {
+ return InsertElementsAt<Item, Allocator, FallibleAlloc>(aIndex, aArray);
+ }
+
+ // Insert a new element without copy-constructing. This is useful to avoid
+ // temporaries.
+ // @return A pointer to the newly inserted element, or null on OOM.
+protected:
+ template<typename ActualAlloc = Alloc>
+ elem_type* InsertElementAt(index_type aIndex);
+
+public:
+
+ MOZ_MUST_USE
+ elem_type* InsertElementAt(index_type aIndex, const mozilla::fallible_t&)
+ {
+ return InsertElementAt<FallibleAlloc>(aIndex);
+ }
+
+ // Insert a new element, move constructing if possible.
+protected:
+ template<class Item, typename ActualAlloc = Alloc>
+ elem_type* InsertElementAt(index_type aIndex, Item&& aItem);
+
+public:
+
+ template<class Item>
+ MOZ_MUST_USE
+ elem_type* InsertElementAt(index_type aIndex, Item&& aItem,
+ const mozilla::fallible_t&)
+ {
+ return InsertElementAt<Item, FallibleAlloc>(aIndex,
+ mozilla::Forward<Item>(aItem));
+ }
+
+ // This method searches for the smallest index of an element that is strictly
+ // greater than |aItem|. If |aItem| is inserted at this index, the array will
+ // remain sorted and |aItem| would come after all elements that are equal to
+ // it. If |aItem| is greater than or equal to all elements in the array, the
+ // array length is returned.
+ //
+ // Note that consumers who want to know whether there are existing items equal
+ // to |aItem| in the array can just check that the return value here is > 0
+ // and indexing into the previous slot gives something equal to |aItem|.
+ //
+ //
+ // @param aItem The item to search for.
+ // @param aComp The Comparator used.
+ // @return The index of greatest element <= to |aItem|
+ // @precondition The array is sorted
+ template<class Item, class Comparator>
+ index_type IndexOfFirstElementGt(const Item& aItem,
+ const Comparator& aComp) const
+ {
+ using mozilla::BinarySearchIf;
+ typedef ::detail::ItemComparatorFirstElementGT<Item, Comparator> Cmp;
+
+ size_t index;
+ BinarySearchIf(*this, 0, Length(), Cmp(aItem, aComp), &index);
+ return index;
+ }
+
+ // A variation on the IndexOfFirstElementGt method defined above.
+ template<class Item>
+ index_type
+ IndexOfFirstElementGt(const Item& aItem) const
+ {
+ return IndexOfFirstElementGt(aItem, nsDefaultComparator<elem_type, Item>());
+ }
+
+ // Inserts |aItem| at such an index to guarantee that if the array
+ // was previously sorted, it will remain sorted after this
+ // insertion.
+protected:
+ template<class Item, class Comparator, typename ActualAlloc = Alloc>
+ elem_type* InsertElementSorted(Item&& aItem, const Comparator& aComp)
+ {
+ index_type index = IndexOfFirstElementGt<Item, Comparator>(aItem, aComp);
+ return InsertElementAt<Item, ActualAlloc>(
+ index, mozilla::Forward<Item>(aItem));
+ }
+public:
+
+ template<class Item, class Comparator>
+ MOZ_MUST_USE
+ elem_type* InsertElementSorted(Item&& aItem, const Comparator& aComp,
+ const mozilla::fallible_t&)
+ {
+ return InsertElementSorted<Item, Comparator, FallibleAlloc>(
+ mozilla::Forward<Item>(aItem), aComp);
+ }
+
+ // A variation on the InsertElementSorted method defined above.
+protected:
+ template<class Item, typename ActualAlloc = Alloc>
+ elem_type* InsertElementSorted(Item&& aItem)
+ {
+ nsDefaultComparator<elem_type, Item> comp;
+ return InsertElementSorted<Item, decltype(comp), ActualAlloc>(
+ mozilla::Forward<Item>(aItem), comp);
+ }
+public:
+
+ template<class Item>
+ MOZ_MUST_USE
+ elem_type* InsertElementSorted(Item&& aItem, const mozilla::fallible_t&)
+ {
+ return InsertElementSorted<Item, FallibleAlloc>(
+ mozilla::Forward<Item>(aItem));
+ }
+
+ // This method appends elements to the end of this array.
+ // @param aArray The elements to append to this array.
+ // @param aArrayLen The number of elements to append to this array.
+ // @return A pointer to the new elements in the array, or null if
+ // the operation failed due to insufficient memory.
+protected:
+ template<class Item, typename ActualAlloc = Alloc>
+ elem_type* AppendElements(const Item* aArray, size_type aArrayLen);
+
+public:
+
+ template<class Item>
+ /* MOZ_MUST_USE */
+ elem_type* AppendElements(const Item* aArray, size_type aArrayLen,
+ const mozilla::fallible_t&)
+ {
+ return AppendElements<Item, FallibleAlloc>(aArray, aArrayLen);
+ }
+
+ // A variation on the AppendElements method defined above.
+protected:
+ template<class Item, class Allocator, typename ActualAlloc = Alloc>
+ elem_type* AppendElements(const nsTArray_Impl<Item, Allocator>& aArray)
+ {
+ return AppendElements<Item, ActualAlloc>(aArray.Elements(), aArray.Length());
+ }
+public:
+
+ template<class Item, class Allocator>
+ /* MOZ_MUST_USE */
+ elem_type* AppendElements(const nsTArray_Impl<Item, Allocator>& aArray,
+ const mozilla::fallible_t&)
+ {
+ return AppendElements<Item, Allocator, FallibleAlloc>(aArray);
+ }
+
+ // Move all elements from another array to the end of this array.
+ // @return A pointer to the newly appended elements, or null on OOM.
+protected:
+ template<class Item, class Allocator, typename ActualAlloc = Alloc>
+ elem_type* AppendElements(nsTArray_Impl<Item, Allocator>&& aArray);
+
+public:
+
+ template<class Item, class Allocator, typename ActualAlloc = Alloc>
+ /* MOZ_MUST_USE */
+ elem_type* AppendElements(nsTArray_Impl<Item, Allocator>&& aArray,
+ const mozilla::fallible_t&)
+ {
+ return AppendElements<Item, Allocator>(mozilla::Move(aArray));
+ }
+
+ // Append a new element, move constructing if possible.
+protected:
+ template<class Item, typename ActualAlloc = Alloc>
+ elem_type* AppendElement(Item&& aItem);
+
+public:
+
+ template<class Item>
+ /* MOZ_MUST_USE */
+ elem_type* AppendElement(Item&& aItem,
+ const mozilla::fallible_t&)
+ {
+ return AppendElement<Item, FallibleAlloc>(mozilla::Forward<Item>(aItem));
+ }
+
+ // Append new elements without copy-constructing. This is useful to avoid
+ // temporaries.
+ // @return A pointer to the newly appended elements, or null on OOM.
+protected:
+ template<typename ActualAlloc = Alloc>
+ elem_type* AppendElements(size_type aCount) {
+ if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
+ Length() + aCount, sizeof(elem_type)))) {
+ return nullptr;
+ }
+ elem_type* elems = Elements() + Length();
+ size_type i;
+ for (i = 0; i < aCount; ++i) {
+ elem_traits::Construct(elems + i);
+ }
+ this->IncrementLength(aCount);
+ return elems;
+ }
+public:
+
+ /* MOZ_MUST_USE */
+ elem_type* AppendElements(size_type aCount,
+ const mozilla::fallible_t&)
+ {
+ return AppendElements<FallibleAlloc>(aCount);
+ }
+
+ // Append a new element without copy-constructing. This is useful to avoid
+ // temporaries.
+ // @return A pointer to the newly appended element, or null on OOM.
+protected:
+ template<typename ActualAlloc = Alloc>
+ elem_type* AppendElement()
+ {
+ return AppendElements<ActualAlloc>(1);
+ }
+public:
+
+ /* MOZ_MUST_USE */
+ elem_type* AppendElement(const mozilla::fallible_t&)
+ {
+ return AppendElement<FallibleAlloc>();
+ }
+
+ // This method removes a range of elements from this array.
+ // @param aStart The starting index of the elements to remove.
+ // @param aCount The number of elements to remove.
+ void RemoveElementsAt(index_type aStart, size_type aCount);
+
+ // A variation on the RemoveElementsAt method defined above.
+ void RemoveElementAt(index_type aIndex) { RemoveElementsAt(aIndex, 1); }
+
+ // A variation on the RemoveElementsAt method defined above.
+ void Clear() { RemoveElementsAt(0, Length()); }
+
+ // This method removes elements based on the return value of the
+ // callback function aPredicate. If the function returns true for
+ // an element, the element is removed. aPredicate will be called
+ // for each element in order. It is not safe to access the array
+ // inside aPredicate.
+ template<typename Predicate>
+ void RemoveElementsBy(Predicate aPredicate);
+
+ // This helper function combines IndexOf with RemoveElementAt to "search
+ // and destroy" the first element that is equal to the given element.
+ // @param aItem The item to search for.
+ // @param aComp The Comparator used to determine element equality.
+ // @return true if the element was found
+ template<class Item, class Comparator>
+ bool RemoveElement(const Item& aItem, const Comparator& aComp)
+ {
+ index_type i = IndexOf(aItem, 0, aComp);
+ if (i == NoIndex) {
+ return false;
+ }
+
+ RemoveElementAt(i);
+ return true;
+ }
+
+ // A variation on the RemoveElement method defined above that assumes
+ // that 'operator==' is defined for elem_type.
+ template<class Item>
+ bool RemoveElement(const Item& aItem)
+ {
+ return RemoveElement(aItem, nsDefaultComparator<elem_type, Item>());
+ }
+
+ // This helper function combines IndexOfFirstElementGt with
+ // RemoveElementAt to "search and destroy" the last element that
+ // is equal to the given element.
+ // @param aItem The item to search for.
+ // @param aComp The Comparator used to determine element equality.
+ // @return true if the element was found
+ template<class Item, class Comparator>
+ bool RemoveElementSorted(const Item& aItem, const Comparator& aComp)
+ {
+ index_type index = IndexOfFirstElementGt(aItem, aComp);
+ if (index > 0 && aComp.Equals(ElementAt(index - 1), aItem)) {
+ RemoveElementAt(index - 1);
+ return true;
+ }
+ return false;
+ }
+
+ // A variation on the RemoveElementSorted method defined above.
+ template<class Item>
+ bool RemoveElementSorted(const Item& aItem)
+ {
+ return RemoveElementSorted(aItem, nsDefaultComparator<elem_type, Item>());
+ }
+
+ // This method causes the elements contained in this array and the given
+ // array to be swapped.
+ template<class Allocator>
+ typename Alloc::ResultType SwapElements(nsTArray_Impl<E, Allocator>& aOther)
+ {
+ return Alloc::Result(this->template SwapArrayElements<Alloc>(
+ aOther, sizeof(elem_type), MOZ_ALIGNOF(elem_type)));
+ }
+
+ //
+ // Allocation
+ //
+
+ // This method may increase the capacity of this array object by the
+ // specified amount. This method may be called in advance of several
+ // AppendElement operations to minimize heap re-allocations. This method
+ // will not reduce the number of elements in this array.
+ // @param aCapacity The desired capacity of this array.
+ // @return True if the operation succeeded; false if we ran out of memory
+protected:
+ template<typename ActualAlloc = Alloc>
+ typename ActualAlloc::ResultType SetCapacity(size_type aCapacity)
+ {
+ return ActualAlloc::Result(this->template EnsureCapacity<ActualAlloc>(
+ aCapacity, sizeof(elem_type)));
+ }
+public:
+
+ MOZ_MUST_USE
+ bool SetCapacity(size_type aCapacity, const mozilla::fallible_t&)
+ {
+ return SetCapacity<FallibleAlloc>(aCapacity);
+ }
+
+ // This method modifies the length of the array. If the new length is
+ // larger than the existing length of the array, then new elements will be
+ // constructed using elem_type's default constructor. Otherwise, this call
+ // removes elements from the array (see also RemoveElementsAt).
+ // @param aNewLen The desired length of this array.
+ // @return True if the operation succeeded; false otherwise.
+ // See also TruncateLength if the new length is guaranteed to be smaller than
+ // the old.
+protected:
+ template<typename ActualAlloc = Alloc>
+ typename ActualAlloc::ResultType SetLength(size_type aNewLen)
+ {
+ size_type oldLen = Length();
+ if (aNewLen > oldLen) {
+ return ActualAlloc::ConvertBoolToResultType(
+ InsertElementsAt<ActualAlloc>(oldLen, aNewLen - oldLen) != nullptr);
+ }
+
+ TruncateLength(aNewLen);
+ return ActualAlloc::ConvertBoolToResultType(true);
+ }
+public:
+
+ MOZ_MUST_USE
+ bool SetLength(size_type aNewLen, const mozilla::fallible_t&)
+ {
+ return SetLength<FallibleAlloc>(aNewLen);
+ }
+
+ // This method modifies the length of the array, but may only be
+ // called when the new length is shorter than the old. It can
+ // therefore be called when elem_type has no default constructor,
+ // unlike SetLength. It removes elements from the array (see also
+ // RemoveElementsAt).
+ // @param aNewLen The desired length of this array.
+ void TruncateLength(size_type aNewLen)
+ {
+ size_type oldLen = Length();
+ MOZ_ASSERT(aNewLen <= oldLen,
+ "caller should use SetLength instead");
+ RemoveElementsAt(aNewLen, oldLen - aNewLen);
+ }
+
+ // This method ensures that the array has length at least the given
+ // length. If the current length is shorter than the given length,
+ // then new elements will be constructed using elem_type's default
+ // constructor.
+ // @param aMinLen The desired minimum length of this array.
+ // @return True if the operation succeeded; false otherwise.
+protected:
+ template<typename ActualAlloc = Alloc>
+ typename ActualAlloc::ResultType EnsureLengthAtLeast(size_type aMinLen)
+ {
+ size_type oldLen = Length();
+ if (aMinLen > oldLen) {
+ return ActualAlloc::ConvertBoolToResultType(
+ !!InsertElementsAt<ActualAlloc>(oldLen, aMinLen - oldLen));
+ }
+ return ActualAlloc::ConvertBoolToResultType(true);
+ }
+public:
+
+ MOZ_MUST_USE
+ bool EnsureLengthAtLeast(size_type aMinLen, const mozilla::fallible_t&)
+ {
+ return EnsureLengthAtLeast<FallibleAlloc>(aMinLen);
+ }
+
+ // This method inserts elements into the array, constructing
+ // them using elem_type's default constructor.
+ // @param aIndex the place to insert the new elements. This must be no
+ // greater than the current length of the array.
+ // @param aCount the number of elements to insert
+protected:
+ template<typename ActualAlloc = Alloc>
+ elem_type* InsertElementsAt(index_type aIndex, size_type aCount)
+ {
+ if (!base_type::template InsertSlotsAt<ActualAlloc>(aIndex, aCount,
+ sizeof(elem_type),
+ MOZ_ALIGNOF(elem_type))) {
+ return nullptr;
+ }
+
+ // Initialize the extra array elements
+ elem_type* iter = Elements() + aIndex;
+ elem_type* iend = iter + aCount;
+ for (; iter != iend; ++iter) {
+ elem_traits::Construct(iter);
+ }
+
+ return Elements() + aIndex;
+ }
+public:
+
+ MOZ_MUST_USE
+ elem_type* InsertElementsAt(index_type aIndex, size_type aCount,
+ const mozilla::fallible_t&)
+ {
+ return InsertElementsAt<FallibleAlloc>(aIndex, aCount);
+ }
+
+ // This method inserts elements into the array, constructing them
+ // elem_type's copy constructor (or whatever one-arg constructor
+ // happens to match the Item type).
+ // @param aIndex the place to insert the new elements. This must be no
+ // greater than the current length of the array.
+ // @param aCount the number of elements to insert.
+ // @param aItem the value to use when constructing the new elements.
+protected:
+ template<class Item, typename ActualAlloc = Alloc>
+ elem_type* InsertElementsAt(index_type aIndex, size_type aCount,
+ const Item& aItem);
+
+public:
+
+ template<class Item>
+ MOZ_MUST_USE
+ elem_type* InsertElementsAt(index_type aIndex, size_type aCount,
+ const Item& aItem, const mozilla::fallible_t&)
+ {
+ return InsertElementsAt<Item, FallibleAlloc>(aIndex, aCount, aItem);
+ }
+
+ // This method may be called to minimize the memory used by this array.
+ void Compact()
+ {
+ ShrinkCapacity(sizeof(elem_type), MOZ_ALIGNOF(elem_type));
+ }
+
+ //
+ // Sorting
+ //
+
+ // This function is meant to be used with the NS_QuickSort function. It
+ // maps the callback API expected by NS_QuickSort to the Comparator API
+ // used by nsTArray_Impl. See nsTArray_Impl::Sort.
+ template<class Comparator>
+ static int Compare(const void* aE1, const void* aE2, void* aData)
+ {
+ const Comparator* c = reinterpret_cast<const Comparator*>(aData);
+ const elem_type* a = static_cast<const elem_type*>(aE1);
+ const elem_type* b = static_cast<const elem_type*>(aE2);
+ return c->LessThan(*a, *b) ? -1 : (c->Equals(*a, *b) ? 0 : 1);
+ }
+
+ // This method sorts the elements of the array. It uses the LessThan
+ // method defined on the given Comparator object to collate elements.
+ // @param aComp The Comparator used to collate elements.
+ template<class Comparator>
+ void Sort(const Comparator& aComp)
+ {
+ NS_QuickSort(Elements(), Length(), sizeof(elem_type),
+ Compare<Comparator>, const_cast<Comparator*>(&aComp));
+ }
+
+ // A variation on the Sort method defined above that assumes that
+ // 'operator<' is defined for elem_type.
+ void Sort() { Sort(nsDefaultComparator<elem_type, elem_type>()); }
+
+protected:
+ using base_type::Hdr;
+ using base_type::ShrinkCapacity;
+
+ // This method invokes elem_type's destructor on a range of elements.
+ // @param aStart The index of the first element to destroy.
+ // @param aCount The number of elements to destroy.
+ void DestructRange(index_type aStart, size_type aCount)
+ {
+ elem_type* iter = Elements() + aStart;
+ elem_type *iend = iter + aCount;
+ for (; iter != iend; ++iter) {
+ elem_traits::Destruct(iter);
+ }
+ }
+
+ // This method invokes elem_type's copy-constructor on a range of elements.
+ // @param aStart The index of the first element to construct.
+ // @param aCount The number of elements to construct.
+ // @param aValues The array of elements to copy.
+ template<class Item>
+ void AssignRange(index_type aStart, size_type aCount, const Item* aValues)
+ {
+ AssignRangeAlgorithm<mozilla::IsPod<Item>::value,
+ mozilla::IsSame<Item, elem_type>::value>
+ ::implementation(Elements(), aStart, aCount, aValues);
+ }
+};
+
+template<typename E, class Alloc>
+template<class Item, typename ActualAlloc>
+auto
+nsTArray_Impl<E, Alloc>::ReplaceElementsAt(index_type aStart, size_type aCount,
+ const Item* aArray, size_type aArrayLen) -> elem_type*
+{
+ // Adjust memory allocation up-front to catch errors.
+ if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
+ Length() + aArrayLen - aCount, sizeof(elem_type)))) {
+ return nullptr;
+ }
+ DestructRange(aStart, aCount);
+ this->template ShiftData<ActualAlloc>(aStart, aCount, aArrayLen,
+ sizeof(elem_type),
+ MOZ_ALIGNOF(elem_type));
+ AssignRange(aStart, aArrayLen, aArray);
+ return Elements() + aStart;
+}
+
+template<typename E, class Alloc>
+void
+nsTArray_Impl<E, Alloc>::RemoveElementsAt(index_type aStart, size_type aCount)
+{
+ MOZ_ASSERT(aCount == 0 || aStart < Length(), "Invalid aStart index");
+ MOZ_ASSERT(aStart + aCount <= Length(), "Invalid length");
+ // Check that the previous assert didn't overflow
+ MOZ_ASSERT(aStart <= aStart + aCount, "Start index plus length overflows");
+ DestructRange(aStart, aCount);
+ this->template ShiftData<InfallibleAlloc>(aStart, aCount, 0,
+ sizeof(elem_type),
+ MOZ_ALIGNOF(elem_type));
+}
+
+template<typename E, class Alloc>
+template<typename Predicate>
+void
+nsTArray_Impl<E, Alloc>::RemoveElementsBy(Predicate aPredicate)
+{
+ if (base_type::mHdr == EmptyHdr()) {
+ return;
+ }
+
+ index_type j = 0;
+ index_type len = Length();
+ for (index_type i = 0; i < len; ++i) {
+ if (aPredicate(Elements()[i])) {
+ elem_traits::Destruct(Elements() + i);
+ } else {
+ if (j < i) {
+ copy_type::MoveNonOverlappingRegion(Elements() + j, Elements() + i,
+ 1, sizeof(elem_type));
+ }
+ ++j;
+ }
+ }
+ base_type::mHdr->mLength = j;
+}
+
+template<typename E, class Alloc>
+template<class Item, typename ActualAlloc>
+auto
+nsTArray_Impl<E, Alloc>::InsertElementsAt(index_type aIndex, size_type aCount,
+ const Item& aItem) -> elem_type*
+{
+ if (!base_type::template InsertSlotsAt<ActualAlloc>(aIndex, aCount,
+ sizeof(elem_type),
+ MOZ_ALIGNOF(elem_type))) {
+ return nullptr;
+ }
+
+ // Initialize the extra array elements
+ elem_type* iter = Elements() + aIndex;
+ elem_type* iend = iter + aCount;
+ for (; iter != iend; ++iter) {
+ elem_traits::Construct(iter, aItem);
+ }
+
+ return Elements() + aIndex;
+}
+
+template<typename E, class Alloc>
+template<typename ActualAlloc>
+auto
+nsTArray_Impl<E, Alloc>::InsertElementAt(index_type aIndex) -> elem_type*
+{
+ if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
+ Length() + 1, sizeof(elem_type)))) {
+ return nullptr;
+ }
+ this->template ShiftData<ActualAlloc>(aIndex, 0, 1, sizeof(elem_type),
+ MOZ_ALIGNOF(elem_type));
+ elem_type* elem = Elements() + aIndex;
+ elem_traits::Construct(elem);
+ return elem;
+}
+
+template<typename E, class Alloc>
+template<class Item, typename ActualAlloc>
+auto
+nsTArray_Impl<E, Alloc>::InsertElementAt(index_type aIndex, Item&& aItem) -> elem_type*
+{
+ if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
+ Length() + 1, sizeof(elem_type)))) {
+ return nullptr;
+ }
+ this->template ShiftData<ActualAlloc>(aIndex, 0, 1, sizeof(elem_type),
+ MOZ_ALIGNOF(elem_type));
+ elem_type* elem = Elements() + aIndex;
+ elem_traits::Construct(elem, mozilla::Forward<Item>(aItem));
+ return elem;
+}
+
+template<typename E, class Alloc>
+template<class Item, typename ActualAlloc>
+auto
+nsTArray_Impl<E, Alloc>::AppendElements(const Item* aArray, size_type aArrayLen) -> elem_type*
+{
+ if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
+ Length() + aArrayLen, sizeof(elem_type)))) {
+ return nullptr;
+ }
+ index_type len = Length();
+ AssignRange(len, aArrayLen, aArray);
+ this->IncrementLength(aArrayLen);
+ return Elements() + len;
+}
+
+template<typename E, class Alloc>
+template<class Item, class Allocator, typename ActualAlloc>
+auto
+nsTArray_Impl<E, Alloc>::AppendElements(nsTArray_Impl<Item, Allocator>&& aArray) -> elem_type*
+{
+ MOZ_ASSERT(&aArray != this, "argument must be different aArray");
+ if (Length() == 0) {
+ SwapElements<ActualAlloc>(aArray);
+ return Elements();
+ }
+
+ index_type len = Length();
+ index_type otherLen = aArray.Length();
+ if (!Alloc::Successful(this->template EnsureCapacity<Alloc>(
+ len + otherLen, sizeof(elem_type)))) {
+ return nullptr;
+ }
+ copy_type::MoveNonOverlappingRegion(Elements() + len, aArray.Elements(), otherLen,
+ sizeof(elem_type));
+ this->IncrementLength(otherLen);
+ aArray.template ShiftData<Alloc>(0, otherLen, 0, sizeof(elem_type),
+ MOZ_ALIGNOF(elem_type));
+ return Elements() + len;
+}
+
+template<typename E, class Alloc>
+template<class Item, typename ActualAlloc>
+auto
+nsTArray_Impl<E, Alloc>::AppendElement(Item&& aItem) -> elem_type*
+{
+ if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
+ Length() + 1, sizeof(elem_type)))) {
+ return nullptr;
+ }
+ elem_type* elem = Elements() + Length();
+ elem_traits::Construct(elem, mozilla::Forward<Item>(aItem));
+ this->IncrementLength(1);
+ return elem;
+}
+
+template<typename E, typename Alloc>
+inline void
+ImplCycleCollectionUnlink(nsTArray_Impl<E, Alloc>& aField)
+{
+ aField.Clear();
+}
+
+template<typename E, typename Alloc>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ nsTArray_Impl<E, Alloc>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ aFlags |= CycleCollectionEdgeNameArrayFlag;
+ size_t length = aField.Length();
+ for (size_t i = 0; i < length; ++i) {
+ ImplCycleCollectionTraverse(aCallback, aField[i], aName, aFlags);
+ }
+}
+
+//
+// nsTArray is an infallible vector class. See the comment at the top of this
+// file for more details.
+//
+template<class E>
+class nsTArray : public nsTArray_Impl<E, nsTArrayInfallibleAllocator>
+{
+public:
+ typedef nsTArray_Impl<E, nsTArrayInfallibleAllocator> base_type;
+ typedef nsTArray<E> self_type;
+ typedef typename base_type::size_type size_type;
+
+ nsTArray() {}
+ explicit nsTArray(size_type aCapacity) : base_type(aCapacity) {}
+ explicit nsTArray(const nsTArray& aOther) : base_type(aOther) {}
+ MOZ_IMPLICIT nsTArray(nsTArray&& aOther) : base_type(mozilla::Move(aOther)) {}
+ MOZ_IMPLICIT nsTArray(std::initializer_list<E> aIL) : base_type(aIL) {}
+
+ template<class Allocator>
+ explicit nsTArray(const nsTArray_Impl<E, Allocator>& aOther)
+ : base_type(aOther)
+ {
+ }
+ template<class Allocator>
+ MOZ_IMPLICIT nsTArray(nsTArray_Impl<E, Allocator>&& aOther)
+ : base_type(mozilla::Move(aOther))
+ {
+ }
+
+ self_type& operator=(const self_type& aOther)
+ {
+ base_type::operator=(aOther);
+ return *this;
+ }
+ template<class Allocator>
+ self_type& operator=(const nsTArray_Impl<E, Allocator>& aOther)
+ {
+ base_type::operator=(aOther);
+ return *this;
+ }
+ self_type& operator=(self_type&& aOther)
+ {
+ base_type::operator=(mozilla::Move(aOther));
+ return *this;
+ }
+ template<class Allocator>
+ self_type& operator=(nsTArray_Impl<E, Allocator>&& aOther)
+ {
+ base_type::operator=(mozilla::Move(aOther));
+ return *this;
+ }
+
+ using base_type::AppendElement;
+ using base_type::AppendElements;
+ using base_type::EnsureLengthAtLeast;
+ using base_type::InsertElementAt;
+ using base_type::InsertElementsAt;
+ using base_type::InsertElementSorted;
+ using base_type::ReplaceElementsAt;
+ using base_type::SetCapacity;
+ using base_type::SetLength;
+};
+
+//
+// FallibleTArray is a fallible vector class.
+//
+template<class E>
+class FallibleTArray : public nsTArray_Impl<E, nsTArrayFallibleAllocator>
+{
+public:
+ typedef nsTArray_Impl<E, nsTArrayFallibleAllocator> base_type;
+ typedef FallibleTArray<E> self_type;
+ typedef typename base_type::size_type size_type;
+
+ FallibleTArray() {}
+ explicit FallibleTArray(size_type aCapacity) : base_type(aCapacity) {}
+ explicit FallibleTArray(const FallibleTArray<E>& aOther) : base_type(aOther) {}
+ FallibleTArray(FallibleTArray<E>&& aOther)
+ : base_type(mozilla::Move(aOther))
+ {
+ }
+
+ template<class Allocator>
+ explicit FallibleTArray(const nsTArray_Impl<E, Allocator>& aOther)
+ : base_type(aOther)
+ {
+ }
+ template<class Allocator>
+ explicit FallibleTArray(nsTArray_Impl<E, Allocator>&& aOther)
+ : base_type(mozilla::Move(aOther))
+ {
+ }
+
+ self_type& operator=(const self_type& aOther)
+ {
+ base_type::operator=(aOther);
+ return *this;
+ }
+ template<class Allocator>
+ self_type& operator=(const nsTArray_Impl<E, Allocator>& aOther)
+ {
+ base_type::operator=(aOther);
+ return *this;
+ }
+ self_type& operator=(self_type&& aOther)
+ {
+ base_type::operator=(mozilla::Move(aOther));
+ return *this;
+ }
+ template<class Allocator>
+ self_type& operator=(nsTArray_Impl<E, Allocator>&& aOther)
+ {
+ base_type::operator=(mozilla::Move(aOther));
+ return *this;
+ }
+};
+
+//
+// AutoTArray<E, N> is like nsTArray<E>, but with N elements of inline storage.
+// Storing more than N elements is fine, but it will cause a heap allocation.
+//
+template<class E, size_t N>
+class MOZ_NON_MEMMOVABLE AutoTArray : public nsTArray<E>
+{
+ static_assert(N != 0, "AutoTArray<E, 0> should be specialized");
+public:
+ typedef AutoTArray<E, N> self_type;
+ typedef nsTArray<E> base_type;
+ typedef typename base_type::Header Header;
+ typedef typename base_type::elem_type elem_type;
+
+ AutoTArray()
+ {
+ Init();
+ }
+
+ AutoTArray(const self_type& aOther)
+ {
+ Init();
+ this->AppendElements(aOther);
+ }
+
+ explicit AutoTArray(const base_type& aOther)
+ {
+ Init();
+ this->AppendElements(aOther);
+ }
+
+ explicit AutoTArray(base_type&& aOther)
+ {
+ Init();
+ this->SwapElements(aOther);
+ }
+
+ template<typename Allocator>
+ explicit AutoTArray(nsTArray_Impl<elem_type, Allocator>&& aOther)
+ {
+ Init();
+ this->SwapElements(aOther);
+ }
+
+ MOZ_IMPLICIT AutoTArray(std::initializer_list<E> aIL)
+ {
+ Init();
+ this->AppendElements(aIL.begin(), aIL.size());
+ }
+
+ self_type& operator=(const self_type& aOther)
+ {
+ base_type::operator=(aOther);
+ return *this;
+ }
+
+ template<typename Allocator>
+ self_type& operator=(const nsTArray_Impl<elem_type, Allocator>& aOther)
+ {
+ base_type::operator=(aOther);
+ return *this;
+ }
+
+private:
+ // nsTArray_base casts itself as an nsAutoArrayBase in order to get a pointer
+ // to mAutoBuf.
+ template<class Allocator, class Copier>
+ friend class nsTArray_base;
+
+ void Init()
+ {
+ static_assert(MOZ_ALIGNOF(elem_type) <= 8,
+ "can't handle alignments greater than 8, "
+ "see nsTArray_base::UsesAutoArrayBuffer()");
+ // Temporary work around for VS2012 RC compiler crash
+ Header** phdr = base_type::PtrToHdr();
+ *phdr = reinterpret_cast<Header*>(&mAutoBuf);
+ (*phdr)->mLength = 0;
+ (*phdr)->mCapacity = N;
+ (*phdr)->mIsAutoArray = 1;
+
+ MOZ_ASSERT(base_type::GetAutoArrayBuffer(MOZ_ALIGNOF(elem_type)) ==
+ reinterpret_cast<Header*>(&mAutoBuf),
+ "GetAutoArrayBuffer needs to be fixed");
+ }
+
+ // Declare mAutoBuf aligned to the maximum of the header's alignment and
+ // elem_type's alignment. We need to use a union rather than
+ // MOZ_ALIGNED_DECL because GCC is picky about what goes into
+ // __attribute__((aligned(foo))).
+ union
+ {
+ char mAutoBuf[sizeof(nsTArrayHeader) + N * sizeof(elem_type)];
+ // Do the max operation inline to ensure that it is a compile-time constant.
+ mozilla::AlignedElem<(MOZ_ALIGNOF(Header) > MOZ_ALIGNOF(elem_type)) ?
+ MOZ_ALIGNOF(Header) : MOZ_ALIGNOF(elem_type)> mAlign;
+ };
+};
+
+//
+// Specialization of AutoTArray<E, N> for the case where N == 0.
+// AutoTArray<E, 0> behaves exactly like nsTArray<E>, but without this
+// specialization, it stores a useless inline header.
+//
+// We do have many AutoTArray<E, 0> objects in memory: about 2,000 per tab as
+// of May 2014. These are typically not explicitly AutoTArray<E, 0> but rather
+// AutoTArray<E, N> for some value N depending on template parameters, in
+// generic code.
+//
+// For that reason, we optimize this case with the below partial specialization,
+// which ensures that AutoTArray<E, 0> is just like nsTArray<E>, without any
+// inline header overhead.
+//
+template<class E>
+class AutoTArray<E, 0> : public nsTArray<E>
+{
+};
+
+template<class E, size_t N>
+struct nsTArray_CopyChooser<AutoTArray<E, N>>
+{
+ typedef nsTArray_CopyWithConstructors<AutoTArray<E, N>> Type;
+};
+
+// Assert that AutoTArray doesn't have any extra padding inside.
+//
+// It's important that the data stored in this auto array takes up a multiple of
+// 8 bytes; e.g. AutoTArray<uint32_t, 1> wouldn't work. Since AutoTArray
+// contains a pointer, its size must be a multiple of alignof(void*). (This is
+// because any type may be placed into an array, and there's no padding between
+// elements of an array.) The compiler pads the end of the structure to
+// enforce this rule.
+//
+// If we used AutoTArray<uint32_t, 1> below, this assertion would fail on a
+// 64-bit system, where the compiler inserts 4 bytes of padding at the end of
+// the auto array to make its size a multiple of alignof(void*) == 8 bytes.
+
+static_assert(sizeof(AutoTArray<uint32_t, 2>) ==
+ sizeof(void*) + sizeof(nsTArrayHeader) + sizeof(uint32_t) * 2,
+ "AutoTArray shouldn't contain any extra padding, "
+ "see the comment");
+
+// Definitions of nsTArray_Impl methods
+#include "nsTArray-inl.h"
+
+#endif // nsTArray_h__
diff --git a/xpcom/glue/nsTArrayForwardDeclare.h b/xpcom/glue/nsTArrayForwardDeclare.h
new file mode 100644
index 000000000..f63ef5f59
--- /dev/null
+++ b/xpcom/glue/nsTArrayForwardDeclare.h
@@ -0,0 +1,36 @@
+/* -*- 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 nsTArrayForwardDeclare_h__
+#define nsTArrayForwardDeclare_h__
+
+//
+// This simple header file contains forward declarations for the TArray family
+// of classes.
+//
+// Including this header is preferable to writing
+//
+// template<class E> class nsTArray;
+//
+// yourself, since this header gives us flexibility to e.g. change the default
+// template parameters.
+//
+
+#include <stddef.h>
+
+template<class E>
+class nsTArray;
+
+template<class E>
+class FallibleTArray;
+
+template<class E, size_t N>
+class AutoTArray;
+
+template<class E>
+using InfallibleTArray = nsTArray<E>;
+
+#endif
diff --git a/xpcom/glue/nsTHashtable.h b/xpcom/glue/nsTHashtable.h
new file mode 100644
index 000000000..705b0294e
--- /dev/null
+++ b/xpcom/glue/nsTHashtable.h
@@ -0,0 +1,577 @@
+/* -*- 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 nsTHashtable_h__
+#define nsTHashtable_h__
+
+#include "PLDHashTable.h"
+#include "nsPointerHashKeys.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/fallible.h"
+#include "mozilla/MemoryChecking.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Move.h"
+#include "mozilla/OperatorNewExtensions.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/TypeTraits.h"
+
+#include <new>
+
+/**
+ * a base class for templated hashtables.
+ *
+ * Clients will rarely need to use this class directly. Check the derived
+ * classes first, to see if they will meet your needs.
+ *
+ * @param EntryType the templated entry-type class that is managed by the
+ * hashtable. <code>EntryType</code> must extend the following declaration,
+ * and <strong>must not declare any virtual functions or derive from classes
+ * with virtual functions.</strong> Any vtable pointer would break the
+ * PLDHashTable code.
+ *<pre> class EntryType : public PLDHashEntryHdr
+ * {
+ * public: or friend nsTHashtable<EntryType>;
+ * // KeyType is what we use when Get()ing or Put()ing this entry
+ * // this should either be a simple datatype (uint32_t, nsISupports*) or
+ * // a const reference (const nsAString&)
+ * typedef something KeyType;
+ * // KeyTypePointer is the pointer-version of KeyType, because
+ * // PLDHashTable.h requires keys to cast to <code>const void*</code>
+ * typedef const something* KeyTypePointer;
+ *
+ * EntryType(KeyTypePointer aKey);
+ *
+ * // A copy or C++11 Move constructor must be defined, even if
+ * // AllowMemMove() == true, otherwise you will cause link errors.
+ * EntryType(const EntryType& aEnt); // Either this...
+ * EntryType(EntryType&& aEnt); // ...or this
+ *
+ * // the destructor must be defined... or you will cause link errors!
+ * ~EntryType();
+ *
+ * // KeyEquals(): does this entry match this key?
+ * bool KeyEquals(KeyTypePointer aKey) const;
+ *
+ * // KeyToPointer(): Convert KeyType to KeyTypePointer
+ * static KeyTypePointer KeyToPointer(KeyType aKey);
+ *
+ * // HashKey(): calculate the hash number
+ * static PLDHashNumber HashKey(KeyTypePointer aKey);
+ *
+ * // ALLOW_MEMMOVE can we move this class with memmove(), or do we have
+ * // to use the copy constructor?
+ * enum { ALLOW_MEMMOVE = true/false };
+ * }</pre>
+ *
+ * @see nsInterfaceHashtable
+ * @see nsDataHashtable
+ * @see nsClassHashtable
+ * @author "Benjamin Smedberg <bsmedberg@covad.net>"
+ */
+
+template<class EntryType>
+class MOZ_NEEDS_NO_VTABLE_TYPE nsTHashtable
+{
+ typedef mozilla::fallible_t fallible_t;
+ static_assert(mozilla::IsPointer<typename EntryType::KeyTypePointer>::value,
+ "KeyTypePointer should be a pointer");
+
+public:
+ // Separate constructors instead of default aInitLength parameter since
+ // otherwise the default no-arg constructor isn't found.
+ nsTHashtable()
+ : mTable(Ops(), sizeof(EntryType), PLDHashTable::kDefaultInitialLength)
+ {}
+ explicit nsTHashtable(uint32_t aInitLength)
+ : mTable(Ops(), sizeof(EntryType), aInitLength)
+ {}
+
+ /**
+ * destructor, cleans up and deallocates
+ */
+ ~nsTHashtable();
+
+ nsTHashtable(nsTHashtable<EntryType>&& aOther);
+
+ /**
+ * Return the generation number for the table. This increments whenever
+ * the table data items are moved.
+ */
+ uint32_t GetGeneration() const { return mTable.Generation(); }
+
+ /**
+ * KeyType is typedef'ed for ease of use.
+ */
+ typedef typename EntryType::KeyType KeyType;
+
+ /**
+ * KeyTypePointer is typedef'ed for ease of use.
+ */
+ typedef typename EntryType::KeyTypePointer KeyTypePointer;
+
+ /**
+ * Return the number of entries in the table.
+ * @return number of entries
+ */
+ uint32_t Count() const { return mTable.EntryCount(); }
+
+ /**
+ * Return true if the hashtable is empty.
+ */
+ bool IsEmpty() const { return Count() == 0; }
+
+ /**
+ * Get the entry associated with a key.
+ * @param aKey the key to retrieve
+ * @return pointer to the entry class, if the key exists; nullptr if the
+ * key doesn't exist
+ */
+ EntryType* GetEntry(KeyType aKey) const
+ {
+ return static_cast<EntryType*>(
+ const_cast<PLDHashTable*>(&mTable)->Search(EntryType::KeyToPointer(aKey)));
+ }
+
+ /**
+ * Return true if an entry for the given key exists, false otherwise.
+ * @param aKey the key to retrieve
+ * @return true if the key exists, false if the key doesn't exist
+ */
+ bool Contains(KeyType aKey) const { return !!GetEntry(aKey); }
+
+ /**
+ * Get the entry associated with a key, or create a new entry,
+ * @param aKey the key to retrieve
+ * @return pointer to the entry class retreived; nullptr only if memory
+ can't be allocated
+ */
+ EntryType* PutEntry(KeyType aKey)
+ {
+ // infallible add
+ return static_cast<EntryType*>(mTable.Add(EntryType::KeyToPointer(aKey)));
+ }
+
+ MOZ_MUST_USE
+ EntryType* PutEntry(KeyType aKey, const fallible_t&)
+ {
+ return static_cast<EntryType*>(mTable.Add(EntryType::KeyToPointer(aKey),
+ mozilla::fallible));
+ }
+
+ /**
+ * Remove the entry associated with a key.
+ * @param aKey of the entry to remove
+ */
+ void RemoveEntry(KeyType aKey)
+ {
+ mTable.Remove(EntryType::KeyToPointer(aKey));
+ }
+
+ /**
+ * Remove the entry associated with a key.
+ * @param aEntry the entry-pointer to remove (obtained from GetEntry)
+ */
+ void RemoveEntry(EntryType* aEntry)
+ {
+ mTable.RemoveEntry(aEntry);
+ }
+
+ /**
+ * Remove the entry associated with a key, but don't resize the hashtable.
+ * This is a low-level method, and is not recommended unless you know what
+ * you're doing. If you use it, please add a comment explaining why you
+ * didn't use RemoveEntry().
+ * @param aEntry the entry-pointer to remove (obtained from GetEntry)
+ */
+ void RawRemoveEntry(EntryType* aEntry)
+ {
+ mTable.RawRemove(aEntry);
+ }
+
+ // This is an iterator that also allows entry removal. Example usage:
+ //
+ // for (auto iter = table.Iter(); !iter.Done(); iter.Next()) {
+ // Entry* entry = iter.Get();
+ // // ... do stuff with |entry| ...
+ // // ... possibly call iter.Remove() once ...
+ // }
+ //
+ class Iterator : public PLDHashTable::Iterator
+ {
+ public:
+ typedef PLDHashTable::Iterator Base;
+
+ explicit Iterator(nsTHashtable* aTable) : Base(&aTable->mTable) {}
+ Iterator(Iterator&& aOther) : Base(aOther.mTable) {}
+ ~Iterator() {}
+
+ EntryType* Get() const { return static_cast<EntryType*>(Base::Get()); }
+
+ private:
+ Iterator() = delete;
+ Iterator(const Iterator&) = delete;
+ Iterator& operator=(const Iterator&) = delete;
+ Iterator& operator=(const Iterator&&) = delete;
+ };
+
+ Iterator Iter() { return Iterator(this); }
+
+ Iterator ConstIter() const
+ {
+ return Iterator(const_cast<nsTHashtable*>(this));
+ }
+
+ /**
+ * Remove all entries, return hashtable to "pristine" state. It's
+ * conceptually the same as calling the destructor and then re-calling the
+ * constructor.
+ */
+ void Clear()
+ {
+ mTable.Clear();
+ }
+
+ /**
+ * Measure the size of the table's entry storage. Does *not* measure anything
+ * hanging off table entries; hence the "Shallow" prefix. To measure that,
+ * either use SizeOfExcludingThis() or iterate manually over the entries,
+ * calling SizeOfExcludingThis() on each one.
+ *
+ * @param aMallocSizeOf the function used to measure heap-allocated blocks
+ * @return the measured shallow size of the table
+ */
+ size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ /**
+ * Like ShallowSizeOfExcludingThis, but includes sizeof(*this).
+ */
+ size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return aMallocSizeOf(this) + ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ /**
+ * This is a "deep" measurement of the table. To use it, |EntryType| must
+ * define SizeOfExcludingThis, and that method will be called on all live
+ * entries.
+ */
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ size_t n = ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = ConstIter(); !iter.Done(); iter.Next()) {
+ n += (*iter.Get()).SizeOfExcludingThis(aMallocSizeOf);
+ }
+ return n;
+ }
+
+ /**
+ * Like SizeOfExcludingThis, but includes sizeof(*this).
+ */
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ /**
+ * Swap the elements in this hashtable with the elements in aOther.
+ */
+ void SwapElements(nsTHashtable<EntryType>& aOther)
+ {
+ MOZ_ASSERT_IF(this->mTable.Ops() && aOther.mTable.Ops(),
+ this->mTable.Ops() == aOther.mTable.Ops());
+ mozilla::Swap(this->mTable, aOther.mTable);
+ }
+
+#ifdef DEBUG
+ /**
+ * Mark the table as constant after initialization.
+ *
+ * This will prevent assertions when a read-only hash is accessed on multiple
+ * threads without synchronization.
+ */
+ void MarkImmutable()
+ {
+ mTable.MarkImmutable();
+ }
+#endif
+
+protected:
+ PLDHashTable mTable;
+
+ static PLDHashNumber s_HashKey(const void* aKey);
+
+ static bool s_MatchEntry(const PLDHashEntryHdr* aEntry,
+ const void* aKey);
+
+ static void s_CopyEntry(PLDHashTable* aTable, const PLDHashEntryHdr* aFrom,
+ PLDHashEntryHdr* aTo);
+
+ static void s_ClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry);
+
+ static void s_InitEntry(PLDHashEntryHdr* aEntry, const void* aKey);
+
+private:
+ // copy constructor, not implemented
+ nsTHashtable(nsTHashtable<EntryType>& aToCopy) = delete;
+
+ /**
+ * Gets the table's ops.
+ */
+ static const PLDHashTableOps* Ops();
+
+ // assignment operator, not implemented
+ nsTHashtable<EntryType>& operator=(nsTHashtable<EntryType>& aToEqual) = delete;
+};
+
+//
+// template definitions
+//
+
+template<class EntryType>
+nsTHashtable<EntryType>::nsTHashtable(nsTHashtable<EntryType>&& aOther)
+ : mTable(mozilla::Move(aOther.mTable))
+{
+ // aOther shouldn't touch mTable after this, because we've stolen the table's
+ // pointers but not overwitten them.
+ MOZ_MAKE_MEM_UNDEFINED(&aOther.mTable, sizeof(aOther.mTable));
+}
+
+template<class EntryType>
+nsTHashtable<EntryType>::~nsTHashtable()
+{
+}
+
+template<class EntryType>
+/* static */ const PLDHashTableOps*
+nsTHashtable<EntryType>::Ops()
+{
+ // If this variable is a global variable, we get strange start-up failures on
+ // WindowsCrtPatch.h (see bug 1166598 comment 20). But putting it inside a
+ // function avoids that problem.
+ static const PLDHashTableOps sOps =
+ {
+ s_HashKey,
+ s_MatchEntry,
+ EntryType::ALLOW_MEMMOVE ? PLDHashTable::MoveEntryStub : s_CopyEntry,
+ s_ClearEntry,
+ s_InitEntry
+ };
+ return &sOps;
+}
+
+// static definitions
+
+template<class EntryType>
+PLDHashNumber
+nsTHashtable<EntryType>::s_HashKey(const void* aKey)
+{
+ return EntryType::HashKey(static_cast<const KeyTypePointer>(aKey));
+}
+
+template<class EntryType>
+bool
+nsTHashtable<EntryType>::s_MatchEntry(const PLDHashEntryHdr* aEntry,
+ const void* aKey)
+{
+ return ((const EntryType*)aEntry)->KeyEquals(
+ static_cast<const KeyTypePointer>(aKey));
+}
+
+template<class EntryType>
+void
+nsTHashtable<EntryType>::s_CopyEntry(PLDHashTable* aTable,
+ const PLDHashEntryHdr* aFrom,
+ PLDHashEntryHdr* aTo)
+{
+ EntryType* fromEntry =
+ const_cast<EntryType*>(static_cast<const EntryType*>(aFrom));
+
+ new (mozilla::KnownNotNull, aTo) EntryType(mozilla::Move(*fromEntry));
+
+ fromEntry->~EntryType();
+}
+
+template<class EntryType>
+void
+nsTHashtable<EntryType>::s_ClearEntry(PLDHashTable* aTable,
+ PLDHashEntryHdr* aEntry)
+{
+ static_cast<EntryType*>(aEntry)->~EntryType();
+}
+
+template<class EntryType>
+void
+nsTHashtable<EntryType>::s_InitEntry(PLDHashEntryHdr* aEntry,
+ const void* aKey)
+{
+ new (mozilla::KnownNotNull, aEntry) EntryType(static_cast<KeyTypePointer>(aKey));
+}
+
+class nsCycleCollectionTraversalCallback;
+
+template<class EntryType>
+inline void
+ImplCycleCollectionUnlink(nsTHashtable<EntryType>& aField)
+{
+ aField.Clear();
+}
+
+template<class EntryType>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ nsTHashtable<EntryType>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ for (auto iter = aField.Iter(); !iter.Done(); iter.Next()) {
+ EntryType* entry = iter.Get();
+ ImplCycleCollectionTraverse(aCallback, *entry, aName, aFlags);
+ }
+}
+
+/**
+ * For nsTHashtable with pointer entries, we can have a template specialization
+ * that layers a typed T* interface on top of a common implementation that
+ * works internally with void pointers. This arrangement saves code size and
+ * might slightly improve performance as well.
+ */
+
+/**
+ * We need a separate entry type class for the inheritance structure of the
+ * nsTHashtable specialization below; nsVoidPtrHashKey is simply typedefed to a
+ * specialization of nsPtrHashKey, and the formulation:
+ *
+ * class nsTHashtable<nsPtrHashKey<T>> : protected nsTHashtable<nsPtrHashKey<const void>
+ *
+ * is not going to turn out very well, since we'd wind up with an nsTHashtable
+ * instantiation that is its own base class.
+ */
+namespace detail {
+
+class VoidPtrHashKey : public nsPtrHashKey<const void>
+{
+ typedef nsPtrHashKey<const void> Base;
+
+public:
+ explicit VoidPtrHashKey(const void* aKey) : Base(aKey) {}
+};
+
+} // namespace detail
+
+/**
+ * See the main nsTHashtable documentation for descriptions of this class's
+ * methods.
+ */
+template<typename T>
+class nsTHashtable<nsPtrHashKey<T>> : protected nsTHashtable<::detail::VoidPtrHashKey>
+{
+ typedef nsTHashtable<::detail::VoidPtrHashKey> Base;
+ typedef nsPtrHashKey<T> EntryType;
+
+ // We play games with reinterpret_cast'ing between these two classes, so
+ // try to ensure that playing said games is reasonable.
+ static_assert(sizeof(nsPtrHashKey<T>) == sizeof(::detail::VoidPtrHashKey),
+ "hash keys must be the same size");
+
+ nsTHashtable(const nsTHashtable& aOther) = delete;
+ nsTHashtable& operator=(const nsTHashtable& aOther) = delete;
+
+public:
+ nsTHashtable() = default;
+ explicit nsTHashtable(uint32_t aInitLength)
+ : Base(aInitLength)
+ {}
+
+ ~nsTHashtable() = default;
+
+ nsTHashtable(nsTHashtable&&) = default;
+
+ /* Wrapper functions */
+ using Base::GetGeneration;
+ using Base::Count;
+ using Base::IsEmpty;
+ using Base::Clear;
+
+ using Base::ShallowSizeOfExcludingThis;
+ using Base::ShallowSizeOfIncludingThis;
+
+#ifdef DEBUG
+ using Base::MarkImmutable;
+#endif
+
+ EntryType* GetEntry(T* aKey) const
+ {
+ return reinterpret_cast<EntryType*>(Base::GetEntry(aKey));
+ }
+
+ bool Contains(T* aKey) const
+ {
+ return Base::Contains(aKey);
+ }
+
+ EntryType* PutEntry(T* aKey)
+ {
+ return reinterpret_cast<EntryType*>(Base::PutEntry(aKey));
+ }
+
+ MOZ_MUST_USE
+ EntryType* PutEntry(T* aKey, const mozilla::fallible_t&)
+ {
+ return reinterpret_cast<EntryType*>(
+ Base::PutEntry(aKey, mozilla::fallible));
+ }
+
+ void RemoveEntry(T* aKey)
+ {
+ Base::RemoveEntry(aKey);
+ }
+
+ void RemoveEntry(EntryType* aEntry)
+ {
+ Base::RemoveEntry(reinterpret_cast<::detail::VoidPtrHashKey*>(aEntry));
+ }
+
+ void RawRemoveEntry(EntryType* aEntry)
+ {
+ Base::RawRemoveEntry(reinterpret_cast<::detail::VoidPtrHashKey*>(aEntry));
+ }
+
+ class Iterator : public Base::Iterator
+ {
+ public:
+ typedef nsTHashtable::Base::Iterator Base;
+
+ explicit Iterator(nsTHashtable* aTable) : Base(aTable) {}
+ Iterator(Iterator&& aOther) : Base(mozilla::Move(aOther)) {}
+ ~Iterator() = default;
+
+ EntryType* Get() const { return reinterpret_cast<EntryType*>(Base::Get()); }
+
+ private:
+ Iterator() = delete;
+ Iterator(const Iterator&) = delete;
+ Iterator& operator=(const Iterator&) = delete;
+ Iterator& operator=(Iterator&&) = delete;
+ };
+
+ Iterator Iter() { return Iterator(this); }
+
+ Iterator ConstIter() const
+ {
+ return Iterator(const_cast<nsTHashtable*>(this));
+ }
+
+ void SwapElements(nsTHashtable& aOther)
+ {
+ Base::SwapElements(aOther);
+ }
+};
+
+#endif // nsTHashtable_h__
diff --git a/xpcom/glue/nsTObserverArray.cpp b/xpcom/glue/nsTObserverArray.cpp
new file mode 100644
index 000000000..dd68631f4
--- /dev/null
+++ b/xpcom/glue/nsTObserverArray.cpp
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsTObserverArray.h"
+
+void
+nsTObserverArray_base::AdjustIterators(index_type aModPos,
+ diff_type aAdjustment)
+{
+ NS_PRECONDITION(aAdjustment == -1 || aAdjustment == 1, "invalid adjustment");
+ Iterator_base* iter = mIterators;
+ while (iter) {
+ if (iter->mPosition > aModPos) {
+ iter->mPosition += aAdjustment;
+ }
+ iter = iter->mNext;
+ }
+}
+
+void
+nsTObserverArray_base::ClearIterators()
+{
+ Iterator_base* iter = mIterators;
+ while (iter) {
+ iter->mPosition = 0;
+ iter = iter->mNext;
+ }
+}
diff --git a/xpcom/glue/nsTObserverArray.h b/xpcom/glue/nsTObserverArray.h
new file mode 100644
index 000000000..e9966d080
--- /dev/null
+++ b/xpcom/glue/nsTObserverArray.h
@@ -0,0 +1,520 @@
+/* -*- 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 nsTObserverArray_h___
+#define nsTObserverArray_h___
+
+#include "mozilla/MemoryReporting.h"
+#include "nsTArray.h"
+#include "nsCycleCollectionNoteChild.h"
+
+/**
+ * An array of observers. Like a normal array, but supports iterators that are
+ * stable even if the array is modified during iteration.
+ * The template parameter T is the observer type the array will hold;
+ * N is the number of built-in storage slots that come with the array.
+ * NOTE: You probably want to use nsTObserverArray, unless you specifically
+ * want built-in storage. See below.
+ * @see nsTObserverArray, nsTArray
+ */
+
+class nsTObserverArray_base
+{
+public:
+ typedef size_t index_type;
+ typedef size_t size_type;
+ typedef ptrdiff_t diff_type;
+
+protected:
+ class Iterator_base
+ {
+ protected:
+ friend class nsTObserverArray_base;
+
+ Iterator_base(index_type aPosition, Iterator_base* aNext)
+ : mPosition(aPosition)
+ , mNext(aNext)
+ {
+ }
+
+ // The current position of the iterator. Its exact meaning differs
+ // depending on iterator. See nsTObserverArray<T>::ForwardIterator.
+ index_type mPosition;
+
+ // The next iterator currently iterating the same array
+ Iterator_base* mNext;
+ };
+
+ nsTObserverArray_base() : mIterators(nullptr) {}
+
+ ~nsTObserverArray_base()
+ {
+ NS_ASSERTION(mIterators == nullptr, "iterators outlasting array");
+ }
+
+ /**
+ * Adjusts iterators after an element has been inserted or removed
+ * from the array.
+ * @param aModPos Position where elements were added or removed.
+ * @param aAdjustment -1 if an element was removed, 1 if an element was
+ * added.
+ */
+ void AdjustIterators(index_type aModPos, diff_type aAdjustment);
+
+ /**
+ * Clears iterators when the array is destroyed.
+ */
+ void ClearIterators();
+
+ mutable Iterator_base* mIterators;
+};
+
+template<class T, size_t N>
+class nsAutoTObserverArray : protected nsTObserverArray_base
+{
+public:
+ typedef T elem_type;
+ typedef nsTArray<T> array_type;
+
+ nsAutoTObserverArray() {}
+
+ //
+ // Accessor methods
+ //
+
+ // @return The number of elements in the array.
+ size_type Length() const { return mArray.Length(); }
+
+ // @return True if the array is empty or false otherwise.
+ bool IsEmpty() const { return mArray.IsEmpty(); }
+
+ // This method provides direct access to an element of the array. The given
+ // index must be within the array bounds. If the underlying array may change
+ // during iteration, use an iterator instead of this function.
+ // @param aIndex The index of an element in the array.
+ // @return A reference to the i'th element of the array.
+ elem_type& ElementAt(index_type aIndex)
+ {
+ return mArray.ElementAt(aIndex);
+ }
+
+ // Same as above, but readonly.
+ const elem_type& ElementAt(index_type aIndex) const
+ {
+ return mArray.ElementAt(aIndex);
+ }
+
+ // This method provides direct access to an element of the array in a bounds
+ // safe manner. If the requested index is out of bounds the provided default
+ // value is returned.
+ // @param aIndex The index of an element in the array.
+ // @param aDef The value to return if the index is out of bounds.
+ elem_type& SafeElementAt(index_type aIndex, elem_type& aDef)
+ {
+ return mArray.SafeElementAt(aIndex, aDef);
+ }
+
+ // Same as above, but readonly.
+ const elem_type& SafeElementAt(index_type aIndex, const elem_type& aDef) const
+ {
+ return mArray.SafeElementAt(aIndex, aDef);
+ }
+
+ // No operator[] is provided because the point of this class is to support
+ // allow modifying the array during iteration, and ElementAt() is not safe
+ // in those conditions.
+
+ //
+ // Search methods
+ //
+
+ // This method searches, starting from the beginning of the array,
+ // for the first element in this array that is equal to the given element.
+ // 'operator==' must be defined for elem_type.
+ // @param aItem The item to search for.
+ // @return true if the element was found.
+ template<class Item>
+ bool Contains(const Item& aItem) const
+ {
+ return IndexOf(aItem) != array_type::NoIndex;
+ }
+
+ // This method searches for the offset of the first element in this
+ // array that is equal to the given element.
+ // 'operator==' must be defined for elem_type.
+ // @param aItem The item to search for.
+ // @param aStart The index to start from.
+ // @return The index of the found element or NoIndex if not found.
+ template<class Item>
+ index_type IndexOf(const Item& aItem, index_type aStart = 0) const
+ {
+ return mArray.IndexOf(aItem, aStart);
+ }
+
+ //
+ // Mutation methods
+ //
+
+ // Insert a given element at the given index.
+ // @param aIndex The index at which to insert item.
+ // @param aItem The item to insert,
+ // @return A pointer to the newly inserted element, or a null on DOM
+ template<class Item>
+ elem_type* InsertElementAt(index_type aIndex, const Item& aItem)
+ {
+ elem_type* item = mArray.InsertElementAt(aIndex, aItem);
+ AdjustIterators(aIndex, 1);
+ return item;
+ }
+
+ // Same as above but without copy constructing.
+ // This is useful to avoid temporaries.
+ elem_type* InsertElementAt(index_type aIndex)
+ {
+ elem_type* item = mArray.InsertElementAt(aIndex);
+ AdjustIterators(aIndex, 1);
+ return item;
+ }
+
+ // Prepend an element to the array unless it already exists in the array.
+ // 'operator==' must be defined for elem_type.
+ // @param aItem The item to prepend.
+ // @return true if the element was found, or inserted successfully.
+ template<class Item>
+ bool PrependElementUnlessExists(const Item& aItem)
+ {
+ if (Contains(aItem)) {
+ return true;
+ }
+
+ bool inserted = mArray.InsertElementAt(0, aItem) != nullptr;
+ AdjustIterators(0, 1);
+ return inserted;
+ }
+
+ // Append an element to the array.
+ // @param aItem The item to append.
+ // @return A pointer to the newly appended element, or null on OOM.
+ template<class Item>
+ elem_type* AppendElement(const Item& aItem)
+ {
+ return mArray.AppendElement(aItem);
+ }
+
+ // Same as above, but without copy-constructing. This is useful to avoid
+ // temporaries.
+ elem_type* AppendElement()
+ {
+ return mArray.AppendElement();
+ }
+
+ // Append an element to the array unless it already exists in the array.
+ // 'operator==' must be defined for elem_type.
+ // @param aItem The item to append.
+ // @return true if the element was found, or inserted successfully.
+ template<class Item>
+ bool AppendElementUnlessExists(const Item& aItem)
+ {
+ return Contains(aItem) || AppendElement(aItem) != nullptr;
+ }
+
+ // Remove an element from the array.
+ // @param aIndex The index of the item to remove.
+ void RemoveElementAt(index_type aIndex)
+ {
+ NS_ASSERTION(aIndex < mArray.Length(), "invalid index");
+ mArray.RemoveElementAt(aIndex);
+ AdjustIterators(aIndex, -1);
+ }
+
+ // This helper function combines IndexOf with RemoveElementAt to "search
+ // and destroy" the first element that is equal to the given element.
+ // 'operator==' must be defined for elem_type.
+ // @param aItem The item to search for.
+ // @return true if the element was found and removed.
+ template<class Item>
+ bool RemoveElement(const Item& aItem)
+ {
+ index_type index = mArray.IndexOf(aItem, 0);
+ if (index == array_type::NoIndex) {
+ return false;
+ }
+
+ mArray.RemoveElementAt(index);
+ AdjustIterators(index, -1);
+ return true;
+ }
+
+ // See nsTArray::RemoveElementsBy.
+ void RemoveElementsBy(mozilla::function<bool(const elem_type&)> aPredicate)
+ {
+ index_type i = 0;
+ mArray.RemoveElementsBy([&](const elem_type& aItem) {
+ if (aPredicate(aItem)) {
+ // This element is going to be removed.
+ AdjustIterators(i, -1);
+ return true;
+ }
+ ++i;
+ return false;
+ });
+ }
+
+ // Removes all observers and collapses all iterators to the beginning of
+ // the array. The result is that forward iterators will see all elements
+ // in the array.
+ void Clear()
+ {
+ mArray.Clear();
+ ClearIterators();
+ }
+
+ // Compact the array to minimize the memory it uses
+ void Compact() { mArray.Compact(); }
+
+ // Returns the number of bytes on the heap taken up by this object, not
+ // including sizeof(*this). If you want to measure anything hanging off the
+ // array, you must iterate over the elements and measure them individually;
+ // hence the "Shallow" prefix.
+ size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return mArray.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ //
+ // Iterators
+ //
+
+ // Base class for iterators. Do not use this directly.
+ class Iterator : public Iterator_base
+ {
+ protected:
+ friend class nsAutoTObserverArray;
+ typedef nsAutoTObserverArray<T, N> array_type;
+
+ Iterator(index_type aPosition, const array_type& aArray)
+ : Iterator_base(aPosition, aArray.mIterators)
+ , mArray(const_cast<array_type&>(aArray))
+ {
+ aArray.mIterators = this;
+ }
+
+ ~Iterator()
+ {
+ NS_ASSERTION(mArray.mIterators == this,
+ "Iterators must currently be destroyed in opposite order "
+ "from the construction order. It is suggested that you "
+ "simply put them on the stack");
+ mArray.mIterators = mNext;
+ }
+
+ // The array we're iterating
+ array_type& mArray;
+ };
+
+ // Iterates the array forward from beginning to end. mPosition points
+ // to the element that will be returned on next call to GetNext.
+ // Elements:
+ // - prepended to the array during iteration *will not* be traversed
+ // - appended during iteration *will* be traversed
+ // - removed during iteration *will not* be traversed.
+ // @see EndLimitedIterator
+ class ForwardIterator : protected Iterator
+ {
+ public:
+ typedef nsAutoTObserverArray<T, N> array_type;
+ typedef Iterator base_type;
+
+ explicit ForwardIterator(const array_type& aArray)
+ : Iterator(0, aArray)
+ {
+ }
+
+ ForwardIterator(const array_type& aArray, index_type aPos)
+ : Iterator(aPos, aArray)
+ {
+ }
+
+ bool operator<(const ForwardIterator& aOther) const
+ {
+ NS_ASSERTION(&this->mArray == &aOther.mArray,
+ "not iterating the same array");
+ return base_type::mPosition < aOther.mPosition;
+ }
+
+ // Returns true if there are more elements to iterate.
+ // This must precede a call to GetNext(). If false is
+ // returned, GetNext() must not be called.
+ bool HasMore() const
+ {
+ return base_type::mPosition < base_type::mArray.Length();
+ }
+
+ // Returns the next element and steps one step. This must
+ // be preceded by a call to HasMore().
+ // @return The next observer.
+ elem_type& GetNext()
+ {
+ NS_ASSERTION(HasMore(), "iterating beyond end of array");
+ return base_type::mArray.ElementAt(base_type::mPosition++);
+ }
+ };
+
+ // EndLimitedIterator works like ForwardIterator, but will not iterate new
+ // observers appended to the array after the iterator was created.
+ class EndLimitedIterator : protected ForwardIterator
+ {
+ public:
+ typedef nsAutoTObserverArray<T, N> array_type;
+ typedef Iterator base_type;
+
+ explicit EndLimitedIterator(const array_type& aArray)
+ : ForwardIterator(aArray)
+ , mEnd(aArray, aArray.Length())
+ {
+ }
+
+ // Returns true if there are more elements to iterate.
+ // This must precede a call to GetNext(). If false is
+ // returned, GetNext() must not be called.
+ bool HasMore() const { return *this < mEnd; }
+
+ // Returns the next element and steps one step. This must
+ // be preceded by a call to HasMore().
+ // @return The next observer.
+ elem_type& GetNext()
+ {
+ NS_ASSERTION(HasMore(), "iterating beyond end of array");
+ return base_type::mArray.ElementAt(base_type::mPosition++);
+ }
+
+ private:
+ ForwardIterator mEnd;
+ };
+
+ // Iterates the array backward from end to start. mPosition points
+ // to the element that was returned last.
+ // Elements:
+ // - prepended to the array during iteration *will* be traversed,
+ // unless the iteration already arrived at the first element
+ // - appended during iteration *will not* be traversed
+ // - removed during iteration *will not* be traversed.
+ class BackwardIterator : protected Iterator
+ {
+ public:
+ typedef nsAutoTObserverArray<T, N> array_type;
+ typedef Iterator base_type;
+
+ explicit BackwardIterator(const array_type& aArray)
+ : Iterator(aArray.Length(), aArray)
+ {
+ }
+
+ // Returns true if there are more elements to iterate.
+ // This must precede a call to GetNext(). If false is
+ // returned, GetNext() must not be called.
+ bool HasMore() const { return base_type::mPosition > 0; }
+
+ // Returns the next element and steps one step. This must
+ // be preceded by a call to HasMore().
+ // @return The next observer.
+ elem_type& GetNext()
+ {
+ NS_ASSERTION(HasMore(), "iterating beyond start of array");
+ return base_type::mArray.ElementAt(--base_type::mPosition);
+ }
+
+ // Removes the element at the current iterator position.
+ // (the last element returned from |GetNext()|)
+ // This will not affect the next call to |GetNext()|
+ void Remove()
+ {
+ return base_type::mArray.RemoveElementAt(base_type::mPosition);
+ }
+ };
+
+protected:
+ AutoTArray<T, N> mArray;
+};
+
+template<class T>
+class nsTObserverArray : public nsAutoTObserverArray<T, 0>
+{
+public:
+ typedef nsAutoTObserverArray<T, 0> base_type;
+ typedef nsTObserverArray_base::size_type size_type;
+
+ //
+ // Initialization methods
+ //
+
+ nsTObserverArray() {}
+
+ // Initialize this array and pre-allocate some number of elements.
+ explicit nsTObserverArray(size_type aCapacity)
+ {
+ base_type::mArray.SetCapacity(aCapacity);
+ }
+};
+
+template<typename T, size_t N>
+inline void
+ImplCycleCollectionUnlink(nsAutoTObserverArray<T, N>& aField)
+{
+ aField.Clear();
+}
+
+template<typename T, size_t N>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ nsAutoTObserverArray<T, N>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ aFlags |= CycleCollectionEdgeNameArrayFlag;
+ size_t length = aField.Length();
+ for (size_t i = 0; i < length; ++i) {
+ ImplCycleCollectionTraverse(aCallback, aField.ElementAt(i), aName, aFlags);
+ }
+}
+
+// XXXbz I wish I didn't have to pass in the observer type, but I
+// don't see a way to get it out of array_.
+// Note that this macro only works if the array holds pointers to XPCOM objects.
+#define NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(array_, obstype_, func_, params_) \
+ PR_BEGIN_MACRO \
+ nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_); \
+ RefPtr<obstype_> obs_; \
+ while (iter_.HasMore()) { \
+ obs_ = iter_.GetNext(); \
+ obs_ -> func_ params_ ; \
+ } \
+ PR_END_MACRO
+
+// Note that this macro only works if the array holds pointers to XPCOM objects.
+#define NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(array_, obstype_, func_, params_) \
+ PR_BEGIN_MACRO \
+ nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_); \
+ obstype_* obs_; \
+ while (iter_.HasMore()) { \
+ obs_ = iter_.GetNext(); \
+ obs_ -> func_ params_ ; \
+ } \
+ PR_END_MACRO
+
+#define NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS_WITH_QI(array_, basetype_, obstype_, func_, params_) \
+ PR_BEGIN_MACRO \
+ nsTObserverArray<basetype_ *>::ForwardIterator iter_(array_); \
+ basetype_* obsbase_; \
+ while (iter_.HasMore()) { \
+ obsbase_ = iter_.GetNext(); \
+ nsCOMPtr<obstype_> obs_ = do_QueryInterface(obsbase_); \
+ if (obs_) { \
+ obs_ -> func_ params_ ; \
+ } \
+ } \
+ PR_END_MACRO
+#endif // nsTObserverArray_h___
diff --git a/xpcom/glue/nsTPriorityQueue.h b/xpcom/glue/nsTPriorityQueue.h
new file mode 100644
index 000000000..20a0fc8a5
--- /dev/null
+++ b/xpcom/glue/nsTPriorityQueue.h
@@ -0,0 +1,161 @@
+/* -*- 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 NS_TPRIORITY_QUEUE_H_
+#define NS_TPRIORITY_QUEUE_H_
+
+#include "nsTArray.h"
+#include "nsDebug.h"
+
+/**
+ * A templatized priority queue data structure that uses an nsTArray to serve as
+ * a binary heap. The default comparator causes this to act like a min-heap.
+ * Only the LessThan method of the comparator is used.
+ */
+template<class T, class Compare = nsDefaultComparator<T, T>>
+class nsTPriorityQueue
+{
+public:
+ typedef typename nsTArray<T>::size_type size_type;
+
+ /**
+ * Default constructor also creates a comparator object using the default
+ * constructor for type Compare.
+ */
+ nsTPriorityQueue() : mCompare(Compare()) {}
+
+ /**
+ * Constructor to allow a specific instance of a comparator object to be
+ * used.
+ */
+ explicit nsTPriorityQueue(const Compare& aComp) : mCompare(aComp) {}
+
+ /**
+ * Copy constructor
+ */
+ nsTPriorityQueue(const nsTPriorityQueue& aOther)
+ : mElements(aOther.mElements)
+ , mCompare(aOther.mCompare)
+ {
+ }
+
+ /**
+ * @return True if the queue is empty or false otherwise.
+ */
+ bool IsEmpty() const { return mElements.IsEmpty(); }
+
+ /**
+ * @return The number of elements in the queue.
+ */
+ size_type Length() const { return mElements.Length(); }
+
+ /**
+ * @return The topmost element in the queue without changing the queue. This
+ * is the element 'a' such that there is no other element 'b' in the queue for
+ * which Compare(b, a) returns true. (Since this container does not check
+ * for duplicate entries there may exist 'b' for which Compare(a, b) returns
+ * false.)
+ */
+ const T& Top() const
+ {
+ MOZ_ASSERT(!mElements.IsEmpty(), "Empty queue");
+ return mElements[0];
+ }
+
+ /**
+ * Adds an element to the queue
+ * @param aElement The element to add
+ * @return true on success, false on out of memory.
+ */
+ bool Push(const T& aElement)
+ {
+ T* elem = mElements.AppendElement(aElement);
+ if (!elem) {
+ return false; // Out of memory
+ }
+
+ // Sift up
+ size_type i = mElements.Length() - 1;
+ while (i) {
+ size_type parent = (size_type)((i - 1) / 2);
+ if (mCompare.LessThan(mElements[parent], mElements[i])) {
+ break;
+ }
+ Swap(i, parent);
+ i = parent;
+ }
+
+ return true;
+ }
+
+ /**
+ * Removes and returns the top-most element from the queue.
+ * @return The topmost element, that is, the element 'a' such that there is no
+ * other element 'b' in the queue for which Compare(b, a) returns true.
+ * @see Top()
+ */
+ T Pop()
+ {
+ MOZ_ASSERT(!mElements.IsEmpty(), "Empty queue");
+ T pop = mElements[0];
+
+ // Move last to front
+ mElements[0] = mElements[mElements.Length() - 1];
+ mElements.TruncateLength(mElements.Length() - 1);
+
+ // Sift down
+ size_type i = 0;
+ while (2 * i + 1 < mElements.Length()) {
+ size_type swap = i;
+ size_type l_child = 2 * i + 1;
+ if (mCompare.LessThan(mElements[l_child], mElements[i])) {
+ swap = l_child;
+ }
+ size_type r_child = l_child + 1;
+ if (r_child < mElements.Length() &&
+ mCompare.LessThan(mElements[r_child], mElements[swap])) {
+ swap = r_child;
+ }
+ if (swap == i) {
+ break;
+ }
+ Swap(i, swap);
+ i = swap;
+ }
+
+ return pop;
+ }
+
+ /**
+ * Removes all elements from the queue.
+ */
+ void Clear() { mElements.Clear(); }
+
+ /**
+ * Provides readonly access to the queue elements as an array. Generally this
+ * should be avoided but may be needed in some situations such as when the
+ * elements contained in the queue need to be enumerated for cycle-collection.
+ * @return A pointer to the first element of the array. If the array is
+ * empty, then this pointer must not be dereferenced.
+ */
+ const T* Elements() const { return mElements.Elements(); }
+
+protected:
+ /**
+ * Swaps the elements at the specified indices.
+ */
+ void Swap(size_type aIndexA, size_type aIndexB)
+ {
+ T temp = mElements[aIndexA];
+ mElements[aIndexA] = mElements[aIndexB];
+ mElements[aIndexB] = temp;
+ }
+
+ nsTArray<T> mElements;
+ Compare mCompare; // Comparator object
+};
+
+#endif // NS_TPRIORITY_QUEUE_H_
diff --git a/xpcom/glue/nsTWeakRef.h b/xpcom/glue/nsTWeakRef.h
new file mode 100644
index 000000000..6c9a5e8eb
--- /dev/null
+++ b/xpcom/glue/nsTWeakRef.h
@@ -0,0 +1,176 @@
+/* -*- 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 nsTWeakRef_h__
+#define nsTWeakRef_h__
+
+#ifndef nsDebug_h___
+#include "nsDebug.h"
+#endif
+
+/**
+ * A weak reference class for use with generic C++ objects. NOT THREADSAFE!
+ *
+ * Example usage:
+ *
+ * class A {
+ * public:
+ * A() : mWeakSelf(this) {
+ * }
+ * ~A() {
+ * mWeakSelf.forget();
+ * }
+ * void Bar() { printf("Bar!\n"); }
+ * const nsTWeakRef<A> &AsWeakRef() const { return mWeakSelf; }
+ * private:
+ * nsTWeakRef<A> mWeakSelf;
+ * };
+ *
+ * class B {
+ * public:
+ * void SetA(const nsTWeakRef<A> &a) {
+ * mA = a;
+ * }
+ * void Foo() {
+ * if (mA)
+ * mA->Bar();
+ * }
+ * private:
+ * nsTWeakRef<A> mA;
+ * };
+ *
+ * void Test() {
+ * B b;
+ * {
+ * A a;
+ * b.SetA(a.AsWeakRef());
+ * b.Foo(); // prints "Bar!"
+ * }
+ * b.Foo(); // prints nothing because |a| has already been destroyed
+ * }
+ *
+ * One can imagine much more complex examples, especially when asynchronous
+ * event processing is involved.
+ *
+ * Keep in mind that you should only ever need a class like this when you have
+ * multiple instances of B, such that it is not possible for A and B to simply
+ * have pointers to one another.
+ */
+template<class Type>
+class nsTWeakRef
+{
+public:
+ ~nsTWeakRef()
+ {}
+
+ /**
+ * Construct from an object pointer (may be null).
+ */
+ explicit nsTWeakRef(Type* aObj = nullptr)
+ {
+ if (aObj) {
+ mRef = new Inner(aObj);
+ } else {
+ mRef = nullptr;
+ }
+ }
+
+ /**
+ * Construct from another weak reference object.
+ */
+ explicit nsTWeakRef(const nsTWeakRef<Type>& aOther) : mRef(aOther.mRef)
+ {}
+
+ /**
+ * Assign from an object pointer.
+ */
+ nsTWeakRef<Type>& operator=(Type* aObj)
+ {
+ if (aObj) {
+ mRef = new Inner(aObj);
+ } else {
+ mRef = nullptr;
+ }
+ return *this;
+ }
+
+ /**
+ * Assign from another weak reference object.
+ */
+ nsTWeakRef<Type>& operator=(const nsTWeakRef<Type>& aOther)
+ {
+ mRef = aOther.mRef;
+ return *this;
+ }
+
+ /**
+ * Get the referenced object. This method may return null if the reference
+ * has been cleared or if an out-of-memory error occurred at assignment.
+ */
+ Type* get() const { return mRef ? mRef->mObj : nullptr; }
+
+ /**
+ * Called to "null out" the weak reference. Typically, the object referenced
+ * by this weak reference calls this method when it is being destroyed.
+ * @returns The former referenced object.
+ */
+ Type* forget()
+ {
+ Type* obj;
+ if (mRef) {
+ obj = mRef->mObj;
+ mRef->mObj = nullptr;
+ mRef = nullptr;
+ } else {
+ obj = nullptr;
+ }
+ return obj;
+ }
+
+ /**
+ * Allow |*this| to be treated as a |Type*| for convenience.
+ */
+ operator Type*() const { return get(); }
+
+ /**
+ * Allow |*this| to be treated as a |Type*| for convenience. Use with
+ * caution since this method will crash if the referenced object is null.
+ */
+ Type* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN
+ {
+ NS_ASSERTION(mRef && mRef->mObj,
+ "You can't dereference a null weak reference with operator->().");
+ return get();
+ }
+
+private:
+
+ struct Inner
+ {
+ int mCnt;
+ Type* mObj;
+
+ explicit Inner(Type* aObj)
+ : mCnt(1)
+ , mObj(aObj)
+ {
+ }
+ void AddRef()
+ {
+ ++mCnt;
+ }
+ void Release()
+ {
+ if (--mCnt == 0) {
+ delete this;
+ }
+ }
+ };
+
+ RefPtr<Inner> mRef;
+};
+
+#endif // nsTWeakRef_h__
diff --git a/xpcom/glue/nsTextFormatter.cpp b/xpcom/glue/nsTextFormatter.cpp
new file mode 100644
index 000000000..ab9941d15
--- /dev/null
+++ b/xpcom/glue/nsTextFormatter.cpp
@@ -0,0 +1,1394 @@
+/* -*- 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/. */
+
+/*
+ * Portable safe sprintf code.
+ *
+ * Code based on mozilla/nsprpub/src/io/prprf.c rev 3.7
+ *
+ * Contributor(s):
+ * Kipp E.B. Hickman <kipp@netscape.com> (original author)
+ * Frank Yung-Fong Tang <ftang@netscape.com>
+ * Daniele Nicolodi <daniele@grinta.net>
+ */
+
+/*
+ * Copied from xpcom/ds/nsTextFormatter.cpp r1.22
+ * Changed to use nsMemory and Frozen linkage
+ * -- Prasad <prasad@medhas.org>
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include "prdtoa.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Sprintf.h"
+#include "prmem.h"
+#include "nsCRTGlue.h"
+#include "nsTextFormatter.h"
+#include "nsMemory.h"
+
+/*
+** Note: on some platforms va_list is defined as an array,
+** and requires array notation.
+*/
+
+#ifdef HAVE_VA_COPY
+#define VARARGS_ASSIGN(foo, bar) VA_COPY(foo,bar)
+#elif defined(HAVE_VA_LIST_AS_ARRAY)
+#define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0]
+#else
+#define VARARGS_ASSIGN(foo, bar) (foo) = (bar)
+#endif
+
+typedef struct SprintfStateStr SprintfState;
+
+struct SprintfStateStr
+{
+ int (*stuff)(SprintfState* aState, const char16_t* aStr, uint32_t aLen);
+
+ char16_t* base;
+ char16_t* cur;
+ uint32_t maxlen;
+
+ void* stuffclosure;
+};
+
+/*
+** Numbered Arguement State
+*/
+struct NumArgState
+{
+ int type; /* type of the current ap */
+ va_list ap; /* point to the corresponding position on ap */
+
+ enum Type
+ {
+ INT16,
+ UINT16,
+ INTN,
+ UINTN,
+ INT32,
+ UINT32,
+ INT64,
+ UINT64,
+ STRING,
+ DOUBLE,
+ INTSTR,
+ UNISTRING,
+ UNKNOWN
+ };
+};
+
+#define NAS_DEFAULT_NUM 20 /* default number of NumberedArgumentState array */
+
+#define _LEFT 0x1
+#define _SIGNED 0x2
+#define _SPACED 0x4
+#define _ZEROS 0x8
+#define _NEG 0x10
+
+#define ELEMENTS_OF(array_) (sizeof(array_) / sizeof(array_[0]))
+
+#define PR_CHECK_DELETE(nas) if (nas && (nas != nasArray)) { PR_DELETE(nas); }
+
+/*
+** Fill into the buffer using the data in src
+*/
+static int
+fill2(SprintfState* aState, const char16_t* aSrc, int aSrcLen, int aWidth,
+ int aFlags)
+{
+ char16_t space = ' ';
+ int rv;
+
+ aWidth -= aSrcLen;
+ /* Right adjusting */
+ if ((aWidth > 0) && ((aFlags & _LEFT) == 0)) {
+ if (aFlags & _ZEROS) {
+ space = '0';
+ }
+ while (--aWidth >= 0) {
+ rv = (*aState->stuff)(aState, &space, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ }
+
+ /* Copy out the source data */
+ rv = (*aState->stuff)(aState, aSrc, aSrcLen);
+ if (rv < 0) {
+ return rv;
+ }
+
+ /* Left adjusting */
+ if ((aWidth > 0) && ((aFlags & _LEFT) != 0)) {
+ while (--aWidth >= 0) {
+ rv = (*aState->stuff)(aState, &space, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+** Fill a number. The order is: optional-sign zero-filling conversion-digits
+*/
+static int
+fill_n(SprintfState* aState, const char16_t* aSrc, int aSrcLen, int aWidth,
+ int aPrec, int aType, int aFlags)
+{
+ int zerowidth = 0;
+ int precwidth = 0;
+ int signwidth = 0;
+ int leftspaces = 0;
+ int rightspaces = 0;
+ int cvtwidth;
+ int rv;
+ char16_t sign;
+ char16_t space = ' ';
+ char16_t zero = '0';
+
+ if ((aType & 1) == 0) {
+ if (aFlags & _NEG) {
+ sign = '-';
+ signwidth = 1;
+ } else if (aFlags & _SIGNED) {
+ sign = '+';
+ signwidth = 1;
+ } else if (aFlags & _SPACED) {
+ sign = ' ';
+ signwidth = 1;
+ }
+ }
+ cvtwidth = signwidth + aSrcLen;
+
+ if (aPrec > 0) {
+ if (aPrec > aSrcLen) {
+ /* Need zero filling */
+ precwidth = aPrec - aSrcLen;
+ cvtwidth += precwidth;
+ }
+ }
+
+ if ((aFlags & _ZEROS) && (aPrec < 0)) {
+ if (aWidth > cvtwidth) {
+ /* Zero filling */
+ zerowidth = aWidth - cvtwidth;
+ cvtwidth += zerowidth;
+ }
+ }
+
+ if (aFlags & _LEFT) {
+ if (aWidth > cvtwidth) {
+ /* Space filling on the right (i.e. left adjusting) */
+ rightspaces = aWidth - cvtwidth;
+ }
+ } else {
+ if (aWidth > cvtwidth) {
+ /* Space filling on the left (i.e. right adjusting) */
+ leftspaces = aWidth - cvtwidth;
+ }
+ }
+ while (--leftspaces >= 0) {
+ rv = (*aState->stuff)(aState, &space, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ if (signwidth) {
+ rv = (*aState->stuff)(aState, &sign, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ while (--precwidth >= 0) {
+ rv = (*aState->stuff)(aState, &space, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ while (--zerowidth >= 0) {
+ rv = (*aState->stuff)(aState, &zero, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ rv = (*aState->stuff)(aState, aSrc, aSrcLen);
+ if (rv < 0) {
+ return rv;
+ }
+ while (--rightspaces >= 0) {
+ rv = (*aState->stuff)(aState, &space, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ return 0;
+}
+
+/*
+** Convert a long into its printable form
+*/
+static int
+cvt_l(SprintfState* aState, long aNum, int aWidth, int aPrec, int aRadix,
+ int aType, int aFlags, const char16_t* aHexStr)
+{
+ char16_t cvtbuf[100];
+ char16_t* cvt;
+ int digits;
+
+ /* according to the man page this needs to happen */
+ if ((aPrec == 0) && (aNum == 0)) {
+ return 0;
+ }
+
+ /*
+ ** Converting decimal is a little tricky. In the unsigned case we
+ ** need to stop when we hit 10 digits. In the signed case, we can
+ ** stop when the number is zero.
+ */
+ cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf);
+ digits = 0;
+ while (aNum) {
+ int digit = (((unsigned long)aNum) % aRadix) & 0xF;
+ *--cvt = aHexStr[digit];
+ digits++;
+ aNum = (long)(((unsigned long)aNum) / aRadix);
+ }
+ if (digits == 0) {
+ *--cvt = '0';
+ digits++;
+ }
+
+ /*
+ ** Now that we have the number converted without its sign, deal with
+ ** the sign and zero padding.
+ */
+ return fill_n(aState, cvt, digits, aWidth, aPrec, aType, aFlags);
+}
+
+/*
+** Convert a 64-bit integer into its printable form
+*/
+static int
+cvt_ll(SprintfState* aState, int64_t aNum, int aWidth, int aPrec, int aRadix,
+ int aType, int aFlags, const char16_t* aHexStr)
+{
+ char16_t cvtbuf[100];
+ char16_t* cvt;
+ int digits;
+ int64_t rad;
+
+ /* according to the man page this needs to happen */
+ if (aPrec == 0 && aNum == 0) {
+ return 0;
+ }
+
+ /*
+ ** Converting decimal is a little tricky. In the unsigned case we
+ ** need to stop when we hit 10 digits. In the signed case, we can
+ ** stop when the number is zero.
+ */
+ rad = aRadix;
+ cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf);
+ digits = 0;
+ while (aNum != 0) {
+ *--cvt = aHexStr[int32_t(aNum % rad) & 0xf];
+ digits++;
+ aNum /= rad;
+ }
+ if (digits == 0) {
+ *--cvt = '0';
+ digits++;
+ }
+
+ /*
+ ** Now that we have the number converted without its sign, deal with
+ ** the sign and zero padding.
+ */
+ return fill_n(aState, cvt, digits, aWidth, aPrec, aType, aFlags);
+}
+
+/*
+** Convert a double precision floating point number into its printable
+** form.
+*/
+static int
+cvt_f(SprintfState* aState, double aDouble, int aWidth, int aPrec,
+ const char16_t aType, int aFlags)
+{
+ int mode = 2;
+ int decpt;
+ int sign;
+ char buf[256];
+ char* bufp = buf;
+ int bufsz = 256;
+ char num[256];
+ char* nump;
+ char* endnum;
+ int numdigits = 0;
+ char exp = 'e';
+
+ if (aPrec == -1) {
+ aPrec = 6;
+ } else if (aPrec > 50) {
+ // limit precision to avoid PR_dtoa bug 108335
+ // and to prevent buffers overflows
+ aPrec = 50;
+ }
+
+ switch (aType) {
+ case 'f':
+ numdigits = aPrec;
+ mode = 3;
+ break;
+ case 'E':
+ exp = 'E';
+ MOZ_FALLTHROUGH;
+ case 'e':
+ numdigits = aPrec + 1;
+ mode = 2;
+ break;
+ case 'G':
+ exp = 'E';
+ MOZ_FALLTHROUGH;
+ case 'g':
+ if (aPrec == 0) {
+ aPrec = 1;
+ }
+ numdigits = aPrec;
+ mode = 2;
+ break;
+ default:
+ NS_ERROR("invalid aType passed to cvt_f");
+ }
+
+ if (PR_dtoa(aDouble, mode, numdigits, &decpt, &sign,
+ &endnum, num, bufsz) == PR_FAILURE) {
+ buf[0] = '\0';
+ return -1;
+ }
+ numdigits = endnum - num;
+ nump = num;
+
+ if (sign) {
+ *bufp++ = '-';
+ } else if (aFlags & _SIGNED) {
+ *bufp++ = '+';
+ }
+
+ if (decpt == 9999) {
+ while ((*bufp++ = *nump++)) {
+ }
+ } else {
+
+ switch (aType) {
+
+ case 'E':
+ case 'e':
+
+ *bufp++ = *nump++;
+ if (aPrec > 0) {
+ *bufp++ = '.';
+ while (*nump) {
+ *bufp++ = *nump++;
+ aPrec--;
+ }
+ while (aPrec-- > 0) {
+ *bufp++ = '0';
+ }
+ }
+ *bufp++ = exp;
+
+ snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt - 1);
+ break;
+
+ case 'f':
+
+ if (decpt < 1) {
+ *bufp++ = '0';
+ if (aPrec > 0) {
+ *bufp++ = '.';
+ while (decpt++ && aPrec-- > 0) {
+ *bufp++ = '0';
+ }
+ while (*nump && aPrec-- > 0) {
+ *bufp++ = *nump++;
+ }
+ while (aPrec-- > 0) {
+ *bufp++ = '0';
+ }
+ }
+ } else {
+ while (*nump && decpt-- > 0) {
+ *bufp++ = *nump++;
+ }
+ while (decpt-- > 0) {
+ *bufp++ = '0';
+ }
+ if (aPrec > 0) {
+ *bufp++ = '.';
+ while (*nump && aPrec-- > 0) {
+ *bufp++ = *nump++;
+ }
+ while (aPrec-- > 0) {
+ *bufp++ = '0';
+ }
+ }
+ }
+ *bufp = '\0';
+ break;
+
+ case 'G':
+ case 'g':
+
+ if ((decpt < -3) || ((decpt - 1) >= aPrec)) {
+ *bufp++ = *nump++;
+ numdigits--;
+ if (numdigits > 0) {
+ *bufp++ = '.';
+ while (*nump) {
+ *bufp++ = *nump++;
+ }
+ }
+ *bufp++ = exp;
+ snprintf(bufp, bufsz - (bufp - buf), "%+03d", decpt - 1);
+ } else {
+ if (decpt < 1) {
+ *bufp++ = '0';
+ if (aPrec > 0) {
+ *bufp++ = '.';
+ while (decpt++) {
+ *bufp++ = '0';
+ }
+ while (*nump) {
+ *bufp++ = *nump++;
+ }
+ }
+ } else {
+ while (*nump && decpt-- > 0) {
+ *bufp++ = *nump++;
+ numdigits--;
+ }
+ while (decpt-- > 0) {
+ *bufp++ = '0';
+ }
+ if (numdigits > 0) {
+ *bufp++ = '.';
+ while (*nump) {
+ *bufp++ = *nump++;
+ }
+ }
+ }
+ *bufp = '\0';
+ }
+ }
+ }
+
+ char16_t rbuf[256];
+ char16_t* rbufp = rbuf;
+ bufp = buf;
+ // cast to char16_t
+ while ((*rbufp++ = *bufp++)) {
+ }
+ *rbufp = '\0';
+
+ return fill2(aState, rbuf, NS_strlen(rbuf), aWidth, aFlags);
+}
+
+/*
+** Convert a string into its printable form. |aWidth| is the output
+** width. |aPrec| is the maximum number of characters of |aStr| to output,
+** where -1 means until NUL.
+*/
+static int
+cvt_S(SprintfState* aState, const char16_t* aStr, int aWidth, int aPrec,
+ int aFlags)
+{
+ int slen;
+
+ if (aPrec == 0) {
+ return 0;
+ }
+
+ /* Limit string length by precision value */
+ slen = aStr ? NS_strlen(aStr) : 6;
+ if (aPrec > 0) {
+ if (aPrec < slen) {
+ slen = aPrec;
+ }
+ }
+
+ /* and away we go */
+ return fill2(aState, aStr ? aStr : u"(null)", slen, aWidth, aFlags);
+}
+
+/*
+** Convert a string into its printable form. |aWidth| is the output
+** width. |aPrec| is the maximum number of characters of |aStr| to output,
+** where -1 means until NUL.
+*/
+static int
+cvt_s(SprintfState* aState, const char* aStr, int aWidth, int aPrec, int aFlags)
+{
+ NS_ConvertUTF8toUTF16 utf16Val(aStr);
+ return cvt_S(aState, utf16Val.get(), aWidth, aPrec, aFlags);
+}
+
+/*
+** BuildArgArray stands for Numbered Argument list Sprintf
+** for example,
+** fmp = "%4$i, %2$d, %3s, %1d";
+** the number must start from 1, and no gap among them
+*/
+
+static struct NumArgState*
+BuildArgArray(const char16_t* aFmt, va_list aAp, int* aRv,
+ struct NumArgState* aNasArray)
+{
+ int number = 0, cn = 0, i;
+ const char16_t* p;
+ char16_t c;
+ struct NumArgState* nas;
+
+ /*
+ ** first pass:
+ ** detemine how many legal % I have got, then allocate space
+ */
+ p = aFmt;
+ *aRv = 0;
+ i = 0;
+ while ((c = *p++) != 0) {
+ if (c != '%') {
+ continue;
+ }
+ /* skip %% case */
+ if ((c = *p++) == '%') {
+ continue;
+ }
+
+ while (c != 0) {
+ if (c > '9' || c < '0') {
+ /* numbered argument csae */
+ if (c == '$') {
+ if (i > 0) {
+ *aRv = -1;
+ return nullptr;
+ }
+ number++;
+ break;
+
+ } else {
+ /* non-numbered argument case */
+ if (number > 0) {
+ *aRv = -1;
+ return nullptr;
+ }
+ i = 1;
+ break;
+ }
+ }
+ c = *p++;
+ }
+ }
+
+ if (number == 0) {
+ return nullptr;
+ }
+
+ if (number > NAS_DEFAULT_NUM) {
+ nas = (struct NumArgState*)moz_xmalloc(number * sizeof(struct NumArgState));
+ if (!nas) {
+ *aRv = -1;
+ return nullptr;
+ }
+ } else {
+ nas = aNasArray;
+ }
+
+ for (i = 0; i < number; i++) {
+ nas[i].type = NumArgState::UNKNOWN;
+ }
+
+ /*
+ ** second pass:
+ ** set nas[].type
+ */
+ p = aFmt;
+ while ((c = *p++) != 0) {
+ if (c != '%') {
+ continue;
+ }
+ c = *p++;
+ if (c == '%') {
+ continue;
+ }
+ cn = 0;
+ /* should improve error check later */
+ while (c && c != '$') {
+ cn = cn * 10 + c - '0';
+ c = *p++;
+ }
+
+ if (!c || cn < 1 || cn > number) {
+ *aRv = -1;
+ break;
+ }
+
+ /* nas[cn] starts from 0, and make sure
+ nas[cn].type is not assigned */
+ cn--;
+ if (nas[cn].type != NumArgState::UNKNOWN) {
+ continue;
+ }
+
+ c = *p++;
+
+ /* width */
+ if (c == '*') {
+ /* not supported feature, for the argument is not numbered */
+ *aRv = -1;
+ break;
+ } else {
+ while ((c >= '0') && (c <= '9')) {
+ c = *p++;
+ }
+ }
+
+ /* precision */
+ if (c == '.') {
+ c = *p++;
+ if (c == '*') {
+ /* not supported feature, for the argument is not numbered */
+ *aRv = -1;
+ break;
+ } else {
+ while ((c >= '0') && (c <= '9')) {
+ c = *p++;
+ }
+ }
+ }
+
+ /* size */
+ nas[cn].type = NumArgState::INTN;
+ if (c == 'h') {
+ nas[cn].type = NumArgState::INT16;
+ c = *p++;
+ } else if (c == 'L') {
+ /* XXX not quite sure here */
+ nas[cn].type = NumArgState::INT64;
+ c = *p++;
+ } else if (c == 'l') {
+ nas[cn].type = NumArgState::INT32;
+ c = *p++;
+ if (c == 'l') {
+ nas[cn].type = NumArgState::INT64;
+ c = *p++;
+ }
+ }
+
+ /* format */
+ switch (c) {
+ case 'd':
+ case 'c':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ break;
+
+ case 'e':
+ case 'f':
+ case 'g':
+ nas[cn].type = NumArgState::DOUBLE;
+ break;
+
+ case 'p':
+ /* XXX should use cpp */
+ if (sizeof(void*) == sizeof(int32_t)) {
+ nas[cn].type = NumArgState::UINT32;
+ } else if (sizeof(void*) == sizeof(int64_t)) {
+ nas[cn].type = NumArgState::UINT64;
+ } else if (sizeof(void*) == sizeof(int)) {
+ nas[cn].type = NumArgState::UINTN;
+ } else {
+ nas[cn].type = NumArgState::UNKNOWN;
+ }
+ break;
+
+ case 'C':
+ /* XXX not supported I suppose */
+ PR_ASSERT(0);
+ nas[cn].type = NumArgState::UNKNOWN;
+ break;
+
+ case 'S':
+ nas[cn].type = NumArgState::UNISTRING;
+ break;
+
+ case 's':
+ nas[cn].type = NumArgState::STRING;
+ break;
+
+ case 'n':
+ nas[cn].type = NumArgState::INTSTR;
+ break;
+
+ default:
+ PR_ASSERT(0);
+ nas[cn].type = NumArgState::UNKNOWN;
+ break;
+ }
+
+ /* get a legal para. */
+ if (nas[cn].type == NumArgState::UNKNOWN) {
+ *aRv = -1;
+ break;
+ }
+ }
+
+
+ /*
+ ** third pass
+ ** fill the nas[cn].ap
+ */
+ if (*aRv < 0) {
+ if (nas != aNasArray) {
+ PR_DELETE(nas);
+ }
+ return nullptr;
+ }
+
+ cn = 0;
+ while (cn < number) {
+ if (nas[cn].type == NumArgState::UNKNOWN) {
+ cn++;
+ continue;
+ }
+
+ VARARGS_ASSIGN(nas[cn].ap, aAp);
+
+ switch (nas[cn].type) {
+ case NumArgState::INT16:
+ case NumArgState::UINT16:
+ case NumArgState::INTN:
+ case NumArgState::UINTN: (void)va_arg(aAp, int); break;
+
+ case NumArgState::INT32: (void)va_arg(aAp, int32_t); break;
+
+ case NumArgState::UINT32: (void)va_arg(aAp, uint32_t); break;
+
+ case NumArgState::INT64: (void)va_arg(aAp, int64_t); break;
+
+ case NumArgState::UINT64: (void)va_arg(aAp, uint64_t); break;
+
+ case NumArgState::STRING: (void)va_arg(aAp, char*); break;
+
+ case NumArgState::INTSTR: (void)va_arg(aAp, int*); break;
+
+ case NumArgState::DOUBLE: (void)va_arg(aAp, double); break;
+
+ case NumArgState::UNISTRING: (void)va_arg(aAp, char16_t*); break;
+
+ default:
+ if (nas != aNasArray) {
+ PR_DELETE(nas);
+ }
+ *aRv = -1;
+ va_end(aAp);
+ return nullptr;
+ }
+ cn++;
+ }
+ va_end(aAp);
+ return nas;
+}
+
+
+/*
+** The workhorse sprintf code.
+*/
+static int
+dosprintf(SprintfState* aState, const char16_t* aFmt, va_list aAp)
+{
+ char16_t c;
+ int flags, width, prec, radix, type;
+ union
+ {
+ char16_t ch;
+ int i;
+ long l;
+ int64_t ll;
+ double d;
+ const char* s;
+ const char16_t* S;
+ int* ip;
+ } u;
+ char16_t space = ' ';
+
+ nsAutoString hex;
+ hex.AssignLiteral("0123456789abcdef");
+
+ nsAutoString HEX;
+ HEX.AssignLiteral("0123456789ABCDEF");
+
+ const char16_t* hexp;
+ int rv, i;
+ struct NumArgState* nas = nullptr;
+ struct NumArgState nasArray[NAS_DEFAULT_NUM];
+
+
+ /*
+ ** build an argument array, IF the aFmt is numbered argument
+ ** list style, to contain the Numbered Argument list pointers
+ */
+ nas = BuildArgArray(aFmt, aAp, &rv, nasArray);
+ if (rv < 0) {
+ /* the aFmt contains error Numbered Argument format, jliu@netscape.com */
+ PR_ASSERT(0);
+ return rv;
+ }
+
+ while ((c = *aFmt++) != 0) {
+ if (c != '%') {
+ rv = (*aState->stuff)(aState, aFmt - 1, 1);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ continue;
+ }
+
+ /*
+ ** Gobble up the % format string. Hopefully we have handled all
+ ** of the strange cases!
+ */
+ flags = 0;
+ c = *aFmt++;
+ if (c == '%') {
+ /* quoting a % with %% */
+ rv = (*aState->stuff)(aState, aFmt - 1, 1);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ continue;
+ }
+
+ if (nas) {
+ /* the aFmt contains the Numbered Arguments feature */
+ i = 0;
+ /* should improve error check later */
+ while (c && c != '$') {
+ i = (i * 10) + (c - '0');
+ c = *aFmt++;
+ }
+
+ if (nas[i - 1].type == NumArgState::UNKNOWN) {
+ if (nas != nasArray) {
+ PR_DELETE(nas);
+ }
+ va_end(aAp);
+ return -1;
+ }
+
+ VARARGS_ASSIGN(aAp, nas[i - 1].ap);
+ c = *aFmt++;
+ }
+
+ /*
+ * Examine optional flags. Note that we do not implement the
+ * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
+ * somewhat ambiguous and not ideal, which is perhaps why
+ * the various sprintf() implementations are inconsistent
+ * on this feature.
+ */
+ while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
+ if (c == '-') {
+ flags |= _LEFT;
+ }
+ if (c == '+') {
+ flags |= _SIGNED;
+ }
+ if (c == ' ') {
+ flags |= _SPACED;
+ }
+ if (c == '0') {
+ flags |= _ZEROS;
+ }
+ c = *aFmt++;
+ }
+ if (flags & _SIGNED) {
+ flags &= ~_SPACED;
+ }
+ if (flags & _LEFT) {
+ flags &= ~_ZEROS;
+ }
+
+ /* width */
+ if (c == '*') {
+ c = *aFmt++;
+ width = va_arg(aAp, int);
+ } else {
+ width = 0;
+ while ((c >= '0') && (c <= '9')) {
+ width = (width * 10) + (c - '0');
+ c = *aFmt++;
+ }
+ }
+
+ /* precision */
+ prec = -1;
+ if (c == '.') {
+ c = *aFmt++;
+ if (c == '*') {
+ c = *aFmt++;
+ prec = va_arg(aAp, int);
+ } else {
+ prec = 0;
+ while ((c >= '0') && (c <= '9')) {
+ prec = (prec * 10) + (c - '0');
+ c = *aFmt++;
+ }
+ }
+ }
+
+ /* size */
+ type = NumArgState::INTN;
+ if (c == 'h') {
+ type = NumArgState::INT16;
+ c = *aFmt++;
+ } else if (c == 'L') {
+ /* XXX not quite sure here */
+ type = NumArgState::INT64;
+ c = *aFmt++;
+ } else if (c == 'l') {
+ type = NumArgState::INT32;
+ c = *aFmt++;
+ if (c == 'l') {
+ type = NumArgState::INT64;
+ c = *aFmt++;
+ }
+ }
+
+ /* format */
+ hexp = hex.get();
+ switch (c) {
+ case 'd':
+ case 'i': /* decimal/integer */
+ radix = 10;
+ goto fetch_and_convert;
+
+ case 'o': /* octal */
+ radix = 8;
+ type |= 1;
+ goto fetch_and_convert;
+
+ case 'u': /* unsigned decimal */
+ radix = 10;
+ type |= 1;
+ goto fetch_and_convert;
+
+ case 'x': /* unsigned hex */
+ radix = 16;
+ type |= 1;
+ goto fetch_and_convert;
+
+ case 'X': /* unsigned HEX */
+ radix = 16;
+ hexp = HEX.get();
+ type |= 1;
+ goto fetch_and_convert;
+
+ fetch_and_convert:
+ switch (type) {
+ case NumArgState::INT16:
+ u.l = va_arg(aAp, int);
+ if (u.l < 0) {
+ u.l = -u.l;
+ flags |= _NEG;
+ }
+ goto do_long;
+ case NumArgState::UINT16:
+ u.l = va_arg(aAp, int) & 0xffff;
+ goto do_long;
+ case NumArgState::INTN:
+ u.l = va_arg(aAp, int);
+ if (u.l < 0) {
+ u.l = -u.l;
+ flags |= _NEG;
+ }
+ goto do_long;
+ case NumArgState::UINTN:
+ u.l = (long)va_arg(aAp, unsigned int);
+ goto do_long;
+
+ case NumArgState::INT32:
+ u.l = va_arg(aAp, int32_t);
+ if (u.l < 0) {
+ u.l = -u.l;
+ flags |= _NEG;
+ }
+ goto do_long;
+ case NumArgState::UINT32:
+ u.l = (long)va_arg(aAp, uint32_t);
+ do_long:
+ rv = cvt_l(aState, u.l, width, prec, radix, type, flags, hexp);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ break;
+
+ case NumArgState::INT64:
+ u.ll = va_arg(aAp, int64_t);
+ if (u.ll < 0) {
+ u.ll = -u.ll;
+ flags |= _NEG;
+ }
+ goto do_longlong;
+ case NumArgState::UINT64:
+ u.ll = va_arg(aAp, uint64_t);
+ do_longlong:
+ rv = cvt_ll(aState, u.ll, width, prec, radix, type, flags, hexp);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ break;
+ }
+ break;
+
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'g':
+ case 'G':
+ u.d = va_arg(aAp, double);
+ rv = cvt_f(aState, u.d, width, prec, c, flags);
+ if (rv < 0) {
+ return rv;
+ }
+ break;
+
+ case 'c':
+ u.ch = va_arg(aAp, int);
+ if ((flags & _LEFT) == 0) {
+ while (width-- > 1) {
+ rv = (*aState->stuff)(aState, &space, 1);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ }
+ }
+ rv = (*aState->stuff)(aState, &u.ch, 1);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ if (flags & _LEFT) {
+ while (width-- > 1) {
+ rv = (*aState->stuff)(aState, &space, 1);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ }
+ }
+ break;
+
+ case 'p':
+ if (sizeof(void*) == sizeof(int32_t)) {
+ type = NumArgState::UINT32;
+ } else if (sizeof(void*) == sizeof(int64_t)) {
+ type = NumArgState::UINT64;
+ } else if (sizeof(void*) == sizeof(int)) {
+ type = NumArgState::UINTN;
+ } else {
+ PR_ASSERT(0);
+ break;
+ }
+ radix = 16;
+ goto fetch_and_convert;
+
+#if 0
+ case 'C':
+ /* XXX not supported I suppose */
+ PR_ASSERT(0);
+ break;
+#endif
+
+ case 'S':
+ u.S = va_arg(aAp, const char16_t*);
+ rv = cvt_S(aState, u.S, width, prec, flags);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ break;
+
+ case 's':
+ u.s = va_arg(aAp, const char*);
+ rv = cvt_s(aState, u.s, width, prec, flags);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ break;
+
+ case 'n':
+ u.ip = va_arg(aAp, int*);
+ if (u.ip) {
+ *u.ip = aState->cur - aState->base;
+ }
+ break;
+
+ default:
+ /* Not a % token after all... skip it */
+#if 0
+ PR_ASSERT(0);
+#endif
+ char16_t perct = '%';
+ rv = (*aState->stuff)(aState, &perct, 1);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ rv = (*aState->stuff)(aState, aFmt - 1, 1);
+ if (rv < 0) {
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+ return rv;
+ }
+ }
+ }
+
+ /* Stuff trailing NUL */
+ char16_t null = '\0';
+
+ rv = (*aState->stuff)(aState, &null, 1);
+
+ va_end(aAp);
+ PR_CHECK_DELETE(nas);
+
+ return rv;
+}
+
+/************************************************************************/
+
+static int
+StringStuff(SprintfState* aState, const char16_t* aStr, uint32_t aLen)
+{
+ if (*aStr == '\0') {
+ return 0;
+ }
+
+ ptrdiff_t off = aState->cur - aState->base;
+
+ nsAString* str = static_cast<nsAString*>(aState->stuffclosure);
+ str->Append(aStr, aLen);
+
+ aState->base = str->BeginWriting();
+ aState->cur = aState->base + off;
+
+ return 0;
+}
+
+/*
+** Stuff routine that automatically grows the malloc'd output buffer
+** before it overflows.
+*/
+static int
+GrowStuff(SprintfState* aState, const char16_t* aStr, uint32_t aLen)
+{
+ ptrdiff_t off;
+ char16_t* newbase;
+ uint32_t newlen;
+
+ off = aState->cur - aState->base;
+ if (off + aLen >= aState->maxlen) {
+ /* Grow the buffer */
+ newlen = aState->maxlen + ((aLen > 32) ? aLen : 32);
+ if (aState->base) {
+ newbase = (char16_t*)moz_xrealloc(aState->base,
+ newlen * sizeof(char16_t));
+ } else {
+ newbase = (char16_t*)moz_xmalloc(newlen * sizeof(char16_t));
+ }
+ if (!newbase) {
+ /* Ran out of memory */
+ return -1;
+ }
+ aState->base = newbase;
+ aState->maxlen = newlen;
+ aState->cur = aState->base + off;
+ }
+
+ /* Copy data */
+ while (aLen) {
+ --aLen;
+ *aState->cur++ = *aStr++;
+ }
+ MOZ_ASSERT((uint32_t)(aState->cur - aState->base) <= aState->maxlen);
+ return 0;
+}
+
+/*
+** sprintf into a malloc'd buffer
+*/
+char16_t*
+nsTextFormatter::smprintf(const char16_t* aFmt, ...)
+{
+ va_list ap;
+ char16_t* rv;
+
+ va_start(ap, aFmt);
+ rv = nsTextFormatter::vsmprintf(aFmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+uint32_t
+nsTextFormatter::ssprintf(nsAString& aOut, const char16_t* aFmt, ...)
+{
+ va_list ap;
+ uint32_t rv;
+
+ va_start(ap, aFmt);
+ rv = nsTextFormatter::vssprintf(aOut, aFmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+uint32_t
+nsTextFormatter::vssprintf(nsAString& aOut, const char16_t* aFmt, va_list aAp)
+{
+ SprintfState ss;
+ ss.stuff = StringStuff;
+ ss.base = 0;
+ ss.cur = 0;
+ ss.maxlen = 0;
+ ss.stuffclosure = &aOut;
+
+ aOut.Truncate();
+ int n = dosprintf(&ss, aFmt, aAp);
+ return n ? n - 1 : n;
+}
+
+char16_t*
+nsTextFormatter::vsmprintf(const char16_t* aFmt, va_list aAp)
+{
+ SprintfState ss;
+ int rv;
+
+ ss.stuff = GrowStuff;
+ ss.base = 0;
+ ss.cur = 0;
+ ss.maxlen = 0;
+ rv = dosprintf(&ss, aFmt, aAp);
+ if (rv < 0) {
+ if (ss.base) {
+ PR_DELETE(ss.base);
+ }
+ return 0;
+ }
+ return ss.base;
+}
+
+/*
+** Stuff routine that discards overflow data
+*/
+static int
+LimitStuff(SprintfState* aState, const char16_t* aStr, uint32_t aLen)
+{
+ uint32_t limit = aState->maxlen - (aState->cur - aState->base);
+
+ if (aLen > limit) {
+ aLen = limit;
+ }
+ while (aLen) {
+ --aLen;
+ *aState->cur++ = *aStr++;
+ }
+ return 0;
+}
+
+/*
+** sprintf into a fixed size buffer. Make sure there is a NUL at the end
+** when finished.
+*/
+uint32_t
+nsTextFormatter::snprintf(char16_t* aOut, uint32_t aOutLen,
+ const char16_t* aFmt, ...)
+{
+ va_list ap;
+ uint32_t rv;
+
+ MOZ_ASSERT((int32_t)aOutLen > 0);
+ if ((int32_t)aOutLen <= 0) {
+ return 0;
+ }
+
+ va_start(ap, aFmt);
+ rv = nsTextFormatter::vsnprintf(aOut, aOutLen, aFmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+uint32_t
+nsTextFormatter::vsnprintf(char16_t* aOut, uint32_t aOutLen,
+ const char16_t* aFmt, va_list aAp)
+{
+ SprintfState ss;
+ uint32_t n;
+
+ MOZ_ASSERT((int32_t)aOutLen > 0);
+ if ((int32_t)aOutLen <= 0) {
+ return 0;
+ }
+
+ ss.stuff = LimitStuff;
+ ss.base = aOut;
+ ss.cur = aOut;
+ ss.maxlen = aOutLen;
+ (void) dosprintf(&ss, aFmt, aAp);
+
+ /* If we added chars, and we didn't append a null, do it now. */
+ if ((ss.cur != ss.base) && (*(ss.cur - 1) != '\0')) {
+ *(--ss.cur) = '\0';
+ }
+
+ n = ss.cur - ss.base;
+ return n ? n - 1 : n;
+}
+
+/*
+ * Free memory allocated, for the caller, by smprintf
+ */
+void
+nsTextFormatter::smprintf_free(char16_t* aMem)
+{
+ free(aMem);
+}
+
diff --git a/xpcom/glue/nsTextFormatter.h b/xpcom/glue/nsTextFormatter.h
new file mode 100644
index 000000000..4cd44be32
--- /dev/null
+++ b/xpcom/glue/nsTextFormatter.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This code was copied from xpcom/ds/nsTextFormatter r1.3
+ * Memory model and Frozen linkage changes only.
+ * -- Prasad <prasad@medhas.org>
+ */
+
+#ifndef nsTextFormatter_h___
+#define nsTextFormatter_h___
+
+/*
+ ** API for PR printf like routines. Supports the following formats
+ ** %d - decimal
+ ** %u - unsigned decimal
+ ** %x - unsigned hex
+ ** %X - unsigned uppercase hex
+ ** %o - unsigned octal
+ ** %hd, %hu, %hx, %hX, %ho - 16-bit versions of above
+ ** %ld, %lu, %lx, %lX, %lo - 32-bit versions of above
+ ** %lld, %llu, %llx, %llX, %llo - 64 bit versions of above
+ ** %s - utf8 string
+ ** %S - char16_t string
+ ** %c - character
+ ** %p - pointer (deals with machine dependent pointer size)
+ ** %f - float
+ ** %g - float
+ */
+#include "prio.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include "nscore.h"
+#include "nsStringGlue.h"
+
+#ifdef XPCOM_GLUE
+#error "nsTextFormatter is not available in the standalone glue due to NSPR dependencies."
+#endif
+
+class nsTextFormatter
+{
+public:
+
+ /*
+ * sprintf into a fixed size buffer. Guarantees that the buffer is null
+ * terminated. Returns the length of the written output, NOT including the
+ * null terminator, or (uint32_t)-1 if an error occurs.
+ */
+ static uint32_t snprintf(char16_t* aOut, uint32_t aOutLen,
+ const char16_t* aFmt, ...);
+
+ /*
+ * sprintf into a moz_xmalloc'd buffer. Return a pointer to
+ * buffer on success, nullptr on failure.
+ */
+ static char16_t* smprintf(const char16_t* aFmt, ...);
+
+ static uint32_t ssprintf(nsAString& aOut, const char16_t* aFmt, ...);
+
+ /*
+ * va_list forms of the above.
+ */
+ static uint32_t vsnprintf(char16_t* aOut, uint32_t aOutLen, const char16_t* aFmt,
+ va_list aAp);
+ static char16_t* vsmprintf(const char16_t* aFmt, va_list aAp);
+ static uint32_t vssprintf(nsAString& aOut, const char16_t* aFmt, va_list aAp);
+
+ /*
+ * Free the memory allocated, for the caller, by smprintf.
+ * -- Deprecated --
+ * Callers can substitute calling smprintf_free with free
+ */
+ static void smprintf_free(char16_t* aMem);
+
+};
+
+#endif /* nsTextFormatter_h___ */
diff --git a/xpcom/glue/nsThreadUtils.cpp b/xpcom/glue/nsThreadUtils.cpp
new file mode 100644
index 000000000..287ada7be
--- /dev/null
+++ b/xpcom/glue/nsThreadUtils.cpp
@@ -0,0 +1,472 @@
+/* -*- 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 "nsThreadUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Likely.h"
+#include "mozilla/TimeStamp.h"
+#include "LeakRefPtr.h"
+
+#ifdef MOZILLA_INTERNAL_API
+# include "nsThreadManager.h"
+#else
+# include "nsXPCOMCIDInternal.h"
+# include "nsIThreadManager.h"
+# include "nsServiceManagerUtils.h"
+#endif
+
+#ifdef XP_WIN
+#include <windows.h>
+#include "mozilla/WindowsVersion.h"
+using mozilla::IsVistaOrLater;
+#elif defined(XP_MACOSX)
+#include <sys/resource.h>
+#endif
+
+#include <pratom.h>
+#include <prthread.h>
+
+using namespace mozilla;
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+
+NS_IMPL_ISUPPORTS(IdlePeriod, nsIIdlePeriod)
+
+NS_IMETHODIMP
+IdlePeriod::GetIdlePeriodHint(TimeStamp* aIdleDeadline)
+{
+ *aIdleDeadline = TimeStamp();
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(Runnable, nsIRunnable)
+
+NS_IMETHODIMP
+Runnable::Run()
+{
+ // Do nothing
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(CancelableRunnable, Runnable,
+ nsICancelableRunnable)
+
+nsresult
+CancelableRunnable::Cancel()
+{
+ // Do nothing
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(IncrementalRunnable, CancelableRunnable,
+ nsIIncrementalRunnable)
+
+void
+IncrementalRunnable::SetDeadline(TimeStamp aDeadline)
+{
+ // Do nothing
+}
+
+#endif // XPCOM_GLUE_AVOID_NSPR
+
+//-----------------------------------------------------------------------------
+
+nsresult
+NS_NewThread(nsIThread** aResult, nsIRunnable* aEvent, uint32_t aStackSize)
+{
+ nsCOMPtr<nsIThread> thread;
+#ifdef MOZILLA_INTERNAL_API
+ nsresult rv =
+ nsThreadManager::get().nsThreadManager::NewThread(0, aStackSize,
+ getter_AddRefs(thread));
+#else
+ nsresult rv;
+ nsCOMPtr<nsIThreadManager> mgr =
+ do_GetService(NS_THREADMANAGER_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = mgr->NewThread(0, aStackSize, getter_AddRefs(thread));
+#endif
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (aEvent) {
+ rv = thread->Dispatch(aEvent, NS_DISPATCH_NORMAL);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ *aResult = nullptr;
+ thread.swap(*aResult);
+ return NS_OK;
+}
+
+nsresult
+NS_GetCurrentThread(nsIThread** aResult)
+{
+#ifdef MOZILLA_INTERNAL_API
+ return nsThreadManager::get().nsThreadManager::GetCurrentThread(aResult);
+#else
+ nsresult rv;
+ nsCOMPtr<nsIThreadManager> mgr =
+ do_GetService(NS_THREADMANAGER_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return mgr->GetCurrentThread(aResult);
+#endif
+}
+
+nsresult
+NS_GetMainThread(nsIThread** aResult)
+{
+#ifdef MOZILLA_INTERNAL_API
+ return nsThreadManager::get().nsThreadManager::GetMainThread(aResult);
+#else
+ nsresult rv;
+ nsCOMPtr<nsIThreadManager> mgr =
+ do_GetService(NS_THREADMANAGER_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return mgr->GetMainThread(aResult);
+#endif
+}
+
+#ifndef MOZILLA_INTERNAL_API
+bool
+NS_IsMainThread()
+{
+ bool result = false;
+ nsCOMPtr<nsIThreadManager> mgr =
+ do_GetService(NS_THREADMANAGER_CONTRACTID);
+ if (mgr) {
+ mgr->GetIsMainThread(&result);
+ }
+ return bool(result);
+}
+#endif
+
+nsresult
+NS_DispatchToCurrentThread(already_AddRefed<nsIRunnable>&& aEvent)
+{
+ nsresult rv;
+ nsCOMPtr<nsIRunnable> event(aEvent);
+#ifdef MOZILLA_INTERNAL_API
+ nsIThread* thread = NS_GetCurrentThread();
+ if (!thread) {
+ return NS_ERROR_UNEXPECTED;
+ }
+#else
+ nsCOMPtr<nsIThread> thread;
+ rv = NS_GetCurrentThread(getter_AddRefs(thread));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+#endif
+ // To keep us from leaking the runnable if dispatch method fails,
+ // we grab the reference on failures and release it.
+ nsIRunnable* temp = event.get();
+ rv = thread->Dispatch(event.forget(), NS_DISPATCH_NORMAL);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ // Dispatch() leaked the reference to the event, but due to caller's
+ // assumptions, we shouldn't leak here. And given we are on the same
+ // thread as the dispatch target, it's mostly safe to do it here.
+ NS_RELEASE(temp);
+ }
+ return rv;
+}
+
+// It is common to call NS_DispatchToCurrentThread with a newly
+// allocated runnable with a refcount of zero. To keep us from leaking
+// the runnable if the dispatch method fails, we take a death grip.
+nsresult
+NS_DispatchToCurrentThread(nsIRunnable* aEvent)
+{
+ nsCOMPtr<nsIRunnable> event(aEvent);
+ return NS_DispatchToCurrentThread(event.forget());
+}
+
+nsresult
+NS_DispatchToMainThread(already_AddRefed<nsIRunnable>&& aEvent, uint32_t aDispatchFlags)
+{
+ LeakRefPtr<nsIRunnable> event(Move(aEvent));
+ nsCOMPtr<nsIThread> thread;
+ nsresult rv = NS_GetMainThread(getter_AddRefs(thread));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ NS_ASSERTION(false, "Failed NS_DispatchToMainThread() in shutdown; leaking");
+ // NOTE: if you stop leaking here, adjust Promise::MaybeReportRejected(),
+ // which assumes a leak here, or split into leaks and no-leaks versions
+ return rv;
+ }
+ return thread->Dispatch(event.take(), aDispatchFlags);
+}
+
+// In the case of failure with a newly allocated runnable with a
+// refcount of zero, we intentionally leak the runnable, because it is
+// likely that the runnable is being dispatched to the main thread
+// because it owns main thread only objects, so it is not safe to
+// release them here.
+nsresult
+NS_DispatchToMainThread(nsIRunnable* aEvent, uint32_t aDispatchFlags)
+{
+ nsCOMPtr<nsIRunnable> event(aEvent);
+ return NS_DispatchToMainThread(event.forget(), aDispatchFlags);
+}
+
+nsresult
+NS_DelayedDispatchToCurrentThread(already_AddRefed<nsIRunnable>&& aEvent, uint32_t aDelayMs)
+{
+ nsCOMPtr<nsIRunnable> event(aEvent);
+#ifdef MOZILLA_INTERNAL_API
+ nsIThread* thread = NS_GetCurrentThread();
+ if (!thread) {
+ return NS_ERROR_UNEXPECTED;
+ }
+#else
+ nsresult rv;
+ nsCOMPtr<nsIThread> thread;
+ rv = NS_GetCurrentThread(getter_AddRefs(thread));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+#endif
+
+ return thread->DelayedDispatch(event.forget(), aDelayMs);
+}
+
+nsresult
+NS_IdleDispatchToCurrentThread(already_AddRefed<nsIRunnable>&& aEvent)
+{
+ nsresult rv;
+ nsCOMPtr<nsIRunnable> event(aEvent);
+#ifdef MOZILLA_INTERNAL_API
+ nsIThread* thread = NS_GetCurrentThread();
+ if (!thread) {
+ return NS_ERROR_UNEXPECTED;
+ }
+#else
+ nsCOMPtr<nsIThread> thread;
+ rv = NS_GetCurrentThread(getter_AddRefs(thread));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+#endif
+ // To keep us from leaking the runnable if dispatch method fails,
+ // we grab the reference on failures and release it.
+ nsIRunnable* temp = event.get();
+ rv = thread->IdleDispatch(event.forget());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ // Dispatch() leaked the reference to the event, but due to caller's
+ // assumptions, we shouldn't leak here. And given we are on the same
+ // thread as the dispatch target, it's mostly safe to do it here.
+ NS_RELEASE(temp);
+ }
+
+ return rv;
+}
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+nsresult
+NS_ProcessPendingEvents(nsIThread* aThread, PRIntervalTime aTimeout)
+{
+ nsresult rv = NS_OK;
+
+#ifdef MOZILLA_INTERNAL_API
+ if (!aThread) {
+ aThread = NS_GetCurrentThread();
+ if (NS_WARN_IF(!aThread)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+#else
+ nsCOMPtr<nsIThread> current;
+ if (!aThread) {
+ rv = NS_GetCurrentThread(getter_AddRefs(current));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ aThread = current.get();
+ }
+#endif
+
+ PRIntervalTime start = PR_IntervalNow();
+ for (;;) {
+ bool processedEvent;
+ rv = aThread->ProcessNextEvent(false, &processedEvent);
+ if (NS_FAILED(rv) || !processedEvent) {
+ break;
+ }
+ if (PR_IntervalNow() - start > aTimeout) {
+ break;
+ }
+ }
+ return rv;
+}
+#endif // XPCOM_GLUE_AVOID_NSPR
+
+inline bool
+hasPendingEvents(nsIThread* aThread)
+{
+ bool val;
+ return NS_SUCCEEDED(aThread->HasPendingEvents(&val)) && val;
+}
+
+bool
+NS_HasPendingEvents(nsIThread* aThread)
+{
+ if (!aThread) {
+#ifndef MOZILLA_INTERNAL_API
+ nsCOMPtr<nsIThread> current;
+ NS_GetCurrentThread(getter_AddRefs(current));
+ return hasPendingEvents(current);
+#else
+ aThread = NS_GetCurrentThread();
+ if (NS_WARN_IF(!aThread)) {
+ return false;
+ }
+#endif
+ }
+ return hasPendingEvents(aThread);
+}
+
+bool
+NS_ProcessNextEvent(nsIThread* aThread, bool aMayWait)
+{
+#ifdef MOZILLA_INTERNAL_API
+ if (!aThread) {
+ aThread = NS_GetCurrentThread();
+ if (NS_WARN_IF(!aThread)) {
+ return false;
+ }
+ }
+#else
+ nsCOMPtr<nsIThread> current;
+ if (!aThread) {
+ NS_GetCurrentThread(getter_AddRefs(current));
+ if (NS_WARN_IF(!current)) {
+ return false;
+ }
+ aThread = current.get();
+ }
+#endif
+ bool val;
+ return NS_SUCCEEDED(aThread->ProcessNextEvent(aMayWait, &val)) && val;
+}
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+
+namespace {
+
+class nsNameThreadRunnable final : public nsIRunnable
+{
+ ~nsNameThreadRunnable() {}
+
+public:
+ explicit nsNameThreadRunnable(const nsACString& aName) : mName(aName) {}
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIRUNNABLE
+
+protected:
+ const nsCString mName;
+};
+
+NS_IMPL_ISUPPORTS(nsNameThreadRunnable, nsIRunnable)
+
+NS_IMETHODIMP
+nsNameThreadRunnable::Run()
+{
+ PR_SetCurrentThreadName(mName.BeginReading());
+ return NS_OK;
+}
+
+} // namespace
+
+void
+NS_SetThreadName(nsIThread* aThread, const nsACString& aName)
+{
+ if (!aThread) {
+ return;
+ }
+
+ aThread->Dispatch(new nsNameThreadRunnable(aName),
+ nsIEventTarget::DISPATCH_NORMAL);
+}
+
+#else // !XPCOM_GLUE_AVOID_NSPR
+
+void
+NS_SetThreadName(nsIThread* aThread, const nsACString& aName)
+{
+ // No NSPR, no love.
+}
+
+#endif
+
+#ifdef MOZILLA_INTERNAL_API
+nsIThread*
+NS_GetCurrentThread()
+{
+ return nsThreadManager::get().GetCurrentThread();
+}
+#endif
+
+// nsThreadPoolNaming
+void
+nsThreadPoolNaming::SetThreadPoolName(const nsACString& aPoolName,
+ nsIThread* aThread)
+{
+ nsCString name(aPoolName);
+ name.AppendLiteral(" #");
+ name.AppendInt(++mCounter, 10); // The counter is declared as volatile
+
+ if (aThread) {
+ // Set on the target thread
+ NS_SetThreadName(aThread, name);
+ } else {
+ // Set on the current thread
+#ifndef XPCOM_GLUE_AVOID_NSPR
+ PR_SetCurrentThreadName(name.BeginReading());
+#endif
+ }
+}
+
+// nsAutoLowPriorityIO
+nsAutoLowPriorityIO::nsAutoLowPriorityIO()
+{
+#if defined(XP_WIN)
+ lowIOPrioritySet = IsVistaOrLater() &&
+ SetThreadPriority(GetCurrentThread(),
+ THREAD_MODE_BACKGROUND_BEGIN);
+#elif defined(XP_MACOSX)
+ oldPriority = getiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD);
+ lowIOPrioritySet = oldPriority != -1 &&
+ setiopolicy_np(IOPOL_TYPE_DISK,
+ IOPOL_SCOPE_THREAD,
+ IOPOL_THROTTLE) != -1;
+#else
+ lowIOPrioritySet = false;
+#endif
+}
+
+nsAutoLowPriorityIO::~nsAutoLowPriorityIO()
+{
+#if defined(XP_WIN)
+ if (MOZ_LIKELY(lowIOPrioritySet)) {
+ // On Windows the old thread priority is automatically restored
+ SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_END);
+ }
+#elif defined(XP_MACOSX)
+ if (MOZ_LIKELY(lowIOPrioritySet)) {
+ setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD, oldPriority);
+ }
+#endif
+}
diff --git a/xpcom/glue/nsThreadUtils.h b/xpcom/glue/nsThreadUtils.h
new file mode 100644
index 000000000..cbd7f7842
--- /dev/null
+++ b/xpcom/glue/nsThreadUtils.h
@@ -0,0 +1,1049 @@
+/* -*- 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 nsThreadUtils_h__
+#define nsThreadUtils_h__
+
+#include "prthread.h"
+#include "prinrval.h"
+#include "MainThreadUtils.h"
+#include "nsIThreadManager.h"
+#include "nsIThread.h"
+#include "nsIRunnable.h"
+#include "nsICancelableRunnable.h"
+#include "nsIIdlePeriod.h"
+#include "nsIIncrementalRunnable.h"
+#include "nsStringGlue.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/IndexSequence.h"
+#include "mozilla/Likely.h"
+#include "mozilla/Move.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Tuple.h"
+#include "mozilla/TypeTraits.h"
+
+//-----------------------------------------------------------------------------
+// These methods are alternatives to the methods on nsIThreadManager, provided
+// for convenience.
+
+/**
+ * Set name of the target thread. This operation is asynchronous.
+ */
+extern void NS_SetThreadName(nsIThread* aThread, const nsACString& aName);
+
+/**
+ * Static length version of the above function checking length of the
+ * name at compile time.
+ */
+template<size_t LEN>
+inline void
+NS_SetThreadName(nsIThread* aThread, const char (&aName)[LEN])
+{
+ static_assert(LEN <= 16,
+ "Thread name must be no more than 16 characters");
+ NS_SetThreadName(aThread, nsDependentCString(aName));
+}
+
+/**
+ * Create a new thread, and optionally provide an initial event for the thread.
+ *
+ * @param aResult
+ * The resulting nsIThread object.
+ * @param aInitialEvent
+ * The initial event to run on this thread. This parameter may be null.
+ * @param aStackSize
+ * The size in bytes to reserve for the thread's stack.
+ *
+ * @returns NS_ERROR_INVALID_ARG
+ * Indicates that the given name is not unique.
+ */
+extern nsresult
+NS_NewThread(nsIThread** aResult,
+ nsIRunnable* aInitialEvent = nullptr,
+ uint32_t aStackSize = nsIThreadManager::DEFAULT_STACK_SIZE);
+
+/**
+ * Creates a named thread, otherwise the same as NS_NewThread
+ */
+template<size_t LEN>
+inline nsresult
+NS_NewNamedThread(const char (&aName)[LEN],
+ nsIThread** aResult,
+ nsIRunnable* aInitialEvent = nullptr,
+ uint32_t aStackSize = nsIThreadManager::DEFAULT_STACK_SIZE)
+{
+ // Hold a ref while dispatching the initial event to match NS_NewThread()
+ nsCOMPtr<nsIThread> thread;
+ nsresult rv = NS_NewThread(getter_AddRefs(thread), nullptr, aStackSize);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ NS_SetThreadName<LEN>(thread, aName);
+ if (aInitialEvent) {
+ rv = thread->Dispatch(aInitialEvent, NS_DISPATCH_NORMAL);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Initial event dispatch failed");
+ }
+
+ *aResult = nullptr;
+ thread.swap(*aResult);
+ return rv;
+}
+
+/**
+ * Get a reference to the current thread.
+ *
+ * @param aResult
+ * The resulting nsIThread object.
+ */
+extern nsresult NS_GetCurrentThread(nsIThread** aResult);
+
+/**
+ * Dispatch the given event to the current thread.
+ *
+ * @param aEvent
+ * The event to dispatch.
+ *
+ * @returns NS_ERROR_INVALID_ARG
+ * If event is null.
+ */
+extern nsresult NS_DispatchToCurrentThread(nsIRunnable* aEvent);
+extern nsresult
+NS_DispatchToCurrentThread(already_AddRefed<nsIRunnable>&& aEvent);
+
+/**
+ * Dispatch the given event to the main thread.
+ *
+ * @param aEvent
+ * The event to dispatch.
+ * @param aDispatchFlags
+ * The flags to pass to the main thread's dispatch method.
+ *
+ * @returns NS_ERROR_INVALID_ARG
+ * If event is null.
+ */
+extern nsresult
+NS_DispatchToMainThread(nsIRunnable* aEvent,
+ uint32_t aDispatchFlags = NS_DISPATCH_NORMAL);
+extern nsresult
+NS_DispatchToMainThread(already_AddRefed<nsIRunnable>&& aEvent,
+ uint32_t aDispatchFlags = NS_DISPATCH_NORMAL);
+
+extern nsresult
+NS_DelayedDispatchToCurrentThread(
+ already_AddRefed<nsIRunnable>&& aEvent, uint32_t aDelayMs);
+
+extern nsresult
+NS_IdleDispatchToCurrentThread(already_AddRefed<nsIRunnable>&& aEvent);
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+/**
+ * Process all pending events for the given thread before returning. This
+ * method simply calls ProcessNextEvent on the thread while HasPendingEvents
+ * continues to return true and the time spent in NS_ProcessPendingEvents
+ * does not exceed the given timeout value.
+ *
+ * @param aThread
+ * The thread object for which to process pending events. If null, then
+ * events will be processed for the current thread.
+ * @param aTimeout
+ * The maximum number of milliseconds to spend processing pending events.
+ * Events are not pre-empted to honor this timeout. Rather, the timeout
+ * value is simply used to determine whether or not to process another event.
+ * Pass PR_INTERVAL_NO_TIMEOUT to specify no timeout.
+ */
+extern nsresult
+NS_ProcessPendingEvents(nsIThread* aThread,
+ PRIntervalTime aTimeout = PR_INTERVAL_NO_TIMEOUT);
+#endif
+
+/**
+ * Shortcut for nsIThread::HasPendingEvents.
+ *
+ * It is an error to call this function when the given thread is not the
+ * current thread. This function will return false if called from some
+ * other thread.
+ *
+ * @param aThread
+ * The current thread or null.
+ *
+ * @returns
+ * A boolean value that if "true" indicates that there are pending events
+ * in the current thread's event queue.
+ */
+extern bool NS_HasPendingEvents(nsIThread* aThread = nullptr);
+
+/**
+ * Shortcut for nsIThread::ProcessNextEvent.
+ *
+ * It is an error to call this function when the given thread is not the
+ * current thread. This function will simply return false if called
+ * from some other thread.
+ *
+ * @param aThread
+ * The current thread or null.
+ * @param aMayWait
+ * A boolean parameter that if "true" indicates that the method may block
+ * the calling thread to wait for a pending event.
+ *
+ * @returns
+ * A boolean value that if "true" indicates that an event from the current
+ * thread's event queue was processed.
+ */
+extern bool NS_ProcessNextEvent(nsIThread* aThread = nullptr,
+ bool aMayWait = true);
+
+//-----------------------------------------------------------------------------
+// Helpers that work with nsCOMPtr:
+
+inline already_AddRefed<nsIThread>
+do_GetCurrentThread()
+{
+ nsIThread* thread = nullptr;
+ NS_GetCurrentThread(&thread);
+ return already_AddRefed<nsIThread>(thread);
+}
+
+inline already_AddRefed<nsIThread>
+do_GetMainThread()
+{
+ nsIThread* thread = nullptr;
+ NS_GetMainThread(&thread);
+ return already_AddRefed<nsIThread>(thread);
+}
+
+//-----------------------------------------------------------------------------
+
+#ifdef MOZILLA_INTERNAL_API
+// Fast access to the current thread. Do not release the returned pointer! If
+// you want to use this pointer from some other thread, then you will need to
+// AddRef it. Otherwise, you should only consider this pointer valid from code
+// running on the current thread.
+extern nsIThread* NS_GetCurrentThread();
+#endif
+
+//-----------------------------------------------------------------------------
+
+#ifndef XPCOM_GLUE_AVOID_NSPR
+
+namespace mozilla {
+
+// This class is designed to be subclassed.
+class IdlePeriod : public nsIIdlePeriod
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIIDLEPERIOD
+
+ IdlePeriod() {}
+
+protected:
+ virtual ~IdlePeriod() {}
+private:
+ IdlePeriod(const IdlePeriod&) = delete;
+ IdlePeriod& operator=(const IdlePeriod&) = delete;
+ IdlePeriod& operator=(const IdlePeriod&&) = delete;
+};
+
+// This class is designed to be subclassed.
+class Runnable : public nsIRunnable
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIRUNNABLE
+
+ Runnable() {}
+
+protected:
+ virtual ~Runnable() {}
+private:
+ Runnable(const Runnable&) = delete;
+ Runnable& operator=(const Runnable&) = delete;
+ Runnable& operator=(const Runnable&&) = delete;
+};
+
+// This class is designed to be subclassed.
+class CancelableRunnable : public Runnable,
+ public nsICancelableRunnable
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ // nsICancelableRunnable
+ virtual nsresult Cancel() override;
+
+ CancelableRunnable() {}
+
+protected:
+ virtual ~CancelableRunnable() {}
+private:
+ CancelableRunnable(const CancelableRunnable&) = delete;
+ CancelableRunnable& operator=(const CancelableRunnable&) = delete;
+ CancelableRunnable& operator=(const CancelableRunnable&&) = delete;
+};
+
+// This class is designed to be subclassed.
+class IncrementalRunnable : public CancelableRunnable,
+ public nsIIncrementalRunnable
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ // nsIIncrementalRunnable
+ virtual void SetDeadline(TimeStamp aDeadline) override;
+
+ IncrementalRunnable() {}
+
+protected:
+ virtual ~IncrementalRunnable() {}
+private:
+ IncrementalRunnable(const IncrementalRunnable&) = delete;
+ IncrementalRunnable& operator=(const IncrementalRunnable&) = delete;
+ IncrementalRunnable& operator=(const IncrementalRunnable&&) = delete;
+};
+
+namespace detail {
+
+// An event that can be used to call a C++11 functions or function objects,
+// including lambdas. The function must have no required arguments, and must
+// return void.
+template<typename StoredFunction>
+class RunnableFunction : public Runnable
+{
+public:
+ template <typename F>
+ explicit RunnableFunction(F&& aFunction)
+ : mFunction(Forward<F>(aFunction))
+ { }
+
+ NS_IMETHOD Run() override {
+ static_assert(IsVoid<decltype(mFunction())>::value,
+ "The lambda must return void!");
+ mFunction();
+ return NS_OK;
+ }
+private:
+ StoredFunction mFunction;
+};
+
+} // namespace detail
+
+} // namespace mozilla
+
+template<typename Function>
+already_AddRefed<mozilla::Runnable>
+NS_NewRunnableFunction(Function&& aFunction)
+{
+ return do_AddRef(new mozilla::detail::RunnableFunction
+ // Make sure we store a non-reference in nsRunnableFunction.
+ <typename mozilla::RemoveReference<Function>::Type>
+ // But still forward aFunction to move if possible.
+ (mozilla::Forward<Function>(aFunction)));
+}
+
+// An event that can be used to call a method on a class. The class type must
+// support reference counting. This event supports Revoke for use
+// with nsRevocableEventPtr.
+template<class ClassType,
+ typename ReturnType = void,
+ bool Owning = true,
+ bool Cancelable = false>
+class nsRunnableMethod : public mozilla::Conditional<!Cancelable,
+ mozilla::Runnable,
+ mozilla::CancelableRunnable>::Type
+{
+public:
+ virtual void Revoke() = 0;
+
+ // These ReturnTypeEnforcer classes set up a blacklist for return types that
+ // we know are not safe. The default ReturnTypeEnforcer compiles just fine but
+ // already_AddRefed will not.
+ template<typename OtherReturnType>
+ class ReturnTypeEnforcer
+ {
+ public:
+ typedef int ReturnTypeIsSafe;
+ };
+
+ template<class T>
+ class ReturnTypeEnforcer<already_AddRefed<T>>
+ {
+ // No ReturnTypeIsSafe makes this illegal!
+ };
+
+ // Make sure this return type is safe.
+ typedef typename ReturnTypeEnforcer<ReturnType>::ReturnTypeIsSafe check;
+};
+
+template<class ClassType, bool Owning>
+struct nsRunnableMethodReceiver
+{
+ RefPtr<ClassType> mObj;
+ explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {}
+ ~nsRunnableMethodReceiver() { Revoke(); }
+ ClassType* Get() const { return mObj.get(); }
+ void Revoke() { mObj = nullptr; }
+};
+
+template<class ClassType>
+struct nsRunnableMethodReceiver<ClassType, false>
+{
+ ClassType* MOZ_NON_OWNING_REF mObj;
+ explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {}
+ ClassType* Get() const { return mObj; }
+ void Revoke() { mObj = nullptr; }
+};
+
+template<typename Method, bool Owning, bool Cancelable> struct nsRunnableMethodTraits;
+
+template<class C, typename R, bool Owning, bool Cancelable, typename... As>
+struct nsRunnableMethodTraits<R(C::*)(As...), Owning, Cancelable>
+{
+ typedef C class_type;
+ typedef R return_type;
+ typedef nsRunnableMethod<C, R, Owning, Cancelable> base_type;
+ static const bool can_cancel = Cancelable;
+};
+
+template<class C, typename R, bool Owning, bool Cancelable, typename... As>
+struct nsRunnableMethodTraits<R(C::*)(As...) const, Owning, Cancelable>
+{
+ typedef const C class_type;
+ typedef R return_type;
+ typedef nsRunnableMethod<C, R, Owning, Cancelable> base_type;
+ static const bool can_cancel = Cancelable;
+};
+
+#ifdef NS_HAVE_STDCALL
+template<class C, typename R, bool Owning, bool Cancelable, typename... As>
+struct nsRunnableMethodTraits<R(__stdcall C::*)(As...), Owning, Cancelable>
+{
+ typedef C class_type;
+ typedef R return_type;
+ typedef nsRunnableMethod<C, R, Owning, Cancelable> base_type;
+ static const bool can_cancel = Cancelable;
+};
+
+template<class C, typename R, bool Owning, bool Cancelable>
+struct nsRunnableMethodTraits<R(NS_STDCALL C::*)(), Owning, Cancelable>
+{
+ typedef C class_type;
+ typedef R return_type;
+ typedef nsRunnableMethod<C, R, Owning, Cancelable> base_type;
+ static const bool can_cancel = Cancelable;
+};
+template<class C, typename R, bool Owning, bool Cancelable, typename... As>
+struct nsRunnableMethodTraits<R(__stdcall C::*)(As...) const, Owning, Cancelable>
+{
+ typedef const C class_type;
+ typedef R return_type;
+ typedef nsRunnableMethod<C, R, Owning, Cancelable> base_type;
+ static const bool can_cancel = Cancelable;
+};
+
+template<class C, typename R, bool Owning, bool Cancelable>
+struct nsRunnableMethodTraits<R(NS_STDCALL C::*)() const, Owning, Cancelable>
+{
+ typedef const C class_type;
+ typedef R return_type;
+ typedef nsRunnableMethod<C, R, Owning, Cancelable> base_type;
+ static const bool can_cancel = Cancelable;
+};
+#endif
+
+
+// IsParameterStorageClass<T>::value is true if T is a parameter-storage class
+// that will be recognized by NS_New[NonOwning]RunnableMethodWithArg[s] to
+// force a specific storage&passing strategy (instead of inferring one,
+// see ParameterStorage).
+// When creating a new storage class, add a specialization for it to be
+// recognized.
+template<typename T>
+struct IsParameterStorageClass : public mozilla::FalseType {};
+
+// StoreXPassByY structs used to inform nsRunnableMethodArguments how to
+// store arguments, and how to pass them to the target method.
+
+template<typename T>
+struct StoreCopyPassByValue
+{
+ typedef T stored_type;
+ typedef T passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StoreCopyPassByValue(A&& a) : m(mozilla::Forward<A>(a)) {}
+ passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreCopyPassByValue<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreCopyPassByConstLRef
+{
+ typedef T stored_type;
+ typedef const T& passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StoreCopyPassByConstLRef(A&& a) : m(mozilla::Forward<A>(a)) {}
+ passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreCopyPassByConstLRef<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreCopyPassByLRef
+{
+ typedef T stored_type;
+ typedef T& passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StoreCopyPassByLRef(A&& a) : m(mozilla::Forward<A>(a)) {}
+ passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreCopyPassByLRef<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreCopyPassByRRef
+{
+ typedef T stored_type;
+ typedef T&& passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StoreCopyPassByRRef(A&& a) : m(mozilla::Forward<A>(a)) {}
+ passed_type PassAsParameter() { return mozilla::Move(m); }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreCopyPassByRRef<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreRefPassByLRef
+{
+ typedef T& stored_type;
+ typedef T& passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StoreRefPassByLRef(A& a) : m(a) {}
+ passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreRefPassByLRef<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreConstRefPassByConstLRef
+{
+ typedef const T& stored_type;
+ typedef const T& passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StoreConstRefPassByConstLRef(const A& a) : m(a) {}
+ passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreConstRefPassByConstLRef<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StorensRefPtrPassByPtr
+{
+ typedef RefPtr<T> stored_type;
+ typedef T* passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StorensRefPtrPassByPtr(A&& a) : m(mozilla::Forward<A>(a)) {}
+ passed_type PassAsParameter() { return m.get(); }
+};
+template<typename S>
+struct IsParameterStorageClass<StorensRefPtrPassByPtr<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StorePtrPassByPtr
+{
+ typedef T* stored_type;
+ typedef T* passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StorePtrPassByPtr(A a) : m(a) {}
+ passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StorePtrPassByPtr<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreConstPtrPassByConstPtr
+{
+ typedef const T* stored_type;
+ typedef const T* passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StoreConstPtrPassByConstPtr(A a) : m(a) {}
+ passed_type PassAsParameter() { return m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreConstPtrPassByConstPtr<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreCopyPassByConstPtr
+{
+ typedef T stored_type;
+ typedef const T* passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StoreCopyPassByConstPtr(A&& a) : m(mozilla::Forward<A>(a)) {}
+ passed_type PassAsParameter() { return &m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreCopyPassByConstPtr<S>>
+ : public mozilla::TrueType {};
+
+template<typename T>
+struct StoreCopyPassByPtr
+{
+ typedef T stored_type;
+ typedef T* passed_type;
+ stored_type m;
+ template <typename A>
+ MOZ_IMPLICIT StoreCopyPassByPtr(A&& a) : m(mozilla::Forward<A>(a)) {}
+ passed_type PassAsParameter() { return &m; }
+};
+template<typename S>
+struct IsParameterStorageClass<StoreCopyPassByPtr<S>>
+ : public mozilla::TrueType {};
+
+namespace detail {
+
+template<typename TWithoutPointer>
+struct NonnsISupportsPointerStorageClass
+ : mozilla::Conditional<mozilla::IsConst<TWithoutPointer>::value,
+ StoreConstPtrPassByConstPtr<
+ typename mozilla::RemoveConst<TWithoutPointer>::Type>,
+ StorePtrPassByPtr<TWithoutPointer>>
+{};
+
+template<typename>
+struct SFINAE1True : mozilla::TrueType
+{};
+
+template<class T>
+static auto HasRefCountMethodsTest(int)
+ -> SFINAE1True<decltype(mozilla::DeclVal<T>().AddRef(),
+ mozilla::DeclVal<T>().Release())>;
+template<class>
+static auto HasRefCountMethodsTest(long) -> mozilla::FalseType;
+
+template<class T>
+struct HasRefCountMethods : decltype(HasRefCountMethodsTest<T>(0))
+{};
+
+template<typename T>
+struct IsRefcountedSmartPointer : public mozilla::FalseType
+{};
+
+template<typename T>
+struct IsRefcountedSmartPointer<RefPtr<T>> : public mozilla::TrueType
+{};
+
+template<typename T>
+struct IsRefcountedSmartPointer<nsCOMPtr<T>> : public mozilla::TrueType
+{};
+
+template<typename T>
+struct StripSmartPointer
+{
+ typedef void Type;
+};
+
+template<typename T>
+struct StripSmartPointer<RefPtr<T>>
+{
+ typedef T Type;
+};
+
+template<typename T>
+struct StripSmartPointer<nsCOMPtr<T>>
+{
+ typedef T Type;
+};
+
+template<typename TWithoutPointer>
+struct PointerStorageClass
+ : mozilla::Conditional<HasRefCountMethods<TWithoutPointer>::value,
+ StorensRefPtrPassByPtr<TWithoutPointer>,
+ typename NonnsISupportsPointerStorageClass<
+ TWithoutPointer
+ >::Type>
+{};
+
+template<typename TWithoutRef>
+struct LValueReferenceStorageClass
+ : mozilla::Conditional<mozilla::IsConst<TWithoutRef>::value,
+ StoreConstRefPassByConstLRef<
+ typename mozilla::RemoveConst<TWithoutRef>::Type>,
+ StoreRefPassByLRef<TWithoutRef>>
+{};
+
+template<typename T>
+struct SmartPointerStorageClass
+ : mozilla::Conditional<IsRefcountedSmartPointer<T>::value,
+ StorensRefPtrPassByPtr<
+ typename StripSmartPointer<T>::Type>,
+ StoreCopyPassByValue<T>>
+{};
+
+template<typename T>
+struct NonLValueReferenceStorageClass
+ : mozilla::Conditional<mozilla::IsRvalueReference<T>::value,
+ StoreCopyPassByRRef<
+ typename mozilla::RemoveReference<T>::Type>,
+ typename SmartPointerStorageClass<T>::Type>
+{};
+
+template<typename T>
+struct NonPointerStorageClass
+ : mozilla::Conditional<mozilla::IsLvalueReference<T>::value,
+ typename LValueReferenceStorageClass<
+ typename mozilla::RemoveReference<T>::Type
+ >::Type,
+ typename NonLValueReferenceStorageClass<T>::Type>
+{};
+
+template<typename T>
+struct NonParameterStorageClass
+ : mozilla::Conditional<mozilla::IsPointer<T>::value,
+ typename PointerStorageClass<
+ typename mozilla::RemovePointer<T>::Type
+ >::Type,
+ typename NonPointerStorageClass<T>::Type>
+{};
+
+// Choose storage&passing strategy based on preferred storage type:
+// - If IsParameterStorageClass<T>::value is true, use as-is.
+// - RC* -> StorensRefPtrPassByPtr<RC> : Store RefPtr<RC>, pass RC*
+// ^^ RC quacks like a ref-counted type (i.e., has AddRef and Release methods)
+// - const T* -> StoreConstPtrPassByConstPtr<T> : Store const T*, pass const T*
+// - T* -> StorePtrPassByPtr<T> : Store T*, pass T*.
+// - const T& -> StoreConstRefPassByConstLRef<T>: Store const T&, pass const T&.
+// - T& -> StoreRefPassByLRef<T> : Store T&, pass T&.
+// - T&& -> StoreCopyPassByRRef<T> : Store T, pass Move(T).
+// - RefPtr<T>, nsCOMPtr<T>
+// -> StorensRefPtrPassByPtr<T> : Store RefPtr<T>, pass T*
+// - Other T -> StoreCopyPassByValue<T> : Store T, pass T.
+// Other available explicit options:
+// - StoreCopyPassByConstLRef<T> : Store T, pass const T&.
+// - StoreCopyPassByLRef<T> : Store T, pass T& (of copy!)
+// - StoreCopyPassByConstPtr<T> : Store T, pass const T*
+// - StoreCopyPassByPtr<T> : Store T, pass T* (of copy!)
+// Or create your own class with PassAsParameter() method, optional
+// clean-up in destructor, and with associated IsParameterStorageClass<>.
+template<typename T>
+struct ParameterStorage
+ : mozilla::Conditional<IsParameterStorageClass<T>::value,
+ T,
+ typename NonParameterStorageClass<T>::Type>
+{};
+
+} /* namespace detail */
+
+namespace mozilla {
+
+namespace detail {
+
+// struct used to store arguments and later apply them to a method.
+template <typename... Ts>
+struct RunnableMethodArguments final
+{
+ Tuple<typename ::detail::ParameterStorage<Ts>::Type...> mArguments;
+ template <typename... As>
+ explicit RunnableMethodArguments(As&&... aArguments)
+ : mArguments(Forward<As>(aArguments)...)
+ {}
+ template<typename C, typename M, typename... Args, size_t... Indices>
+ static auto
+ applyImpl(C* o, M m, Tuple<Args...>& args, IndexSequence<Indices...>)
+ -> decltype(((*o).*m)(Get<Indices>(args).PassAsParameter()...))
+ {
+ return ((*o).*m)(Get<Indices>(args).PassAsParameter()...);
+ }
+ template<class C, typename M> auto apply(C* o, M m)
+ -> decltype(applyImpl(o, m, mArguments,
+ typename IndexSequenceFor<Ts...>::Type()))
+ {
+ return applyImpl(o, m, mArguments,
+ typename IndexSequenceFor<Ts...>::Type());
+ }
+};
+
+template<typename Method, bool Owning, bool Cancelable, typename... Storages>
+class RunnableMethodImpl final
+ : public ::nsRunnableMethodTraits<Method, Owning, Cancelable>::base_type
+{
+ typedef typename ::nsRunnableMethodTraits<Method, Owning, Cancelable>::class_type
+ ClassType;
+ ::nsRunnableMethodReceiver<ClassType, Owning> mReceiver;
+ Method mMethod;
+ RunnableMethodArguments<Storages...> mArgs;
+private:
+ virtual ~RunnableMethodImpl() { Revoke(); };
+public:
+ template<typename... Args>
+ explicit RunnableMethodImpl(ClassType* aObj, Method aMethod,
+ Args&&... aArgs)
+ : mReceiver(aObj)
+ , mMethod(aMethod)
+ , mArgs(Forward<Args>(aArgs)...)
+ {
+ static_assert(sizeof...(Storages) == sizeof...(Args), "Storages and Args should have equal sizes");
+ }
+ NS_IMETHOD Run()
+ {
+ if (MOZ_LIKELY(mReceiver.Get())) {
+ mArgs.apply(mReceiver.Get(), mMethod);
+ }
+ return NS_OK;
+ }
+ nsresult Cancel() {
+ static_assert(Cancelable, "Don't use me!");
+ Revoke();
+ return NS_OK;
+ }
+ void Revoke() { mReceiver.Revoke(); }
+};
+
+} // namespace detail
+
+// Use this template function like so:
+//
+// nsCOMPtr<nsIRunnable> event =
+// mozilla::NewRunnableMethod(myObject, &MyClass::HandleEvent);
+// NS_DispatchToCurrentThread(event);
+//
+// Statically enforced constraints:
+// - myObject must be of (or implicitly convertible to) type MyClass
+// - MyClass must define AddRef and Release methods
+//
+
+template<typename PtrType, typename Method>
+already_AddRefed<typename ::nsRunnableMethodTraits<Method, true, false>::base_type>
+NewRunnableMethod(PtrType aPtr, Method aMethod)
+{
+ return do_AddRef(new detail::RunnableMethodImpl<Method, true, false>(aPtr, aMethod));
+}
+
+template<typename PtrType, typename Method>
+already_AddRefed<typename ::nsRunnableMethodTraits<Method, true, true>::base_type>
+NewCancelableRunnableMethod(PtrType aPtr, Method aMethod)
+{
+ return do_AddRef(new detail::RunnableMethodImpl<Method, true, true>(aPtr, aMethod));
+}
+
+template<typename PtrType, typename Method>
+already_AddRefed<typename ::nsRunnableMethodTraits<Method, false, false>::base_type>
+NewNonOwningRunnableMethod(PtrType&& aPtr, Method aMethod)
+{
+ return do_AddRef(new detail::RunnableMethodImpl<Method, false, false>(aPtr, aMethod));
+}
+
+template<typename PtrType, typename Method>
+already_AddRefed<typename ::nsRunnableMethodTraits<Method, false, true>::base_type>
+NewNonOwningCancelableRunnableMethod(PtrType&& aPtr, Method aMethod)
+{
+ return do_AddRef(new detail::RunnableMethodImpl<Method, false, true>(aPtr, aMethod));
+}
+
+// Similar to NewRunnableMethod. Call like so:
+// nsCOMPtr<nsIRunnable> event =
+// NewRunnableMethod<Types,...>(myObject, &MyClass::HandleEvent, myArg1,...);
+// 'Types' are the stored type for each argument, see ParameterStorage for details.
+template<typename... Storages, typename Method, typename PtrType, typename... Args>
+already_AddRefed<typename ::nsRunnableMethodTraits<Method, true, false>::base_type>
+NewRunnableMethod(PtrType&& aPtr, Method aMethod, Args&&... aArgs)
+{
+ static_assert(sizeof...(Storages) == sizeof...(Args),
+ "<Storages...> size should be equal to number of arguments");
+ return do_AddRef(new detail::RunnableMethodImpl<Method, true, false, Storages...>(
+ aPtr, aMethod, mozilla::Forward<Args>(aArgs)...));
+}
+
+template<typename... Storages, typename Method, typename PtrType, typename... Args>
+already_AddRefed<typename ::nsRunnableMethodTraits<Method, false, false>::base_type>
+NewNonOwningRunnableMethod(PtrType&& aPtr, Method aMethod, Args&&... aArgs)
+{
+ static_assert(sizeof...(Storages) == sizeof...(Args),
+ "<Storages...> size should be equal to number of arguments");
+ return do_AddRef(new detail::RunnableMethodImpl<Method, false, false, Storages...>(
+ aPtr, aMethod, mozilla::Forward<Args>(aArgs)...));
+}
+
+template<typename... Storages, typename Method, typename PtrType, typename... Args>
+already_AddRefed<typename ::nsRunnableMethodTraits<Method, true, true>::base_type>
+NewCancelableRunnableMethod(PtrType&& aPtr, Method aMethod, Args&&... aArgs)
+{
+ static_assert(sizeof...(Storages) == sizeof...(Args),
+ "<Storages...> size should be equal to number of arguments");
+ return do_AddRef(new detail::RunnableMethodImpl<Method, true, true, Storages...>(
+ aPtr, aMethod, mozilla::Forward<Args>(aArgs)...));
+}
+
+template<typename... Storages, typename Method, typename PtrType, typename... Args>
+already_AddRefed<typename ::nsRunnableMethodTraits<Method, false, true>::base_type>
+NewNonOwningCancelableRunnableMethod(PtrType&& aPtr, Method aMethod,
+ Args&&... aArgs)
+{
+ static_assert(sizeof...(Storages) == sizeof...(Args),
+ "<Storages...> size should be equal to number of arguments");
+ return do_AddRef(new detail::RunnableMethodImpl<Method, false, true, Storages...>(
+ aPtr, aMethod, mozilla::Forward<Args>(aArgs)...));
+}
+
+} // namespace mozilla
+
+#endif // XPCOM_GLUE_AVOID_NSPR
+
+// This class is designed to be used when you have an event class E that has a
+// pointer back to resource class R. If R goes away while E is still pending,
+// then it is important to "revoke" E so that it does not try use R after R has
+// been destroyed. nsRevocableEventPtr makes it easy for R to manage such
+// situations:
+//
+// class R;
+//
+// class E : public mozilla::Runnable {
+// public:
+// void Revoke() {
+// mResource = nullptr;
+// }
+// private:
+// R *mResource;
+// };
+//
+// class R {
+// public:
+// void EventHandled() {
+// mEvent.Forget();
+// }
+// private:
+// nsRevocableEventPtr<E> mEvent;
+// };
+//
+// void R::PostEvent() {
+// // Make sure any pending event is revoked.
+// mEvent->Revoke();
+//
+// nsCOMPtr<nsIRunnable> event = new E();
+// if (NS_SUCCEEDED(NS_DispatchToCurrentThread(event))) {
+// // Keep pointer to event so we can revoke it.
+// mEvent = event;
+// }
+// }
+//
+// NS_IMETHODIMP E::Run() {
+// if (!mResource)
+// return NS_OK;
+// ...
+// mResource->EventHandled();
+// return NS_OK;
+// }
+//
+template<class T>
+class nsRevocableEventPtr
+{
+public:
+ nsRevocableEventPtr() : mEvent(nullptr) {}
+ ~nsRevocableEventPtr() { Revoke(); }
+
+ const nsRevocableEventPtr& operator=(T* aEvent)
+ {
+ if (mEvent != aEvent) {
+ Revoke();
+ mEvent = aEvent;
+ }
+ return *this;
+ }
+
+ const nsRevocableEventPtr& operator=(already_AddRefed<T> aEvent)
+ {
+ RefPtr<T> event = aEvent;
+ if (mEvent != event) {
+ Revoke();
+ mEvent = event.forget();
+ }
+ return *this;
+ }
+
+ void Revoke()
+ {
+ if (mEvent) {
+ mEvent->Revoke();
+ mEvent = nullptr;
+ }
+ }
+
+ void Forget() { mEvent = nullptr; }
+ bool IsPending() { return mEvent != nullptr; }
+ T* get() { return mEvent; }
+
+private:
+ // Not implemented
+ nsRevocableEventPtr(const nsRevocableEventPtr&);
+ nsRevocableEventPtr& operator=(const nsRevocableEventPtr&);
+
+ RefPtr<T> mEvent;
+};
+
+/**
+ * A simple helper to suffix thread pool name
+ * with incremental numbers.
+ */
+class nsThreadPoolNaming
+{
+public:
+ nsThreadPoolNaming() : mCounter(0) {}
+
+ /**
+ * Creates and sets next thread name as "<aPoolName> #<n>"
+ * on the specified thread. If no thread is specified (aThread
+ * is null) then the name is synchronously set on the current thread.
+ */
+ void SetThreadPoolName(const nsACString& aPoolName,
+ nsIThread* aThread = nullptr);
+
+private:
+ mozilla::Atomic<uint32_t> mCounter;
+
+ nsThreadPoolNaming(const nsThreadPoolNaming&) = delete;
+ void operator=(const nsThreadPoolNaming&) = delete;
+};
+
+/**
+ * Thread priority in most operating systems affect scheduling, not IO. This
+ * helper is used to set the current thread to low IO priority for the lifetime
+ * of the created object. You can only use this low priority IO setting within
+ * the context of the current thread.
+ */
+class MOZ_STACK_CLASS nsAutoLowPriorityIO
+{
+public:
+ nsAutoLowPriorityIO();
+ ~nsAutoLowPriorityIO();
+
+private:
+ bool lowIOPrioritySet;
+#if defined(XP_MACOSX)
+ int oldPriority;
+#endif
+};
+
+void
+NS_SetMainThread();
+
+#endif // nsThreadUtils_h__
diff --git a/xpcom/glue/nsVersionComparator.cpp b/xpcom/glue/nsVersionComparator.cpp
new file mode 100644
index 000000000..6be32b974
--- /dev/null
+++ b/xpcom/glue/nsVersionComparator.cpp
@@ -0,0 +1,379 @@
+/* -*- 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 "nsVersionComparator.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#if defined(XP_WIN) && !defined(UPDATER_NO_STRING_GLUE_STL)
+#include <wchar.h>
+#include "nsStringGlue.h"
+#endif
+
+struct VersionPart
+{
+ int32_t numA;
+
+ const char* strB; // NOT null-terminated, can be a null pointer
+ uint32_t strBlen;
+
+ int32_t numC;
+
+ char* extraD; // null-terminated
+};
+
+#ifdef XP_WIN
+struct VersionPartW
+{
+ int32_t numA;
+
+ wchar_t* strB; // NOT null-terminated, can be a null pointer
+ uint32_t strBlen;
+
+ int32_t numC;
+
+ wchar_t* extraD; // null-terminated
+
+};
+#endif
+
+/**
+ * Parse a version part into a number and "extra text".
+ *
+ * @returns A pointer to the next versionpart, or null if none.
+ */
+static char*
+ParseVP(char* aPart, VersionPart& aResult)
+{
+ char* dot;
+
+ aResult.numA = 0;
+ aResult.strB = nullptr;
+ aResult.strBlen = 0;
+ aResult.numC = 0;
+ aResult.extraD = nullptr;
+
+ if (!aPart) {
+ return aPart;
+ }
+
+ dot = strchr(aPart, '.');
+ if (dot) {
+ *dot = '\0';
+ }
+
+ if (aPart[0] == '*' && aPart[1] == '\0') {
+ aResult.numA = INT32_MAX;
+ aResult.strB = "";
+ } else {
+ aResult.numA = strtol(aPart, const_cast<char**>(&aResult.strB), 10);
+ }
+
+ if (!*aResult.strB) {
+ aResult.strB = nullptr;
+ aResult.strBlen = 0;
+ } else {
+ if (aResult.strB[0] == '+') {
+ static const char kPre[] = "pre";
+
+ ++aResult.numA;
+ aResult.strB = kPre;
+ aResult.strBlen = sizeof(kPre) - 1;
+ } else {
+ const char* numstart = strpbrk(aResult.strB, "0123456789+-");
+ if (!numstart) {
+ aResult.strBlen = strlen(aResult.strB);
+ } else {
+ aResult.strBlen = numstart - aResult.strB;
+
+ aResult.numC = strtol(numstart, &aResult.extraD, 10);
+ if (!*aResult.extraD) {
+ aResult.extraD = nullptr;
+ }
+ }
+ }
+ }
+
+ if (dot) {
+ ++dot;
+
+ if (!*dot) {
+ dot = nullptr;
+ }
+ }
+
+ return dot;
+}
+
+
+/**
+ * Parse a version part into a number and "extra text".
+ *
+ * @returns A pointer to the next versionpart, or null if none.
+ */
+#ifdef XP_WIN
+static wchar_t*
+ParseVP(wchar_t* aPart, VersionPartW& aResult)
+{
+
+ wchar_t* dot;
+
+ aResult.numA = 0;
+ aResult.strB = nullptr;
+ aResult.strBlen = 0;
+ aResult.numC = 0;
+ aResult.extraD = nullptr;
+
+ if (!aPart) {
+ return aPart;
+ }
+
+ dot = wcschr(aPart, '.');
+ if (dot) {
+ *dot = '\0';
+ }
+
+ if (aPart[0] == '*' && aPart[1] == '\0') {
+ aResult.numA = INT32_MAX;
+ aResult.strB = L"";
+ } else {
+ aResult.numA = wcstol(aPart, const_cast<wchar_t**>(&aResult.strB), 10);
+ }
+
+ if (!*aResult.strB) {
+ aResult.strB = nullptr;
+ aResult.strBlen = 0;
+ } else {
+ if (aResult.strB[0] == '+') {
+ static wchar_t kPre[] = L"pre";
+
+ ++aResult.numA;
+ aResult.strB = kPre;
+ aResult.strBlen = sizeof(kPre) - 1;
+ } else {
+ const wchar_t* numstart = wcspbrk(aResult.strB, L"0123456789+-");
+ if (!numstart) {
+ aResult.strBlen = wcslen(aResult.strB);
+ } else {
+ aResult.strBlen = numstart - aResult.strB;
+
+ aResult.numC = wcstol(numstart, &aResult.extraD, 10);
+ if (!*aResult.extraD) {
+ aResult.extraD = nullptr;
+ }
+ }
+ }
+ }
+
+ if (dot) {
+ ++dot;
+
+ if (!*dot) {
+ dot = nullptr;
+ }
+ }
+
+ return dot;
+}
+#endif
+
+// compare two null-terminated strings, which may be null pointers
+static int32_t
+ns_strcmp(const char* aStr1, const char* aStr2)
+{
+ // any string is *before* no string
+ if (!aStr1) {
+ return aStr2 != 0;
+ }
+
+ if (!aStr2) {
+ return -1;
+ }
+
+ return strcmp(aStr1, aStr2);
+}
+
+// compare two length-specified string, which may be null pointers
+static int32_t
+ns_strnncmp(const char* aStr1, uint32_t aLen1,
+ const char* aStr2, uint32_t aLen2)
+{
+ // any string is *before* no string
+ if (!aStr1) {
+ return aStr2 != 0;
+ }
+
+ if (!aStr2) {
+ return -1;
+ }
+
+ for (; aLen1 && aLen2; --aLen1, --aLen2, ++aStr1, ++aStr2) {
+ if (*aStr1 < *aStr2) {
+ return -1;
+ }
+
+ if (*aStr1 > *aStr2) {
+ return 1;
+ }
+ }
+
+ if (aLen1 == 0) {
+ return aLen2 == 0 ? 0 : -1;
+ }
+
+ return 1;
+}
+
+// compare two int32_t
+static int32_t
+ns_cmp(int32_t aNum1, int32_t aNum2)
+{
+ if (aNum1 < aNum2) {
+ return -1;
+ }
+
+ return aNum1 != aNum2;
+}
+
+/**
+ * Compares two VersionParts
+ */
+static int32_t
+CompareVP(VersionPart& aVer1, VersionPart& aVer2)
+{
+ int32_t r = ns_cmp(aVer1.numA, aVer2.numA);
+ if (r) {
+ return r;
+ }
+
+ r = ns_strnncmp(aVer1.strB, aVer1.strBlen, aVer2.strB, aVer2.strBlen);
+ if (r) {
+ return r;
+ }
+
+ r = ns_cmp(aVer1.numC, aVer2.numC);
+ if (r) {
+ return r;
+ }
+
+ return ns_strcmp(aVer1.extraD, aVer2.extraD);
+}
+
+/**
+ * Compares two VersionParts
+ */
+#ifdef XP_WIN
+static int32_t
+CompareVP(VersionPartW& aVer1, VersionPartW& aVer2)
+{
+ int32_t r = ns_cmp(aVer1.numA, aVer2.numA);
+ if (r) {
+ return r;
+ }
+
+ r = wcsncmp(aVer1.strB, aVer2.strB, XPCOM_MIN(aVer1.strBlen, aVer2.strBlen));
+ if (r) {
+ return r;
+ }
+
+ r = ns_cmp(aVer1.numC, aVer2.numC);
+ if (r) {
+ return r;
+ }
+
+ if (!aVer1.extraD) {
+ return aVer2.extraD != 0;
+ }
+
+ if (!aVer2.extraD) {
+ return -1;
+ }
+
+ return wcscmp(aVer1.extraD, aVer2.extraD);
+}
+#endif
+
+namespace mozilla {
+
+#ifdef XP_WIN
+int32_t
+CompareVersions(const char16_t* aStrA, const char16_t* aStrB)
+{
+ wchar_t* A2 = wcsdup(char16ptr_t(aStrA));
+ if (!A2) {
+ return 1;
+ }
+
+ wchar_t* B2 = wcsdup(char16ptr_t(aStrB));
+ if (!B2) {
+ free(A2);
+ return 1;
+ }
+
+ int32_t result;
+ wchar_t* a = A2;
+ wchar_t* b = B2;
+
+ do {
+ VersionPartW va, vb;
+
+ a = ParseVP(a, va);
+ b = ParseVP(b, vb);
+
+ result = CompareVP(va, vb);
+ if (result) {
+ break;
+ }
+
+ } while (a || b);
+
+ free(A2);
+ free(B2);
+
+ return result;
+}
+#endif
+
+int32_t
+CompareVersions(const char* aStrA, const char* aStrB)
+{
+ char* A2 = strdup(aStrA);
+ if (!A2) {
+ return 1;
+ }
+
+ char* B2 = strdup(aStrB);
+ if (!B2) {
+ free(A2);
+ return 1;
+ }
+
+ int32_t result;
+ char* a = A2;
+ char* b = B2;
+
+ do {
+ VersionPart va, vb;
+
+ a = ParseVP(a, va);
+ b = ParseVP(b, vb);
+
+ result = CompareVP(va, vb);
+ if (result) {
+ break;
+ }
+
+ } while (a || b);
+
+ free(A2);
+ free(B2);
+
+ return result;
+}
+
+} // namespace mozilla
+
diff --git a/xpcom/glue/nsVersionComparator.h b/xpcom/glue/nsVersionComparator.h
new file mode 100644
index 000000000..0dbf8532b
--- /dev/null
+++ b/xpcom/glue/nsVersionComparator.h
@@ -0,0 +1,174 @@
+/* -*- 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 nsVersionComparator_h__
+#define nsVersionComparator_h__
+
+#include "nscore.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#if defined(XP_WIN) && !defined(UPDATER_NO_STRING_GLUE_STL)
+#include <wchar.h>
+#include "nsStringGlue.h"
+#endif
+
+/**
+ * In order to compare version numbers in Mozilla, you need to use the
+ * mozilla::Version class. You can construct an object of this type by passing
+ * in a string version number to the constructor. Objects of this type can be
+ * compared using the standard comparison operators.
+ *
+ * For example, let's say that you want to make sure that a given version
+ * number is not older than 15.a2. Here's how you would write a function to
+ * do that.
+ *
+ * bool IsVersionValid(const char* version) {
+ * return mozilla::Version("15.a2") <= mozilla::Version(version);
+ * }
+ *
+ * Or, since Version's constructor is implicit, you can simplify this code:
+ *
+ * bool IsVersionValid(const char* version) {
+ * return mozilla::Version("15.a2") <= version;
+ * }
+ *
+ * On Windows, if your version strings are wide characters, you should use the
+ * mozilla::VersionW variant instead. The semantics of that class is the same
+ * as Version.
+ */
+
+namespace mozilla {
+
+int32_t CompareVersions(const char* aStrA, const char* aStrB);
+
+#ifdef XP_WIN
+int32_t CompareVersions(const char16_t* aStrA, const char16_t* aStrB);
+#endif
+
+struct Version
+{
+ explicit Version(const char* aVersionString)
+ {
+ versionContent = strdup(aVersionString);
+ }
+
+ const char* ReadContent() const
+ {
+ return versionContent;
+ }
+
+ ~Version()
+ {
+ free(versionContent);
+ }
+
+ bool operator<(const Version& aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs.ReadContent()) == -1;
+ }
+ bool operator<=(const Version& aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs.ReadContent()) < 1;
+ }
+ bool operator>(const Version& aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs.ReadContent()) == 1;
+ }
+ bool operator>=(const Version& aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs.ReadContent()) > -1;
+ }
+ bool operator==(const Version& aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs.ReadContent()) == 0;
+ }
+ bool operator!=(const Version& aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs.ReadContent()) != 0;
+ }
+ bool operator<(const char* aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs) == -1;
+ }
+ bool operator<=(const char* aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs) < 1;
+ }
+ bool operator>(const char* aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs) == 1;
+ }
+ bool operator>=(const char* aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs) > -1;
+ }
+ bool operator==(const char* aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs) == 0;
+ }
+ bool operator!=(const char* aRhs) const
+ {
+ return CompareVersions(versionContent, aRhs) != 0;
+ }
+
+private:
+ char* versionContent;
+};
+
+#ifdef XP_WIN
+struct VersionW
+{
+ VersionW(const char16_t* aVersionStringW)
+ {
+ versionContentW =
+ reinterpret_cast<char16_t*>(wcsdup(char16ptr_t(aVersionStringW)));
+ }
+
+ const char16_t* ReadContentW() const
+ {
+ return versionContentW;
+ }
+
+ ~VersionW()
+ {
+ free(versionContentW);
+ }
+
+ bool operator<(const VersionW& aRhs) const
+ {
+ return CompareVersions(versionContentW, aRhs.ReadContentW()) == -1;
+ }
+ bool operator<=(const VersionW& aRhs) const
+ {
+ return CompareVersions(versionContentW, aRhs.ReadContentW()) < 1;
+ }
+ bool operator>(const VersionW& aRhs) const
+ {
+ return CompareVersions(versionContentW, aRhs.ReadContentW()) == 1;
+ }
+ bool operator>=(const VersionW& aRhs) const
+ {
+ return CompareVersions(versionContentW, aRhs.ReadContentW()) > -1;
+ }
+ bool operator==(const VersionW& aRhs) const
+ {
+ return CompareVersions(versionContentW, aRhs.ReadContentW()) == 0;
+ }
+ bool operator!=(const VersionW& aRhs) const
+ {
+ return CompareVersions(versionContentW, aRhs.ReadContentW()) != 0;
+ }
+
+private:
+ char16_t* versionContentW;
+};
+#endif
+
+} // namespace mozilla
+
+#endif // nsVersionComparator_h__
+
diff --git a/xpcom/glue/nsWeakReference.cpp b/xpcom/glue/nsWeakReference.cpp
new file mode 100644
index 000000000..57f372641
--- /dev/null
+++ b/xpcom/glue/nsWeakReference.cpp
@@ -0,0 +1,164 @@
+/* -*- 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/. */
+
+// nsWeakReference.cpp
+
+#include "mozilla/Attributes.h"
+
+#include "nsWeakReference.h"
+#include "nsCOMPtr.h"
+#include "nsDebug.h"
+
+#ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED
+
+#define MOZ_WEAKREF_DECL_OWNINGTHREAD nsAutoOwningThread _mWeakRefOwningThread;
+#define MOZ_WEAKREF_ASSERT_OWNINGTHREAD \
+ NS_CheckThreadSafe(_mWeakRefOwningThread.GetThread(), "nsWeakReference not thread-safe")
+#define MOZ_WEAKREF_ASSERT_OWNINGTHREAD_DELEGATED(that) \
+ NS_CheckThreadSafe((that)->_mWeakRefOwningThread.GetThread(), "nsWeakReference not thread-safe")
+
+#else
+
+#define MOZ_WEAKREF_DECL_OWNINGTHREAD
+#define MOZ_WEAKREF_ASSERT_OWNINGTHREAD do { } while (false)
+#define MOZ_WEAKREF_ASSERT_OWNINGTHREAD_DELEGATED(that) do { } while (false)
+
+#endif
+
+class nsWeakReference final : public nsIWeakReference
+{
+public:
+ // nsISupports...
+ NS_DECL_ISUPPORTS
+
+ // nsIWeakReference...
+ NS_DECL_NSIWEAKREFERENCE
+ virtual size_t SizeOfOnlyThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
+
+private:
+ MOZ_WEAKREF_DECL_OWNINGTHREAD
+
+ friend class nsSupportsWeakReference;
+
+ explicit nsWeakReference(nsSupportsWeakReference* aReferent)
+ : mReferent(aReferent)
+ // ...I can only be constructed by an |nsSupportsWeakReference|
+ {
+ }
+
+ ~nsWeakReference()
+ // ...I will only be destroyed by calling |delete| myself.
+ {
+ MOZ_WEAKREF_ASSERT_OWNINGTHREAD;
+ if (mReferent) {
+ mReferent->NoticeProxyDestruction();
+ }
+ }
+
+ void
+ NoticeReferentDestruction()
+ // ...called (only) by an |nsSupportsWeakReference| from _its_ dtor.
+ {
+ MOZ_WEAKREF_ASSERT_OWNINGTHREAD;
+ mReferent = nullptr;
+ }
+
+ nsSupportsWeakReference* MOZ_NON_OWNING_REF mReferent;
+};
+
+nsresult
+nsQueryReferent::operator()(const nsIID& aIID, void** aAnswer) const
+{
+ nsresult status;
+ if (mWeakPtr) {
+ if (NS_FAILED(status = mWeakPtr->QueryReferent(aIID, aAnswer))) {
+ *aAnswer = 0;
+ }
+ } else {
+ status = NS_ERROR_NULL_POINTER;
+ }
+
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+nsIWeakReference* // or else |already_AddRefed<nsIWeakReference>|
+NS_GetWeakReference(nsISupports* aInstancePtr, nsresult* aErrorPtr)
+{
+ nsresult status;
+
+ nsIWeakReference* result = nullptr;
+
+ if (aInstancePtr) {
+ nsCOMPtr<nsISupportsWeakReference> factoryPtr =
+ do_QueryInterface(aInstancePtr, &status);
+ if (factoryPtr) {
+ status = factoryPtr->GetWeakReference(&result);
+ }
+ // else, |status| has already been set by |do_QueryInterface|
+ } else {
+ status = NS_ERROR_NULL_POINTER;
+ }
+
+ if (aErrorPtr) {
+ *aErrorPtr = status;
+ }
+ return result;
+}
+
+nsresult
+nsSupportsWeakReference::GetWeakReference(nsIWeakReference** aInstancePtr)
+{
+ if (!aInstancePtr) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (!mProxy) {
+ mProxy = new nsWeakReference(this);
+ } else {
+ MOZ_WEAKREF_ASSERT_OWNINGTHREAD_DELEGATED(mProxy);
+ }
+ *aInstancePtr = mProxy;
+
+ nsresult status;
+ if (!*aInstancePtr) {
+ status = NS_ERROR_OUT_OF_MEMORY;
+ } else {
+ NS_ADDREF(*aInstancePtr);
+ status = NS_OK;
+ }
+
+ return status;
+}
+
+NS_IMPL_ISUPPORTS(nsWeakReference, nsIWeakReference)
+
+NS_IMETHODIMP
+nsWeakReference::QueryReferent(const nsIID& aIID, void** aInstancePtr)
+{
+ MOZ_WEAKREF_ASSERT_OWNINGTHREAD;
+
+ return mReferent ? mReferent->QueryInterface(aIID, aInstancePtr) :
+ NS_ERROR_NULL_POINTER;
+}
+
+size_t
+nsWeakReference::SizeOfOnlyThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ return aMallocSizeOf(this);
+}
+
+void
+nsSupportsWeakReference::ClearWeakReferences()
+{
+ if (mProxy) {
+ mProxy->NoticeReferentDestruction();
+ mProxy = nullptr;
+ }
+}
+
diff --git a/xpcom/glue/nsWeakReference.h b/xpcom/glue/nsWeakReference.h
new file mode 100644
index 000000000..fc875ba96
--- /dev/null
+++ b/xpcom/glue/nsWeakReference.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/. */
+
+#ifndef nsWeakReference_h__
+#define nsWeakReference_h__
+
+// nsWeakReference.h
+
+// See mfbt/WeakPtr.h for a more typesafe C++ implementation of weak references
+
+#include "nsIWeakReferenceUtils.h"
+
+class nsWeakReference;
+
+class nsSupportsWeakReference : public nsISupportsWeakReference
+{
+public:
+ nsSupportsWeakReference() : mProxy(0) {}
+
+ NS_DECL_NSISUPPORTSWEAKREFERENCE
+
+protected:
+ inline ~nsSupportsWeakReference();
+
+private:
+ friend class nsWeakReference;
+
+ // Called (only) by an |nsWeakReference| from _its_ dtor.
+ // The thread safety check is made by the caller.
+ void NoticeProxyDestruction() { mProxy = nullptr; }
+
+ nsWeakReference* MOZ_NON_OWNING_REF mProxy;
+
+protected:
+
+ void ClearWeakReferences();
+ bool HasWeakReferences() const { return !!mProxy; }
+};
+
+inline
+nsSupportsWeakReference::~nsSupportsWeakReference()
+{
+ ClearWeakReferences();
+}
+
+#endif
diff --git a/xpcom/glue/nsXPTCUtils.h b/xpcom/glue/nsXPTCUtils.h
new file mode 100644
index 000000000..8922aca16
--- /dev/null
+++ b/xpcom/glue/nsXPTCUtils.h
@@ -0,0 +1,45 @@
+/* -*- 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 nsXPTCUtils_h__
+#define nsXPTCUtils_h__
+
+#include "xptcall.h"
+#include "mozilla/MemoryReporting.h"
+
+/**
+ * A helper class that initializes an xptcall helper at construction
+ * and releases it at destruction.
+ */
+class nsAutoXPTCStub : protected nsIXPTCProxy
+{
+public:
+ nsISomeInterface* mXPTCStub;
+
+protected:
+ nsAutoXPTCStub() : mXPTCStub(nullptr) {}
+
+ nsresult
+ InitStub(const nsIID& aIID)
+ {
+ return NS_GetXPTCallStub(aIID, this, &mXPTCStub);
+ }
+
+ ~nsAutoXPTCStub()
+ {
+ if (mXPTCStub) {
+ NS_DestroyXPTCallStub(mXPTCStub);
+ }
+ }
+
+ size_t
+ SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return mXPTCStub ? NS_SizeOfIncludingThisXPTCallStub(mXPTCStub, aMallocSizeOf) : 0;
+ }
+};
+
+#endif // nsXPTCUtils_h__
diff --git a/xpcom/glue/objs.mozbuild b/xpcom/glue/objs.mozbuild
new file mode 100644
index 000000000..8161e1ebc
--- /dev/null
+++ b/xpcom/glue/objs.mozbuild
@@ -0,0 +1,48 @@
+# -*- 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/.
+
+xpcom_glue_src_lcppsrcs = [
+ 'AppData.cpp',
+ 'FileUtils.cpp',
+ 'nsArrayEnumerator.cpp',
+ 'nsArrayUtils.cpp',
+ 'nsCategoryCache.cpp',
+ 'nsClassInfoImpl.cpp',
+ 'nsCOMArray.cpp',
+ 'nsComponentManagerUtils.cpp',
+ 'nsCOMPtr.cpp',
+ 'nsCRTGlue.cpp',
+ 'nsCycleCollectionParticipant.cpp',
+ 'nsDeque.cpp',
+ 'nsEnumeratorUtils.cpp',
+ 'nsID.cpp',
+ 'nsIInterfaceRequestorUtils.cpp',
+ 'nsINIParser.cpp',
+ 'nsISupportsImpl.cpp',
+ 'nsMemory.cpp',
+ 'nsQuickSort.cpp',
+ 'nsTArray.cpp',
+ 'nsThreadUtils.cpp',
+ 'nsTObserverArray.cpp',
+ 'nsVersionComparator.cpp',
+ 'nsWeakReference.cpp',
+ 'PLDHashTable.cpp',
+]
+
+xpcom_glue_src_cppsrcs = [
+ '/xpcom/glue/%s' % s for s in xpcom_glue_src_lcppsrcs
+]
+
+xpcom_gluens_src_lcppsrcs = [
+ 'BlockingResourceBase.cpp',
+ 'GenericFactory.cpp',
+ 'nsProxyRelease.cpp',
+ 'nsTextFormatter.cpp',
+]
+
+xpcom_gluens_src_cppsrcs = [
+ '/xpcom/glue/%s' % s for s in xpcom_gluens_src_lcppsrcs
+]
diff --git a/xpcom/glue/standalone/moz.build b/xpcom/glue/standalone/moz.build
new file mode 100644
index 000000000..fd91fa0bb
--- /dev/null
+++ b/xpcom/glue/standalone/moz.build
@@ -0,0 +1,58 @@
+# -*- 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/.
+
+# On win we build two glue libs - glue linked to crt dlls here and in staticruntime we build
+# a statically linked glue lib.
+if CONFIG['OS_ARCH'] == 'WINNT':
+ DIRS += ['staticruntime']
+
+include('../objs.mozbuild')
+
+SOURCES += xpcom_glue_src_cppsrcs
+
+SOURCES += [
+ '../nsStringAPI.cpp',
+ 'nsXPCOMGlue.cpp',
+]
+
+Library('xpcomglue')
+
+EXPORTS += [
+ 'nsXPCOMGlue.h',
+]
+
+SDK_LIBRARY = True
+
+FORCE_STATIC_LIB = True
+
+if CONFIG['_MSC_VER']:
+ DEFINES['_USE_ANSI_CPP'] = True
+ # Don't include directives about which CRT to use
+ CFLAGS += ['-Zl']
+ CXXFLAGS += ['-Zl']
+
+DEFINES['XPCOM_GLUE'] = True
+
+LOCAL_INCLUDES += [
+ '../../build',
+ '../../threads',
+]
+
+# Don't use STL wrappers here (i.e. wrapped <new>); they require mozalloc
+DISABLE_STL_WRAPPING = True
+
+# Include fallible for third party code using the xpcom glue
+USE_LIBS += [
+ 'fallible',
+]
+
+# Force to build a static library only
+NO_EXPAND_LIBS = True
+
+DIST_INSTALL = True
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
+ CXXFLAGS += CONFIG['GLIB_CFLAGS']
diff --git a/xpcom/glue/standalone/nsXPCOMGlue.cpp b/xpcom/glue/standalone/nsXPCOMGlue.cpp
new file mode 100644
index 000000000..b657d7050
--- /dev/null
+++ b/xpcom/glue/standalone/nsXPCOMGlue.cpp
@@ -0,0 +1,927 @@
+/* -*- 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 "nsXPCOMGlue.h"
+
+#include "nspr.h"
+#include "nsDebug.h"
+#include "nsIServiceManager.h"
+#include "nsXPCOMPrivate.h"
+#include "nsCOMPtr.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "mozilla/FileUtils.h"
+#include "mozilla/Sprintf.h"
+
+using namespace mozilla;
+
+#define XPCOM_DEPENDENT_LIBS_LIST "dependentlibs.list"
+
+static XPCOMFunctions xpcomFunctions;
+static bool do_preload = false;
+
+#if defined(XP_WIN)
+#define READ_TEXTMODE L"rt"
+#else
+#define READ_TEXTMODE "r"
+#endif
+
+#if defined(XP_WIN)
+#include <windows.h>
+#include <mbstring.h>
+
+typedef HINSTANCE LibHandleType;
+
+static LibHandleType
+GetLibHandle(pathstr_t aDependentLib)
+{
+ LibHandleType libHandle =
+ LoadLibraryExW(aDependentLib, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
+
+#ifdef DEBUG
+ if (!libHandle) {
+ DWORD err = GetLastError();
+ LPWSTR lpMsgBuf;
+ FormatMessageW(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr,
+ err,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPWSTR)&lpMsgBuf,
+ 0,
+ nullptr
+ );
+ wprintf(L"Error loading %ls: %s\n", aDependentLib, lpMsgBuf);
+ LocalFree(lpMsgBuf);
+ }
+#endif
+
+ return libHandle;
+}
+
+static NSFuncPtr
+GetSymbol(LibHandleType aLibHandle, const char* aSymbol)
+{
+ return (NSFuncPtr)GetProcAddress(aLibHandle, aSymbol);
+}
+
+static void
+CloseLibHandle(LibHandleType aLibHandle)
+{
+ FreeLibrary(aLibHandle);
+}
+
+#else
+#include <dlfcn.h>
+
+#if defined(MOZ_LINKER) && !defined(ANDROID)
+extern "C" {
+NS_HIDDEN __typeof(dlopen) __wrap_dlopen;
+NS_HIDDEN __typeof(dlsym) __wrap_dlsym;
+NS_HIDDEN __typeof(dlclose) __wrap_dlclose;
+}
+
+#define dlopen __wrap_dlopen
+#define dlsym __wrap_dlsym
+#define dlclose __wrap_dlclose
+#endif
+
+typedef void* LibHandleType;
+
+static LibHandleType
+GetLibHandle(pathstr_t aDependentLib)
+{
+ LibHandleType libHandle = dlopen(aDependentLib,
+ RTLD_GLOBAL | RTLD_LAZY
+#ifdef XP_MACOSX
+ | RTLD_FIRST
+#endif
+ );
+ if (!libHandle) {
+ fprintf(stderr, "XPCOMGlueLoad error for file %s:\n%s\n", aDependentLib,
+ dlerror());
+ }
+ return libHandle;
+}
+
+static NSFuncPtr
+GetSymbol(LibHandleType aLibHandle, const char* aSymbol)
+{
+ return (NSFuncPtr)dlsym(aLibHandle, aSymbol);
+}
+
+static void
+CloseLibHandle(LibHandleType aLibHandle)
+{
+ dlclose(aLibHandle);
+}
+#endif
+
+struct DependentLib
+{
+ LibHandleType libHandle;
+ DependentLib* next;
+};
+
+static DependentLib* sTop;
+
+static void
+AppendDependentLib(LibHandleType aLibHandle)
+{
+ DependentLib* d = new DependentLib;
+ if (!d) {
+ return;
+ }
+
+ d->next = sTop;
+ d->libHandle = aLibHandle;
+
+ sTop = d;
+}
+
+static bool
+ReadDependentCB(pathstr_t aDependentLib, bool aDoPreload)
+{
+ if (aDoPreload) {
+ ReadAheadLib(aDependentLib);
+ }
+ LibHandleType libHandle = GetLibHandle(aDependentLib);
+ if (libHandle) {
+ AppendDependentLib(libHandle);
+ }
+
+ return libHandle;
+}
+
+#ifdef XP_WIN
+static bool
+ReadDependentCB(const char* aDependentLib, bool do_preload)
+{
+ wchar_t wideDependentLib[MAX_PATH];
+ MultiByteToWideChar(CP_UTF8, 0, aDependentLib, -1, wideDependentLib, MAX_PATH);
+ return ReadDependentCB(wideDependentLib, do_preload);
+}
+
+inline FILE*
+TS_tfopen(const char* path, const wchar_t* mode)
+{
+ wchar_t wPath[MAX_PATH];
+ MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH);
+ return _wfopen(wPath, mode);
+}
+#else
+inline FILE*
+TS_tfopen(const char* aPath, const char* aMode)
+{
+ return fopen(aPath, aMode);
+}
+#endif
+
+/* RAII wrapper for FILE descriptors */
+struct ScopedCloseFileTraits
+{
+ typedef FILE* type;
+ static type empty() { return nullptr; }
+ static void release(type aFile)
+ {
+ if (aFile) {
+ fclose(aFile);
+ }
+ }
+};
+typedef Scoped<ScopedCloseFileTraits> ScopedCloseFile;
+
+static void
+XPCOMGlueUnload()
+{
+ while (sTop) {
+ CloseLibHandle(sTop->libHandle);
+
+ DependentLib* temp = sTop;
+ sTop = sTop->next;
+
+ delete temp;
+ }
+}
+
+#if defined(XP_WIN)
+// like strpbrk but finds the *last* char, not the first
+static const char*
+ns_strrpbrk(const char* string, const char* strCharSet)
+{
+ const char* found = nullptr;
+ for (; *string; ++string) {
+ for (const char* search = strCharSet; *search; ++search) {
+ if (*search == *string) {
+ found = string;
+ // Since we're looking for the last char, we save "found"
+ // until we're at the end of the string.
+ }
+ }
+ }
+
+ return found;
+}
+#endif
+
+static GetFrozenFunctionsFunc
+XPCOMGlueLoad(const char* aXPCOMFile)
+{
+ char xpcomDir[MAXPATHLEN];
+#ifdef XP_WIN
+ const char* lastSlash = ns_strrpbrk(aXPCOMFile, "/\\");
+#elif XP_MACOSX
+ // On OSX, the dependentlibs.list file lives under Contents/Resources.
+ // However, the actual libraries listed in dependentlibs.list live under
+ // Contents/MacOS. We want to read the list from Contents/Resources, then
+ // load the libraries from Contents/MacOS.
+ const char *tempSlash = strrchr(aXPCOMFile, '/');
+ size_t tempLen = size_t(tempSlash - aXPCOMFile);
+ if (tempLen > MAXPATHLEN) {
+ return nullptr;
+ }
+ char tempBuffer[MAXPATHLEN];
+ memcpy(tempBuffer, aXPCOMFile, tempLen);
+ tempBuffer[tempLen] = '\0';
+ const char *slash = strrchr(tempBuffer, '/');
+ tempLen = size_t(slash - tempBuffer);
+ const char *lastSlash = aXPCOMFile + tempLen;
+#else
+ const char* lastSlash = strrchr(aXPCOMFile, '/');
+#endif
+ char* cursor;
+ if (lastSlash) {
+ size_t len = size_t(lastSlash - aXPCOMFile);
+
+ if (len > MAXPATHLEN - sizeof(XPCOM_FILE_PATH_SEPARATOR
+#ifdef XP_MACOSX
+ "Resources"
+ XPCOM_FILE_PATH_SEPARATOR
+#endif
+ XPCOM_DEPENDENT_LIBS_LIST)) {
+ return nullptr;
+ }
+ memcpy(xpcomDir, aXPCOMFile, len);
+ strcpy(xpcomDir + len, XPCOM_FILE_PATH_SEPARATOR
+#ifdef XP_MACOSX
+ "Resources"
+ XPCOM_FILE_PATH_SEPARATOR
+#endif
+ XPCOM_DEPENDENT_LIBS_LIST);
+ cursor = xpcomDir + len + 1;
+ } else {
+ strcpy(xpcomDir, XPCOM_DEPENDENT_LIBS_LIST);
+ cursor = xpcomDir;
+ }
+
+ if (getenv("MOZ_RUN_GTEST")) {
+ strcat(xpcomDir, ".gtest");
+ }
+
+ ScopedCloseFile flist;
+ flist = TS_tfopen(xpcomDir, READ_TEXTMODE);
+ if (!flist) {
+ return nullptr;
+ }
+
+#ifdef XP_MACOSX
+ tempLen = size_t(cursor - xpcomDir);
+ if (tempLen > MAXPATHLEN - sizeof("MacOS" XPCOM_FILE_PATH_SEPARATOR) - 1) {
+ return nullptr;
+ }
+ strcpy(cursor, "MacOS" XPCOM_FILE_PATH_SEPARATOR);
+ cursor += strlen(cursor);
+#endif
+ *cursor = '\0';
+
+ char buffer[MAXPATHLEN];
+
+ while (fgets(buffer, sizeof(buffer), flist)) {
+ int l = strlen(buffer);
+
+ // ignore empty lines and comments
+ if (l == 0 || *buffer == '#') {
+ continue;
+ }
+
+ // cut the trailing newline, if present
+ if (buffer[l - 1] == '\n') {
+ buffer[l - 1] = '\0';
+ }
+
+ if (l + size_t(cursor - xpcomDir) > MAXPATHLEN) {
+ return nullptr;
+ }
+
+ strcpy(cursor, buffer);
+ if (!ReadDependentCB(xpcomDir, do_preload)) {
+ XPCOMGlueUnload();
+ return nullptr;
+ }
+ }
+
+ GetFrozenFunctionsFunc sym =
+ (GetFrozenFunctionsFunc)GetSymbol(sTop->libHandle,
+ "NS_GetFrozenFunctions");
+
+ if (!sym) { // No symbol found.
+ XPCOMGlueUnload();
+ return nullptr;
+ }
+
+ return sym;
+}
+
+nsresult
+XPCOMGlueLoadXULFunctions(const nsDynamicFunctionLoad* aSymbols)
+{
+ // We don't null-check sXULLibHandle because this might work even
+ // if it is null (same as RTLD_DEFAULT)
+
+ nsresult rv = NS_OK;
+ while (aSymbols->functionName) {
+ char buffer[512];
+ SprintfLiteral(buffer, "%s", aSymbols->functionName);
+
+ *aSymbols->function = (NSFuncPtr)GetSymbol(sTop->libHandle, buffer);
+ if (!*aSymbols->function) {
+ rv = NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
+ }
+
+ ++aSymbols;
+ }
+ return rv;
+}
+
+void
+XPCOMGlueEnablePreload()
+{
+ do_preload = true;
+}
+
+#if defined(MOZ_WIDGET_GTK) && (defined(MOZ_MEMORY) || defined(__FreeBSD__) || defined(__NetBSD__))
+#define MOZ_GSLICE_INIT
+#endif
+
+#ifdef MOZ_GSLICE_INIT
+#include <glib.h>
+
+class GSliceInit {
+public:
+ GSliceInit() {
+ mHadGSlice = bool(getenv("G_SLICE"));
+ if (!mHadGSlice) {
+ // Disable the slice allocator, since jemalloc already uses similar layout
+ // algorithms, and using a sub-allocator tends to increase fragmentation.
+ // This must be done before g_thread_init() is called.
+ // glib >= 2.36 initializes g_slice as a side effect of its various static
+ // initializers, so this needs to happen before glib is loaded, which is
+ // this is hooked in XPCOMGlueStartup before libxul is loaded. This
+ // relies on the main executable not depending on glib.
+ setenv("G_SLICE", "always-malloc", 1);
+ }
+ }
+
+ ~GSliceInit() {
+#if MOZ_WIDGET_GTK == 2
+ if (sTop) {
+ auto XRE_GlibInit = (void (*)(void)) GetSymbol(sTop->libHandle,
+ "XRE_GlibInit");
+ // Initialize glib enough for G_SLICE to have an effect before it is unset.
+ // unset.
+ XRE_GlibInit();
+ }
+#endif
+ if (!mHadGSlice) {
+ unsetenv("G_SLICE");
+ }
+ }
+
+private:
+ bool mHadGSlice;
+};
+#endif
+
+nsresult
+XPCOMGlueStartup(const char* aXPCOMFile)
+{
+#ifdef MOZ_GSLICE_INIT
+ GSliceInit gSliceInit;
+#endif
+ xpcomFunctions.version = XPCOM_GLUE_VERSION;
+ xpcomFunctions.size = sizeof(XPCOMFunctions);
+
+ if (!aXPCOMFile) {
+ aXPCOMFile = XPCOM_DLL;
+ }
+
+ GetFrozenFunctionsFunc func = XPCOMGlueLoad(aXPCOMFile);
+ if (!func) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsresult rv = (*func)(&xpcomFunctions, nullptr);
+ if (NS_FAILED(rv)) {
+ XPCOMGlueUnload();
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+XPCOM_API(nsresult)
+NS_InitXPCOM2(nsIServiceManager** aResult,
+ nsIFile* aBinDirectory,
+ nsIDirectoryServiceProvider* aAppFileLocationProvider)
+{
+ if (!xpcomFunctions.init) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.init(aResult, aBinDirectory, aAppFileLocationProvider);
+}
+
+XPCOM_API(nsresult)
+NS_ShutdownXPCOM(nsIServiceManager* aServMgr)
+{
+ if (!xpcomFunctions.shutdown) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.shutdown(aServMgr);
+}
+
+XPCOM_API(nsresult)
+NS_GetServiceManager(nsIServiceManager** aResult)
+{
+ if (!xpcomFunctions.getServiceManager) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.getServiceManager(aResult);
+}
+
+XPCOM_API(nsresult)
+NS_GetComponentManager(nsIComponentManager** aResult)
+{
+ if (!xpcomFunctions.getComponentManager) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.getComponentManager(aResult);
+}
+
+XPCOM_API(nsresult)
+NS_GetComponentRegistrar(nsIComponentRegistrar** aResult)
+{
+ if (!xpcomFunctions.getComponentRegistrar) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.getComponentRegistrar(aResult);
+}
+
+XPCOM_API(nsresult)
+NS_GetMemoryManager(nsIMemory** aResult)
+{
+ if (!xpcomFunctions.getMemoryManager) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.getMemoryManager(aResult);
+}
+
+XPCOM_API(nsresult)
+NS_NewLocalFile(const nsAString& aPath, bool aFollowLinks, nsIFile** aResult)
+{
+ if (!xpcomFunctions.newLocalFile) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.newLocalFile(aPath, aFollowLinks, aResult);
+}
+
+XPCOM_API(nsresult)
+NS_NewNativeLocalFile(const nsACString& aPath, bool aFollowLinks,
+ nsIFile** aResult)
+{
+ if (!xpcomFunctions.newNativeLocalFile) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.newNativeLocalFile(aPath, aFollowLinks, aResult);
+}
+
+XPCOM_API(nsresult)
+NS_GetDebug(nsIDebug2** aResult)
+{
+ if (!xpcomFunctions.getDebug) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.getDebug(aResult);
+}
+
+
+XPCOM_API(nsresult)
+NS_StringContainerInit(nsStringContainer& aStr)
+{
+ if (!xpcomFunctions.stringContainerInit) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.stringContainerInit(aStr);
+}
+
+XPCOM_API(nsresult)
+NS_StringContainerInit2(nsStringContainer& aStr, const char16_t* aData,
+ uint32_t aDataLength, uint32_t aFlags)
+{
+ if (!xpcomFunctions.stringContainerInit2) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.stringContainerInit2(aStr, aData, aDataLength, aFlags);
+}
+
+XPCOM_API(void)
+NS_StringContainerFinish(nsStringContainer& aStr)
+{
+ if (xpcomFunctions.stringContainerFinish) {
+ xpcomFunctions.stringContainerFinish(aStr);
+ }
+}
+
+XPCOM_API(uint32_t)
+NS_StringGetData(const nsAString& aStr, const char16_t** aBuf, bool* aTerm)
+{
+ if (!xpcomFunctions.stringGetData) {
+ *aBuf = nullptr;
+ return 0;
+ }
+ return xpcomFunctions.stringGetData(aStr, aBuf, aTerm);
+}
+
+XPCOM_API(uint32_t)
+NS_StringGetMutableData(nsAString& aStr, uint32_t aLen, char16_t** aBuf)
+{
+ if (!xpcomFunctions.stringGetMutableData) {
+ *aBuf = nullptr;
+ return 0;
+ }
+ return xpcomFunctions.stringGetMutableData(aStr, aLen, aBuf);
+}
+
+XPCOM_API(char16_t*)
+NS_StringCloneData(const nsAString& aStr)
+{
+ if (!xpcomFunctions.stringCloneData) {
+ return nullptr;
+ }
+ return xpcomFunctions.stringCloneData(aStr);
+}
+
+XPCOM_API(nsresult)
+NS_StringSetData(nsAString& aStr, const char16_t* aBuf, uint32_t aCount)
+{
+ if (!xpcomFunctions.stringSetData) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return xpcomFunctions.stringSetData(aStr, aBuf, aCount);
+}
+
+XPCOM_API(nsresult)
+NS_StringSetDataRange(nsAString& aStr, uint32_t aCutStart, uint32_t aCutLength,
+ const char16_t* aBuf, uint32_t aCount)
+{
+ if (!xpcomFunctions.stringSetDataRange) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.stringSetDataRange(aStr, aCutStart, aCutLength, aBuf,
+ aCount);
+}
+
+XPCOM_API(nsresult)
+NS_StringCopy(nsAString& aDest, const nsAString& aSrc)
+{
+ if (!xpcomFunctions.stringCopy) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.stringCopy(aDest, aSrc);
+}
+
+XPCOM_API(void)
+NS_StringSetIsVoid(nsAString& aStr, const bool aIsVoid)
+{
+ if (xpcomFunctions.stringSetIsVoid) {
+ xpcomFunctions.stringSetIsVoid(aStr, aIsVoid);
+ }
+}
+
+XPCOM_API(bool)
+NS_StringGetIsVoid(const nsAString& aStr)
+{
+ if (!xpcomFunctions.stringGetIsVoid) {
+ return false;
+ }
+ return xpcomFunctions.stringGetIsVoid(aStr);
+}
+
+XPCOM_API(nsresult)
+NS_CStringContainerInit(nsCStringContainer& aStr)
+{
+ if (!xpcomFunctions.cstringContainerInit) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.cstringContainerInit(aStr);
+}
+
+XPCOM_API(nsresult)
+NS_CStringContainerInit2(nsCStringContainer& aStr, const char* aData,
+ uint32_t aDataLength, uint32_t aFlags)
+{
+ if (!xpcomFunctions.cstringContainerInit2) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.cstringContainerInit2(aStr, aData, aDataLength, aFlags);
+}
+
+XPCOM_API(void)
+NS_CStringContainerFinish(nsCStringContainer& aStr)
+{
+ if (xpcomFunctions.cstringContainerFinish) {
+ xpcomFunctions.cstringContainerFinish(aStr);
+ }
+}
+
+XPCOM_API(uint32_t)
+NS_CStringGetData(const nsACString& aStr, const char** aBuf, bool* aTerm)
+{
+ if (!xpcomFunctions.cstringGetData) {
+ *aBuf = nullptr;
+ return 0;
+ }
+ return xpcomFunctions.cstringGetData(aStr, aBuf, aTerm);
+}
+
+XPCOM_API(uint32_t)
+NS_CStringGetMutableData(nsACString& aStr, uint32_t aLen, char** aBuf)
+{
+ if (!xpcomFunctions.cstringGetMutableData) {
+ *aBuf = nullptr;
+ return 0;
+ }
+ return xpcomFunctions.cstringGetMutableData(aStr, aLen, aBuf);
+}
+
+XPCOM_API(char*)
+NS_CStringCloneData(const nsACString& aStr)
+{
+ if (!xpcomFunctions.cstringCloneData) {
+ return nullptr;
+ }
+ return xpcomFunctions.cstringCloneData(aStr);
+}
+
+XPCOM_API(nsresult)
+NS_CStringSetData(nsACString& aStr, const char* aBuf, uint32_t aCount)
+{
+ if (!xpcomFunctions.cstringSetData) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.cstringSetData(aStr, aBuf, aCount);
+}
+
+XPCOM_API(nsresult)
+NS_CStringSetDataRange(nsACString& aStr, uint32_t aCutStart,
+ uint32_t aCutLength, const char* aBuf, uint32_t aCount)
+{
+ if (!xpcomFunctions.cstringSetDataRange) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.cstringSetDataRange(aStr, aCutStart, aCutLength, aBuf,
+ aCount);
+}
+
+XPCOM_API(nsresult)
+NS_CStringCopy(nsACString& aDest, const nsACString& aSrc)
+{
+ if (!xpcomFunctions.cstringCopy) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.cstringCopy(aDest, aSrc);
+}
+
+XPCOM_API(void)
+NS_CStringSetIsVoid(nsACString& aStr, const bool aIsVoid)
+{
+ if (xpcomFunctions.cstringSetIsVoid) {
+ xpcomFunctions.cstringSetIsVoid(aStr, aIsVoid);
+ }
+}
+
+XPCOM_API(bool)
+NS_CStringGetIsVoid(const nsACString& aStr)
+{
+ if (!xpcomFunctions.cstringGetIsVoid) {
+ return false;
+ }
+ return xpcomFunctions.cstringGetIsVoid(aStr);
+}
+
+XPCOM_API(nsresult)
+NS_CStringToUTF16(const nsACString& aSrc, nsCStringEncoding aSrcEncoding,
+ nsAString& aDest)
+{
+ if (!xpcomFunctions.cstringToUTF16) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.cstringToUTF16(aSrc, aSrcEncoding, aDest);
+}
+
+XPCOM_API(nsresult)
+NS_UTF16ToCString(const nsAString& aSrc, nsCStringEncoding aDestEncoding,
+ nsACString& aDest)
+{
+ if (!xpcomFunctions.utf16ToCString) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return xpcomFunctions.utf16ToCString(aSrc, aDestEncoding, aDest);
+}
+
+XPCOM_API(void*)
+NS_Alloc(size_t aSize)
+{
+ if (!xpcomFunctions.allocFunc) {
+ return nullptr;
+ }
+ return xpcomFunctions.allocFunc(aSize);
+}
+
+XPCOM_API(void*)
+NS_Realloc(void* aPtr, size_t aSize)
+{
+ if (!xpcomFunctions.reallocFunc) {
+ return nullptr;
+ }
+ return xpcomFunctions.reallocFunc(aPtr, aSize);
+}
+
+XPCOM_API(void)
+NS_Free(void* aPtr)
+{
+ if (xpcomFunctions.freeFunc) {
+ xpcomFunctions.freeFunc(aPtr);
+ }
+}
+
+XPCOM_API(void)
+NS_DebugBreak(uint32_t aSeverity, const char* aStr, const char* aExpr,
+ const char* aFile, int32_t aLine)
+{
+ if (xpcomFunctions.debugBreakFunc) {
+ xpcomFunctions.debugBreakFunc(aSeverity, aStr, aExpr, aFile, aLine);
+ }
+}
+
+XPCOM_API(void)
+NS_LogInit()
+{
+ if (xpcomFunctions.logInitFunc) {
+ xpcomFunctions.logInitFunc();
+ }
+}
+
+XPCOM_API(void)
+NS_LogTerm()
+{
+ if (xpcomFunctions.logTermFunc) {
+ xpcomFunctions.logTermFunc();
+ }
+}
+
+XPCOM_API(void)
+NS_LogAddRef(void* aPtr, nsrefcnt aNewRefCnt,
+ const char* aTypeName, uint32_t aInstanceSize)
+{
+ if (xpcomFunctions.logAddRefFunc)
+ xpcomFunctions.logAddRefFunc(aPtr, aNewRefCnt,
+ aTypeName, aInstanceSize);
+}
+
+XPCOM_API(void)
+NS_LogRelease(void* aPtr, nsrefcnt aNewRefCnt, const char* aTypeName)
+{
+ if (xpcomFunctions.logReleaseFunc) {
+ xpcomFunctions.logReleaseFunc(aPtr, aNewRefCnt, aTypeName);
+ }
+}
+
+XPCOM_API(void)
+NS_LogCtor(void* aPtr, const char* aTypeName, uint32_t aInstanceSize)
+{
+ if (xpcomFunctions.logCtorFunc) {
+ xpcomFunctions.logCtorFunc(aPtr, aTypeName, aInstanceSize);
+ }
+}
+
+XPCOM_API(void)
+NS_LogDtor(void* aPtr, const char* aTypeName, uint32_t aInstanceSize)
+{
+ if (xpcomFunctions.logDtorFunc) {
+ xpcomFunctions.logDtorFunc(aPtr, aTypeName, aInstanceSize);
+ }
+}
+
+XPCOM_API(void)
+NS_LogCOMPtrAddRef(void* aCOMPtr, nsISupports* aObject)
+{
+ if (xpcomFunctions.logCOMPtrAddRefFunc) {
+ xpcomFunctions.logCOMPtrAddRefFunc(aCOMPtr, aObject);
+ }
+}
+
+XPCOM_API(void)
+NS_LogCOMPtrRelease(void* aCOMPtr, nsISupports* aObject)
+{
+ if (xpcomFunctions.logCOMPtrReleaseFunc) {
+ xpcomFunctions.logCOMPtrReleaseFunc(aCOMPtr, aObject);
+ }
+}
+
+XPCOM_API(nsresult)
+NS_GetXPTCallStub(REFNSIID aIID, nsIXPTCProxy* aOuter,
+ nsISomeInterface** aStub)
+{
+ if (!xpcomFunctions.getXPTCallStubFunc) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return xpcomFunctions.getXPTCallStubFunc(aIID, aOuter, aStub);
+}
+
+XPCOM_API(void)
+NS_DestroyXPTCallStub(nsISomeInterface* aStub)
+{
+ if (xpcomFunctions.destroyXPTCallStubFunc) {
+ xpcomFunctions.destroyXPTCallStubFunc(aStub);
+ }
+}
+
+XPCOM_API(nsresult)
+NS_InvokeByIndex(nsISupports* aThat, uint32_t aMethodIndex,
+ uint32_t aParamCount, nsXPTCVariant* aParams)
+{
+ if (!xpcomFunctions.invokeByIndexFunc) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return xpcomFunctions.invokeByIndexFunc(aThat, aMethodIndex,
+ aParamCount, aParams);
+}
+
+XPCOM_API(bool)
+NS_CycleCollectorSuspect(nsISupports* aObj)
+{
+ if (!xpcomFunctions.cycleSuspectFunc) {
+ return false;
+ }
+
+ return xpcomFunctions.cycleSuspectFunc(aObj);
+}
+
+XPCOM_API(bool)
+NS_CycleCollectorForget(nsISupports* aObj)
+{
+ if (!xpcomFunctions.cycleForgetFunc) {
+ return false;
+ }
+
+ return xpcomFunctions.cycleForgetFunc(aObj);
+}
+
+XPCOM_API(nsPurpleBufferEntry*)
+NS_CycleCollectorSuspect2(void* aObj, nsCycleCollectionParticipant* aCp)
+{
+ if (!xpcomFunctions.cycleSuspect2Func) {
+ return nullptr;
+ }
+
+ return xpcomFunctions.cycleSuspect2Func(aObj, aCp);
+}
+
+XPCOM_API(void)
+NS_CycleCollectorSuspect3(void* aObj, nsCycleCollectionParticipant* aCp,
+ nsCycleCollectingAutoRefCnt* aRefCnt,
+ bool* aShouldDelete)
+{
+ if (xpcomFunctions.cycleSuspect3Func) {
+ xpcomFunctions.cycleSuspect3Func(aObj, aCp, aRefCnt, aShouldDelete);
+ }
+}
+
+XPCOM_API(bool)
+NS_CycleCollectorForget2(nsPurpleBufferEntry* aEntry)
+{
+ if (!xpcomFunctions.cycleForget2Func) {
+ return false;
+ }
+
+ return xpcomFunctions.cycleForget2Func(aEntry);
+}
diff --git a/xpcom/glue/standalone/nsXPCOMGlue.h b/xpcom/glue/standalone/nsXPCOMGlue.h
new file mode 100644
index 000000000..e23bfa498
--- /dev/null
+++ b/xpcom/glue/standalone/nsXPCOMGlue.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/. */
+
+#ifndef nsXPCOMGlue_h__
+#define nsXPCOMGlue_h__
+
+#include "nscore.h"
+
+#ifdef XPCOM_GLUE
+
+/**
+ * The following functions are only available in the standalone glue.
+ */
+
+/**
+ * Enabled preloading of dynamically loaded libraries
+ */
+extern "C" NS_HIDDEN_(void) XPCOMGlueEnablePreload();
+
+/**
+ * Initialize the XPCOM glue by dynamically linking against the XPCOM
+ * shared library indicated by xpcomFile.
+ */
+extern "C" NS_HIDDEN_(nsresult) XPCOMGlueStartup(const char* aXPCOMFile);
+
+typedef void (*NSFuncPtr)();
+
+struct nsDynamicFunctionLoad
+{
+ const char* functionName;
+ NSFuncPtr* function;
+};
+
+/**
+ * Dynamically load functions from libxul.
+ *
+ * @throws NS_ERROR_NOT_INITIALIZED if XPCOMGlueStartup() was not called or
+ * if the libxul DLL was not found.
+ * @throws NS_ERROR_LOSS_OF_SIGNIFICANT_DATA if only some of the required
+ * functions were found.
+ */
+extern "C" NS_HIDDEN_(nsresult)
+XPCOMGlueLoadXULFunctions(const nsDynamicFunctionLoad* aSymbols);
+
+#endif // XPCOM_GLUE
+#endif // nsXPCOMGlue_h__
diff --git a/xpcom/glue/standalone/staticruntime/moz.build b/xpcom/glue/standalone/staticruntime/moz.build
new file mode 100644
index 000000000..735086ab0
--- /dev/null
+++ b/xpcom/glue/standalone/staticruntime/moz.build
@@ -0,0 +1,50 @@
+# -*- 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/.
+
+include('../../objs.mozbuild')
+
+SOURCES += xpcom_glue_src_cppsrcs
+
+SOURCES += [
+ '../../nsStringAPI.cpp',
+ '../nsXPCOMGlue.cpp',
+]
+
+Library('xpcomglue_staticruntime')
+
+SDK_LIBRARY = True
+
+# create a static lib
+FORCE_STATIC_LIB = True
+
+if CONFIG['_MSC_VER']:
+ DEFINES['_USE_ANSI_CPP'] = True
+ # Don't include directives about which CRT to use
+ CFLAGS += ['-Zl']
+ CXXFLAGS += ['-Zl']
+
+DEFINES['XPCOM_GLUE'] = True
+
+LOCAL_INCLUDES += [
+ '../../../build',
+ '../../../threads',
+]
+
+# Statically link to the CRT on Windows
+USE_STATIC_LIBS = True
+
+# Don't use STL wrappers here (i.e. wrapped <new>); they require mozalloc
+DISABLE_STL_WRAPPING = True
+
+# Include fallible for third party code using the xpcom glue
+USE_LIBS += [
+ 'fallible',
+]
+
+# Force to build a static library only
+NO_EXPAND_LIBS = True
+
+DIST_INSTALL = True
diff --git a/xpcom/glue/staticruntime/moz.build b/xpcom/glue/staticruntime/moz.build
new file mode 100644
index 000000000..384bc6878
--- /dev/null
+++ b/xpcom/glue/staticruntime/moz.build
@@ -0,0 +1,48 @@
+# -*- 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/.
+
+include('../objs.mozbuild')
+
+UNIFIED_SOURCES += xpcom_gluens_src_cppsrcs
+UNIFIED_SOURCES += xpcom_glue_src_cppsrcs
+
+UNIFIED_SOURCES += [
+ '../GenericModule.cpp',
+ '../nsStringAPI.cpp',
+]
+
+Library('xpcomglue_staticruntime_s')
+
+SDK_LIBRARY = True
+
+FORCE_STATIC_LIB = True
+
+if CONFIG['_MSC_VER']:
+ DEFINES['_USE_ANSI_CPP'] = True
+ # Don't include directives about which CRT to use
+ CFLAGS += ['-Zl']
+ CXXFLAGS += ['-Zl']
+
+LOCAL_INCLUDES += [
+ '../../build',
+ '../../threads',
+]
+
+# Statically link to the CRT on Windows
+USE_STATIC_LIBS = True
+
+# Don't use STL wrappers here (i.e. wrapped <new>); they require mozalloc
+DISABLE_STL_WRAPPING = True
+
+# Include fallible for third party code using the xpcom glue
+USE_LIBS += [
+ 'fallible',
+]
+
+# Force to build a static library only
+NO_EXPAND_LIBS = True
+
+DIST_INSTALL = True
diff --git a/xpcom/glue/tests/gtest/TestArray.cpp b/xpcom/glue/tests/gtest/TestArray.cpp
new file mode 100644
index 000000000..72d28b4df
--- /dev/null
+++ b/xpcom/glue/tests/gtest/TestArray.cpp
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "gtest/gtest.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"
+
+// {9e70a320-be02-11d1-8031-006008159b5a}
+#define NS_IFOO_IID \
+ {0x9e70a320, 0xbe02, 0x11d1, \
+ {0x80, 0x31, 0x00, 0x60, 0x08, 0x15, 0x9b, 0x5a}}
+
+namespace TestArray {
+
+class IFoo : public nsISupports {
+public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID)
+
+ NS_IMETHOD_(nsrefcnt) RefCnt() = 0;
+ NS_IMETHOD_(int32_t) ID() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(IFoo, NS_IFOO_IID)
+
+class Foo final : public IFoo {
+public:
+
+ explicit Foo(int32_t aID);
+
+ // nsISupports implementation
+ NS_DECL_ISUPPORTS
+
+ // IFoo implementation
+ NS_IMETHOD_(nsrefcnt) RefCnt() override { return mRefCnt; }
+ NS_IMETHOD_(int32_t) ID() override { return mID; }
+
+ static int32_t gCount;
+
+ int32_t mID;
+
+private:
+ ~Foo();
+};
+
+int32_t Foo::gCount;
+
+Foo::Foo(int32_t aID)
+{
+ mID = aID;
+ ++gCount;
+}
+
+Foo::~Foo()
+{
+ --gCount;
+}
+
+NS_IMPL_ISUPPORTS(Foo, IFoo)
+
+void CheckArray(nsISupportsArray* aArray, int32_t aExpectedCount, int32_t aElementIDs[], int32_t aExpectedTotal)
+{
+ uint32_t cnt = 0;
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ aArray->Count(&cnt);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Count failed");
+ int32_t count = cnt;
+ int32_t index;
+
+ EXPECT_EQ(Foo::gCount, aExpectedTotal);
+ EXPECT_EQ(count, aExpectedCount);
+
+ for (index = 0; (index < count) && (index < aExpectedCount); index++) {
+ nsCOMPtr<IFoo> foo = do_QueryElementAt(aArray, index);
+ EXPECT_EQ(foo->ID(), aElementIDs[index]);
+ }
+}
+
+void FillArray(nsISupportsArray* aArray, int32_t aCount)
+{
+ int32_t index;
+ for (index = 0; index < aCount; index++) {
+ nsCOMPtr<IFoo> foo = new Foo(index);
+ aArray->AppendElement(foo);
+ }
+}
+
+} // namespace TestArray
+
+using namespace TestArray;
+
+TEST(Array, main)
+{
+ nsISupportsArray* array;
+ nsresult rv;
+
+ if (NS_OK == (rv = NS_NewISupportsArray(&array))) {
+ FillArray(array, 10);
+ int32_t fillResult[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+ CheckArray(array, 10, fillResult, 10);
+
+ // test insert
+ nsCOMPtr<IFoo> foo = do_QueryElementAt(array, 3);
+ array->InsertElementAt(foo, 5);
+ int32_t insertResult[11] = {0, 1, 2, 3, 4, 3, 5, 6, 7, 8, 9};
+ CheckArray(array, 11, insertResult, 10);
+ array->InsertElementAt(foo, 0);
+ int32_t insertResult2[12] = {3, 0, 1, 2, 3, 4, 3, 5, 6, 7, 8, 9};
+ CheckArray(array, 12, insertResult2, 10);
+ array->AppendElement(foo);
+ int32_t appendResult[13] = {3, 0, 1, 2, 3, 4, 3, 5, 6, 7, 8, 9, 3};
+ CheckArray(array, 13, appendResult, 10);
+
+
+ // test IndexOf
+ int32_t expectedIndex = 0;
+ int32_t index = array->IndexOf(foo);
+ EXPECT_EQ(index, expectedIndex);
+
+ // test ReplaceElementAt
+ array->ReplaceElementAt(foo, 8);
+ int32_t replaceResult[13] = {3, 0, 1, 2, 3, 4, 3, 5, 3, 7, 8, 9, 3};
+ CheckArray(array, 13, replaceResult, 9);
+
+ // test RemoveElementAt, RemoveElement
+ array->RemoveElementAt(0);
+ int32_t removeResult[12] = {0, 1, 2, 3, 4, 3, 5, 3, 7, 8, 9, 3};
+ CheckArray(array, 12, removeResult, 9);
+ array->RemoveElementAt(7);
+ int32_t removeResult2[11] = {0, 1, 2, 3, 4, 3, 5, 7, 8, 9, 3};
+ CheckArray(array, 11, removeResult2, 9);
+ array->RemoveElement(foo);
+ int32_t removeResult3[10] = {0, 1, 2, 4, 3, 5, 7, 8, 9, 3};
+ CheckArray(array, 10, removeResult3, 9);
+
+ foo = nullptr;
+
+ // test clear
+ array->Clear();
+ FillArray(array, 4);
+ CheckArray(array, 4, fillResult, 4);
+
+ // test delete
+ NS_RELEASE(array);
+ }
+}
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#elif defined(_MSC_VER)
+#pragma warning (pop)
+#endif
diff --git a/xpcom/glue/tests/gtest/TestFileUtils.cpp b/xpcom/glue/tests/gtest/TestFileUtils.cpp
new file mode 100644
index 000000000..55106c6c5
--- /dev/null
+++ b/xpcom/glue/tests/gtest/TestFileUtils.cpp
@@ -0,0 +1,283 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* Test ReadSysFile() */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "FileUtils.h"
+
+#include "gtest/gtest.h"
+
+namespace mozilla {
+
+#ifdef ReadSysFile_PRESENT
+
+/**
+ * Create a file with the specified contents.
+ */
+static bool
+WriteFile(
+ const char* aFilename,
+ const void* aContents,
+ size_t aContentsLen)
+{
+ int fd;
+ ssize_t ret;
+ size_t offt;
+
+ fd = MOZ_TEMP_FAILURE_RETRY(
+ open(aFilename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR));
+ if (fd == -1) {
+ fprintf(stderr, "open(): %s: %s\n", aFilename, strerror(errno));
+ return false;
+ }
+
+ offt = 0;
+ do {
+ ret = MOZ_TEMP_FAILURE_RETRY(
+ write(fd, (char*)aContents + offt, aContentsLen - offt));
+ if (ret == -1) {
+ fprintf(stderr, "write(): %s: %s\n", aFilename, strerror(errno));
+ close(fd);
+ return false;
+ }
+ offt += ret;
+ } while (offt < aContentsLen);
+
+ ret = MOZ_TEMP_FAILURE_RETRY(close(fd));
+ if (ret == -1) {
+ fprintf(stderr, "close(): %s: %s\n", aFilename, strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+TEST(ReadSysFile, Nonexistent) {
+ bool ret;
+ int errno_saved;
+
+ ret = ReadSysFile("/nonexistent", nullptr, 0);
+ errno_saved = errno;
+
+ ASSERT_FALSE(ret);
+ ASSERT_EQ(errno_saved, ENOENT);
+}
+
+TEST(ReadSysFile, Main) {
+ /* Use a different file name for each test since different tests could be
+ executed concurrently. */
+ static const char* fn = "TestReadSysFileMain";
+ /* If we have a file which contains "abcd" and we read it with ReadSysFile(),
+ providing a buffer of size 10 bytes, we would expect 5 bytes to be written
+ to that buffer: "abcd\0". */
+ struct
+ {
+ /* input (file contents), e.g. "abcd" */
+ const char* input;
+ /* pretended output buffer size, e.g. 10; the actual buffer is larger
+ and we check if anything was written past the end of the allowed length */
+ size_t output_size;
+ /* expected number of bytes written to the output buffer, including the
+ terminating '\0', e.g. 5 */
+ size_t output_len;
+ /* expected output buffer contents, e.g. "abcd\0", the first output_len
+ bytes of the output buffer should match the first 'output_len' bytes from
+ 'output', the rest of the output buffer should be untouched. */
+ const char* output;
+ } tests[] = {
+ /* No new lines */
+ {"", 0, 0, ""},
+ {"", 1, 1, "\0"}, /* \0 is redundant, but we write it for clarity */
+ {"", 9, 1, "\0"},
+
+ {"a", 0, 0, ""},
+ {"a", 1, 1, "\0"},
+ {"a", 2, 2, "a\0"},
+ {"a", 9, 2, "a\0"},
+
+ {"abcd", 0, 0, ""},
+ {"abcd", 1, 1, "\0"},
+ {"abcd", 2, 2, "a\0"},
+ {"abcd", 3, 3, "ab\0"},
+ {"abcd", 4, 4, "abc\0"},
+ {"abcd", 5, 5, "abcd\0"},
+ {"abcd", 9, 5, "abcd\0"},
+
+ /* A single trailing new line */
+ {"\n", 0, 0, ""},
+ {"\n", 1, 1, "\0"},
+ {"\n", 2, 1, "\0"},
+ {"\n", 9, 1, "\0"},
+
+ {"a\n", 0, 0, ""},
+ {"a\n", 1, 1, "\0"},
+ {"a\n", 2, 2, "a\0"},
+ {"a\n", 3, 2, "a\0"},
+ {"a\n", 9, 2, "a\0"},
+
+ {"abcd\n", 0, 0, ""},
+ {"abcd\n", 1, 1, "\0"},
+ {"abcd\n", 2, 2, "a\0"},
+ {"abcd\n", 3, 3, "ab\0"},
+ {"abcd\n", 4, 4, "abc\0"},
+ {"abcd\n", 5, 5, "abcd\0"},
+ {"abcd\n", 6, 5, "abcd\0"},
+ {"abcd\n", 9, 5, "abcd\0"},
+
+ /* Multiple trailing new lines */
+ {"\n\n", 0, 0, ""},
+ {"\n\n", 1, 1, "\0"},
+ {"\n\n", 2, 2, "\n\0"},
+ {"\n\n", 3, 2, "\n\0"},
+ {"\n\n", 9, 2, "\n\0"},
+
+ {"a\n\n", 0, 0, ""},
+ {"a\n\n", 1, 1, "\0"},
+ {"a\n\n", 2, 2, "a\0"},
+ {"a\n\n", 3, 3, "a\n\0"},
+ {"a\n\n", 4, 3, "a\n\0"},
+ {"a\n\n", 9, 3, "a\n\0"},
+
+ {"abcd\n\n", 0, 0, ""},
+ {"abcd\n\n", 1, 1, "\0"},
+ {"abcd\n\n", 2, 2, "a\0"},
+ {"abcd\n\n", 3, 3, "ab\0"},
+ {"abcd\n\n", 4, 4, "abc\0"},
+ {"abcd\n\n", 5, 5, "abcd\0"},
+ {"abcd\n\n", 6, 6, "abcd\n\0"},
+ {"abcd\n\n", 7, 6, "abcd\n\0"},
+ {"abcd\n\n", 9, 6, "abcd\n\0"},
+
+ /* New line in the middle */
+ {"ab\ncd", 9, 6, "ab\ncd\0"},
+ };
+
+ for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+ ASSERT_TRUE(WriteFile(fn, tests[i].input, strlen(tests[i].input)));
+ /* Leave the file to exist if some of the assertions fail. */
+
+ char buf[128];
+ static const char unmodified = 'X';
+
+ memset(buf, unmodified, sizeof(buf));
+
+ ASSERT_TRUE(ReadSysFile(fn, buf, tests[i].output_size));
+
+ if (tests[i].output_size == 0) {
+ /* The buffer must be unmodified. We check only the first byte. */
+ ASSERT_EQ(unmodified, buf[0]);
+ } else {
+ ASSERT_EQ(tests[i].output_len, strlen(buf) + 1);
+ ASSERT_STREQ(tests[i].output, buf);
+ /* Check that the first byte after the trailing '\0' has not been
+ modified. */
+ ASSERT_EQ(unmodified, buf[tests[i].output_len]);
+ }
+ }
+
+ unlink(fn);
+}
+
+TEST(ReadSysFile, Int) {
+ static const char* fn = "TestReadSysFileInt";
+ struct
+ {
+ /* input (file contents), e.g. "5" */
+ const char* input;
+ /* expected return value, if false, then the output is not checked */
+ bool ret;
+ /* expected result */
+ int output;
+ } tests[] = {
+ {"0", true, 0},
+ {"00", true, 0},
+ {"1", true, 1},
+ {"5", true, 5},
+ {"55", true, 55},
+
+ {" 123", true, 123},
+ {"123 ", true, 123},
+ {" 123 ", true, 123},
+ {"123\n", true, 123},
+
+ {"", false, 0},
+ {" ", false, 0},
+ {"a", false, 0},
+
+ {"-1", true, -1},
+ {" -456 ", true, -456},
+ {" -78.9 ", true, -78},
+ };
+
+ for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+ ASSERT_TRUE(WriteFile(fn, tests[i].input, strlen(tests[i].input)));
+ /* Leave the file to exist if some of the assertions fail. */
+
+ bool ret;
+ int output = 424242;
+
+ ret = ReadSysFile(fn, &output);
+
+ ASSERT_EQ(tests[i].ret, ret);
+
+ if (ret) {
+ ASSERT_EQ(tests[i].output, output);
+ }
+ }
+
+ unlink(fn);
+}
+
+TEST(ReadSysFile, Bool) {
+ static const char* fn = "TestReadSysFileBool";
+ struct
+ {
+ /* input (file contents), e.g. "1" */
+ const char* input;
+ /* expected return value */
+ bool ret;
+ /* expected result */
+ bool output;
+ } tests[] = {
+ {"0", true, false},
+ {"00", true, false},
+ {"1", true, true},
+ {"5", true, true},
+ {"23", true, true},
+ {"-1", true, true},
+
+ {"", false, true /* unused */},
+ };
+
+ for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+ ASSERT_TRUE(WriteFile(fn, tests[i].input, strlen(tests[i].input)));
+ /* Leave the file to exist if some of the assertions fail. */
+
+ bool ret;
+ bool output;
+
+ ret = ReadSysFile(fn, &output);
+
+ ASSERT_EQ(tests[i].ret, ret);
+
+ if (ret) {
+ ASSERT_EQ(tests[i].output, output);
+ }
+ }
+
+ unlink(fn);
+}
+
+#endif /* ReadSysFile_PRESENT */
+
+} // namespace mozilla
diff --git a/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp b/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp
new file mode 100644
index 000000000..5bf10ab05
--- /dev/null
+++ b/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp
@@ -0,0 +1,140 @@
+/* -*- 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/. */
+
+/*
+ * Tests that generational garbage collection post-barriers are correctly
+ * implemented for nsTArrays that contain JavaScript Values.
+ */
+
+#include "jsapi.h"
+#include "nsTArray.h"
+
+#include "gtest/gtest.h"
+
+#include "js/TracingAPI.h"
+#include "js/HeapAPI.h"
+
+#include "mozilla/CycleCollectedJSContext.h"
+
+using namespace JS;
+using namespace mozilla;
+
+template<class ArrayT>
+static void
+TraceArray(JSTracer* trc, void* data)
+{
+ ArrayT* array = static_cast<ArrayT *>(data);
+ for (unsigned i = 0; i < array->Length(); ++i)
+ JS::TraceEdge(trc, &array->ElementAt(i), "array-element");
+}
+
+/*
+ * Use arrays with initial size much smaller than the final number of elements
+ * to test that moving Heap<T> elements works correctly.
+ */
+const size_t ElementCount = 100;
+const size_t InitialElements = ElementCount / 10;
+
+template<class ArrayT>
+static void
+RunTest(JSContext* cx, ArrayT* array)
+{
+ JS_GC(cx);
+
+ ASSERT_TRUE(array != nullptr);
+ JS_AddExtraGCRootsTracer(cx, TraceArray<ArrayT>, array);
+
+ /*
+ * Create the array and fill it with new JS objects. With GGC these will be
+ * allocated in the nursery.
+ */
+ RootedValue value(cx);
+ const char* property = "foo";
+ for (size_t i = 0; i < ElementCount; ++i) {
+ RootedObject obj(cx, JS_NewPlainObject(cx));
+ ASSERT_FALSE(JS::ObjectIsTenured(obj));
+ value = Int32Value(i);
+ ASSERT_TRUE(JS_SetProperty(cx, obj, property, value));
+ ASSERT_TRUE(array->AppendElement(obj, fallible));
+ }
+
+ /*
+ * If postbarriers are not working, we will crash here when we try to mark
+ * objects that have been moved to the tenured heap.
+ */
+ JS_GC(cx);
+
+ /*
+ * Sanity check that our array contains what we expect.
+ */
+ for (size_t i = 0; i < ElementCount; ++i) {
+ RootedObject obj(cx, array->ElementAt(i));
+ ASSERT_TRUE(JS::ObjectIsTenured(obj));
+ ASSERT_TRUE(JS_GetProperty(cx, obj, property, &value));
+ ASSERT_TRUE(value.isInt32());
+ ASSERT_EQ(static_cast<int32_t>(i), value.toInt32());
+ }
+
+ JS_RemoveExtraGCRootsTracer(cx, TraceArray<ArrayT>, array);
+}
+
+static void
+CreateGlobalAndRunTest(JSContext* cx)
+{
+ static const JSClassOps GlobalClassOps = {
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr,
+ JS_GlobalObjectTraceHook
+ };
+
+ static const JSClass GlobalClass = {
+ "global", JSCLASS_GLOBAL_FLAGS,
+ &GlobalClassOps
+ };
+
+ JS::CompartmentOptions options;
+ options.behaviors().setVersion(JSVERSION_LATEST);
+ JS::PersistentRootedObject global(cx);
+ global = JS_NewGlobalObject(cx, &GlobalClass, nullptr, JS::FireOnNewGlobalHook, options);
+ ASSERT_TRUE(global != nullptr);
+
+ JSCompartment *oldCompartment = JS_EnterCompartment(cx, global);
+
+ typedef Heap<JSObject*> ElementT;
+
+ {
+ nsTArray<ElementT>* array = new nsTArray<ElementT>(InitialElements);
+ RunTest(cx, array);
+ delete array;
+ }
+
+ {
+ FallibleTArray<ElementT>* array = new FallibleTArray<ElementT>(InitialElements);
+ RunTest(cx, array);
+ delete array;
+ }
+
+ {
+ AutoTArray<ElementT, InitialElements> array;
+ RunTest(cx, &array);
+ }
+
+ JS_LeaveCompartment(cx, oldCompartment);
+}
+
+TEST(GCPostBarriers, nsTArray) {
+ CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
+ ASSERT_TRUE(ccjscx != nullptr);
+ JSContext* cx = ccjscx->Context();
+ ASSERT_TRUE(cx != nullptr);
+
+ JS_BeginRequest(cx);
+
+ CreateGlobalAndRunTest(cx);
+
+ JS_EndRequest(cx);
+}
diff --git a/xpcom/glue/tests/gtest/TestNsDeque.cpp b/xpcom/glue/tests/gtest/TestNsDeque.cpp
new file mode 100644
index 000000000..b84e1b781
--- /dev/null
+++ b/xpcom/glue/tests/gtest/TestNsDeque.cpp
@@ -0,0 +1,342 @@
+/* -*- 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 "gtest/gtest.h"
+#include "nsDeque.h"
+#include "nsCRT.h"
+#include <stdio.h>
+
+/**************************************************************
+ Now define the token deallocator class...
+ **************************************************************/
+namespace TestNsDeque {
+
+ class _Dealloc: public nsDequeFunctor
+ {
+ virtual void* operator()(void* aObject) {
+ return 0;
+ }
+ };
+
+ static bool VerifyContents(const nsDeque& aDeque, const int* aContents, size_t aLength)
+ {
+ for (size_t i=0; i<aLength; ++i) {
+ if (*(int*)aDeque.ObjectAt(i) != aContents[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ class Deallocator: public nsDequeFunctor
+ {
+ virtual void* operator()(void* aObject)
+ {
+ if (aObject)
+ {
+ // Set value to -1, to use in test function.
+ *((int*)aObject) = -1;
+ }
+
+ return nullptr;
+ }
+ };
+
+ class ForEachAdder: public nsDequeFunctor
+ {
+ virtual void* operator()(void* aObject)
+ {
+ if (aObject)
+ {
+ sum += *(int*)aObject;
+ }
+
+ return aObject;
+ }
+
+ private:
+ int sum = 0;
+
+ public:
+ int GetSum() { return sum; }
+
+ };
+}
+
+using namespace TestNsDeque;
+
+TEST(NsDeque, OriginalTest)
+{
+ const size_t size = 200;
+ int ints[size];
+ size_t i=0;
+ int temp;
+ nsDeque theDeque(new _Dealloc); //construct a simple one...
+
+ // ints = [0...199]
+ for (i=0;i<size;i++) { //initialize'em
+ ints[i]=static_cast<int>(i);
+ }
+ // queue = [0...69]
+ for (i=0;i<70;i++) {
+ theDeque.Push(&ints[i]);
+ temp=*(int*)theDeque.Peek();
+ EXPECT_EQ(static_cast<int>(i), temp) << "Verify end after push #1";
+ EXPECT_EQ(i + 1, theDeque.GetSize()) << "Verify size after push #1";
+ }
+
+ EXPECT_EQ(70u,theDeque.GetSize()) << "Verify overall size after pushes #1";
+
+ // queue = [0...14]
+ for (i=1;i<=55;i++) {
+ temp=*(int*)theDeque.Pop();
+ EXPECT_EQ(70-static_cast<int>(i),temp) << "Verify end after pop # 1";
+ EXPECT_EQ(70u - i,theDeque.GetSize()) << "Verify size after pop # 1";
+ }
+ EXPECT_EQ(15u,theDeque.GetSize()) << "Verify overall size after pops";
+
+ // queue = [0...14,0...54]
+ for (i=0;i<55;i++) {
+ theDeque.Push(&ints[i]);
+ temp=*(int*)theDeque.Peek();
+ EXPECT_EQ(static_cast<int>(i),temp) << "Verify end after push #2";
+ EXPECT_EQ(i + 15u + 1,theDeque.GetSize()) << "Verify size after push # 2";
+ }
+ EXPECT_EQ(70u,theDeque.GetSize()) << "Verify size after end of all pushes #2";
+
+ // queue = [0...14,0...19]
+ for (i=1;i<=35;i++) {
+ temp=*(int*)theDeque.Pop();
+ EXPECT_EQ(55-static_cast<int>(i),temp ) << "Verify end after pop # 2";
+ EXPECT_EQ(70u - i,theDeque.GetSize()) << "Verify size after pop #2";
+ }
+ EXPECT_EQ(35u,theDeque.GetSize()) << "Verify overall size after end of all pops #2";
+
+ // queue = [0...14,0...19,0...34]
+ for (i=0;i<35;i++) {
+ theDeque.Push(&ints[i]);
+ temp = *(int*)theDeque.Peek();
+ EXPECT_EQ(static_cast<int>(i),temp) << "Verify end after push # 3";
+ EXPECT_EQ(35u + 1u + i,theDeque.GetSize()) << "Verify size after push #3";
+ }
+
+ // queue = [0...14,0...19]
+ for (i=0;i<35;i++) {
+ temp=*(int*)theDeque.Pop();
+ EXPECT_EQ(34 - static_cast<int>(i), temp) << "Verify end after pop # 3";
+ }
+
+ // queue = [0...14]
+ for (i=0;i<20;i++) {
+ temp=*(int*)theDeque.Pop();
+ EXPECT_EQ(19 - static_cast<int>(i),temp) << "Verify end after pop # 4";
+ }
+
+ // queue = []
+ for (i=0;i<15;i++) {
+ temp=*(int*)theDeque.Pop();
+ EXPECT_EQ(14 - static_cast<int>(i),temp) << "Verify end after pop # 5";
+ }
+
+ EXPECT_EQ(0u,theDeque.GetSize()) << "Deque should finish empty.";
+}
+
+TEST(NsDeque, OriginalFlaw)
+{
+ int ints[200];
+ int i=0;
+ int temp;
+ nsDeque d(new _Dealloc);
+ /**
+ * Test 1. Origin near end, semi full, call Peek().
+ * you start, mCapacity is 8
+ */
+ for (i=0; i<30; i++)
+ ints[i]=i;
+
+ for (i=0; i<6; i++) {
+ d.Push(&ints[i]);
+ temp = *(int*)d.Peek();
+ EXPECT_EQ(i, temp) << "OriginalFlaw push #1";
+ }
+ EXPECT_EQ(6u, d.GetSize()) << "OriginalFlaw size check #1";
+
+ for (i=0; i<4; i++) {
+ temp=*(int*)d.PopFront();
+ EXPECT_EQ(i, temp) << "PopFront test";
+ }
+ // d = [4,5]
+ EXPECT_EQ(2u, d.GetSize()) << "OriginalFlaw size check #2";
+
+ for (i=0; i<4; i++) {
+ d.Push(&ints[6 + i]);
+ }
+
+ // d = [4...9]
+ for (i=4; i<=9; i++) {
+ temp=*(int*)d.PopFront();
+ EXPECT_EQ(i, temp) << "OriginalFlaw empty check";
+ }
+}
+
+TEST(NsDeque, TestObjectAt)
+{
+ nsDeque d;
+ const int count = 10;
+ int ints[count];
+ for (int i=0; i<count; i++) {
+ ints[i] = i;
+ }
+
+ for (int i=0; i<6; i++) {
+ d.Push(&ints[i]);
+ }
+ // d = [0...5]
+ d.PopFront();
+ d.PopFront();
+
+ // d = [2..5]
+ for (size_t i=2; i<=5; i++) {
+ int t = *(int*)d.ObjectAt(i-2);
+ EXPECT_EQ(static_cast<int>(i),t) << "Verify ObjectAt()";
+ }
+}
+
+TEST(NsDeque, TestPushFront)
+{
+ // PushFront has some interesting corner cases, primarily we're interested in whether:
+ // - wrapping around works properly
+ // - growing works properly
+
+ nsDeque d;
+
+ const int kPoolSize = 10;
+ const size_t kMaxSizeBeforeGrowth = 8;
+
+ int pool[kPoolSize];
+ for (int i = 0; i < kPoolSize; i++) {
+ pool[i] = i;
+ }
+
+ for (size_t i = 0; i < kMaxSizeBeforeGrowth; i++) {
+ d.PushFront(pool + i);
+ }
+
+ EXPECT_EQ(kMaxSizeBeforeGrowth, d.GetSize()) << "verify size";
+
+ static const int t1[] = {7,6,5,4,3,2,1,0};
+ EXPECT_TRUE(VerifyContents(d, t1, kMaxSizeBeforeGrowth)) << "verify pushfront 1";
+
+ // Now push one more so it grows
+ d.PushFront(pool + kMaxSizeBeforeGrowth);
+ EXPECT_EQ(kMaxSizeBeforeGrowth + 1, d.GetSize()) << "verify size";
+
+ static const int t2[] = {8,7,6,5,4,3,2,1,0};
+ EXPECT_TRUE(VerifyContents(d, t2, kMaxSizeBeforeGrowth + 1)) << "verify pushfront 2";
+
+ // And one more so that it wraps again
+ d.PushFront(pool + kMaxSizeBeforeGrowth + 1);
+ EXPECT_EQ(kMaxSizeBeforeGrowth + 2, d.GetSize()) << "verify size";
+
+ static const int t3[] = {9,8,7,6,5,4,3,2,1,0};
+ EXPECT_TRUE(VerifyContents(d, t3, kMaxSizeBeforeGrowth + 2)) <<"verify pushfront 3";
+}
+
+void CheckIfQueueEmpty(nsDeque& d)
+{
+ EXPECT_EQ(0u, d.GetSize()) << "Size should be 0";
+ EXPECT_EQ(nullptr, d.Pop()) << "Invalid operation should return nullptr";
+ EXPECT_EQ(nullptr, d.PopFront()) << "Invalid operation should return nullptr";
+ EXPECT_EQ(nullptr, d.Peek()) << "Invalid operation should return nullptr";
+ EXPECT_EQ(nullptr, d.PeekFront()) << "Invalid operation should return nullptr";
+ EXPECT_EQ(nullptr, d.ObjectAt(0u)) << "Invalid operation should return nullptr";
+}
+
+TEST(NsDeque,TestEmpty)
+{
+ // Make sure nsDeque gives sane results if it's empty.
+ nsDeque d;
+ size_t numberOfEntries = 8;
+
+ CheckIfQueueEmpty(d);
+
+ // Fill it up and drain it.
+ for (size_t i = 0; i < numberOfEntries; i++) {
+ d.Push((void*)0xAA);
+ }
+
+ EXPECT_EQ(numberOfEntries, d.GetSize());
+
+ for (size_t i = 0; i < numberOfEntries; i++) {
+ (void)d.Pop();
+ }
+
+ // Now check it again.
+ CheckIfQueueEmpty(d);
+}
+
+TEST(NsDeque,TestEraseMethod)
+{
+ nsDeque d;
+ const size_t numberOfEntries = 8;
+
+ // Fill it up before calling Erase
+ for (size_t i = 0; i < numberOfEntries; i++) {
+ d.Push((void*)0xAA);
+ }
+
+ // Call Erase
+ d.Erase();
+
+ // Now check it again.
+ CheckIfQueueEmpty(d);
+}
+
+TEST(NsDeque,TestEraseShouldCallDeallocator)
+{
+ nsDeque d(new Deallocator());
+ const size_t NumTestValues = 8;
+
+ int* testArray[NumTestValues];
+ for (size_t i=0; i < NumTestValues; i++)
+ {
+ testArray[i] = new int();
+ *(testArray[i]) = i;
+ d.Push((void*)testArray[i]);
+ }
+
+ d.Erase();
+
+ // Now check it again.
+ CheckIfQueueEmpty(d);
+
+ for (size_t i=0; i < NumTestValues; i++)
+ {
+ EXPECT_EQ(-1, *(testArray[i])) << "Erase should call deallocator: " << *(testArray[i]);
+ }
+}
+
+TEST(NsDeque, TestForEach)
+{
+ nsDeque d(new Deallocator());
+ const size_t NumTestValues = 8;
+ int sum = 0;
+
+ int* testArray[NumTestValues];
+ for (size_t i=0; i < NumTestValues; i++)
+ {
+ testArray[i] = new int();
+ *(testArray[i]) = i;
+ sum += i;
+ d.Push((void*)testArray[i]);
+ }
+
+ ForEachAdder adder;
+ d.ForEach(adder);
+ EXPECT_EQ(sum, adder.GetSum()) << "For each should iterate over values";
+
+ d.Erase();
+}
diff --git a/xpcom/glue/tests/gtest/TestThreadUtils.cpp b/xpcom/glue/tests/gtest/TestThreadUtils.cpp
new file mode 100644
index 000000000..728bae612
--- /dev/null
+++ b/xpcom/glue/tests/gtest/TestThreadUtils.cpp
@@ -0,0 +1,937 @@
+/* -*- 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 <stdio.h>
+#include <stdlib.h>
+#include "nsThreadUtils.h"
+#include "gtest/gtest.h"
+
+// {9e70a320-be02-11d1-8031-006008159b5a}
+#define NS_IFOO_IID \
+ {0x9e70a320, 0xbe02, 0x11d1, \
+ {0x80, 0x31, 0x00, 0x60, 0x08, 0x15, 0x9b, 0x5a}}
+
+namespace TestThreadUtils {
+
+static bool gDebug = false;
+static int gAlive, gZombies;
+static int gAllConstructions, gConstructions, gCopyConstructions,
+ gMoveConstructions, gDestructions, gAssignments, gMoves;
+struct Spy
+{
+ static void ClearActions()
+ {
+ gAllConstructions = gConstructions = gCopyConstructions
+ = gMoveConstructions = gDestructions = gAssignments = gMoves = 0;
+ }
+ static void ClearAll()
+ {
+ ClearActions();
+ gAlive = 0;
+ }
+
+ explicit Spy(int aID) : mID(aID)
+ {
+ ++gAlive; ++gAllConstructions; ++gConstructions;
+ if (gDebug) { printf("Spy[%d@%p]()\n", mID, this); }
+ }
+ Spy(const Spy& o) : mID(o.mID)
+ {
+ ++gAlive; ++gAllConstructions; ++gCopyConstructions;
+ if (gDebug) { printf("Spy[%d@%p](&[%d@%p])\n", mID, this, o.mID, &o); }
+ }
+ Spy(Spy&& o) : mID(o.mID)
+ {
+ o.mID = -o.mID;
+ ++gZombies; ++gAllConstructions; ++gMoveConstructions;
+ if (gDebug) { printf("Spy[%d@%p](&&[%d->%d@%p])\n", mID, this, -o.mID, o.mID, &o); }
+ }
+ ~Spy()
+ {
+ if (mID >= 0) { --gAlive; } else { --gZombies; } ++gDestructions;
+ if (gDebug) { printf("~Spy[%d@%p]()\n", mID, this); }
+ mID = 0;
+ }
+ Spy& operator=(const Spy& o)
+ {
+ ++gAssignments;
+ if (gDebug) { printf("Spy[%d->%d@%p] = &[%d@%p]\n", mID, o.mID, this, o.mID, &o); }
+ mID = o.mID;
+ return *this;
+ };
+ Spy& operator=(Spy&& o)
+ {
+ --gAlive; ++gZombies;
+ ++gMoves;
+ if (gDebug) { printf("Spy[%d->%d@%p] = &&[%d->%d@%p]\n", mID, o.mID, this, o.mID, -o.mID, &o); }
+ mID = o.mID; o.mID = -o.mID;
+ return *this;
+ };
+
+ int mID; // ID given at construction, or negation if was moved from; 0 when destroyed.
+};
+
+struct ISpyWithISupports : public nsISupports
+{
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID)
+ NS_IMETHOD_(nsrefcnt) RefCnt() = 0;
+ NS_IMETHOD_(int32_t) ID() = 0;
+};
+NS_DEFINE_STATIC_IID_ACCESSOR(ISpyWithISupports, NS_IFOO_IID)
+struct SpyWithISupports : public ISpyWithISupports, public Spy
+{
+private:
+ virtual ~SpyWithISupports() = default;
+public:
+ explicit SpyWithISupports(int aID) : Spy(aID) {};
+ NS_DECL_ISUPPORTS
+ NS_IMETHOD_(nsrefcnt) RefCnt() override { return mRefCnt; }
+ NS_IMETHOD_(int32_t) ID() override { return mID; }
+};
+NS_IMPL_ISUPPORTS(SpyWithISupports, ISpyWithISupports)
+
+
+class IThreadUtilsObject : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID)
+
+ NS_IMETHOD_(nsrefcnt) RefCnt() = 0;
+ NS_IMETHOD_(int32_t) ID() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(IThreadUtilsObject, NS_IFOO_IID)
+
+struct ThreadUtilsObject : public IThreadUtilsObject
+{
+ // nsISupports implementation
+ NS_DECL_ISUPPORTS
+
+ // IThreadUtilsObject implementation
+ NS_IMETHOD_(nsrefcnt) RefCnt() override { return mRefCnt; }
+ NS_IMETHOD_(int32_t) ID() override { return 0; }
+
+ int mCount; // Number of calls + arguments processed.
+ int mA0, mA1, mA2, mA3;
+ Spy mSpy; const Spy* mSpyPtr;
+ ThreadUtilsObject()
+ : mCount(0)
+ , mA0(0), mA1(0), mA2(0), mA3(0)
+ , mSpy(1), mSpyPtr(nullptr)
+ {}
+private:
+ virtual ~ThreadUtilsObject() = default;
+public:
+ void Test0() { mCount += 1; }
+ void Test1i(int a0) { mCount += 2; mA0 = a0; }
+ void Test2i(int a0, int a1) { mCount += 3; mA0 = a0; mA1 = a1; }
+ void Test3i(int a0, int a1, int a2)
+ {
+ mCount += 4; mA0 = a0; mA1 = a1; mA2 = a2;
+ }
+ void Test4i(int a0, int a1, int a2, int a3)
+ {
+ mCount += 5; mA0 = a0; mA1 = a1; mA2 = a2; mA3 = a3;
+ }
+ void Test1pi(int* ap)
+ {
+ mCount += 2; mA0 = ap ? *ap : -1;
+ }
+ void Test1pci(const int* ap)
+ {
+ mCount += 2; mA0 = ap ? *ap : -1;
+ }
+ void Test1ri(int& ar)
+ {
+ mCount += 2; mA0 = ar;
+ }
+ void Test1rri(int&& arr)
+ {
+ mCount += 2; mA0 = arr;
+ }
+ void Test1upi(mozilla::UniquePtr<int> aup)
+ {
+ mCount += 2; mA0 = aup ? *aup : -1;
+ }
+ void Test1rupi(mozilla::UniquePtr<int>& aup)
+ {
+ mCount += 2; mA0 = aup ? *aup : -1;
+ }
+ void Test1rrupi(mozilla::UniquePtr<int>&& aup)
+ {
+ mCount += 2; mA0 = aup ? *aup : -1;
+ }
+
+ void Test1s(Spy) { mCount += 2; }
+ void Test1ps(Spy*) { mCount += 2; }
+ void Test1rs(Spy&) { mCount += 2; }
+ void Test1rrs(Spy&&) { mCount += 2; }
+ void Test1ups(mozilla::UniquePtr<Spy>) { mCount += 2; }
+ void Test1rups(mozilla::UniquePtr<Spy>&) { mCount += 2; }
+ void Test1rrups(mozilla::UniquePtr<Spy>&&) { mCount += 2; }
+
+ // Possible parameter passing styles:
+ void TestByValue(Spy s)
+ {
+ if (gDebug) { printf("TestByValue(Spy[%d@%p])\n", s.mID, &s); }
+ mSpy = s;
+ };
+ void TestByConstLRef(const Spy& s)
+ {
+ if (gDebug) { printf("TestByConstLRef(Spy[%d@%p]&)\n", s.mID, &s); }
+ mSpy = s;
+ };
+ void TestByRRef(Spy&& s)
+ {
+ if (gDebug) { printf("TestByRRef(Spy[%d@%p]&&)\n", s.mID, &s); }
+ mSpy = mozilla::Move(s);
+ };
+ void TestByLRef(Spy& s)
+ {
+ if (gDebug) { printf("TestByLRef(Spy[%d@%p]&)\n", s.mID, &s); }
+ mSpy = s;
+ mSpyPtr = &s;
+ };
+ void TestByPointer(Spy* p)
+ {
+ if (p) {
+ if (gDebug) { printf("TestByPointer(&Spy[%d@%p])\n", p->mID, p); }
+ mSpy = *p;
+ } else {
+ if (gDebug) { printf("TestByPointer(nullptr)\n"); }
+ }
+ mSpyPtr = p;
+ };
+ void TestByPointerToConst(const Spy* p)
+ {
+ if (p) {
+ if (gDebug) { printf("TestByPointerToConst(&Spy[%d@%p])\n", p->mID, p); }
+ mSpy = *p;
+ } else {
+ if (gDebug) { printf("TestByPointerToConst(nullptr)\n"); }
+ }
+ mSpyPtr = p;
+ };
+};
+
+NS_IMPL_ISUPPORTS(ThreadUtilsObject, IThreadUtilsObject)
+
+class ThreadUtilsRefCountedFinal final
+{
+public:
+ ThreadUtilsRefCountedFinal() : m_refCount(0) {}
+ ~ThreadUtilsRefCountedFinal() {}
+ // 'AddRef' and 'Release' methods with different return types, to verify
+ // that the return type doesn't influence storage selection.
+ long AddRef(void) { return ++m_refCount; }
+ void Release(void) { --m_refCount; }
+private:
+ long m_refCount;
+};
+
+class ThreadUtilsRefCountedBase
+{
+public:
+ ThreadUtilsRefCountedBase() : m_refCount(0) {}
+ virtual ~ThreadUtilsRefCountedBase() {}
+ // 'AddRef' and 'Release' methods with different return types, to verify
+ // that the return type doesn't influence storage selection.
+ virtual void AddRef(void) { ++m_refCount; }
+ virtual MozExternalRefCountType Release(void) { return --m_refCount; }
+private:
+ MozExternalRefCountType m_refCount;
+};
+
+class ThreadUtilsRefCountedDerived
+ : public ThreadUtilsRefCountedBase
+{};
+
+class ThreadUtilsNonRefCounted
+{};
+
+} // namespace TestThreadUtils
+
+TEST(ThreadUtils, main)
+{
+#ifndef XPCOM_GLUE_AVOID_NSPR
+ using namespace TestThreadUtils;
+
+ static_assert(!IsParameterStorageClass<int>::value,
+ "'int' should not be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StoreCopyPassByValue<int>>::value,
+ "StoreCopyPassByValue<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StoreCopyPassByConstLRef<int>>::value,
+ "StoreCopyPassByConstLRef<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StoreCopyPassByLRef<int>>::value,
+ "StoreCopyPassByLRef<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StoreCopyPassByRRef<int>>::value,
+ "StoreCopyPassByRRef<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StoreRefPassByLRef<int>>::value,
+ "StoreRefPassByLRef<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StoreConstRefPassByConstLRef<int>>::value,
+ "StoreConstRefPassByConstLRef<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StorensRefPtrPassByPtr<int>>::value,
+ "StorensRefPtrPassByPtr<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StorePtrPassByPtr<int>>::value,
+ "StorePtrPassByPtr<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StoreConstPtrPassByConstPtr<int>>::value,
+ "StoreConstPtrPassByConstPtr<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StoreCopyPassByConstPtr<int>>::value,
+ "StoreCopyPassByConstPtr<int> should be recognized as Storage Class");
+ static_assert(IsParameterStorageClass<StoreCopyPassByPtr<int>>::value,
+ "StoreCopyPassByPtr<int> should be recognized as Storage Class");
+
+ RefPtr<ThreadUtilsObject> rpt(new ThreadUtilsObject);
+ int count = 0;
+
+ // Test legacy functions.
+
+ nsCOMPtr<nsIRunnable> r1 =
+ NewRunnableMethod(rpt, &ThreadUtilsObject::Test0);
+ r1->Run();
+ EXPECT_EQ(count += 1, rpt->mCount);
+
+ r1 = NewRunnableMethod<int>(rpt, &ThreadUtilsObject::Test1i, 11);
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(11, rpt->mA0);
+
+ // Test variadic function with simple POD arguments.
+
+ r1 = NewRunnableMethod(rpt, &ThreadUtilsObject::Test0);
+ r1->Run();
+ EXPECT_EQ(count += 1, rpt->mCount);
+
+ static_assert(
+ mozilla::IsSame< ::detail::ParameterStorage<int>::Type,
+ StoreCopyPassByValue<int>>::value,
+ "detail::ParameterStorage<int>::Type should be StoreCopyPassByValue<int>");
+ static_assert(
+ mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByValue<int>>::Type,
+ StoreCopyPassByValue<int>>::value,
+ "detail::ParameterStorage<StoreCopyPassByValue<int>>::Type should be StoreCopyPassByValue<int>");
+
+ r1 = NewRunnableMethod<int>(rpt, &ThreadUtilsObject::Test1i, 12);
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(12, rpt->mA0);
+
+ r1 = NewRunnableMethod<int, int>(
+ rpt, &ThreadUtilsObject::Test2i, 21, 22);
+ r1->Run();
+ EXPECT_EQ(count += 3, rpt->mCount);
+ EXPECT_EQ(21, rpt->mA0);
+ EXPECT_EQ(22, rpt->mA1);
+
+ r1 = NewRunnableMethod<int, int, int>(
+ rpt, &ThreadUtilsObject::Test3i, 31, 32, 33);
+ r1->Run();
+ EXPECT_EQ(count += 4, rpt->mCount);
+ EXPECT_EQ(31, rpt->mA0);
+ EXPECT_EQ(32, rpt->mA1);
+ EXPECT_EQ(33, rpt->mA2);
+
+ r1 = NewRunnableMethod<int, int, int, int>(
+ rpt, &ThreadUtilsObject::Test4i, 41, 42, 43, 44);
+ r1->Run();
+ EXPECT_EQ(count += 5, rpt->mCount);
+ EXPECT_EQ(41, rpt->mA0);
+ EXPECT_EQ(42, rpt->mA1);
+ EXPECT_EQ(43, rpt->mA2);
+ EXPECT_EQ(44, rpt->mA3);
+
+ // More interesting types of arguments.
+
+ // Passing a short to make sure forwarding works with an inexact type match.
+ short int si = 11;
+ r1 = NewRunnableMethod<int>(rpt, &ThreadUtilsObject::Test1i, si);
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(si, rpt->mA0);
+
+ // Raw pointer, possible cv-qualified.
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int*>::Type,
+ StorePtrPassByPtr<int>>::value,
+ "detail::ParameterStorage<int*>::Type should be StorePtrPassByPtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int* const>::Type,
+ StorePtrPassByPtr<int>>::value,
+ "detail::ParameterStorage<int* const>::Type should be StorePtrPassByPtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int* volatile>::Type,
+ StorePtrPassByPtr<int>>::value,
+ "detail::ParameterStorage<int* volatile>::Type should be StorePtrPassByPtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int* const volatile>::Type,
+ StorePtrPassByPtr<int>>::value,
+ "detail::ParameterStorage<int* const volatile>::Type should be StorePtrPassByPtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int*>::Type::stored_type,
+ int*>::value,
+ "detail::ParameterStorage<int*>::Type::stored_type should be int*");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int*>::Type::passed_type,
+ int*>::value,
+ "detail::ParameterStorage<int*>::Type::passed_type should be int*");
+ {
+ int i = 12;
+ r1 = NewRunnableMethod<int*>(rpt, &ThreadUtilsObject::Test1pi, &i);
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(i, rpt->mA0);
+ }
+
+ // Raw pointer to const.
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int*>::Type,
+ StoreConstPtrPassByConstPtr<int>>::value,
+ "detail::ParameterStorage<const int*>::Type should be StoreConstPtrPassByConstPtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int* const>::Type,
+ StoreConstPtrPassByConstPtr<int>>::value,
+ "detail::ParameterStorage<const int* const>::Type should be StoreConstPtrPassByConstPtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int* volatile>::Type,
+ StoreConstPtrPassByConstPtr<int>>::value,
+ "detail::ParameterStorage<const int* volatile>::Type should be StoreConstPtrPassByConstPtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int* const volatile>::Type,
+ StoreConstPtrPassByConstPtr<int>>::value,
+ "detail::ParameterStorage<const int* const volatile>::Type should be StoreConstPtrPassByConstPtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int*>::Type::stored_type,
+ const int*>::value,
+ "detail::ParameterStorage<const int*>::Type::stored_type should be const int*");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<const int*>::Type::passed_type,
+ const int*>::value,
+ "detail::ParameterStorage<const int*>::Type::passed_type should be const int*");
+ {
+ int i = 1201;
+ r1 = NewRunnableMethod<const int*>(rpt, &ThreadUtilsObject::Test1pci, &i);
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(i, rpt->mA0);
+ }
+
+ // Raw pointer to copy.
+ static_assert(mozilla::IsSame<StoreCopyPassByPtr<int>::stored_type,
+ int>::value,
+ "StoreCopyPassByPtr<int>::stored_type should be int");
+ static_assert(mozilla::IsSame<StoreCopyPassByPtr<int>::passed_type,
+ int*>::value,
+ "StoreCopyPassByPtr<int>::passed_type should be int*");
+ {
+ int i = 1202;
+ r1 = NewRunnableMethod<StoreCopyPassByPtr<int>>(
+ rpt, &ThreadUtilsObject::Test1pi, i);
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(i, rpt->mA0);
+ }
+
+ // Raw pointer to const copy.
+ static_assert(mozilla::IsSame<StoreCopyPassByConstPtr<int>::stored_type,
+ int>::value,
+ "StoreCopyPassByConstPtr<int>::stored_type should be int");
+ static_assert(mozilla::IsSame<StoreCopyPassByConstPtr<int>::passed_type,
+ const int*>::value,
+ "StoreCopyPassByConstPtr<int>::passed_type should be const int*");
+ {
+ int i = 1203;
+ r1 = NewRunnableMethod<StoreCopyPassByConstPtr<int>>(
+ rpt, &ThreadUtilsObject::Test1pci, i);
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(i, rpt->mA0);
+ }
+
+ // nsRefPtr to pointer.
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<StorensRefPtrPassByPtr<SpyWithISupports>>::Type,
+ StorensRefPtrPassByPtr<SpyWithISupports>>::value,
+ "ParameterStorage<StorensRefPtrPassByPtr<SpyWithISupports>>::Type should be StorensRefPtrPassByPtr<SpyWithISupports>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<SpyWithISupports*>::Type,
+ StorensRefPtrPassByPtr<SpyWithISupports>>::value,
+ "ParameterStorage<SpyWithISupports*>::Type should be StorensRefPtrPassByPtr<SpyWithISupports>");
+ static_assert(mozilla::IsSame<StorensRefPtrPassByPtr<SpyWithISupports>::stored_type,
+ RefPtr<SpyWithISupports>>::value,
+ "StorensRefPtrPassByPtr<SpyWithISupports>::stored_type should be RefPtr<SpyWithISupports>");
+ static_assert(mozilla::IsSame<StorensRefPtrPassByPtr<SpyWithISupports>::passed_type,
+ SpyWithISupports*>::value,
+ "StorensRefPtrPassByPtr<SpyWithISupports>::passed_type should be SpyWithISupports*");
+ // (more nsRefPtr tests below)
+
+ // nsRefPtr for ref-countable classes that do not derive from ISupports.
+ static_assert(::detail::HasRefCountMethods<ThreadUtilsRefCountedFinal>::value,
+ "ThreadUtilsRefCountedFinal has AddRef() and Release()");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<ThreadUtilsRefCountedFinal*>::Type,
+ StorensRefPtrPassByPtr<ThreadUtilsRefCountedFinal>>::value,
+ "ParameterStorage<ThreadUtilsRefCountedFinal*>::Type should be StorensRefPtrPassByPtr<ThreadUtilsRefCountedFinal>");
+ static_assert(::detail::HasRefCountMethods<ThreadUtilsRefCountedBase>::value,
+ "ThreadUtilsRefCountedBase has AddRef() and Release()");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<ThreadUtilsRefCountedBase*>::Type,
+ StorensRefPtrPassByPtr<ThreadUtilsRefCountedBase>>::value,
+ "ParameterStorage<ThreadUtilsRefCountedBase*>::Type should be StorensRefPtrPassByPtr<ThreadUtilsRefCountedBase>");
+ static_assert(::detail::HasRefCountMethods<ThreadUtilsRefCountedDerived>::value,
+ "ThreadUtilsRefCountedDerived has AddRef() and Release()");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<ThreadUtilsRefCountedDerived*>::Type,
+ StorensRefPtrPassByPtr<ThreadUtilsRefCountedDerived>>::value,
+ "ParameterStorage<ThreadUtilsRefCountedDerived*>::Type should be StorensRefPtrPassByPtr<ThreadUtilsRefCountedDerived>");
+
+ static_assert(!::detail::HasRefCountMethods<ThreadUtilsNonRefCounted>::value,
+ "ThreadUtilsNonRefCounted doesn't have AddRef() and Release()");
+ static_assert(!mozilla::IsSame< ::detail::ParameterStorage<ThreadUtilsNonRefCounted*>::Type,
+ StorensRefPtrPassByPtr<ThreadUtilsNonRefCounted>>::value,
+ "ParameterStorage<ThreadUtilsNonRefCounted*>::Type should NOT be StorensRefPtrPassByPtr<ThreadUtilsNonRefCounted>");
+
+ // Lvalue reference.
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type,
+ StoreRefPassByLRef<int>>::value,
+ "ParameterStorage<int&>::Type should be StoreRefPassByLRef<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type::stored_type,
+ StoreRefPassByLRef<int>::stored_type>::value,
+ "ParameterStorage<int&>::Type::stored_type should be StoreRefPassByLRef<int>::stored_type");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type::stored_type,
+ int&>::value,
+ "ParameterStorage<int&>::Type::stored_type should be int&");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&>::Type::passed_type,
+ int&>::value,
+ "ParameterStorage<int&>::Type::passed_type should be int&");
+ {
+ int i = 13;
+ r1 = NewRunnableMethod<int&>(rpt, &ThreadUtilsObject::Test1ri, i);
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(i, rpt->mA0);
+ }
+
+ // Rvalue reference -- Actually storing a copy and then moving it.
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type,
+ StoreCopyPassByRRef<int>>::value,
+ "ParameterStorage<int&&>::Type should be StoreCopyPassByRRef<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type::stored_type,
+ StoreCopyPassByRRef<int>::stored_type>::value,
+ "ParameterStorage<int&&>::Type::stored_type should be StoreCopyPassByRRef<int>::stored_type");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type::stored_type,
+ int>::value,
+ "ParameterStorage<int&&>::Type::stored_type should be int");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<int&&>::Type::passed_type,
+ int&&>::value,
+ "ParameterStorage<int&&>::Type::passed_type should be int&&");
+ {
+ int i = 14;
+ r1 = NewRunnableMethod<int&&>(
+ rpt, &ThreadUtilsObject::Test1rri, mozilla::Move(i));
+ }
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(14, rpt->mA0);
+
+ // Null unique pointer, by semi-implicit store&move with "T&&" syntax.
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type,
+ StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::value,
+ "ParameterStorage<UniquePtr<int>&&>::Type should be StoreCopyPassByRRef<UniquePtr<int>>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type::stored_type,
+ StoreCopyPassByRRef<mozilla::UniquePtr<int>>::stored_type>::value,
+ "ParameterStorage<UniquePtr<int>&&>::Type::stored_type should be StoreCopyPassByRRef<UniquePtr<int>>::stored_type");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type::stored_type,
+ mozilla::UniquePtr<int>>::value,
+ "ParameterStorage<UniquePtr<int>&&>::Type::stored_type should be UniquePtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<mozilla::UniquePtr<int>&&>::Type::passed_type,
+ mozilla::UniquePtr<int>&&>::value,
+ "ParameterStorage<UniquePtr<int>&&>::Type::passed_type should be UniquePtr<int>&&");
+ {
+ mozilla::UniquePtr<int> upi;
+ r1 = NewRunnableMethod<mozilla::UniquePtr<int>&&>(
+ rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi));
+ }
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(-1, rpt->mA0);
+ rpt->mA0 = 0;
+
+ // Null unique pointer, by explicit store&move with "StoreCopyPassByRRef<T>" syntax.
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::stored_type,
+ StoreCopyPassByRRef<mozilla::UniquePtr<int>>::stored_type>::value,
+ "ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::stored_type should be StoreCopyPassByRRef<UniquePtr<int>>::stored_type");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::stored_type,
+ StoreCopyPassByRRef<mozilla::UniquePtr<int>>::stored_type>::value,
+ "ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::stored_type should be StoreCopyPassByRRef<UniquePtr<int>>::stored_type");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::stored_type,
+ mozilla::UniquePtr<int>>::value,
+ "ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::stored_type should be UniquePtr<int>");
+ static_assert(mozilla::IsSame< ::detail::ParameterStorage<StoreCopyPassByRRef<mozilla::UniquePtr<int>>>::Type::passed_type,
+ mozilla::UniquePtr<int>&&>::value,
+ "ParameterStorage<StoreCopyPassByRRef<UniquePtr<int>>>::Type::passed_type should be UniquePtr<int>&&");
+ {
+ mozilla::UniquePtr<int> upi;
+ r1 = NewRunnableMethod
+ <StoreCopyPassByRRef<mozilla::UniquePtr<int>>>(
+ rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi));
+ }
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(-1, rpt->mA0);
+
+ // Unique pointer as xvalue.
+ {
+ mozilla::UniquePtr<int> upi = mozilla::MakeUnique<int>(1);
+ r1 = NewRunnableMethod<mozilla::UniquePtr<int>&&>(
+ rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi));
+ }
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(1, rpt->mA0);
+
+ {
+ mozilla::UniquePtr<int> upi = mozilla::MakeUnique<int>(1);
+ r1 = NewRunnableMethod
+ <StoreCopyPassByRRef<mozilla::UniquePtr<int>>>
+ (rpt, &ThreadUtilsObject::Test1upi, mozilla::Move(upi));
+ }
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(1, rpt->mA0);
+
+ // Unique pointer as prvalue.
+ r1 = NewRunnableMethod<mozilla::UniquePtr<int>&&>(
+ rpt, &ThreadUtilsObject::Test1upi, mozilla::MakeUnique<int>(2));
+ r1->Run();
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(2, rpt->mA0);
+
+ // Unique pointer as lvalue to lref.
+ {
+ mozilla::UniquePtr<int> upi;
+ r1 = NewRunnableMethod<mozilla::UniquePtr<int>&>(
+ rpt, &ThreadUtilsObject::Test1rupi, upi);
+ // Passed as lref, so Run() must be called while local upi is still alive!
+ r1->Run();
+ }
+ EXPECT_EQ(count += 2, rpt->mCount);
+ EXPECT_EQ(-1, rpt->mA0);
+
+ // Verify copy/move assumptions.
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store copy from lvalue, pass by value\n", __LINE__); }
+ { // Block around nsCOMPtr lifetime.
+ nsCOMPtr<nsIRunnable> r2;
+ { // Block around Spy lifetime.
+ if (gDebug) { printf("%d - Spy s(10)\n", __LINE__); }
+ Spy s(10);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_EQ(1, gAlive);
+ if (gDebug) { printf("%d - r2 = NewRunnableMethod<StoreCopyPassByValue<Spy>>(&TestByValue, s)\n", __LINE__); }
+ r2 = NewRunnableMethod<StoreCopyPassByValue<Spy>>(
+ rpt, &ThreadUtilsObject::TestByValue, s);
+ EXPECT_EQ(2, gAlive);
+ EXPECT_LE(1, gCopyConstructions); // At least 1 copy-construction.
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with Spy s(10)\n", __LINE__); }
+ }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r2->Run();
+ EXPECT_LE(1, gCopyConstructions); // Another copy-construction in call.
+ EXPECT_EQ(10, rpt->mSpy.mID);
+ EXPECT_LE(1, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store copy from prvalue, pass by value\n", __LINE__); }
+ {
+ if (gDebug) { printf("%d - r3 = NewRunnableMethod<StoreCopyPassByValue<Spy>>(&TestByValue, Spy(11))\n", __LINE__); }
+ nsCOMPtr<nsIRunnable> r3 =
+ NewRunnableMethod<StoreCopyPassByValue<Spy>>(
+ rpt, &ThreadUtilsObject::TestByValue, Spy(11));
+ EXPECT_EQ(1, gAlive);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_LE(1, gMoveConstructions);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r3->Run();
+ EXPECT_LE(1, gCopyConstructions); // Another copy-construction in call.
+ EXPECT_EQ(11, rpt->mSpy.mID);
+ EXPECT_LE(1, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+
+ Spy::ClearAll();
+ { // Store copy from xvalue, pass by value.
+ nsCOMPtr<nsIRunnable> r4;
+ {
+ Spy s(12);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ r4 = NewRunnableMethod<StoreCopyPassByValue<Spy>>(
+ rpt, &ThreadUtilsObject::TestByValue, mozilla::Move(s));
+ EXPECT_LE(1, gMoveConstructions);
+ EXPECT_EQ(1, gAlive);
+ EXPECT_EQ(1, gZombies);
+ Spy::ClearActions();
+ }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ EXPECT_EQ(0, gZombies);
+ Spy::ClearActions();
+ r4->Run();
+ EXPECT_LE(1, gCopyConstructions); // Another copy-construction in call.
+ EXPECT_EQ(12, rpt->mSpy.mID);
+ EXPECT_LE(1, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+ // Won't test xvalues anymore, prvalues are enough to verify all rvalues.
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store copy from lvalue, pass by const lvalue ref\n", __LINE__); }
+ { // Block around nsCOMPtr lifetime.
+ nsCOMPtr<nsIRunnable> r5;
+ { // Block around Spy lifetime.
+ if (gDebug) { printf("%d - Spy s(20)\n", __LINE__); }
+ Spy s(20);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_EQ(1, gAlive);
+ if (gDebug) { printf("%d - r5 = NewRunnableMethod<StoreCopyPassByConstLRef<Spy>>(&TestByConstLRef, s)\n", __LINE__); }
+ r5 = NewRunnableMethod<StoreCopyPassByConstLRef<Spy>>(
+ rpt, &ThreadUtilsObject::TestByConstLRef, s);
+ EXPECT_EQ(2, gAlive);
+ EXPECT_LE(1, gCopyConstructions); // At least 1 copy-construction.
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with Spy s(20)\n", __LINE__); }
+ }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r5->Run();
+ EXPECT_EQ(0, gCopyConstructions); // No copies in call.
+ EXPECT_EQ(20, rpt->mSpy.mID);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store copy from prvalue, pass by const lvalue ref\n", __LINE__); }
+ {
+ if (gDebug) { printf("%d - r6 = NewRunnableMethod<StoreCopyPassByConstLRef<Spy>>(&TestByConstLRef, Spy(21))\n", __LINE__); }
+ nsCOMPtr<nsIRunnable> r6 =
+ NewRunnableMethod<StoreCopyPassByConstLRef<Spy>>(
+ rpt, &ThreadUtilsObject::TestByConstLRef, Spy(21));
+ EXPECT_EQ(1, gAlive);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_LE(1, gMoveConstructions);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r6->Run();
+ EXPECT_EQ(0, gCopyConstructions); // No copies in call.
+ EXPECT_EQ(21, rpt->mSpy.mID);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store copy from lvalue, pass by rvalue ref\n", __LINE__); }
+ { // Block around nsCOMPtr lifetime.
+ nsCOMPtr<nsIRunnable> r7;
+ { // Block around Spy lifetime.
+ if (gDebug) { printf("%d - Spy s(30)\n", __LINE__); }
+ Spy s(30);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_EQ(1, gAlive);
+ if (gDebug) { printf("%d - r7 = NewRunnableMethod<StoreCopyPassByRRef<Spy>>(&TestByRRef, s)\n", __LINE__); }
+ r7 = NewRunnableMethod<StoreCopyPassByRRef<Spy>>(
+ rpt, &ThreadUtilsObject::TestByRRef, s);
+ EXPECT_EQ(2, gAlive);
+ EXPECT_LE(1, gCopyConstructions); // At least 1 copy-construction.
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with Spy s(30)\n", __LINE__); }
+ }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r7->Run();
+ EXPECT_LE(1, gMoves); // Move in call.
+ EXPECT_EQ(30, rpt->mSpy.mID);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(0, gAlive); // Spy inside Test is not counted.
+ EXPECT_EQ(1, gZombies); // Our local spy should now be a zombie.
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store copy from prvalue, pass by rvalue ref\n", __LINE__); }
+ {
+ if (gDebug) { printf("%d - r8 = NewRunnableMethod<StoreCopyPassByRRef<Spy>>(&TestByRRef, Spy(31))\n", __LINE__); }
+ nsCOMPtr<nsIRunnable> r8 =
+ NewRunnableMethod<StoreCopyPassByRRef<Spy>>(
+ rpt, &ThreadUtilsObject::TestByRRef, Spy(31));
+ EXPECT_EQ(1, gAlive);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_LE(1, gMoveConstructions);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r8->Run();
+ EXPECT_LE(1, gMoves); // Move in call.
+ EXPECT_EQ(31, rpt->mSpy.mID);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(0, gAlive); // Spy inside Test is not counted.
+ EXPECT_EQ(1, gZombies); // Our local spy should now be a zombie.
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store lvalue ref, pass lvalue ref\n", __LINE__); }
+ {
+ if (gDebug) { printf("%d - Spy s(40)\n", __LINE__); }
+ Spy s(40);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - r9 = NewRunnableMethod<Spy&>(&TestByLRef, s)\n", __LINE__); }
+ nsCOMPtr<nsIRunnable> r9 =
+ NewRunnableMethod<Spy&>(
+ rpt, &ThreadUtilsObject::TestByLRef, s);
+ EXPECT_EQ(0, gAllConstructions);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r9->Run();
+ EXPECT_LE(1, gAssignments); // Assignment from reference in call.
+ EXPECT_EQ(40, rpt->mSpy.mID);
+ EXPECT_EQ(&s, rpt->mSpyPtr);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive); // Spy inside Test is not counted.
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store nsRefPtr, pass by pointer\n", __LINE__); }
+ { // Block around nsCOMPtr lifetime.
+ nsCOMPtr<nsIRunnable> r10;
+ SpyWithISupports* ptr = 0;
+ { // Block around RefPtr<Spy> lifetime.
+ if (gDebug) { printf("%d - RefPtr<SpyWithISupports> s(new SpyWithISupports(45))\n", __LINE__); }
+ RefPtr<SpyWithISupports> s(new SpyWithISupports(45));
+ ptr = s.get();
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_EQ(1, gAlive);
+ if (gDebug) { printf("%d - r10 = NewRunnableMethod<StorensRefPtrPassByPtr<Spy>>(&TestByRRef, s.get())\n", __LINE__); }
+ r10 = NewRunnableMethod<StorensRefPtrPassByPtr<SpyWithISupports>>(
+ rpt, &ThreadUtilsObject::TestByPointer, s.get());
+ EXPECT_LE(0, gAllConstructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with RefPtr<Spy> s\n", __LINE__); }
+ }
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r10->Run();
+ EXPECT_LE(1, gAssignments); // Assignment from pointee in call.
+ EXPECT_EQ(45, rpt->mSpy.mID);
+ EXPECT_EQ(ptr, rpt->mSpyPtr);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive); // Spy inside Test is not counted.
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store pointer to lvalue, pass by pointer\n", __LINE__); }
+ {
+ if (gDebug) { printf("%d - Spy s(55)\n", __LINE__); }
+ Spy s(55);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - r11 = NewRunnableMethod<Spy*>(&TestByPointer, s)\n", __LINE__); }
+ nsCOMPtr<nsIRunnable> r11 =
+ NewRunnableMethod<Spy*>(
+ rpt, &ThreadUtilsObject::TestByPointer, &s);
+ EXPECT_EQ(0, gAllConstructions);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r11->Run();
+ EXPECT_LE(1, gAssignments); // Assignment from pointee in call.
+ EXPECT_EQ(55, rpt->mSpy.mID);
+ EXPECT_EQ(&s, rpt->mSpyPtr);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive); // Spy inside Test is not counted.
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+
+ Spy::ClearAll();
+ if (gDebug) { printf("%d - Test: Store pointer to const lvalue, pass by pointer\n", __LINE__); }
+ {
+ if (gDebug) { printf("%d - Spy s(60)\n", __LINE__); }
+ Spy s(60);
+ EXPECT_EQ(1, gConstructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - r12 = NewRunnableMethod<Spy*>(&TestByPointer, s)\n", __LINE__); }
+ nsCOMPtr<nsIRunnable> r12 =
+ NewRunnableMethod<const Spy*>(
+ rpt, &ThreadUtilsObject::TestByPointerToConst, &s);
+ EXPECT_EQ(0, gAllConstructions);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive);
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - Run()\n", __LINE__); }
+ r12->Run();
+ EXPECT_LE(1, gAssignments); // Assignment from pointee in call.
+ EXPECT_EQ(60, rpt->mSpy.mID);
+ EXPECT_EQ(&s, rpt->mSpyPtr);
+ EXPECT_EQ(0, gDestructions);
+ EXPECT_EQ(1, gAlive); // Spy inside Test is not counted.
+ Spy::ClearActions();
+ if (gDebug) { printf("%d - End block with r\n", __LINE__); }
+ }
+ if (gDebug) { printf("%d - After end block with r\n", __LINE__); }
+ EXPECT_EQ(1, gDestructions);
+ EXPECT_EQ(0, gAlive);
+#endif // XPCOM_GLUE_AVOID_NSPR
+}
diff --git a/xpcom/glue/tests/gtest/moz.build b/xpcom/glue/tests/gtest/moz.build
new file mode 100644
index 000000000..9f4d83a3e
--- /dev/null
+++ b/xpcom/glue/tests/gtest/moz.build
@@ -0,0 +1,22 @@
+# -*- 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/.
+
+UNIFIED_SOURCES += [
+ 'TestArray.cpp',
+ 'TestFileUtils.cpp',
+ 'TestGCPostBarriers.cpp',
+ 'TestNsDeque.cpp',
+ 'TestThreadUtils.cpp',
+]
+
+LOCAL_INCLUDES = [
+ '../..',
+]
+
+FINAL_LIBRARY = 'xul-gtest'
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']