summaryrefslogtreecommitdiff
path: root/xpcom/base
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/base')
-rw-r--r--xpcom/base/AvailableMemoryTracker.cpp447
-rw-r--r--xpcom/base/AvailableMemoryTracker.h30
-rw-r--r--xpcom/base/ClearOnShutdown.cpp45
-rw-r--r--xpcom/base/ClearOnShutdown.h124
-rw-r--r--xpcom/base/CodeAddressService.h198
-rw-r--r--xpcom/base/CountingAllocatorBase.h140
-rw-r--r--xpcom/base/CycleCollectedJSContext.cpp1717
-rw-r--r--xpcom/base/CycleCollectedJSContext.h494
-rw-r--r--xpcom/base/Debug.cpp23
-rw-r--r--xpcom/base/Debug.h21
-rw-r--r--xpcom/base/DebuggerOnGCRunnable.cpp47
-rw-r--r--xpcom/base/DebuggerOnGCRunnable.h35
-rw-r--r--xpcom/base/DeferredFinalize.cpp28
-rw-r--r--xpcom/base/DeferredFinalize.h33
-rw-r--r--xpcom/base/ErrorList.h1026
-rw-r--r--xpcom/base/ErrorNames.cpp84
-rw-r--r--xpcom/base/ErrorNames.h25
-rw-r--r--xpcom/base/HoldDropJSObjects.cpp68
-rw-r--r--xpcom/base/HoldDropJSObjects.h77
-rw-r--r--xpcom/base/JSObjectHolder.cpp9
-rw-r--r--xpcom/base/JSObjectHolder.h42
-rw-r--r--xpcom/base/LinuxUtils.cpp50
-rw-r--r--xpcom/base/LinuxUtils.h34
-rw-r--r--xpcom/base/LogModulePrefWatcher.cpp170
-rw-r--r--xpcom/base/LogModulePrefWatcher.h42
-rw-r--r--xpcom/base/Logging.cpp568
-rw-r--r--xpcom/base/Logging.h255
-rw-r--r--xpcom/base/MacHelpers.h18
-rw-r--r--xpcom/base/MacHelpers.mm41
-rw-r--r--xpcom/base/NSPRLogModulesParser.cpp54
-rw-r--r--xpcom/base/NSPRLogModulesParser.h22
-rw-r--r--xpcom/base/OwningNonNull.h198
-rw-r--r--xpcom/base/StaticMutex.h96
-rw-r--r--xpcom/base/StaticPtr.h270
-rw-r--r--xpcom/base/SystemMemoryReporter.cpp989
-rw-r--r--xpcom/base/SystemMemoryReporter.h29
-rw-r--r--xpcom/base/moz.build159
-rw-r--r--xpcom/base/nsAgg.h336
-rw-r--r--xpcom/base/nsAlgorithm.h75
-rw-r--r--xpcom/base/nsAllocator.h17
-rw-r--r--xpcom/base/nsAutoPtr.h454
-rw-r--r--xpcom/base/nsAutoRef.h672
-rw-r--r--xpcom/base/nsCom.h12
-rw-r--r--xpcom/base/nsConsoleMessage.cpp56
-rw-r--r--xpcom/base/nsConsoleMessage.h33
-rw-r--r--xpcom/base/nsConsoleService.cpp473
-rw-r--r--xpcom/base/nsConsoleService.h118
-rw-r--r--xpcom/base/nsCrashOnException.cpp44
-rw-r--r--xpcom/base/nsCrashOnException.h23
-rw-r--r--xpcom/base/nsCycleCollector.cpp4213
-rw-r--r--xpcom/base/nsCycleCollector.h72
-rw-r--r--xpcom/base/nsCycleCollectorTraceJSHelpers.cpp105
-rw-r--r--xpcom/base/nsDebugImpl.cpp607
-rw-r--r--xpcom/base/nsDebugImpl.h41
-rw-r--r--xpcom/base/nsDumpUtils.cpp513
-rw-r--r--xpcom/base/nsDumpUtils.h203
-rw-r--r--xpcom/base/nsError.h219
-rw-r--r--xpcom/base/nsErrorService.cpp50
-rw-r--r--xpcom/base/nsErrorService.h37
-rw-r--r--xpcom/base/nsGZFileWriter.cpp111
-rw-r--r--xpcom/base/nsGZFileWriter.h55
-rw-r--r--xpcom/base/nsIConsoleListener.idl18
-rw-r--r--xpcom/base/nsIConsoleMessage.idl43
-rw-r--r--xpcom/base/nsIConsoleService.idl56
-rw-r--r--xpcom/base/nsICycleCollectorListener.idl164
-rw-r--r--xpcom/base/nsIDebug2.idl82
-rw-r--r--xpcom/base/nsIErrorService.idl49
-rw-r--r--xpcom/base/nsIException.idl86
-rw-r--r--xpcom/base/nsIGZFileWriter.idl82
-rw-r--r--xpcom/base/nsIID.h10
-rw-r--r--xpcom/base/nsIInterfaceRequestor.idl36
-rw-r--r--xpcom/base/nsIMacUtils.idl32
-rw-r--r--xpcom/base/nsIMemory.idl78
-rw-r--r--xpcom/base/nsIMemoryInfoDumper.idl162
-rw-r--r--xpcom/base/nsIMemoryReporter.idl581
-rw-r--r--xpcom/base/nsIMessageLoop.idl36
-rw-r--r--xpcom/base/nsIMutable.idl22
-rw-r--r--xpcom/base/nsIProgrammingLanguage.idl25
-rw-r--r--xpcom/base/nsISecurityConsoleMessage.idl20
-rw-r--r--xpcom/base/nsISizeOf.h35
-rw-r--r--xpcom/base/nsIStatusReporter.idl90
-rw-r--r--xpcom/base/nsISupports.idl44
-rw-r--r--xpcom/base/nsISupportsBase.h85
-rw-r--r--xpcom/base/nsIUUIDGenerator.idl39
-rw-r--r--xpcom/base/nsIVersionComparator.idl49
-rw-r--r--xpcom/base/nsIWeakReference.idl74
-rw-r--r--xpcom/base/nsInterfaceRequestorAgg.cpp86
-rw-r--r--xpcom/base/nsInterfaceRequestorAgg.h38
-rw-r--r--xpcom/base/nsMacUtilsImpl.cpp147
-rw-r--r--xpcom/base/nsMacUtilsImpl.h42
-rw-r--r--xpcom/base/nsMemoryImpl.cpp184
-rw-r--r--xpcom/base/nsMemoryImpl.h55
-rw-r--r--xpcom/base/nsMemoryInfoDumper.cpp830
-rw-r--r--xpcom/base/nsMemoryInfoDumper.h46
-rw-r--r--xpcom/base/nsMemoryReporterManager.cpp2717
-rw-r--r--xpcom/base/nsMemoryReporterManager.h288
-rw-r--r--xpcom/base/nsMessageLoop.cpp172
-rw-r--r--xpcom/base/nsMessageLoop.h31
-rw-r--r--xpcom/base/nsObjCExceptions.h230
-rw-r--r--xpcom/base/nsQueryObject.h109
-rw-r--r--xpcom/base/nsSecurityConsoleMessage.cpp45
-rw-r--r--xpcom/base/nsSecurityConsoleMessage.h30
-rw-r--r--xpcom/base/nsSetDllDirectory.h45
-rw-r--r--xpcom/base/nsStatusReporterManager.cpp320
-rw-r--r--xpcom/base/nsStatusReporterManager.h41
-rw-r--r--xpcom/base/nsSystemInfo.cpp985
-rw-r--r--xpcom/base/nsSystemInfo.h66
-rw-r--r--xpcom/base/nsTraceRefcnt.cpp1319
-rw-r--r--xpcom/base/nsTraceRefcnt.h40
-rw-r--r--xpcom/base/nsUUIDGenerator.cpp177
-rw-r--r--xpcom/base/nsUUIDGenerator.h44
-rw-r--r--xpcom/base/nsVersionComparatorImpl.cpp22
-rw-r--r--xpcom/base/nsVersionComparatorImpl.h25
-rw-r--r--xpcom/base/nsWeakPtr.h15
-rw-r--r--xpcom/base/nsWindowsHelpers.h371
-rw-r--r--xpcom/base/nscore.h288
-rw-r--r--xpcom/base/nsrootidl.idl97
117 files changed, 27150 insertions, 0 deletions
diff --git a/xpcom/base/AvailableMemoryTracker.cpp b/xpcom/base/AvailableMemoryTracker.cpp
new file mode 100644
index 0000000000..6272d89cf9
--- /dev/null
+++ b/xpcom/base/AvailableMemoryTracker.cpp
@@ -0,0 +1,447 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/AvailableMemoryTracker.h"
+
+#if defined(XP_WIN)
+#include "prinrval.h"
+#include "prenv.h"
+#include "nsIMemoryReporter.h"
+#include "nsMemoryPressure.h"
+#endif
+
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIRunnable.h"
+#include "nsISupports.h"
+#include "nsThreadUtils.h"
+
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+
+#if defined(XP_WIN)
+# include "nsWindowsDllInterceptor.h"
+# include <windows.h>
+#endif
+
+#if defined(MOZ_MEMORY)
+# include "mozmemory.h"
+#endif // MOZ_MEMORY
+
+using namespace mozilla;
+
+namespace {
+
+#if defined(_M_IX86) && defined(XP_WIN)
+
+
+uint32_t sLowVirtualMemoryThreshold = 0;
+uint32_t sLowCommitSpaceThreshold = 0;
+uint32_t sLowPhysicalMemoryThreshold = 0;
+uint32_t sLowMemoryNotificationIntervalMS = 0;
+
+Atomic<uint32_t> sNumLowVirtualMemEvents;
+Atomic<uint32_t> sNumLowCommitSpaceEvents;
+Atomic<uint32_t> sNumLowPhysicalMemEvents;
+
+WindowsDllInterceptor sKernel32Intercept;
+WindowsDllInterceptor sGdi32Intercept;
+
+// Has Init() been called?
+bool sInitialized = false;
+
+// Has Activate() been called? The hooks don't do anything until this happens.
+bool sHooksActive = false;
+
+// Alas, we'd like to use mozilla::TimeStamp, but we can't, because it acquires
+// a lock!
+volatile bool sHasScheduledOneLowMemoryNotification = false;
+volatile PRIntervalTime sLastLowMemoryNotificationTime;
+
+// These are function pointers to the functions we wrap in Init().
+
+void* (WINAPI* sVirtualAllocOrig)(LPVOID aAddress, SIZE_T aSize,
+ DWORD aAllocationType, DWORD aProtect);
+
+void* (WINAPI* sMapViewOfFileOrig)(HANDLE aFileMappingObject,
+ DWORD aDesiredAccess, DWORD aFileOffsetHigh,
+ DWORD aFileOffsetLow, SIZE_T aNumBytesToMap);
+
+HBITMAP(WINAPI* sCreateDIBSectionOrig)(HDC aDC, const BITMAPINFO* aBitmapInfo,
+ UINT aUsage, VOID** aBits,
+ HANDLE aSection, DWORD aOffset);
+
+/**
+ * Fire a memory pressure event if it's been long enough since the last one we
+ * fired.
+ */
+bool
+MaybeScheduleMemoryPressureEvent()
+{
+ // If this interval rolls over, we may fire an extra memory pressure
+ // event, but that's not a big deal.
+ PRIntervalTime interval = PR_IntervalNow() - sLastLowMemoryNotificationTime;
+ if (sHasScheduledOneLowMemoryNotification &&
+ PR_IntervalToMilliseconds(interval) < sLowMemoryNotificationIntervalMS) {
+
+ return false;
+ }
+
+ // There's a bit of a race condition here, since an interval may be a
+ // 64-bit number, and 64-bit writes aren't atomic on x86-32. But let's
+ // not worry about it -- the races only happen when we're already
+ // experiencing memory pressure and firing notifications, so the worst
+ // thing that can happen is that we fire two notifications when we
+ // should have fired only one.
+ sHasScheduledOneLowMemoryNotification = true;
+ sLastLowMemoryNotificationTime = PR_IntervalNow();
+
+ NS_DispatchEventualMemoryPressure(MemPressure_New);
+ return true;
+}
+
+void
+CheckMemAvailable()
+{
+ if (!sHooksActive) {
+ return;
+ }
+
+ MEMORYSTATUSEX stat;
+ stat.dwLength = sizeof(stat);
+ bool success = GlobalMemoryStatusEx(&stat);
+
+ if (success) {
+ // sLowVirtualMemoryThreshold is in MB, but ullAvailVirtual is in bytes.
+ if (stat.ullAvailVirtual < sLowVirtualMemoryThreshold * 1024 * 1024) {
+ // If we're running low on virtual memory, unconditionally schedule the
+ // notification. We'll probably crash if we run out of virtual memory,
+ // so don't worry about firing this notification too often.
+ ++sNumLowVirtualMemEvents;
+ NS_DispatchEventualMemoryPressure(MemPressure_New);
+ } else if (stat.ullAvailPageFile < sLowCommitSpaceThreshold * 1024 * 1024) {
+ if (MaybeScheduleMemoryPressureEvent()) {
+ ++sNumLowCommitSpaceEvents;
+ }
+ } else if (stat.ullAvailPhys < sLowPhysicalMemoryThreshold * 1024 * 1024) {
+ if (MaybeScheduleMemoryPressureEvent()) {
+ ++sNumLowPhysicalMemEvents;
+ }
+ }
+ }
+}
+
+LPVOID WINAPI
+VirtualAllocHook(LPVOID aAddress, SIZE_T aSize,
+ DWORD aAllocationType,
+ DWORD aProtect)
+{
+ // It's tempting to see whether we have enough free virtual address space for
+ // this allocation and, if we don't, synchronously fire a low-memory
+ // notification to free some before we allocate.
+ //
+ // Unfortunately that doesn't work, principally because code doesn't expect a
+ // call to malloc could trigger a GC (or call into the other routines which
+ // are triggered by a low-memory notification).
+ //
+ // I think the best we can do here is try to allocate the memory and check
+ // afterwards how much free virtual address space we have. If we're running
+ // low, we schedule a low-memory notification to run as soon as possible.
+
+ LPVOID result = sVirtualAllocOrig(aAddress, aSize, aAllocationType, aProtect);
+
+ // Don't call CheckMemAvailable for MEM_RESERVE if we're not tracking low
+ // virtual memory. Similarly, don't call CheckMemAvailable for MEM_COMMIT if
+ // we're not tracking low physical memory.
+ if ((sLowVirtualMemoryThreshold != 0 && aAllocationType & MEM_RESERVE) ||
+ (sLowPhysicalMemoryThreshold != 0 && aAllocationType & MEM_COMMIT)) {
+ CheckMemAvailable();
+ }
+
+ return result;
+}
+
+LPVOID WINAPI
+MapViewOfFileHook(HANDLE aFileMappingObject,
+ DWORD aDesiredAccess,
+ DWORD aFileOffsetHigh,
+ DWORD aFileOffsetLow,
+ SIZE_T aNumBytesToMap)
+{
+ LPVOID result = sMapViewOfFileOrig(aFileMappingObject, aDesiredAccess,
+ aFileOffsetHigh, aFileOffsetLow,
+ aNumBytesToMap);
+ CheckMemAvailable();
+ return result;
+}
+
+HBITMAP WINAPI
+CreateDIBSectionHook(HDC aDC,
+ const BITMAPINFO* aBitmapInfo,
+ UINT aUsage,
+ VOID** aBits,
+ HANDLE aSection,
+ DWORD aOffset)
+{
+ // There are a lot of calls to CreateDIBSection, so we make some effort not
+ // to CheckMemAvailable() for calls to CreateDIBSection which allocate only
+ // a small amount of memory.
+
+ // If aSection is non-null, CreateDIBSection won't allocate any new memory.
+ bool doCheck = false;
+ if (sHooksActive && !aSection && aBitmapInfo) {
+ uint16_t bitCount = aBitmapInfo->bmiHeader.biBitCount;
+ if (bitCount == 0) {
+ // MSDN says bitCount == 0 means that it figures out how many bits each
+ // pixel gets by examining the corresponding JPEG or PNG data. We'll just
+ // assume the worst.
+ bitCount = 32;
+ }
+
+ // |size| contains the expected allocation size in *bits*. Height may be
+ // negative (indicating the direction the DIB is drawn in), so we take the
+ // absolute value.
+ int64_t size = bitCount * aBitmapInfo->bmiHeader.biWidth *
+ aBitmapInfo->bmiHeader.biHeight;
+ if (size < 0) {
+ size *= -1;
+ }
+
+ // If we're allocating more than 1MB, check how much memory is left after
+ // the allocation.
+ if (size > 1024 * 1024 * 8) {
+ doCheck = true;
+ }
+ }
+
+ HBITMAP result = sCreateDIBSectionOrig(aDC, aBitmapInfo, aUsage, aBits,
+ aSection, aOffset);
+
+ if (doCheck) {
+ CheckMemAvailable();
+ }
+
+ return result;
+}
+
+static int64_t
+LowMemoryEventsVirtualDistinguishedAmount()
+{
+ return sNumLowVirtualMemEvents;
+}
+
+static int64_t
+LowMemoryEventsPhysicalDistinguishedAmount()
+{
+ return sNumLowPhysicalMemEvents;
+}
+
+class LowEventsReporter final : public nsIMemoryReporter
+{
+ ~LowEventsReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ MOZ_COLLECT_REPORT(
+ "low-memory-events/virtual", KIND_OTHER, UNITS_COUNT_CUMULATIVE,
+ LowMemoryEventsVirtualDistinguishedAmount(),
+"Number of low-virtual-memory events fired since startup. We fire such an "
+"event if we notice there is less than memory.low_virtual_mem_threshold_mb of "
+"virtual address space available (if zero, this behavior is disabled). The "
+"process will probably crash if it runs out of virtual address space, so "
+"this event is dire.");
+
+ MOZ_COLLECT_REPORT(
+ "low-memory-events/commit-space", KIND_OTHER, UNITS_COUNT_CUMULATIVE,
+ sNumLowCommitSpaceEvents,
+"Number of low-commit-space events fired since startup. We fire such an "
+"event if we notice there is less than memory.low_commit_space_threshold_mb of "
+"commit space available (if zero, this behavior is disabled). Windows will "
+"likely kill the process if it runs out of commit space, so this event is "
+"dire.");
+
+ MOZ_COLLECT_REPORT(
+ "low-memory-events/physical", KIND_OTHER, UNITS_COUNT_CUMULATIVE,
+ LowMemoryEventsPhysicalDistinguishedAmount(),
+"Number of low-physical-memory events fired since startup. We fire such an "
+"event if we notice there is less than memory.low_physical_memory_threshold_mb "
+"of physical memory available (if zero, this behavior is disabled). The "
+"machine will start to page if it runs out of physical memory. This may "
+"cause it to run slowly, but it shouldn't cause it to crash.");
+
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(LowEventsReporter, nsIMemoryReporter)
+
+#endif // defined(_M_IX86) && defined(XP_WIN)
+
+/**
+ * This runnable is executed in response to a memory-pressure event; we spin
+ * the event-loop when receiving the memory-pressure event in the hope that
+ * other observers will synchronously free some memory that we'll be able to
+ * purge here.
+ */
+class nsJemallocFreeDirtyPagesRunnable final : public nsIRunnable
+{
+ ~nsJemallocFreeDirtyPagesRunnable() {}
+
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIRUNNABLE
+};
+
+NS_IMPL_ISUPPORTS(nsJemallocFreeDirtyPagesRunnable, nsIRunnable)
+
+NS_IMETHODIMP
+nsJemallocFreeDirtyPagesRunnable::Run()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+#if defined(MOZ_MEMORY)
+ jemalloc_free_dirty_pages();
+#endif
+
+ return NS_OK;
+}
+
+/**
+ * The memory pressure watcher is used for listening to memory-pressure events
+ * and reacting upon them. We use one instance per process currently only for
+ * cleaning up dirty unused pages held by jemalloc.
+ */
+class nsMemoryPressureWatcher final : public nsIObserver
+{
+ ~nsMemoryPressureWatcher() {}
+
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ void Init();
+
+private:
+ static bool sFreeDirtyPages;
+};
+
+NS_IMPL_ISUPPORTS(nsMemoryPressureWatcher, nsIObserver)
+
+bool nsMemoryPressureWatcher::sFreeDirtyPages = false;
+
+/**
+ * Initialize and subscribe to the memory-pressure events. We subscribe to the
+ * observer service in this method and not in the constructor because we need
+ * to hold a strong reference to 'this' before calling the observer service.
+ */
+void
+nsMemoryPressureWatcher::Init()
+{
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+
+ if (os) {
+ os->AddObserver(this, "memory-pressure", /* ownsWeak */ false);
+ }
+
+ Preferences::AddBoolVarCache(&sFreeDirtyPages, "memory.free_dirty_pages",
+ false);
+}
+
+/**
+ * Reacts to all types of memory-pressure events, launches a runnable to
+ * free dirty pages held by jemalloc.
+ */
+NS_IMETHODIMP
+nsMemoryPressureWatcher::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ MOZ_ASSERT(!strcmp(aTopic, "memory-pressure"), "Unknown topic");
+
+ if (sFreeDirtyPages) {
+ nsCOMPtr<nsIRunnable> runnable = new nsJemallocFreeDirtyPagesRunnable();
+
+ NS_DispatchToMainThread(runnable);
+ }
+
+ return NS_OK;
+}
+
+} // namespace
+
+namespace mozilla {
+namespace AvailableMemoryTracker {
+
+void
+Activate()
+{
+#if defined(_M_IX86) && defined(XP_WIN)
+ MOZ_ASSERT(sInitialized);
+ MOZ_ASSERT(!sHooksActive);
+
+ Preferences::AddUintVarCache(&sLowVirtualMemoryThreshold,
+ "memory.low_virtual_mem_threshold_mb", 256);
+ Preferences::AddUintVarCache(&sLowPhysicalMemoryThreshold,
+ "memory.low_physical_memory_threshold_mb", 0);
+ Preferences::AddUintVarCache(&sLowCommitSpaceThreshold,
+ "memory.low_commit_space_threshold_mb", 256);
+ Preferences::AddUintVarCache(&sLowMemoryNotificationIntervalMS,
+ "memory.low_memory_notification_interval_ms",
+ 10000);
+
+ RegisterStrongMemoryReporter(new LowEventsReporter());
+ RegisterLowMemoryEventsVirtualDistinguishedAmount(
+ LowMemoryEventsVirtualDistinguishedAmount);
+ RegisterLowMemoryEventsPhysicalDistinguishedAmount(
+ LowMemoryEventsPhysicalDistinguishedAmount);
+ sHooksActive = true;
+#endif
+
+ // This object is held alive by the observer service.
+ RefPtr<nsMemoryPressureWatcher> watcher = new nsMemoryPressureWatcher();
+ watcher->Init();
+}
+
+void
+Init()
+{
+ // Do nothing on x86-64, because nsWindowsDllInterceptor is not thread-safe
+ // on 64-bit. (On 32-bit, it's probably thread-safe.) Even if we run Init()
+ // before any other of our threads are running, another process may have
+ // started a remote thread which could call VirtualAlloc!
+ //
+ // Moreover, the benefit of this code is less clear when we're a 64-bit
+ // process, because we aren't going to run out of virtual memory, and the
+ // system is likely to have a fair bit of physical memory.
+
+#if defined(_M_IX86) && defined(XP_WIN)
+ // Don't register the hooks if we're a build instrumented for PGO: If we're
+ // an instrumented build, the compiler adds function calls all over the place
+ // which may call VirtualAlloc; this makes it hard to prevent
+ // VirtualAllocHook from reentering itself.
+ if (!PR_GetEnv("MOZ_PGO_INSTRUMENTED")) {
+ sKernel32Intercept.Init("Kernel32.dll");
+ sKernel32Intercept.AddHook("VirtualAlloc",
+ reinterpret_cast<intptr_t>(VirtualAllocHook),
+ reinterpret_cast<void**>(&sVirtualAllocOrig));
+ sKernel32Intercept.AddHook("MapViewOfFile",
+ reinterpret_cast<intptr_t>(MapViewOfFileHook),
+ reinterpret_cast<void**>(&sMapViewOfFileOrig));
+
+ sGdi32Intercept.Init("Gdi32.dll");
+ sGdi32Intercept.AddHook("CreateDIBSection",
+ reinterpret_cast<intptr_t>(CreateDIBSectionHook),
+ reinterpret_cast<void**>(&sCreateDIBSectionOrig));
+ }
+
+ sInitialized = true;
+#endif
+}
+
+} // namespace AvailableMemoryTracker
+} // namespace mozilla
diff --git a/xpcom/base/AvailableMemoryTracker.h b/xpcom/base/AvailableMemoryTracker.h
new file mode 100644
index 0000000000..33572f9c71
--- /dev/null
+++ b/xpcom/base/AvailableMemoryTracker.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_AvailableMemoryTracker_h
+#define mozilla_AvailableMemoryTracker_h
+
+namespace mozilla {
+namespace AvailableMemoryTracker {
+
+// The AvailableMemoryTracker launches a memory pressure watcher on all
+// platforms to react to low-memory situations and on Windows it implements
+// the full functionality used to monitor how much memory is available.
+//
+// Init() must be called before any other threads have started, because it
+// modifies the in-memory implementations of some DLL functions in
+// non-thread-safe ways.
+//
+// The hooks don't do anything until Activate() is called. It's an error to
+// call Activate() without first calling Init().
+
+void Init();
+void Activate();
+
+} // namespace AvailableMemoryTracker
+} // namespace mozilla
+
+#endif // ifndef mozilla_AvailableMemoryTracker_h
diff --git a/xpcom/base/ClearOnShutdown.cpp b/xpcom/base/ClearOnShutdown.cpp
new file mode 100644
index 0000000000..bfb3142bc3
--- /dev/null
+++ b/xpcom/base/ClearOnShutdown.cpp
@@ -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/. */
+
+#include "mozilla/ClearOnShutdown.h"
+
+namespace mozilla {
+namespace ClearOnShutdown_Internal {
+
+Array<StaticAutoPtr<ShutdownList>,
+ static_cast<size_t>(ShutdownPhase::ShutdownPhase_Length)> sShutdownObservers;
+ShutdownPhase sCurrentShutdownPhase = ShutdownPhase::NotInShutdown;
+
+} // namespace ClearOnShutdown_Internal
+
+// Called when XPCOM is shutting down, after all shutdown notifications have
+// been sent and after all threads' event loops have been purged.
+void
+KillClearOnShutdown(ShutdownPhase aPhase)
+{
+ using namespace ClearOnShutdown_Internal;
+
+ MOZ_ASSERT(NS_IsMainThread());
+ // Shutdown only goes one direction...
+ MOZ_ASSERT(static_cast<size_t>(sCurrentShutdownPhase) < static_cast<size_t>(aPhase));
+
+ // It's impossible to add an entry for a "past" phase; this is blocked in
+ // ClearOnShutdown, but clear them out anyways in case there are phases
+ // that weren't passed to KillClearOnShutdown.
+ for (size_t phase = static_cast<size_t>(ShutdownPhase::First);
+ phase <= static_cast<size_t>(aPhase);
+ phase++) {
+ if (sShutdownObservers[static_cast<size_t>(phase)]) {
+ while (ShutdownObserver* observer = sShutdownObservers[static_cast<size_t>(phase)]->popFirst()) {
+ observer->Shutdown();
+ delete observer;
+ }
+ sShutdownObservers[static_cast<size_t>(phase)] = nullptr;
+ }
+ }
+}
+
+} // namespace mozilla
diff --git a/xpcom/base/ClearOnShutdown.h b/xpcom/base/ClearOnShutdown.h
new file mode 100644
index 0000000000..5c39c281cc
--- /dev/null
+++ b/xpcom/base/ClearOnShutdown.h
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ClearOnShutdown_h
+#define mozilla_ClearOnShutdown_h
+
+#include "mozilla/LinkedList.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Array.h"
+#include "MainThreadUtils.h"
+
+/*
+ * This header exports one public method in the mozilla namespace:
+ *
+ * template<class SmartPtr>
+ * void ClearOnShutdown(SmartPtr *aPtr, aPhase=ShutdownPhase::ShutdownFinal)
+ *
+ * This function takes a pointer to a smart pointer and nulls the smart pointer
+ * on shutdown (and a particular phase of shutdown as needed). If a phase
+ * is specified, the ptr will be cleared at the start of that phase. Also,
+ * if a phase has already occurred when ClearOnShutdown() is called it will
+ * cause a MOZ_ASSERT. In case a phase is not explicitly cleared we will
+ * clear it on the next phase that occurs.
+ *
+ * This is useful if you have a global smart pointer object which you don't
+ * want to "leak" on shutdown.
+ *
+ * Although ClearOnShutdown will work with any smart pointer (i.e., nsCOMPtr,
+ * nsRefPtr, nsAutoPtr, StaticRefPtr, and StaticAutoPtr), you probably want to
+ * use it only with StaticRefPtr and StaticAutoPtr. There is no way to undo a
+ * call to ClearOnShutdown, so you can call it only on smart pointers which you
+ * know will live until the program shuts down. In practice, these are likely
+ * global variables, which should be Static{Ref,Auto}Ptr.
+ *
+ * ClearOnShutdown is currently main-thread only because we don't want to
+ * accidentally free an object from a different thread than the one it was
+ * created on.
+ */
+
+namespace mozilla {
+
+// Must be contiguous starting at 0
+enum class ShutdownPhase {
+ NotInShutdown = 0,
+ WillShutdown,
+ Shutdown,
+ ShutdownThreads,
+ ShutdownLoaders,
+ ShutdownFinal,
+ ShutdownPhase_Length, // never pass this value
+ First = WillShutdown, // for iteration
+ Last = ShutdownFinal
+};
+
+namespace ClearOnShutdown_Internal {
+
+class ShutdownObserver : public LinkedListElement<ShutdownObserver>
+{
+public:
+ virtual void Shutdown() = 0;
+ virtual ~ShutdownObserver()
+ {
+ }
+};
+
+template<class SmartPtr>
+class PointerClearer : public ShutdownObserver
+{
+public:
+ explicit PointerClearer(SmartPtr* aPtr)
+ : mPtr(aPtr)
+ {
+ }
+
+ virtual void Shutdown() override
+ {
+ if (mPtr) {
+ *mPtr = nullptr;
+ }
+ }
+
+private:
+ SmartPtr* mPtr;
+};
+
+typedef LinkedList<ShutdownObserver> ShutdownList;
+extern Array<StaticAutoPtr<ShutdownList>,
+ static_cast<size_t>(ShutdownPhase::ShutdownPhase_Length)> sShutdownObservers;
+extern ShutdownPhase sCurrentShutdownPhase;
+
+} // namespace ClearOnShutdown_Internal
+
+template<class SmartPtr>
+inline void
+ClearOnShutdown(SmartPtr* aPtr, ShutdownPhase aPhase = ShutdownPhase::ShutdownFinal)
+{
+ using namespace ClearOnShutdown_Internal;
+
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aPhase != ShutdownPhase::ShutdownPhase_Length);
+
+ // Adding a ClearOnShutdown for a "past" phase is an error.
+ if (!(static_cast<size_t>(sCurrentShutdownPhase) < static_cast<size_t>(aPhase))) {
+ MOZ_ASSERT(false, "ClearOnShutdown for phase that already was cleared");
+ *aPtr = nullptr;
+ return;
+ }
+
+ if (!(sShutdownObservers[static_cast<size_t>(aPhase)])) {
+ sShutdownObservers[static_cast<size_t>(aPhase)] = new ShutdownList();
+ }
+ sShutdownObservers[static_cast<size_t>(aPhase)]->insertBack(new PointerClearer<SmartPtr>(aPtr));
+}
+
+// Called when XPCOM is shutting down, after all shutdown notifications have
+// been sent and after all threads' event loops have been purged.
+void KillClearOnShutdown(ShutdownPhase aPhase);
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/base/CodeAddressService.h b/xpcom/base/CodeAddressService.h
new file mode 100644
index 0000000000..7f91f93a66
--- /dev/null
+++ b/xpcom/base/CodeAddressService.h
@@ -0,0 +1,198 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.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 CodeAddressService_h__
+#define CodeAddressService_h__
+
+#include "mozilla/Assertions.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Types.h"
+
+#include "mozilla/StackWalk.h"
+
+namespace mozilla {
+
+// This class is used to print details about code locations.
+//
+// |StringTable| must implement an Intern() method that returns an interned
+// copy of the string that was passed in, as well as a standard
+// SizeOfExcludingThis() method.
+//
+// |StringAlloc| must implement |copy| and |free|. |copy| copies a string,
+// while |free| is used to free strings created by |copy|.
+//
+// |DescribeCodeAddressLock| is needed when the callers may be holding a lock
+// used by MozDescribeCodeAddress. |DescribeCodeAddressLock| must implement
+// static methods IsLocked(), Unlock() and Lock().
+template <class StringTable,
+ class StringAlloc,
+ class DescribeCodeAddressLock>
+class CodeAddressService
+{
+ // GetLocation() is the key function in this class. It's basically a wrapper
+ // around MozDescribeCodeAddress.
+ //
+ // However, MozDescribeCodeAddress is very slow on some platforms, and we
+ // have lots of repeated (i.e. same PC) calls to it. So we do some caching
+ // of results. Each cached result includes two strings (|mFunction| and
+ // |mLibrary|), so we also optimize them for space in the following ways.
+ //
+ // - The number of distinct library names is small, e.g. a few dozen. There
+ // is lots of repetition, especially of libxul. So we intern them in their
+ // own table, which saves space over duplicating them for each cache entry.
+ //
+ // - The number of distinct function names is much higher, so we duplicate
+ // them in each cache entry. That's more space-efficient than interning
+ // because entries containing single-occurrence function names are quickly
+ // overwritten, and their copies released. In addition, empty function
+ // names are common, so we use nullptr to represent them compactly.
+
+ StringTable mLibraryStrings;
+
+ struct Entry
+ {
+ const void* mPc;
+ char* mFunction; // owned by the Entry; may be null
+ const char* mLibrary; // owned by mLibraryStrings; never null
+ // in a non-empty entry is in use
+ ptrdiff_t mLOffset;
+ char* mFileName; // owned by the Entry; may be null
+ uint32_t mLineNo:31;
+ uint32_t mInUse:1; // is the entry used?
+
+ Entry()
+ : mPc(0), mFunction(nullptr), mLibrary(nullptr), mLOffset(0),
+ mFileName(nullptr), mLineNo(0), mInUse(0)
+ {}
+
+ ~Entry()
+ {
+ // We don't free mLibrary because it's externally owned.
+ StringAlloc::free(mFunction);
+ StringAlloc::free(mFileName);
+ }
+
+ void Replace(const void* aPc, const char* aFunction,
+ const char* aLibrary, ptrdiff_t aLOffset,
+ const char* aFileName, unsigned long aLineNo)
+ {
+ mPc = aPc;
+
+ // Convert "" to nullptr. Otherwise, make a copy of the name.
+ StringAlloc::free(mFunction);
+ mFunction = !aFunction[0] ? nullptr : StringAlloc::copy(aFunction);
+ StringAlloc::free(mFileName);
+ mFileName = !aFileName[0] ? nullptr : StringAlloc::copy(aFileName);
+
+ mLibrary = aLibrary;
+ mLOffset = aLOffset;
+ mLineNo = aLineNo;
+
+ mInUse = 1;
+ }
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ // Don't measure mLibrary because it's externally owned.
+ size_t n = 0;
+ n += aMallocSizeOf(mFunction);
+ n += aMallocSizeOf(mFileName);
+ return n;
+ }
+ };
+
+ // A direct-mapped cache. When doing dmd::Analyze() just after starting
+ // desktop Firefox (which is similar to analyzing after a longer-running
+ // session, thanks to the limit on how many records we print), a cache with
+ // 2^24 entries (which approximates an infinite-entry cache) has a ~91% hit
+ // rate. A cache with 2^12 entries has a ~83% hit rate, and takes up ~85 KiB
+ // (on 32-bit platforms) or ~150 KiB (on 64-bit platforms).
+ static const size_t kNumEntries = 1 << 12;
+ static const size_t kMask = kNumEntries - 1;
+ Entry mEntries[kNumEntries];
+
+ size_t mNumCacheHits;
+ size_t mNumCacheMisses;
+
+public:
+ CodeAddressService()
+ : mEntries(), mNumCacheHits(0), mNumCacheMisses(0)
+ {
+ }
+
+ void GetLocation(uint32_t aFrameNumber, const void* aPc, char* aBuf,
+ size_t aBufLen)
+ {
+ MOZ_ASSERT(DescribeCodeAddressLock::IsLocked());
+
+ uint32_t index = HashGeneric(aPc) & kMask;
+ MOZ_ASSERT(index < kNumEntries);
+ Entry& entry = mEntries[index];
+
+ if (!entry.mInUse || entry.mPc != aPc) {
+ mNumCacheMisses++;
+
+ // MozDescribeCodeAddress can (on Linux) acquire a lock inside
+ // the shared library loader. Another thread might call malloc
+ // while holding that lock (when loading a shared library). So
+ // we have to exit the lock around this call. For details, see
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=363334#c3
+ MozCodeAddressDetails details;
+ {
+ DescribeCodeAddressLock::Unlock();
+ (void)MozDescribeCodeAddress(const_cast<void*>(aPc), &details);
+ DescribeCodeAddressLock::Lock();
+ }
+
+ const char* library = mLibraryStrings.Intern(details.library);
+ entry.Replace(aPc, details.function, library, details.loffset,
+ details.filename, details.lineno);
+
+ } else {
+ mNumCacheHits++;
+ }
+
+ MOZ_ASSERT(entry.mPc == aPc);
+
+ MozFormatCodeAddress(aBuf, aBufLen, aFrameNumber, entry.mPc,
+ entry.mFunction, entry.mLibrary, entry.mLOffset,
+ entry.mFileName, entry.mLineNo);
+ }
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ size_t n = aMallocSizeOf(this);
+ for (uint32_t i = 0; i < kNumEntries; i++) {
+ n += mEntries[i].SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ n += mLibraryStrings.SizeOfExcludingThis(aMallocSizeOf);
+
+ return n;
+ }
+
+ size_t CacheCapacity() const { return kNumEntries; }
+
+ size_t CacheCount() const
+ {
+ size_t n = 0;
+ for (size_t i = 0; i < kNumEntries; i++) {
+ if (mEntries[i].mInUse) {
+ n++;
+ }
+ }
+ return n;
+ }
+
+ size_t NumCacheHits() const { return mNumCacheHits; }
+ size_t NumCacheMisses() const { return mNumCacheMisses; }
+};
+
+} // namespace mozilla
+
+#endif // CodeAddressService_h__
diff --git a/xpcom/base/CountingAllocatorBase.h b/xpcom/base/CountingAllocatorBase.h
new file mode 100644
index 0000000000..fb4d2ffe8e
--- /dev/null
+++ b/xpcom/base/CountingAllocatorBase.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 CountingAllocatorBase_h
+#define CountingAllocatorBase_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "nsIMemoryReporter.h"
+
+namespace mozilla {
+
+// This CRTP class handles several details of wrapping allocators and should
+// be preferred to manually counting with MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC
+// and MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE. The typical use is in a memory
+// reporter for a particular third party library:
+//
+// class MyMemoryReporter : public CountingAllocatorBase<MyMemoryReporter>
+// {
+// ...
+// NS_IMETHOD
+// CollectReports(nsIHandleReportCallback* aHandleReport,
+// nsISupports* aData, bool aAnonymize) override
+// {
+// MOZ_COLLECT_REPORT(
+// "explicit/path/to/somewhere", KIND_HEAP, UNITS_BYTES,
+// MemoryAllocated(),
+// "A description of what we are reporting.");
+//
+// return NS_OK;
+// }
+// };
+//
+// ...somewhere later in the code...
+// SetThirdPartyMemoryFunctions(MyMemoryReporter::CountingAlloc,
+// MyMemoryReporter::CountingFree);
+template<typename T>
+class CountingAllocatorBase
+{
+public:
+ CountingAllocatorBase()
+ {
+#ifdef DEBUG
+ // There must be only one instance of this class, due to |sAmount| being
+ // static.
+ static bool hasRun = false;
+ MOZ_ASSERT(!hasRun);
+ hasRun = true;
+#endif
+ }
+
+ static size_t
+ MemoryAllocated()
+ {
+ return sAmount;
+ }
+
+ static void*
+ CountingMalloc(size_t size)
+ {
+ void* p = malloc(size);
+ sAmount += MallocSizeOfOnAlloc(p);
+ return p;
+ }
+
+ static void*
+ CountingCalloc(size_t nmemb, size_t size)
+ {
+ void* p = calloc(nmemb, size);
+ sAmount += MallocSizeOfOnAlloc(p);
+ return p;
+ }
+
+ static void*
+ CountingRealloc(void* p, size_t size)
+ {
+ size_t oldsize = MallocSizeOfOnFree(p);
+ void *pnew = realloc(p, size);
+ if (pnew) {
+ size_t newsize = MallocSizeOfOnAlloc(pnew);
+ sAmount += newsize - oldsize;
+ } else if (size == 0) {
+ // We asked for a 0-sized (re)allocation of some existing pointer
+ // and received NULL in return. 0-sized allocations are permitted
+ // to either return NULL or to allocate a unique object per call (!).
+ // For a malloc implementation that chooses the second strategy,
+ // that allocation may fail (unlikely, but possible).
+ //
+ // Given a NULL return value and an allocation size of 0, then, we
+ // don't know if that means the original pointer was freed or if
+ // the allocation of the unique object failed. If the original
+ // pointer was freed, then we have nothing to do here. If the
+ // allocation of the unique object failed, the original pointer is
+ // still valid and we ought to undo the decrement from above.
+ // However, we have no way of knowing how the underlying realloc
+ // implementation is behaving. Assuming that the original pointer
+ // was freed is the safest course of action. We do, however, need
+ // to note that we freed memory.
+ sAmount -= oldsize;
+ } else {
+ // realloc failed. The amount allocated hasn't changed.
+ }
+ return pnew;
+ }
+
+ // Some library code expects that realloc(x, 0) will free x, which is not
+ // the behavior of the version of jemalloc we're using, so this wrapped
+ // version of realloc is needed.
+ static void*
+ CountingFreeingRealloc(void* p, size_t size)
+ {
+ if (size == 0) {
+ CountingFree(p);
+ return nullptr;
+ }
+ return CountingRealloc(p, size);
+ }
+
+ static void
+ CountingFree(void* p)
+ {
+ sAmount -= MallocSizeOfOnFree(p);
+ free(p);
+ }
+
+private:
+ // |sAmount| can be (implicitly) accessed by multiple threads, so it
+ // must be thread-safe.
+ static Atomic<size_t> sAmount;
+
+ MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc)
+ MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree)
+};
+
+} // namespace mozilla
+
+#endif // CountingAllocatorBase_h
diff --git a/xpcom/base/CycleCollectedJSContext.cpp b/xpcom/base/CycleCollectedJSContext.cpp
new file mode 100644
index 0000000000..87e123078b
--- /dev/null
+++ b/xpcom/base/CycleCollectedJSContext.cpp
@@ -0,0 +1,1717 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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're dividing JS objects into 3 categories:
+//
+// 1. "real" roots, held by the JS engine itself or rooted through the root
+// and lock JS APIs. Roots from this category are considered black in the
+// cycle collector, any cycle they participate in is uncollectable.
+//
+// 2. certain roots held by C++ objects that are guaranteed to be alive.
+// Roots from this category are considered black in the cycle collector,
+// and any cycle they participate in is uncollectable. These roots are
+// traced from TraceNativeBlackRoots.
+//
+// 3. all other roots held by C++ objects that participate in cycle
+// collection, held by us (see TraceNativeGrayRoots). Roots from this
+// category are considered grey in the cycle collector; whether or not
+// they are collected depends on the objects that hold them.
+//
+// Note that if a root is in multiple categories the fact that it is in
+// category 1 or 2 that takes precedence, so it will be considered black.
+//
+// During garbage collection we switch to an additional mark color (gray)
+// when tracing inside TraceNativeGrayRoots. This allows us to walk those
+// roots later on and add all objects reachable only from them to the
+// cycle collector.
+//
+// Phases:
+//
+// 1. marking of the roots in category 1 by having the JS GC do its marking
+// 2. marking of the roots in category 2 by having the JS GC call us back
+// (via JS_SetExtraGCRootsTracer) and running TraceNativeBlackRoots
+// 3. marking of the roots in category 3 by TraceNativeGrayRoots using an
+// additional color (gray).
+// 4. end of GC, GC can sweep its heap
+//
+// At some later point, when the cycle collector runs:
+//
+// 5. walk gray objects and add them to the cycle collector, cycle collect
+//
+// JS objects that are part of cycles the cycle collector breaks will be
+// collected by the next JS GC.
+//
+// If WantAllTraces() is false the cycle collector will not traverse roots
+// from category 1 or any JS objects held by them. Any JS objects they hold
+// will already be marked by the JS GC and will thus be colored black
+// themselves. Any C++ objects they hold will have a missing (untraversed)
+// edge from the JS object to the C++ object and so it will be marked black
+// too. This decreases the number of objects that the cycle collector has to
+// deal with.
+// To improve debugging, if WantAllTraces() is true all JS objects are
+// traversed.
+
+#include "mozilla/CycleCollectedJSContext.h"
+#include <algorithm>
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/Move.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/TimelineConsumers.h"
+#include "mozilla/TimelineMarker.h"
+#include "mozilla/Unused.h"
+#include "mozilla/DebuggerOnGCRunnable.h"
+#include "mozilla/dom/DOMJSClass.h"
+#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/PromiseBinding.h"
+#include "mozilla/dom/PromiseDebugging.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "jsprf.h"
+#include "js/Debug.h"
+#include "nsContentUtils.h"
+#include "nsCycleCollectionNoteRootCallback.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsCycleCollector.h"
+#include "nsDOMJSUtils.h"
+#include "nsJSUtils.h"
+#include "nsWrapperCache.h"
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+
+#include "nsIException.h"
+#include "nsIPlatformInfo.h"
+#include "nsThread.h"
+#include "nsThreadUtils.h"
+#include "xpcpublic.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+namespace mozilla {
+
+struct DeferredFinalizeFunctionHolder
+{
+ DeferredFinalizeFunction run;
+ void* data;
+};
+
+class IncrementalFinalizeRunnable : public Runnable
+{
+ typedef AutoTArray<DeferredFinalizeFunctionHolder, 16> DeferredFinalizeArray;
+ typedef CycleCollectedJSContext::DeferredFinalizerTable DeferredFinalizerTable;
+
+ CycleCollectedJSContext* mContext;
+ DeferredFinalizeArray mDeferredFinalizeFunctions;
+ uint32_t mFinalizeFunctionToRun;
+ bool mReleasing;
+
+ static const PRTime SliceMillis = 5; /* ms */
+
+public:
+ IncrementalFinalizeRunnable(CycleCollectedJSContext* aCx,
+ DeferredFinalizerTable& aFinalizerTable);
+ virtual ~IncrementalFinalizeRunnable();
+
+ void ReleaseNow(bool aLimited);
+
+ NS_DECL_NSIRUNNABLE
+};
+
+} // namespace mozilla
+
+struct NoteWeakMapChildrenTracer : public JS::CallbackTracer
+{
+ NoteWeakMapChildrenTracer(JSContext* aCx,
+ nsCycleCollectionNoteRootCallback& aCb)
+ : JS::CallbackTracer(aCx), mCb(aCb), mTracedAny(false), mMap(nullptr),
+ mKey(nullptr), mKeyDelegate(nullptr)
+ {
+ }
+ void onChild(const JS::GCCellPtr& aThing) override;
+ nsCycleCollectionNoteRootCallback& mCb;
+ bool mTracedAny;
+ JSObject* mMap;
+ JS::GCCellPtr mKey;
+ JSObject* mKeyDelegate;
+};
+
+void
+NoteWeakMapChildrenTracer::onChild(const JS::GCCellPtr& aThing)
+{
+ if (aThing.is<JSString>()) {
+ return;
+ }
+
+ if (!JS::GCThingIsMarkedGray(aThing) && !mCb.WantAllTraces()) {
+ return;
+ }
+
+ if (AddToCCKind(aThing.kind())) {
+ mCb.NoteWeakMapping(mMap, mKey, mKeyDelegate, aThing);
+ mTracedAny = true;
+ } else {
+ JS::TraceChildren(this, aThing);
+ }
+}
+
+struct NoteWeakMapsTracer : public js::WeakMapTracer
+{
+ NoteWeakMapsTracer(JSContext* aCx, nsCycleCollectionNoteRootCallback& aCccb)
+ : js::WeakMapTracer(aCx), mCb(aCccb), mChildTracer(aCx, aCccb)
+ {
+ }
+ void trace(JSObject* aMap, JS::GCCellPtr aKey, JS::GCCellPtr aValue) override;
+ nsCycleCollectionNoteRootCallback& mCb;
+ NoteWeakMapChildrenTracer mChildTracer;
+};
+
+void
+NoteWeakMapsTracer::trace(JSObject* aMap, JS::GCCellPtr aKey,
+ JS::GCCellPtr aValue)
+{
+ // If nothing that could be held alive by this entry is marked gray, return.
+ if ((!aKey || !JS::GCThingIsMarkedGray(aKey)) &&
+ MOZ_LIKELY(!mCb.WantAllTraces())) {
+ if (!aValue || !JS::GCThingIsMarkedGray(aValue) || aValue.is<JSString>()) {
+ return;
+ }
+ }
+
+ // The cycle collector can only properly reason about weak maps if it can
+ // reason about the liveness of their keys, which in turn requires that
+ // the key can be represented in the cycle collector graph. All existing
+ // uses of weak maps use either objects or scripts as keys, which are okay.
+ MOZ_ASSERT(AddToCCKind(aKey.kind()));
+
+ // As an emergency fallback for non-debug builds, if the key is not
+ // representable in the cycle collector graph, we treat it as marked. This
+ // can cause leaks, but is preferable to ignoring the binding, which could
+ // cause the cycle collector to free live objects.
+ if (!AddToCCKind(aKey.kind())) {
+ aKey = nullptr;
+ }
+
+ JSObject* kdelegate = nullptr;
+ if (aKey.is<JSObject>()) {
+ kdelegate = js::GetWeakmapKeyDelegate(&aKey.as<JSObject>());
+ }
+
+ if (AddToCCKind(aValue.kind())) {
+ mCb.NoteWeakMapping(aMap, aKey, kdelegate, aValue);
+ } else {
+ mChildTracer.mTracedAny = false;
+ mChildTracer.mMap = aMap;
+ mChildTracer.mKey = aKey;
+ mChildTracer.mKeyDelegate = kdelegate;
+
+ if (!aValue.is<JSString>()) {
+ JS::TraceChildren(&mChildTracer, aValue);
+ }
+
+ // The delegate could hold alive the key, so report something to the CC
+ // if we haven't already.
+ if (!mChildTracer.mTracedAny &&
+ aKey && JS::GCThingIsMarkedGray(aKey) && kdelegate) {
+ mCb.NoteWeakMapping(aMap, aKey, kdelegate, nullptr);
+ }
+ }
+}
+
+// This is based on the logic in FixWeakMappingGrayBitsTracer::trace.
+struct FixWeakMappingGrayBitsTracer : public js::WeakMapTracer
+{
+ explicit FixWeakMappingGrayBitsTracer(JSContext* aCx)
+ : js::WeakMapTracer(aCx)
+ {
+ }
+
+ void
+ FixAll()
+ {
+ do {
+ mAnyMarked = false;
+ js::TraceWeakMaps(this);
+ } while (mAnyMarked);
+ }
+
+ void trace(JSObject* aMap, JS::GCCellPtr aKey, JS::GCCellPtr aValue) override
+ {
+ // If nothing that could be held alive by this entry is marked gray, return.
+ bool keyMightNeedMarking = aKey && JS::GCThingIsMarkedGray(aKey);
+ bool valueMightNeedMarking = aValue && JS::GCThingIsMarkedGray(aValue) &&
+ aValue.kind() != JS::TraceKind::String;
+ if (!keyMightNeedMarking && !valueMightNeedMarking) {
+ return;
+ }
+
+ if (!AddToCCKind(aKey.kind())) {
+ aKey = nullptr;
+ }
+
+ if (keyMightNeedMarking && aKey.is<JSObject>()) {
+ JSObject* kdelegate = js::GetWeakmapKeyDelegate(&aKey.as<JSObject>());
+ if (kdelegate && !JS::ObjectIsMarkedGray(kdelegate) &&
+ (!aMap || !JS::ObjectIsMarkedGray(aMap)))
+ {
+ if (JS::UnmarkGrayGCThingRecursively(aKey)) {
+ mAnyMarked = true;
+ }
+ }
+ }
+
+ if (aValue && JS::GCThingIsMarkedGray(aValue) &&
+ (!aKey || !JS::GCThingIsMarkedGray(aKey)) &&
+ (!aMap || !JS::ObjectIsMarkedGray(aMap)) &&
+ aValue.kind() != JS::TraceKind::Shape) {
+ if (JS::UnmarkGrayGCThingRecursively(aValue)) {
+ mAnyMarked = true;
+ }
+ }
+ }
+
+ MOZ_INIT_OUTSIDE_CTOR bool mAnyMarked;
+};
+
+static void
+CheckParticipatesInCycleCollection(JS::GCCellPtr aThing, const char* aName,
+ void* aClosure)
+{
+ bool* cycleCollectionEnabled = static_cast<bool*>(aClosure);
+
+ if (*cycleCollectionEnabled) {
+ return;
+ }
+
+ if (AddToCCKind(aThing.kind()) && JS::GCThingIsMarkedGray(aThing)) {
+ *cycleCollectionEnabled = true;
+ }
+}
+
+NS_IMETHODIMP
+JSGCThingParticipant::Traverse(void* aPtr,
+ nsCycleCollectionTraversalCallback& aCb)
+{
+ auto runtime = reinterpret_cast<CycleCollectedJSContext*>(
+ reinterpret_cast<char*>(this) - offsetof(CycleCollectedJSContext,
+ mGCThingCycleCollectorGlobal));
+
+ JS::GCCellPtr cellPtr(aPtr, JS::GCThingTraceKind(aPtr));
+ runtime->TraverseGCThing(CycleCollectedJSContext::TRAVERSE_FULL, cellPtr, aCb);
+ return NS_OK;
+}
+
+// NB: This is only used to initialize the participant in
+// CycleCollectedJSContext. It should never be used directly.
+static JSGCThingParticipant sGCThingCycleCollectorGlobal;
+
+NS_IMETHODIMP
+JSZoneParticipant::Traverse(void* aPtr, nsCycleCollectionTraversalCallback& aCb)
+{
+ auto runtime = reinterpret_cast<CycleCollectedJSContext*>(
+ reinterpret_cast<char*>(this) - offsetof(CycleCollectedJSContext,
+ mJSZoneCycleCollectorGlobal));
+
+ MOZ_ASSERT(!aCb.WantAllTraces());
+ JS::Zone* zone = static_cast<JS::Zone*>(aPtr);
+
+ runtime->TraverseZone(zone, aCb);
+ return NS_OK;
+}
+
+struct TraversalTracer : public JS::CallbackTracer
+{
+ TraversalTracer(JSContext* aCx, nsCycleCollectionTraversalCallback& aCb)
+ : JS::CallbackTracer(aCx, DoNotTraceWeakMaps), mCb(aCb)
+ {
+ }
+ void onChild(const JS::GCCellPtr& aThing) override;
+ nsCycleCollectionTraversalCallback& mCb;
+};
+
+void
+TraversalTracer::onChild(const JS::GCCellPtr& aThing)
+{
+ // Don't traverse non-gray objects, unless we want all traces.
+ if (!JS::GCThingIsMarkedGray(aThing) && !mCb.WantAllTraces()) {
+ return;
+ }
+
+ /*
+ * This function needs to be careful to avoid stack overflow. Normally, when
+ * AddToCCKind is true, the recursion terminates immediately as we just add
+ * |thing| to the CC graph. So overflow is only possible when there are long
+ * or cyclic chains of non-AddToCCKind GC things. Places where this can occur
+ * use special APIs to handle such chains iteratively.
+ */
+ if (AddToCCKind(aThing.kind())) {
+ if (MOZ_UNLIKELY(mCb.WantDebugInfo())) {
+ char buffer[200];
+ getTracingEdgeName(buffer, sizeof(buffer));
+ mCb.NoteNextEdgeName(buffer);
+ }
+ mCb.NoteJSChild(aThing);
+ } else if (aThing.is<js::Shape>()) {
+ // The maximum depth of traversal when tracing a Shape is unbounded, due to
+ // the parent pointers on the shape.
+ JS_TraceShapeCycleCollectorChildren(this, aThing);
+ } else if (aThing.is<js::ObjectGroup>()) {
+ // The maximum depth of traversal when tracing an ObjectGroup is unbounded,
+ // due to information attached to the groups which can lead other groups to
+ // be traced.
+ JS_TraceObjectGroupCycleCollectorChildren(this, aThing);
+ } else if (!aThing.is<JSString>()) {
+ JS::TraceChildren(this, aThing);
+ }
+}
+
+static void
+NoteJSChildGrayWrapperShim(void* aData, JS::GCCellPtr aThing)
+{
+ TraversalTracer* trc = static_cast<TraversalTracer*>(aData);
+ trc->onChild(aThing);
+}
+
+/*
+ * The cycle collection participant for a Zone is intended to produce the same
+ * results as if all of the gray GCthings in a zone were merged into a single node,
+ * except for self-edges. This avoids the overhead of representing all of the GCthings in
+ * the zone in the cycle collector graph, which should be much faster if many of
+ * the GCthings in the zone are gray.
+ *
+ * Zone merging should not always be used, because it is a conservative
+ * approximation of the true cycle collector graph that can incorrectly identify some
+ * garbage objects as being live. For instance, consider two cycles that pass through a
+ * zone, where one is garbage and the other is live. If we merge the entire
+ * zone, the cycle collector will think that both are alive.
+ *
+ * We don't have to worry about losing track of a garbage cycle, because any such garbage
+ * cycle incorrectly identified as live must contain at least one C++ to JS edge, and
+ * XPConnect will always add the C++ object to the CC graph. (This is in contrast to pure
+ * C++ garbage cycles, which must always be properly identified, because we clear the
+ * purple buffer during every CC, which may contain the last reference to a garbage
+ * cycle.)
+ */
+
+// NB: This is only used to initialize the participant in
+// CycleCollectedJSContext. It should never be used directly.
+static const JSZoneParticipant sJSZoneCycleCollectorGlobal;
+
+static
+void JSObjectsTenuredCb(JSContext* aContext, void* aData)
+{
+ static_cast<CycleCollectedJSContext*>(aData)->JSObjectsTenured();
+}
+
+bool
+mozilla::GetBuildId(JS::BuildIdCharVector* aBuildID)
+{
+ nsCOMPtr<nsIPlatformInfo> info = do_GetService("@mozilla.org/xre/app-info;1");
+ if (!info) {
+ return false;
+ }
+
+ nsCString buildID;
+ nsresult rv = info->GetPlatformBuildID(buildID);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ if (!aBuildID->resize(buildID.Length())) {
+ return false;
+ }
+
+ for (size_t i = 0; i < buildID.Length(); i++) {
+ (*aBuildID)[i] = buildID[i];
+ }
+
+ return true;
+}
+
+CycleCollectedJSContext::CycleCollectedJSContext()
+ : mGCThingCycleCollectorGlobal(sGCThingCycleCollectorGlobal)
+ , mJSZoneCycleCollectorGlobal(sJSZoneCycleCollectorGlobal)
+ , mJSContext(nullptr)
+ , mPrevGCSliceCallback(nullptr)
+ , mPrevGCNurseryCollectionCallback(nullptr)
+ , mJSHolders(256)
+ , mDoingStableStates(false)
+ , mDisableMicroTaskCheckpoint(false)
+ , mOutOfMemoryState(OOMState::OK)
+ , mLargeAllocationFailureState(OOMState::OK)
+{
+ nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
+ mOwningThread = thread.forget().downcast<nsThread>().take();
+ MOZ_RELEASE_ASSERT(mOwningThread);
+}
+
+CycleCollectedJSContext::~CycleCollectedJSContext()
+{
+ // If the allocation failed, here we are.
+ if (!mJSContext) {
+ return;
+ }
+
+ MOZ_ASSERT(!mDeferredFinalizerTable.Count());
+
+ // Last chance to process any events.
+ ProcessMetastableStateQueue(mBaseRecursionDepth);
+ MOZ_ASSERT(mMetastableStateEvents.IsEmpty());
+
+ ProcessStableStateQueue();
+ MOZ_ASSERT(mStableStateEvents.IsEmpty());
+
+ // Clear mPendingException first, since it might be cycle collected.
+ mPendingException = nullptr;
+
+ MOZ_ASSERT(mDebuggerPromiseMicroTaskQueue.empty());
+ MOZ_ASSERT(mPromiseMicroTaskQueue.empty());
+
+#ifdef SPIDERMONKEY_PROMISE
+ mUncaughtRejections.reset();
+ mConsumedRejections.reset();
+#endif // SPIDERMONKEY_PROMISE
+
+ JS_DestroyContext(mJSContext);
+ mJSContext = nullptr;
+ nsCycleCollector_forgetJSContext();
+
+ mozilla::dom::DestroyScriptSettings();
+
+ mOwningThread->SetScriptObserver(nullptr);
+ NS_RELEASE(mOwningThread);
+}
+
+static void
+MozCrashWarningReporter(JSContext*, JSErrorReport*)
+{
+ MOZ_CRASH("Why is someone touching JSAPI without an AutoJSAPI?");
+}
+
+nsresult
+CycleCollectedJSContext::Initialize(JSContext* aParentContext,
+ uint32_t aMaxBytes,
+ uint32_t aMaxNurseryBytes)
+{
+ MOZ_ASSERT(!mJSContext);
+
+ mOwningThread->SetScriptObserver(this);
+ // The main thread has a base recursion depth of 0, workers of 1.
+ mBaseRecursionDepth = RecursionDepth();
+
+ mozilla::dom::InitScriptSettings();
+ mJSContext = JS_NewContext(aMaxBytes, aMaxNurseryBytes, aParentContext);
+ if (!mJSContext) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ NS_GetCurrentThread()->SetCanInvokeJS(true);
+
+ if (!JS_AddExtraGCRootsTracer(mJSContext, TraceBlackJS, this)) {
+ MOZ_CRASH("JS_AddExtraGCRootsTracer failed");
+ }
+ JS_SetGrayGCRootsTracer(mJSContext, TraceGrayJS, this);
+ JS_SetGCCallback(mJSContext, GCCallback, this);
+ mPrevGCSliceCallback = JS::SetGCSliceCallback(mJSContext, GCSliceCallback);
+
+ if (NS_IsMainThread()) {
+ // We would like to support all threads here, but the way timeline consumers
+ // are set up currently, you can either add a marker for one specific
+ // docshell, or for every consumer globally. We would like to add a marker
+ // for every consumer observing anything on this thread, but that is not
+ // currently possible. For now, add global markers only when we are on the
+ // main thread, since the UI for this tracing data only displays data
+ // relevant to the main-thread.
+ mPrevGCNurseryCollectionCallback = JS::SetGCNurseryCollectionCallback(
+ mJSContext, GCNurseryCollectionCallback);
+ }
+
+ JS_SetObjectsTenuredCallback(mJSContext, JSObjectsTenuredCb, this);
+ JS::SetOutOfMemoryCallback(mJSContext, OutOfMemoryCallback, this);
+ JS::SetLargeAllocationFailureCallback(mJSContext,
+ LargeAllocationFailureCallback, this);
+ JS_SetDestroyZoneCallback(mJSContext, XPCStringConvert::FreeZoneCache);
+ JS_SetSweepZoneCallback(mJSContext, XPCStringConvert::ClearZoneCache);
+ JS::SetBuildIdOp(mJSContext, GetBuildId);
+ JS::SetWarningReporter(mJSContext, MozCrashWarningReporter);
+#ifdef MOZ_CRASHREPORTER
+ js::AutoEnterOOMUnsafeRegion::setAnnotateOOMAllocationSizeCallback(
+ CrashReporter::AnnotateOOMAllocationSize);
+#endif
+
+ static js::DOMCallbacks DOMcallbacks = {
+ InstanceClassHasProtoAtDepth
+ };
+ SetDOMCallbacks(mJSContext, &DOMcallbacks);
+ js::SetScriptEnvironmentPreparer(mJSContext, &mEnvironmentPreparer);
+
+ JS::SetGetIncumbentGlobalCallback(mJSContext, GetIncumbentGlobalCallback);
+
+#ifdef SPIDERMONKEY_PROMISE
+ JS::SetEnqueuePromiseJobCallback(mJSContext, EnqueuePromiseJobCallback, this);
+ JS::SetPromiseRejectionTrackerCallback(mJSContext, PromiseRejectionTrackerCallback, this);
+ mUncaughtRejections.init(mJSContext, JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>(js::SystemAllocPolicy()));
+ mConsumedRejections.init(mJSContext, JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>(js::SystemAllocPolicy()));
+#endif // SPIDERMONKEY_PROMISE
+
+ JS::dbg::SetDebuggerMallocSizeOf(mJSContext, moz_malloc_size_of);
+
+ nsCycleCollector_registerJSContext(this);
+
+ return NS_OK;
+}
+
+size_t
+CycleCollectedJSContext::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ size_t n = 0;
+
+ // We're deliberately not measuring anything hanging off the entries in
+ // mJSHolders.
+ n += mJSHolders.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ return n;
+}
+
+void
+CycleCollectedJSContext::UnmarkSkippableJSHolders()
+{
+ for (auto iter = mJSHolders.Iter(); !iter.Done(); iter.Next()) {
+ void* holder = iter.Key();
+ nsScriptObjectTracer*& tracer = iter.Data();
+ tracer->CanSkip(holder, true);
+ }
+}
+
+void
+CycleCollectedJSContext::DescribeGCThing(bool aIsMarked, JS::GCCellPtr aThing,
+ nsCycleCollectionTraversalCallback& aCb) const
+{
+ if (!aCb.WantDebugInfo()) {
+ aCb.DescribeGCedNode(aIsMarked, "JS Object");
+ return;
+ }
+
+ char name[72];
+ uint64_t compartmentAddress = 0;
+ if (aThing.is<JSObject>()) {
+ JSObject* obj = &aThing.as<JSObject>();
+ compartmentAddress = (uint64_t)js::GetObjectCompartment(obj);
+ const js::Class* clasp = js::GetObjectClass(obj);
+
+ // Give the subclass a chance to do something
+ if (DescribeCustomObjects(obj, clasp, name)) {
+ // Nothing else to do!
+ } else if (js::IsFunctionObject(obj)) {
+ JSFunction* fun = JS_GetObjectFunction(obj);
+ JSString* str = JS_GetFunctionDisplayId(fun);
+ if (str) {
+ JSFlatString* flat = JS_ASSERT_STRING_IS_FLAT(str);
+ nsAutoString chars;
+ AssignJSFlatString(chars, flat);
+ NS_ConvertUTF16toUTF8 fname(chars);
+ SprintfLiteral(name, "JS Object (Function - %s)", fname.get());
+ } else {
+ SprintfLiteral(name, "JS Object (Function)");
+ }
+ } else {
+ SprintfLiteral(name, "JS Object (%s)", clasp->name);
+ }
+ } else {
+ SprintfLiteral(name, "JS %s", JS::GCTraceKindToAscii(aThing.kind()));
+ }
+
+ // Disable printing global for objects while we figure out ObjShrink fallout.
+ aCb.DescribeGCedNode(aIsMarked, name, compartmentAddress);
+}
+
+void
+CycleCollectedJSContext::NoteGCThingJSChildren(JS::GCCellPtr aThing,
+ nsCycleCollectionTraversalCallback& aCb) const
+{
+ MOZ_ASSERT(mJSContext);
+ TraversalTracer trc(mJSContext, aCb);
+ JS::TraceChildren(&trc, aThing);
+}
+
+void
+CycleCollectedJSContext::NoteGCThingXPCOMChildren(const js::Class* aClasp,
+ JSObject* aObj,
+ nsCycleCollectionTraversalCallback& aCb) const
+{
+ MOZ_ASSERT(aClasp);
+ MOZ_ASSERT(aClasp == js::GetObjectClass(aObj));
+
+ if (NoteCustomGCThingXPCOMChildren(aClasp, aObj, aCb)) {
+ // Nothing else to do!
+ return;
+ }
+ // XXX This test does seem fragile, we should probably whitelist classes
+ // that do hold a strong reference, but that might not be possible.
+ else if (aClasp->flags & JSCLASS_HAS_PRIVATE &&
+ aClasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) {
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "js::GetObjectPrivate(obj)");
+ aCb.NoteXPCOMChild(static_cast<nsISupports*>(js::GetObjectPrivate(aObj)));
+ } else {
+ const DOMJSClass* domClass = GetDOMClass(aObj);
+ if (domClass) {
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "UnwrapDOMObject(obj)");
+ // It's possible that our object is an unforgeable holder object, in
+ // which case it doesn't actually have a C++ DOM object associated with
+ // it. Use UnwrapPossiblyNotInitializedDOMObject, which produces null in
+ // that case, since NoteXPCOMChild/NoteNativeChild are null-safe.
+ if (domClass->mDOMObjectIsISupports) {
+ aCb.NoteXPCOMChild(UnwrapPossiblyNotInitializedDOMObject<nsISupports>(aObj));
+ } else if (domClass->mParticipant) {
+ aCb.NoteNativeChild(UnwrapPossiblyNotInitializedDOMObject<void>(aObj),
+ domClass->mParticipant);
+ }
+ }
+ }
+}
+
+void
+CycleCollectedJSContext::TraverseGCThing(TraverseSelect aTs, JS::GCCellPtr aThing,
+ nsCycleCollectionTraversalCallback& aCb)
+{
+ bool isMarkedGray = JS::GCThingIsMarkedGray(aThing);
+
+ if (aTs == TRAVERSE_FULL) {
+ DescribeGCThing(!isMarkedGray, aThing, aCb);
+ }
+
+ // If this object is alive, then all of its children are alive. For JS objects,
+ // the black-gray invariant ensures the children are also marked black. For C++
+ // objects, the ref count from this object will keep them alive. Thus we don't
+ // need to trace our children, unless we are debugging using WantAllTraces.
+ if (!isMarkedGray && !aCb.WantAllTraces()) {
+ return;
+ }
+
+ if (aTs == TRAVERSE_FULL) {
+ NoteGCThingJSChildren(aThing, aCb);
+ }
+
+ if (aThing.is<JSObject>()) {
+ JSObject* obj = &aThing.as<JSObject>();
+ NoteGCThingXPCOMChildren(js::GetObjectClass(obj), obj, aCb);
+ }
+}
+
+struct TraverseObjectShimClosure
+{
+ nsCycleCollectionTraversalCallback& cb;
+ CycleCollectedJSContext* self;
+};
+
+void
+CycleCollectedJSContext::TraverseZone(JS::Zone* aZone,
+ nsCycleCollectionTraversalCallback& aCb)
+{
+ MOZ_ASSERT(mJSContext);
+
+ /*
+ * We treat the zone as being gray. We handle non-gray GCthings in the
+ * zone by not reporting their children to the CC. The black-gray invariant
+ * ensures that any JS children will also be non-gray, and thus don't need to be
+ * added to the graph. For C++ children, not representing the edge from the
+ * non-gray JS GCthings to the C++ object will keep the child alive.
+ *
+ * We don't allow zone merging in a WantAllTraces CC, because then these
+ * assumptions don't hold.
+ */
+ aCb.DescribeGCedNode(false, "JS Zone");
+
+ /*
+ * Every JS child of everything in the zone is either in the zone
+ * or is a cross-compartment wrapper. In the former case, we don't need to
+ * represent these edges in the CC graph because JS objects are not ref counted.
+ * In the latter case, the JS engine keeps a map of these wrappers, which we
+ * iterate over. Edges between compartments in the same zone will add
+ * unnecessary loop edges to the graph (bug 842137).
+ */
+ TraversalTracer trc(mJSContext, aCb);
+ js::VisitGrayWrapperTargets(aZone, NoteJSChildGrayWrapperShim, &trc);
+
+ /*
+ * To find C++ children of things in the zone, we scan every JS Object in
+ * the zone. Only JS Objects can have C++ children.
+ */
+ TraverseObjectShimClosure closure = { aCb, this };
+ js::IterateGrayObjects(aZone, TraverseObjectShim, &closure);
+}
+
+/* static */ void
+CycleCollectedJSContext::TraverseObjectShim(void* aData, JS::GCCellPtr aThing)
+{
+ TraverseObjectShimClosure* closure =
+ static_cast<TraverseObjectShimClosure*>(aData);
+
+ MOZ_ASSERT(aThing.is<JSObject>());
+ closure->self->TraverseGCThing(CycleCollectedJSContext::TRAVERSE_CPP,
+ aThing, closure->cb);
+}
+
+void
+CycleCollectedJSContext::TraverseNativeRoots(nsCycleCollectionNoteRootCallback& aCb)
+{
+ // NB: This is here just to preserve the existing XPConnect order. I doubt it
+ // would hurt to do this after the JS holders.
+ TraverseAdditionalNativeRoots(aCb);
+
+ for (auto iter = mJSHolders.Iter(); !iter.Done(); iter.Next()) {
+ void* holder = iter.Key();
+ nsScriptObjectTracer*& tracer = iter.Data();
+
+ bool noteRoot = false;
+ if (MOZ_UNLIKELY(aCb.WantAllTraces())) {
+ noteRoot = true;
+ } else {
+ tracer->Trace(holder,
+ TraceCallbackFunc(CheckParticipatesInCycleCollection),
+ &noteRoot);
+ }
+
+ if (noteRoot) {
+ aCb.NoteNativeRoot(holder, tracer);
+ }
+ }
+}
+
+/* static */ void
+CycleCollectedJSContext::TraceBlackJS(JSTracer* aTracer, void* aData)
+{
+ CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
+
+ self->TraceNativeBlackRoots(aTracer);
+}
+
+/* static */ void
+CycleCollectedJSContext::TraceGrayJS(JSTracer* aTracer, void* aData)
+{
+ CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
+
+ // Mark these roots as gray so the CC can walk them later.
+ self->TraceNativeGrayRoots(aTracer);
+}
+
+/* static */ void
+CycleCollectedJSContext::GCCallback(JSContext* aContext,
+ JSGCStatus aStatus,
+ void* aData)
+{
+ CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
+
+ MOZ_ASSERT(aContext == self->Context());
+
+ self->OnGC(aStatus);
+}
+
+/* static */ void
+CycleCollectedJSContext::GCSliceCallback(JSContext* aContext,
+ JS::GCProgress aProgress,
+ const JS::GCDescription& aDesc)
+{
+ CycleCollectedJSContext* self = CycleCollectedJSContext::Get();
+ MOZ_ASSERT(self->Context() == aContext);
+
+ if (aProgress == JS::GC_CYCLE_END) {
+ JS::gcreason::Reason reason = aDesc.reason_;
+ Unused <<
+ NS_WARN_IF(NS_FAILED(DebuggerOnGCRunnable::Enqueue(aContext, aDesc)) &&
+ reason != JS::gcreason::SHUTDOWN_CC &&
+ reason != JS::gcreason::DESTROY_RUNTIME &&
+ reason != JS::gcreason::XPCONNECT_SHUTDOWN);
+ }
+
+ if (self->mPrevGCSliceCallback) {
+ self->mPrevGCSliceCallback(aContext, aProgress, aDesc);
+ }
+}
+
+class MinorGCMarker : public TimelineMarker
+{
+private:
+ JS::gcreason::Reason mReason;
+
+public:
+ MinorGCMarker(MarkerTracingType aTracingType,
+ JS::gcreason::Reason aReason)
+ : TimelineMarker("MinorGC",
+ aTracingType,
+ MarkerStackRequest::NO_STACK)
+ , mReason(aReason)
+ {
+ MOZ_ASSERT(aTracingType == MarkerTracingType::START ||
+ aTracingType == MarkerTracingType::END);
+ }
+
+ MinorGCMarker(JS::GCNurseryProgress aProgress,
+ JS::gcreason::Reason aReason)
+ : TimelineMarker("MinorGC",
+ aProgress == JS::GCNurseryProgress::GC_NURSERY_COLLECTION_START
+ ? MarkerTracingType::START
+ : MarkerTracingType::END,
+ MarkerStackRequest::NO_STACK)
+ , mReason(aReason)
+ { }
+
+ virtual void
+ AddDetails(JSContext* aCx,
+ dom::ProfileTimelineMarker& aMarker) override
+ {
+ TimelineMarker::AddDetails(aCx, aMarker);
+
+ if (GetTracingType() == MarkerTracingType::START) {
+ auto reason = JS::gcreason::ExplainReason(mReason);
+ aMarker.mCauseName.Construct(NS_ConvertUTF8toUTF16(reason));
+ }
+ }
+
+ virtual UniquePtr<AbstractTimelineMarker>
+ Clone() override
+ {
+ auto clone = MakeUnique<MinorGCMarker>(GetTracingType(), mReason);
+ clone->SetCustomTime(GetTime());
+ return UniquePtr<AbstractTimelineMarker>(Move(clone));
+ }
+};
+
+/* static */ void
+CycleCollectedJSContext::GCNurseryCollectionCallback(JSContext* aContext,
+ JS::GCNurseryProgress aProgress,
+ JS::gcreason::Reason aReason)
+{
+ CycleCollectedJSContext* self = CycleCollectedJSContext::Get();
+ MOZ_ASSERT(self->Context() == aContext);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+ if (timelines && !timelines->IsEmpty()) {
+ UniquePtr<AbstractTimelineMarker> abstractMarker(
+ MakeUnique<MinorGCMarker>(aProgress, aReason));
+ timelines->AddMarkerForAllObservedDocShells(abstractMarker);
+ }
+
+ if (self->mPrevGCNurseryCollectionCallback) {
+ self->mPrevGCNurseryCollectionCallback(aContext, aProgress, aReason);
+ }
+}
+
+
+/* static */ void
+CycleCollectedJSContext::OutOfMemoryCallback(JSContext* aContext,
+ void* aData)
+{
+ CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
+
+ MOZ_ASSERT(aContext == self->Context());
+
+ self->OnOutOfMemory();
+}
+
+/* static */ void
+CycleCollectedJSContext::LargeAllocationFailureCallback(void* aData)
+{
+ CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
+
+ self->OnLargeAllocationFailure();
+}
+
+class PromiseJobRunnable final : public Runnable
+{
+public:
+ PromiseJobRunnable(JS::HandleObject aCallback, JS::HandleObject aAllocationSite,
+ nsIGlobalObject* aIncumbentGlobal)
+ : mCallback(new PromiseJobCallback(aCallback, aAllocationSite, aIncumbentGlobal))
+ {
+ }
+
+ virtual ~PromiseJobRunnable()
+ {
+ }
+
+protected:
+ NS_IMETHOD
+ Run() override
+ {
+ nsIGlobalObject* global = xpc::NativeGlobal(mCallback->CallbackPreserveColor());
+ if (global && !global->IsDying()) {
+ mCallback->Call("promise callback");
+ }
+ return NS_OK;
+ }
+
+private:
+ RefPtr<PromiseJobCallback> mCallback;
+};
+
+/* static */
+JSObject*
+CycleCollectedJSContext::GetIncumbentGlobalCallback(JSContext* aCx)
+{
+ nsIGlobalObject* global = mozilla::dom::GetIncumbentGlobal();
+ if (global) {
+ return global->GetGlobalJSObject();
+ }
+ return nullptr;
+}
+
+/* static */
+bool
+CycleCollectedJSContext::EnqueuePromiseJobCallback(JSContext* aCx,
+ JS::HandleObject aJob,
+ JS::HandleObject aAllocationSite,
+ JS::HandleObject aIncumbentGlobal,
+ void* aData)
+{
+ CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
+ MOZ_ASSERT(aCx == self->Context());
+ MOZ_ASSERT(Get() == self);
+
+ nsIGlobalObject* global = nullptr;
+ if (aIncumbentGlobal) {
+ global = xpc::NativeGlobal(aIncumbentGlobal);
+ }
+ nsCOMPtr<nsIRunnable> runnable = new PromiseJobRunnable(aJob, aAllocationSite, global);
+ self->DispatchToMicroTask(runnable.forget());
+ return true;
+}
+
+#ifdef SPIDERMONKEY_PROMISE
+/* static */
+void
+CycleCollectedJSContext::PromiseRejectionTrackerCallback(JSContext* aCx,
+ JS::HandleObject aPromise,
+ PromiseRejectionHandlingState state,
+ void* aData)
+{
+#ifdef DEBUG
+ CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
+#endif // DEBUG
+ MOZ_ASSERT(aCx == self->Context());
+ MOZ_ASSERT(Get() == self);
+
+ if (state == PromiseRejectionHandlingState::Unhandled) {
+ PromiseDebugging::AddUncaughtRejection(aPromise);
+ } else {
+ PromiseDebugging::AddConsumedRejection(aPromise);
+ }
+}
+#endif // SPIDERMONKEY_PROMISE
+
+struct JsGcTracer : public TraceCallbacks
+{
+ virtual void Trace(JS::Heap<JS::Value>* aPtr, const char* aName,
+ void* aClosure) const override
+ {
+ JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
+ }
+ virtual void Trace(JS::Heap<jsid>* aPtr, const char* aName,
+ void* aClosure) const override
+ {
+ JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
+ }
+ virtual void Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
+ void* aClosure) const override
+ {
+ JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
+ }
+ virtual void Trace(JSObject** aPtr, const char* aName,
+ void* aClosure) const override
+ {
+ js::UnsafeTraceManuallyBarrieredEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
+ }
+ virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
+ void* aClosure) const override
+ {
+ JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
+ }
+ virtual void Trace(JS::Heap<JSString*>* aPtr, const char* aName,
+ void* aClosure) const override
+ {
+ JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
+ }
+ virtual void Trace(JS::Heap<JSScript*>* aPtr, const char* aName,
+ void* aClosure) const override
+ {
+ JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
+ }
+ virtual void Trace(JS::Heap<JSFunction*>* aPtr, const char* aName,
+ void* aClosure) const override
+ {
+ JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
+ }
+};
+
+void
+mozilla::TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer)
+{
+ nsXPCOMCycleCollectionParticipant* participant = nullptr;
+ CallQueryInterface(aHolder, &participant);
+ participant->Trace(aHolder, JsGcTracer(), aTracer);
+}
+
+void
+CycleCollectedJSContext::TraceNativeGrayRoots(JSTracer* aTracer)
+{
+ MOZ_ASSERT(mJSContext);
+
+ // NB: This is here just to preserve the existing XPConnect order. I doubt it
+ // would hurt to do this after the JS holders.
+ TraceAdditionalNativeGrayRoots(aTracer);
+
+ for (auto iter = mJSHolders.Iter(); !iter.Done(); iter.Next()) {
+ void* holder = iter.Key();
+ nsScriptObjectTracer*& tracer = iter.Data();
+ tracer->Trace(holder, JsGcTracer(), aTracer);
+ }
+}
+
+void
+CycleCollectedJSContext::AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer)
+{
+ MOZ_ASSERT(mJSContext);
+ mJSHolders.Put(aHolder, aTracer);
+}
+
+struct ClearJSHolder : public TraceCallbacks
+{
+ virtual void Trace(JS::Heap<JS::Value>* aPtr, const char*, void*) const override
+ {
+ aPtr->setUndefined();
+ }
+
+ virtual void Trace(JS::Heap<jsid>* aPtr, const char*, void*) const override
+ {
+ *aPtr = JSID_VOID;
+ }
+
+ virtual void Trace(JS::Heap<JSObject*>* aPtr, const char*, void*) const override
+ {
+ *aPtr = nullptr;
+ }
+
+ virtual void Trace(JSObject** aPtr, const char* aName,
+ void* aClosure) const override
+ {
+ *aPtr = nullptr;
+ }
+
+ virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char*, void*) const override
+ {
+ *aPtr = nullptr;
+ }
+
+ virtual void Trace(JS::Heap<JSString*>* aPtr, const char*, void*) const override
+ {
+ *aPtr = nullptr;
+ }
+
+ virtual void Trace(JS::Heap<JSScript*>* aPtr, const char*, void*) const override
+ {
+ *aPtr = nullptr;
+ }
+
+ virtual void Trace(JS::Heap<JSFunction*>* aPtr, const char*, void*) const override
+ {
+ *aPtr = nullptr;
+ }
+};
+
+void
+CycleCollectedJSContext::RemoveJSHolder(void* aHolder)
+{
+ MOZ_ASSERT(mJSContext);
+
+ nsScriptObjectTracer* tracer = mJSHolders.Get(aHolder);
+ if (!tracer) {
+ return;
+ }
+ tracer->Trace(aHolder, ClearJSHolder(), nullptr);
+ mJSHolders.Remove(aHolder);
+}
+
+#ifdef DEBUG
+bool
+CycleCollectedJSContext::IsJSHolder(void* aHolder)
+{
+ MOZ_ASSERT(mJSContext);
+ return mJSHolders.Get(aHolder, nullptr);
+}
+
+static void
+AssertNoGcThing(JS::GCCellPtr aGCThing, const char* aName, void* aClosure)
+{
+ MOZ_ASSERT(!aGCThing);
+}
+
+void
+CycleCollectedJSContext::AssertNoObjectsToTrace(void* aPossibleJSHolder)
+{
+ MOZ_ASSERT(mJSContext);
+
+ nsScriptObjectTracer* tracer = mJSHolders.Get(aPossibleJSHolder);
+ if (tracer) {
+ tracer->Trace(aPossibleJSHolder, TraceCallbackFunc(AssertNoGcThing), nullptr);
+ }
+}
+#endif
+
+already_AddRefed<nsIException>
+CycleCollectedJSContext::GetPendingException() const
+{
+ MOZ_ASSERT(mJSContext);
+
+ nsCOMPtr<nsIException> out = mPendingException;
+ return out.forget();
+}
+
+void
+CycleCollectedJSContext::SetPendingException(nsIException* aException)
+{
+ MOZ_ASSERT(mJSContext);
+ mPendingException = aException;
+}
+
+std::queue<nsCOMPtr<nsIRunnable>>&
+CycleCollectedJSContext::GetPromiseMicroTaskQueue()
+{
+ MOZ_ASSERT(mJSContext);
+ return mPromiseMicroTaskQueue;
+}
+
+std::queue<nsCOMPtr<nsIRunnable>>&
+CycleCollectedJSContext::GetDebuggerPromiseMicroTaskQueue()
+{
+ MOZ_ASSERT(mJSContext);
+ return mDebuggerPromiseMicroTaskQueue;
+}
+
+nsCycleCollectionParticipant*
+CycleCollectedJSContext::GCThingParticipant()
+{
+ MOZ_ASSERT(mJSContext);
+ return &mGCThingCycleCollectorGlobal;
+}
+
+nsCycleCollectionParticipant*
+CycleCollectedJSContext::ZoneParticipant()
+{
+ MOZ_ASSERT(mJSContext);
+ return &mJSZoneCycleCollectorGlobal;
+}
+
+nsresult
+CycleCollectedJSContext::TraverseRoots(nsCycleCollectionNoteRootCallback& aCb)
+{
+ MOZ_ASSERT(mJSContext);
+
+ TraverseNativeRoots(aCb);
+
+ NoteWeakMapsTracer trc(mJSContext, aCb);
+ js::TraceWeakMaps(&trc);
+
+ return NS_OK;
+}
+
+bool
+CycleCollectedJSContext::UsefulToMergeZones() const
+{
+ return false;
+}
+
+void
+CycleCollectedJSContext::FixWeakMappingGrayBits() const
+{
+ MOZ_ASSERT(mJSContext);
+ MOZ_ASSERT(!JS::IsIncrementalGCInProgress(mJSContext),
+ "Don't call FixWeakMappingGrayBits during a GC.");
+ FixWeakMappingGrayBitsTracer fixer(mJSContext);
+ fixer.FixAll();
+}
+
+bool
+CycleCollectedJSContext::AreGCGrayBitsValid() const
+{
+ MOZ_ASSERT(mJSContext);
+ return js::AreGCGrayBitsValid(mJSContext);
+}
+
+void
+CycleCollectedJSContext::GarbageCollect(uint32_t aReason) const
+{
+ MOZ_ASSERT(mJSContext);
+
+ MOZ_ASSERT(aReason < JS::gcreason::NUM_REASONS);
+ JS::gcreason::Reason gcreason = static_cast<JS::gcreason::Reason>(aReason);
+
+ JS::PrepareForFullGC(mJSContext);
+ JS::GCForReason(mJSContext, GC_NORMAL, gcreason);
+}
+
+void
+CycleCollectedJSContext::JSObjectsTenured()
+{
+ MOZ_ASSERT(mJSContext);
+
+ for (auto iter = mNurseryObjects.Iter(); !iter.Done(); iter.Next()) {
+ nsWrapperCache* cache = iter.Get();
+ JSObject* wrapper = cache->GetWrapperPreserveColor();
+ MOZ_ASSERT(wrapper);
+ if (!JS::ObjectIsTenured(wrapper)) {
+ MOZ_ASSERT(!cache->PreservingWrapper());
+ const JSClass* jsClass = js::GetObjectJSClass(wrapper);
+ jsClass->doFinalize(nullptr, wrapper);
+ }
+ }
+
+#ifdef DEBUG
+for (auto iter = mPreservedNurseryObjects.Iter(); !iter.Done(); iter.Next()) {
+ MOZ_ASSERT(JS::ObjectIsTenured(iter.Get().get()));
+}
+#endif
+
+ mNurseryObjects.Clear();
+ mPreservedNurseryObjects.Clear();
+}
+
+void
+CycleCollectedJSContext::NurseryWrapperAdded(nsWrapperCache* aCache)
+{
+ MOZ_ASSERT(mJSContext);
+ MOZ_ASSERT(aCache);
+ MOZ_ASSERT(aCache->GetWrapperPreserveColor());
+ MOZ_ASSERT(!JS::ObjectIsTenured(aCache->GetWrapperPreserveColor()));
+ mNurseryObjects.InfallibleAppend(aCache);
+}
+
+void
+CycleCollectedJSContext::NurseryWrapperPreserved(JSObject* aWrapper)
+{
+ MOZ_ASSERT(mJSContext);
+
+ mPreservedNurseryObjects.InfallibleAppend(
+ JS::PersistentRooted<JSObject*>(mJSContext, aWrapper));
+}
+
+void
+CycleCollectedJSContext::DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
+ DeferredFinalizeFunction aFunc,
+ void* aThing)
+{
+ MOZ_ASSERT(mJSContext);
+
+ void* thingArray = nullptr;
+ bool hadThingArray = mDeferredFinalizerTable.Get(aFunc, &thingArray);
+
+ thingArray = aAppendFunc(thingArray, aThing);
+ if (!hadThingArray) {
+ mDeferredFinalizerTable.Put(aFunc, thingArray);
+ }
+}
+
+void
+CycleCollectedJSContext::DeferredFinalize(nsISupports* aSupports)
+{
+ MOZ_ASSERT(mJSContext);
+
+ typedef DeferredFinalizerImpl<nsISupports> Impl;
+ DeferredFinalize(Impl::AppendDeferredFinalizePointer, Impl::DeferredFinalize,
+ aSupports);
+}
+
+void
+CycleCollectedJSContext::DumpJSHeap(FILE* aFile)
+{
+ js::DumpHeap(Context(), aFile, js::CollectNurseryBeforeDump);
+}
+
+void
+CycleCollectedJSContext::ProcessStableStateQueue()
+{
+ MOZ_ASSERT(mJSContext);
+ MOZ_RELEASE_ASSERT(!mDoingStableStates);
+ mDoingStableStates = true;
+
+ for (uint32_t i = 0; i < mStableStateEvents.Length(); ++i) {
+ nsCOMPtr<nsIRunnable> event = mStableStateEvents[i].forget();
+ event->Run();
+ }
+
+ mStableStateEvents.Clear();
+ mDoingStableStates = false;
+}
+
+void
+CycleCollectedJSContext::ProcessMetastableStateQueue(uint32_t aRecursionDepth)
+{
+ MOZ_ASSERT(mJSContext);
+ MOZ_RELEASE_ASSERT(!mDoingStableStates);
+ mDoingStableStates = true;
+
+ nsTArray<RunInMetastableStateData> localQueue = Move(mMetastableStateEvents);
+
+ for (uint32_t i = 0; i < localQueue.Length(); ++i)
+ {
+ RunInMetastableStateData& data = localQueue[i];
+ if (data.mRecursionDepth != aRecursionDepth) {
+ continue;
+ }
+
+ {
+ nsCOMPtr<nsIRunnable> runnable = data.mRunnable.forget();
+ runnable->Run();
+ }
+
+ localQueue.RemoveElementAt(i--);
+ }
+
+ // If the queue has events in it now, they were added from something we called,
+ // so they belong at the end of the queue.
+ localQueue.AppendElements(mMetastableStateEvents);
+ localQueue.SwapElements(mMetastableStateEvents);
+ mDoingStableStates = false;
+}
+
+void
+CycleCollectedJSContext::AfterProcessTask(uint32_t aRecursionDepth)
+{
+ MOZ_ASSERT(mJSContext);
+
+ // See HTML 6.1.4.2 Processing model
+
+ // Execute any events that were waiting for a microtask to complete.
+ // This is not (yet) in the spec.
+ ProcessMetastableStateQueue(aRecursionDepth);
+
+ // Step 4.1: Execute microtasks.
+ if (!mDisableMicroTaskCheckpoint) {
+ if (NS_IsMainThread()) {
+ nsContentUtils::PerformMainThreadMicroTaskCheckpoint();
+ Promise::PerformMicroTaskCheckpoint();
+ } else {
+ Promise::PerformWorkerMicroTaskCheckpoint();
+ }
+ }
+
+ // Step 4.2 Execute any events that were waiting for a stable state.
+ ProcessStableStateQueue();
+}
+
+void
+CycleCollectedJSContext::AfterProcessMicrotask()
+{
+ MOZ_ASSERT(mJSContext);
+ AfterProcessMicrotask(RecursionDepth());
+}
+
+void
+CycleCollectedJSContext::AfterProcessMicrotask(uint32_t aRecursionDepth)
+{
+ MOZ_ASSERT(mJSContext);
+
+ // Between microtasks, execute any events that were waiting for a microtask
+ // to complete.
+ ProcessMetastableStateQueue(aRecursionDepth);
+}
+
+uint32_t
+CycleCollectedJSContext::RecursionDepth()
+{
+ return mOwningThread->RecursionDepth();
+}
+
+void
+CycleCollectedJSContext::RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable)
+{
+ MOZ_ASSERT(mJSContext);
+ mStableStateEvents.AppendElement(Move(aRunnable));
+}
+
+void
+CycleCollectedJSContext::RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable)
+{
+ MOZ_ASSERT(mJSContext);
+
+ RunInMetastableStateData data;
+ data.mRunnable = aRunnable;
+
+ MOZ_ASSERT(mOwningThread);
+ data.mRecursionDepth = RecursionDepth();
+
+ // There must be an event running to get here.
+#ifndef MOZ_WIDGET_COCOA
+ MOZ_ASSERT(data.mRecursionDepth > mBaseRecursionDepth);
+#else
+ // XXX bug 1261143
+ // Recursion depth should be greater than mBaseRecursionDepth,
+ // or the runnable will stay in the queue forever.
+ if (data.mRecursionDepth <= mBaseRecursionDepth) {
+ data.mRecursionDepth = mBaseRecursionDepth + 1;
+ }
+#endif
+
+ mMetastableStateEvents.AppendElement(Move(data));
+}
+
+IncrementalFinalizeRunnable::IncrementalFinalizeRunnable(CycleCollectedJSContext* aCx,
+ DeferredFinalizerTable& aFinalizers)
+ : mContext(aCx)
+ , mFinalizeFunctionToRun(0)
+ , mReleasing(false)
+{
+ for (auto iter = aFinalizers.Iter(); !iter.Done(); iter.Next()) {
+ DeferredFinalizeFunction& function = iter.Key();
+ void*& data = iter.Data();
+
+ DeferredFinalizeFunctionHolder* holder =
+ mDeferredFinalizeFunctions.AppendElement();
+ holder->run = function;
+ holder->data = data;
+
+ iter.Remove();
+ }
+}
+
+IncrementalFinalizeRunnable::~IncrementalFinalizeRunnable()
+{
+ MOZ_ASSERT(this != mContext->mFinalizeRunnable);
+}
+
+void
+IncrementalFinalizeRunnable::ReleaseNow(bool aLimited)
+{
+ if (mReleasing) {
+ NS_WARNING("Re-entering ReleaseNow");
+ return;
+ }
+ {
+ mozilla::AutoRestore<bool> ar(mReleasing);
+ mReleasing = true;
+ MOZ_ASSERT(mDeferredFinalizeFunctions.Length() != 0,
+ "We should have at least ReleaseSliceNow to run");
+ MOZ_ASSERT(mFinalizeFunctionToRun < mDeferredFinalizeFunctions.Length(),
+ "No more finalizers to run?");
+
+ TimeDuration sliceTime = TimeDuration::FromMilliseconds(SliceMillis);
+ TimeStamp started = TimeStamp::Now();
+ bool timeout = false;
+ do {
+ const DeferredFinalizeFunctionHolder& function =
+ mDeferredFinalizeFunctions[mFinalizeFunctionToRun];
+ if (aLimited) {
+ bool done = false;
+ while (!timeout && !done) {
+ /*
+ * We don't want to read the clock too often, so we try to
+ * release slices of 100 items.
+ */
+ done = function.run(100, function.data);
+ timeout = TimeStamp::Now() - started >= sliceTime;
+ }
+ if (done) {
+ ++mFinalizeFunctionToRun;
+ }
+ if (timeout) {
+ break;
+ }
+ } else {
+ while (!function.run(UINT32_MAX, function.data));
+ ++mFinalizeFunctionToRun;
+ }
+ } while (mFinalizeFunctionToRun < mDeferredFinalizeFunctions.Length());
+ }
+
+ if (mFinalizeFunctionToRun == mDeferredFinalizeFunctions.Length()) {
+ MOZ_ASSERT(mContext->mFinalizeRunnable == this);
+ mDeferredFinalizeFunctions.Clear();
+ // NB: This may delete this!
+ mContext->mFinalizeRunnable = nullptr;
+ }
+}
+
+NS_IMETHODIMP
+IncrementalFinalizeRunnable::Run()
+{
+ if (mContext->mFinalizeRunnable != this) {
+ /* These items were already processed synchronously in JSGC_END. */
+ MOZ_ASSERT(!mDeferredFinalizeFunctions.Length());
+ return NS_OK;
+ }
+
+ TimeStamp start = TimeStamp::Now();
+ ReleaseNow(true);
+
+ if (mDeferredFinalizeFunctions.Length()) {
+ nsresult rv = NS_DispatchToCurrentThread(this);
+ if (NS_FAILED(rv)) {
+ ReleaseNow(false);
+ }
+ }
+
+ uint32_t duration = (uint32_t)((TimeStamp::Now() - start).ToMilliseconds());
+ Telemetry::Accumulate(Telemetry::DEFERRED_FINALIZE_ASYNC, duration);
+
+ return NS_OK;
+}
+
+void
+CycleCollectedJSContext::FinalizeDeferredThings(DeferredFinalizeType aType)
+{
+ MOZ_ASSERT(mJSContext);
+
+ /*
+ * If the previous GC created a runnable to finalize objects
+ * incrementally, and if it hasn't finished yet, finish it now. We
+ * don't want these to build up. We also don't want to allow any
+ * existing incremental finalize runnables to run after a
+ * non-incremental GC, since they are often used to detect leaks.
+ */
+ if (mFinalizeRunnable) {
+ mFinalizeRunnable->ReleaseNow(false);
+ if (mFinalizeRunnable) {
+ // If we re-entered ReleaseNow, we couldn't delete mFinalizeRunnable and
+ // we need to just continue processing it.
+ return;
+ }
+ }
+
+ if (mDeferredFinalizerTable.Count() == 0) {
+ return;
+ }
+
+ mFinalizeRunnable = new IncrementalFinalizeRunnable(this,
+ mDeferredFinalizerTable);
+
+ // Everything should be gone now.
+ MOZ_ASSERT(mDeferredFinalizerTable.Count() == 0);
+
+ if (aType == FinalizeIncrementally) {
+ NS_DispatchToCurrentThread(mFinalizeRunnable);
+ } else {
+ mFinalizeRunnable->ReleaseNow(false);
+ MOZ_ASSERT(!mFinalizeRunnable);
+ }
+}
+
+void
+CycleCollectedJSContext::AnnotateAndSetOutOfMemory(OOMState* aStatePtr,
+ OOMState aNewState)
+{
+ MOZ_ASSERT(mJSContext);
+
+ *aStatePtr = aNewState;
+#ifdef MOZ_CRASHREPORTER
+ CrashReporter::AnnotateCrashReport(aStatePtr == &mOutOfMemoryState
+ ? NS_LITERAL_CSTRING("JSOutOfMemory")
+ : NS_LITERAL_CSTRING("JSLargeAllocationFailure"),
+ aNewState == OOMState::Reporting
+ ? NS_LITERAL_CSTRING("Reporting")
+ : aNewState == OOMState::Reported
+ ? NS_LITERAL_CSTRING("Reported")
+ : NS_LITERAL_CSTRING("Recovered"));
+#endif
+}
+
+void
+CycleCollectedJSContext::OnGC(JSGCStatus aStatus)
+{
+ MOZ_ASSERT(mJSContext);
+
+ switch (aStatus) {
+ case JSGC_BEGIN:
+ nsCycleCollector_prepareForGarbageCollection();
+ mZonesWaitingForGC.Clear();
+ break;
+ case JSGC_END: {
+#ifdef MOZ_CRASHREPORTER
+ if (mOutOfMemoryState == OOMState::Reported) {
+ AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Recovered);
+ }
+ if (mLargeAllocationFailureState == OOMState::Reported) {
+ AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Recovered);
+ }
+#endif
+
+ // Do any deferred finalization of native objects.
+ FinalizeDeferredThings(JS::WasIncrementalGC(mJSContext) ? FinalizeIncrementally :
+ FinalizeNow);
+ break;
+ }
+ default:
+ MOZ_CRASH();
+ }
+
+ CustomGCCallback(aStatus);
+}
+
+void
+CycleCollectedJSContext::OnOutOfMemory()
+{
+ MOZ_ASSERT(mJSContext);
+
+ AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Reporting);
+ CustomOutOfMemoryCallback();
+ AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Reported);
+}
+
+void
+CycleCollectedJSContext::OnLargeAllocationFailure()
+{
+ MOZ_ASSERT(mJSContext);
+
+ AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Reporting);
+ CustomLargeAllocationFailureCallback();
+ AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Reported);
+}
+
+void
+CycleCollectedJSContext::PrepareWaitingZonesForGC()
+{
+ if (mZonesWaitingForGC.Count() == 0) {
+ JS::PrepareForFullGC(Context());
+ } else {
+ for (auto iter = mZonesWaitingForGC.Iter(); !iter.Done(); iter.Next()) {
+ JS::PrepareZoneForGC(iter.Get()->GetKey());
+ }
+ mZonesWaitingForGC.Clear();
+ }
+}
+
+void
+CycleCollectedJSContext::DispatchToMicroTask(already_AddRefed<nsIRunnable> aRunnable)
+{
+ RefPtr<nsIRunnable> runnable(aRunnable);
+
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(runnable);
+
+ mPromiseMicroTaskQueue.push(runnable.forget());
+}
+
+void
+CycleCollectedJSContext::EnvironmentPreparer::invoke(JS::HandleObject scope,
+ js::ScriptEnvironmentPreparer::Closure& closure)
+{
+ nsIGlobalObject* global = xpc::NativeGlobal(scope);
+
+ // Not much we can do if we simply don't have a usable global here...
+ NS_ENSURE_TRUE_VOID(global && global->GetGlobalJSObject());
+
+ AutoEntryScript aes(global, "JS-engine-initiated execution");
+
+ MOZ_ASSERT(!JS_IsExceptionPending(aes.cx()));
+
+ DebugOnly<bool> ok = closure(aes.cx());
+
+ MOZ_ASSERT_IF(ok, !JS_IsExceptionPending(aes.cx()));
+
+ // The AutoEntryScript will check for JS_IsExceptionPending on the
+ // JSContext and report it as needed as it comes off the stack.
+}
diff --git a/xpcom/base/CycleCollectedJSContext.h b/xpcom/base/CycleCollectedJSContext.h
new file mode 100644
index 0000000000..9415634b89
--- /dev/null
+++ b/xpcom/base/CycleCollectedJSContext.h
@@ -0,0 +1,494 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_CycleCollectedJSContext_h__
+#define mozilla_CycleCollectedJSContext_h__
+
+#include <queue>
+
+#include "mozilla/DeferredFinalize.h"
+#include "mozilla/mozalloc.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/SegmentedVector.h"
+#include "jsapi.h"
+#include "jsfriendapi.h"
+
+#include "nsCycleCollectionParticipant.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsTArray.h"
+#include "nsTHashtable.h"
+
+class nsCycleCollectionNoteRootCallback;
+class nsIException;
+class nsIRunnable;
+class nsThread;
+class nsWrapperCache;
+
+namespace js {
+struct Class;
+} // namespace js
+
+namespace mozilla {
+
+class JSGCThingParticipant: public nsCycleCollectionParticipant
+{
+public:
+ NS_IMETHOD_(void) Root(void*) override
+ {
+ MOZ_ASSERT(false, "Don't call Root on GC things");
+ }
+
+ NS_IMETHOD_(void) Unlink(void*) override
+ {
+ MOZ_ASSERT(false, "Don't call Unlink on GC things, as they may be dead");
+ }
+
+ NS_IMETHOD_(void) Unroot(void*) override
+ {
+ MOZ_ASSERT(false, "Don't call Unroot on GC things, as they may be dead");
+ }
+
+ NS_IMETHOD_(void) DeleteCycleCollectable(void* aPtr) override
+ {
+ MOZ_ASSERT(false, "Can't directly delete a cycle collectable GC thing");
+ }
+
+ NS_IMETHOD Traverse(void* aPtr, nsCycleCollectionTraversalCallback& aCb)
+ override;
+
+ NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(JSGCThingParticipant)
+};
+
+class JSZoneParticipant : public nsCycleCollectionParticipant
+{
+public:
+ constexpr JSZoneParticipant(): nsCycleCollectionParticipant()
+ {
+ }
+
+ NS_IMETHOD_(void) Root(void*) override
+ {
+ MOZ_ASSERT(false, "Don't call Root on GC things");
+ }
+
+ NS_IMETHOD_(void) Unlink(void*) override
+ {
+ MOZ_ASSERT(false, "Don't call Unlink on GC things, as they may be dead");
+ }
+
+ NS_IMETHOD_(void) Unroot(void*) override
+ {
+ MOZ_ASSERT(false, "Don't call Unroot on GC things, as they may be dead");
+ }
+
+ NS_IMETHOD_(void) DeleteCycleCollectable(void*) override
+ {
+ MOZ_ASSERT(false, "Can't directly delete a cycle collectable GC thing");
+ }
+
+ NS_IMETHOD Traverse(void* aPtr, nsCycleCollectionTraversalCallback& aCb)
+ override;
+
+ NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(JSZoneParticipant)
+};
+
+class IncrementalFinalizeRunnable;
+
+// Contains various stats about the cycle collection.
+struct CycleCollectorResults
+{
+ CycleCollectorResults()
+ {
+ // Initialize here so when we increment mNumSlices the first time we're
+ // not using uninitialized memory.
+ Init();
+ }
+
+ void Init()
+ {
+ mForcedGC = false;
+ mMergedZones = false;
+ mAnyManual = false;
+ mVisitedRefCounted = 0;
+ mVisitedGCed = 0;
+ mFreedRefCounted = 0;
+ mFreedGCed = 0;
+ mFreedJSZones = 0;
+ mNumSlices = 1;
+ // mNumSlices is initialized to one, because we call Init() after the
+ // per-slice increment of mNumSlices has already occurred.
+ }
+
+ bool mForcedGC;
+ bool mMergedZones;
+ bool mAnyManual; // true if any slice of the CC was manually triggered, or at shutdown.
+ uint32_t mVisitedRefCounted;
+ uint32_t mVisitedGCed;
+ uint32_t mFreedRefCounted;
+ uint32_t mFreedGCed;
+ uint32_t mFreedJSZones;
+ uint32_t mNumSlices;
+};
+
+class CycleCollectedJSContext
+{
+ friend class JSGCThingParticipant;
+ friend class JSZoneParticipant;
+ friend class IncrementalFinalizeRunnable;
+protected:
+ CycleCollectedJSContext();
+ virtual ~CycleCollectedJSContext();
+
+ MOZ_IS_CLASS_INIT
+ nsresult Initialize(JSContext* aParentContext,
+ uint32_t aMaxBytes,
+ uint32_t aMaxNurseryBytes);
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+ void UnmarkSkippableJSHolders();
+
+ virtual void
+ TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& aCb) {}
+ virtual void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) {}
+
+ virtual void CustomGCCallback(JSGCStatus aStatus) {}
+ virtual void CustomOutOfMemoryCallback() {}
+ virtual void CustomLargeAllocationFailureCallback() {}
+
+ std::queue<nsCOMPtr<nsIRunnable>> mPromiseMicroTaskQueue;
+ std::queue<nsCOMPtr<nsIRunnable>> mDebuggerPromiseMicroTaskQueue;
+
+private:
+ void
+ DescribeGCThing(bool aIsMarked, JS::GCCellPtr aThing,
+ nsCycleCollectionTraversalCallback& aCb) const;
+
+ virtual bool
+ DescribeCustomObjects(JSObject* aObject, const js::Class* aClasp,
+ char (&aName)[72]) const
+ {
+ return false; // We did nothing.
+ }
+
+ void
+ NoteGCThingJSChildren(JS::GCCellPtr aThing,
+ nsCycleCollectionTraversalCallback& aCb) const;
+
+ void
+ NoteGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj,
+ nsCycleCollectionTraversalCallback& aCb) const;
+
+ virtual bool
+ NoteCustomGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj,
+ nsCycleCollectionTraversalCallback& aCb) const
+ {
+ return false; // We did nothing.
+ }
+
+ enum TraverseSelect {
+ TRAVERSE_CPP,
+ TRAVERSE_FULL
+ };
+
+ void
+ TraverseGCThing(TraverseSelect aTs, JS::GCCellPtr aThing,
+ nsCycleCollectionTraversalCallback& aCb);
+
+ void
+ TraverseZone(JS::Zone* aZone, nsCycleCollectionTraversalCallback& aCb);
+
+ static void
+ TraverseObjectShim(void* aData, JS::GCCellPtr aThing);
+
+ void TraverseNativeRoots(nsCycleCollectionNoteRootCallback& aCb);
+
+ static void TraceBlackJS(JSTracer* aTracer, void* aData);
+ static void TraceGrayJS(JSTracer* aTracer, void* aData);
+ static void GCCallback(JSContext* aContext, JSGCStatus aStatus, void* aData);
+ static void GCSliceCallback(JSContext* aContext, JS::GCProgress aProgress,
+ const JS::GCDescription& aDesc);
+ static void GCNurseryCollectionCallback(JSContext* aContext,
+ JS::GCNurseryProgress aProgress,
+ JS::gcreason::Reason aReason);
+ static void OutOfMemoryCallback(JSContext* aContext, void* aData);
+ static void LargeAllocationFailureCallback(void* aData);
+ static bool ContextCallback(JSContext* aCx, unsigned aOperation,
+ void* aData);
+ static JSObject* GetIncumbentGlobalCallback(JSContext* aCx);
+ static bool EnqueuePromiseJobCallback(JSContext* aCx,
+ JS::HandleObject aJob,
+ JS::HandleObject aAllocationSite,
+ JS::HandleObject aIncumbentGlobal,
+ void* aData);
+#ifdef SPIDERMONKEY_PROMISE
+ static void PromiseRejectionTrackerCallback(JSContext* aCx,
+ JS::HandleObject aPromise,
+ PromiseRejectionHandlingState state,
+ void* aData);
+#endif // SPIDERMONKEY_PROMISE
+
+ virtual void TraceNativeBlackRoots(JSTracer* aTracer) { };
+ void TraceNativeGrayRoots(JSTracer* aTracer);
+
+ void AfterProcessMicrotask(uint32_t aRecursionDepth);
+public:
+ void ProcessStableStateQueue();
+private:
+ void ProcessMetastableStateQueue(uint32_t aRecursionDepth);
+
+public:
+ enum DeferredFinalizeType {
+ FinalizeIncrementally,
+ FinalizeNow,
+ };
+
+ void FinalizeDeferredThings(DeferredFinalizeType aType);
+
+ // Two conditions, JSOutOfMemory and JSLargeAllocationFailure, are noted in
+ // crash reports. Here are the values that can appear in the reports:
+ enum class OOMState : uint32_t {
+ // The condition has never happened. No entry appears in the crash report.
+ OK,
+
+ // We are currently reporting the given condition.
+ //
+ // Suppose a crash report contains "JSLargeAllocationFailure:
+ // Reporting". This means we crashed while executing memory-pressure
+ // observers, trying to shake loose some memory. The large allocation in
+ // question did not return null: it is still on the stack. Had we not
+ // crashed, it would have been retried.
+ Reporting,
+
+ // The condition has been reported since the last GC.
+ //
+ // If a crash report contains "JSOutOfMemory: Reported", that means a small
+ // allocation failed, and then we crashed, probably due to buggy
+ // error-handling code that ran after allocation returned null.
+ //
+ // This contrasts with "Reporting" which means that no error-handling code
+ // had executed yet.
+ Reported,
+
+ // The condition has happened, but a GC cycle ended since then.
+ //
+ // GC is taken as a proxy for "we've been banging on the heap a good bit
+ // now and haven't crashed; the OOM was probably handled correctly".
+ Recovered
+ };
+
+private:
+ void AnnotateAndSetOutOfMemory(OOMState* aStatePtr, OOMState aNewState);
+ void OnGC(JSGCStatus aStatus);
+ void OnOutOfMemory();
+ void OnLargeAllocationFailure();
+
+public:
+ void AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer);
+ void RemoveJSHolder(void* aHolder);
+#ifdef DEBUG
+ bool IsJSHolder(void* aHolder);
+ void AssertNoObjectsToTrace(void* aPossibleJSHolder);
+#endif
+
+ already_AddRefed<nsIException> GetPendingException() const;
+ void SetPendingException(nsIException* aException);
+
+ std::queue<nsCOMPtr<nsIRunnable>>& GetPromiseMicroTaskQueue();
+ std::queue<nsCOMPtr<nsIRunnable>>& GetDebuggerPromiseMicroTaskQueue();
+
+ nsCycleCollectionParticipant* GCThingParticipant();
+ nsCycleCollectionParticipant* ZoneParticipant();
+
+ nsresult TraverseRoots(nsCycleCollectionNoteRootCallback& aCb);
+ virtual bool UsefulToMergeZones() const;
+ void FixWeakMappingGrayBits() const;
+ bool AreGCGrayBitsValid() const;
+ void GarbageCollect(uint32_t aReason) const;
+
+ void NurseryWrapperAdded(nsWrapperCache* aCache);
+ void NurseryWrapperPreserved(JSObject* aWrapper);
+ void JSObjectsTenured();
+
+ void DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
+ DeferredFinalizeFunction aFunc,
+ void* aThing);
+ void DeferredFinalize(nsISupports* aSupports);
+
+ void DumpJSHeap(FILE* aFile);
+
+ virtual void PrepareForForgetSkippable() = 0;
+ virtual void BeginCycleCollectionCallback() = 0;
+ virtual void EndCycleCollectionCallback(CycleCollectorResults& aResults) = 0;
+ virtual void DispatchDeferredDeletion(bool aContinuation, bool aPurge = false) = 0;
+
+ JSContext* Context() const
+ {
+ MOZ_ASSERT(mJSContext);
+ return mJSContext;
+ }
+
+ JS::RootingContext* RootingCx() const
+ {
+ MOZ_ASSERT(mJSContext);
+ return JS::RootingContext::get(mJSContext);
+ }
+
+ bool MicroTaskCheckpointDisabled() const
+ {
+ return mDisableMicroTaskCheckpoint;
+ }
+
+ void DisableMicroTaskCheckpoint(bool aDisable)
+ {
+ mDisableMicroTaskCheckpoint = aDisable;
+ }
+
+ class MOZ_RAII AutoDisableMicroTaskCheckpoint
+ {
+ public:
+ AutoDisableMicroTaskCheckpoint()
+ : mCCJSCX(CycleCollectedJSContext::Get())
+ {
+ mOldValue = mCCJSCX->MicroTaskCheckpointDisabled();
+ mCCJSCX->DisableMicroTaskCheckpoint(true);
+ }
+
+ ~AutoDisableMicroTaskCheckpoint()
+ {
+ mCCJSCX->DisableMicroTaskCheckpoint(mOldValue);
+ }
+
+ CycleCollectedJSContext* mCCJSCX;
+ bool mOldValue;
+ };
+
+protected:
+ JSContext* MaybeContext() const { return mJSContext; }
+
+public:
+ // nsThread entrypoints
+ virtual void BeforeProcessTask(bool aMightBlock) { };
+ virtual void AfterProcessTask(uint32_t aRecursionDepth);
+
+ // microtask processor entry point
+ void AfterProcessMicrotask();
+
+ uint32_t RecursionDepth();
+
+ // Run in stable state (call through nsContentUtils)
+ void RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable);
+ // This isn't in the spec at all yet, but this gets the behavior we want for IDB.
+ // Runs after the current microtask completes.
+ void RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable);
+
+ // Get the current thread's CycleCollectedJSContext. Returns null if there
+ // isn't one.
+ static CycleCollectedJSContext* Get();
+
+ // Add aZone to the set of zones waiting for a GC.
+ void AddZoneWaitingForGC(JS::Zone* aZone)
+ {
+ mZonesWaitingForGC.PutEntry(aZone);
+ }
+
+ // Prepare any zones for GC that have been passed to AddZoneWaitingForGC()
+ // since the last GC or since the last call to PrepareWaitingZonesForGC(),
+ // whichever was most recent. If there were no such zones, prepare for a
+ // full GC.
+ void PrepareWaitingZonesForGC();
+
+ // Queue an async microtask to the current main or worker thread.
+ virtual void DispatchToMicroTask(already_AddRefed<nsIRunnable> aRunnable);
+
+ // Storage for watching rejected promises waiting for some client to
+ // consume their rejection.
+#ifdef SPIDERMONKEY_PROMISE
+ // Promises in this list have been rejected in the last turn of the
+ // event loop without the rejection being handled.
+ // Note that this can contain nullptrs in place of promises removed because
+ // they're consumed before it'd be reported.
+ JS::PersistentRooted<JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>> mUncaughtRejections;
+
+ // Promises in this list have previously been reported as rejected
+ // (because they were in the above list), but the rejection was handled
+ // in the last turn of the event loop.
+ JS::PersistentRooted<JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>> mConsumedRejections;
+#else
+ // We store values as `nsISupports` to avoid adding compile-time dependencies
+ // from xpcom to dom/promise, but they can really only have a single concrete
+ // type.
+ nsTArray<nsCOMPtr<nsISupports /* Promise */>> mUncaughtRejections;
+ nsTArray<nsCOMPtr<nsISupports /* Promise */ >> mConsumedRejections;
+#endif // SPIDERMONKEY_PROMISE
+ nsTArray<nsCOMPtr<nsISupports /* UncaughtRejectionObserver */ >> mUncaughtRejectionObservers;
+
+private:
+ JSGCThingParticipant mGCThingCycleCollectorGlobal;
+
+ JSZoneParticipant mJSZoneCycleCollectorGlobal;
+
+ JSContext* mJSContext;
+
+ JS::GCSliceCallback mPrevGCSliceCallback;
+ JS::GCNurseryCollectionCallback mPrevGCNurseryCollectionCallback;
+
+ nsDataHashtable<nsPtrHashKey<void>, nsScriptObjectTracer*> mJSHolders;
+
+ typedef nsDataHashtable<nsFuncPtrHashKey<DeferredFinalizeFunction>, void*>
+ DeferredFinalizerTable;
+ DeferredFinalizerTable mDeferredFinalizerTable;
+
+ RefPtr<IncrementalFinalizeRunnable> mFinalizeRunnable;
+
+ nsCOMPtr<nsIException> mPendingException;
+ nsThread* mOwningThread; // Manual refcounting to avoid include hell.
+
+ struct RunInMetastableStateData
+ {
+ nsCOMPtr<nsIRunnable> mRunnable;
+ uint32_t mRecursionDepth;
+ };
+
+ nsTArray<nsCOMPtr<nsIRunnable>> mStableStateEvents;
+ nsTArray<RunInMetastableStateData> mMetastableStateEvents;
+ uint32_t mBaseRecursionDepth;
+ bool mDoingStableStates;
+
+ bool mDisableMicroTaskCheckpoint;
+
+ OOMState mOutOfMemoryState;
+ OOMState mLargeAllocationFailureState;
+
+ static const size_t kSegmentSize = 512;
+ SegmentedVector<nsWrapperCache*, kSegmentSize, InfallibleAllocPolicy>
+ mNurseryObjects;
+ SegmentedVector<JS::PersistentRooted<JSObject*>, kSegmentSize,
+ InfallibleAllocPolicy>
+ mPreservedNurseryObjects;
+
+ nsTHashtable<nsPtrHashKey<JS::Zone>> mZonesWaitingForGC;
+
+ struct EnvironmentPreparer : public js::ScriptEnvironmentPreparer {
+ void invoke(JS::HandleObject scope, Closure& closure) override;
+ };
+ EnvironmentPreparer mEnvironmentPreparer;
+};
+
+void TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer);
+
+// Returns true if the JS::TraceKind is one the cycle collector cares about.
+inline bool AddToCCKind(JS::TraceKind aKind)
+{
+ return aKind == JS::TraceKind::Object || aKind == JS::TraceKind::Script || aKind == JS::TraceKind::Scope;
+}
+
+bool
+GetBuildId(JS::BuildIdCharVector* aBuildID);
+
+} // namespace mozilla
+
+#endif // mozilla_CycleCollectedJSContext_h__
diff --git a/xpcom/base/Debug.cpp b/xpcom/base/Debug.cpp
new file mode 100644
index 0000000000..cc5272da05
--- /dev/null
+++ b/xpcom/base/Debug.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 "mozilla/Debug.h"
+
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+#ifdef XP_WIN
+
+void
+mozilla::PrintToDebugger(const char* aStr)
+{
+ if (::IsDebuggerPresent()) {
+ ::OutputDebugStringA(aStr);
+ }
+}
+
+#endif
diff --git a/xpcom/base/Debug.h b/xpcom/base/Debug.h
new file mode 100644
index 0000000000..2479799aef
--- /dev/null
+++ b/xpcom/base/Debug.h
@@ -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/. */
+
+#ifndef mozilla_Debug_h__
+#define mozilla_Debug_h__
+
+namespace mozilla {
+
+#ifdef XP_WIN
+
+// Print aStr to a debugger if the debugger is attached.
+void PrintToDebugger(const char* aStr);
+
+#endif
+
+} // namespace mozilla
+
+#endif // mozilla_Debug_h__
diff --git a/xpcom/base/DebuggerOnGCRunnable.cpp b/xpcom/base/DebuggerOnGCRunnable.cpp
new file mode 100644
index 0000000000..96f4cddcb7
--- /dev/null
+++ b/xpcom/base/DebuggerOnGCRunnable.cpp
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/DebuggerOnGCRunnable.h"
+
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/CycleCollectedJSContext.h"
+#include "mozilla/Move.h"
+#include "js/Debug.h"
+
+namespace mozilla {
+
+/* static */ nsresult
+DebuggerOnGCRunnable::Enqueue(JSContext* aCx, const JS::GCDescription& aDesc)
+{
+ auto gcEvent = aDesc.toGCEvent(aCx);
+ if (!gcEvent) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ RefPtr<DebuggerOnGCRunnable> runOnGC =
+ new DebuggerOnGCRunnable(Move(gcEvent));
+ return NS_DispatchToCurrentThread(runOnGC);
+}
+
+NS_IMETHODIMP
+DebuggerOnGCRunnable::Run()
+{
+ AutoJSAPI jsapi;
+ jsapi.Init();
+ if (!JS::dbg::FireOnGarbageCollectionHook(jsapi.cx(), Move(mGCData))) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ return NS_OK;
+}
+
+nsresult
+DebuggerOnGCRunnable::Cancel()
+{
+ mGCData = nullptr;
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/xpcom/base/DebuggerOnGCRunnable.h b/xpcom/base/DebuggerOnGCRunnable.h
new file mode 100644
index 0000000000..8f96216136
--- /dev/null
+++ b/xpcom/base/DebuggerOnGCRunnable.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_DebuggerOnGCRunnable_h
+#define mozilla_DebuggerOnGCRunnable_h
+
+#include "nsThreadUtils.h"
+#include "js/GCAPI.h"
+#include "mozilla/Move.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+
+// Runnable to fire the SpiderMonkey Debugger API's onGarbageCollection hook.
+class DebuggerOnGCRunnable : public CancelableRunnable
+{
+ JS::dbg::GarbageCollectionEvent::Ptr mGCData;
+
+ explicit DebuggerOnGCRunnable(JS::dbg::GarbageCollectionEvent::Ptr&& aGCData)
+ : mGCData(Move(aGCData))
+ { }
+
+public:
+ static nsresult Enqueue(JSContext* aCx, const JS::GCDescription& aDesc);
+
+ NS_DECL_NSIRUNNABLE
+ nsresult Cancel() override;
+};
+
+} // namespace mozilla
+
+#endif // ifdef mozilla_dom_DebuggerOnGCRunnable_h
diff --git a/xpcom/base/DeferredFinalize.cpp b/xpcom/base/DeferredFinalize.cpp
new file mode 100644
index 0000000000..b9a71b81cd
--- /dev/null
+++ b/xpcom/base/DeferredFinalize.cpp
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/DeferredFinalize.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/CycleCollectedJSContext.h"
+
+void
+mozilla::DeferredFinalize(nsISupports* aSupports)
+{
+ CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
+ MOZ_ASSERT(cx, "Should have a CycleCollectedJSContext by now");
+ cx->DeferredFinalize(aSupports);
+}
+
+void
+mozilla::DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
+ DeferredFinalizeFunction aFunc,
+ void* aThing)
+{
+ CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
+ MOZ_ASSERT(cx, "Should have a CycleCollectedJSContext by now");
+ cx->DeferredFinalize(aAppendFunc, aFunc, aThing);
+}
diff --git a/xpcom/base/DeferredFinalize.h b/xpcom/base/DeferredFinalize.h
new file mode 100644
index 0000000000..7d9c588811
--- /dev/null
+++ b/xpcom/base/DeferredFinalize.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_DeferredFinalize_h
+#define mozilla_DeferredFinalize_h
+
+class nsISupports;
+
+namespace mozilla {
+
+// Called back from DeferredFinalize. Should add 'thing' to the array of smart
+// pointers in 'pointers', creating the array if 'pointers' is null, and return
+// the array.
+typedef void* (*DeferredFinalizeAppendFunction)(void* aPointers, void* aThing);
+
+// Called to finalize a number of objects. Slice is the number of objects to
+// finalize. The return value indicates whether it finalized all objects in the
+// buffer. If it returns true, the function will not be called again, so the
+// function should free aData.
+typedef bool (*DeferredFinalizeFunction)(uint32_t aSlice, void* aData);
+
+void DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
+ DeferredFinalizeFunction aFunc,
+ void* aThing);
+
+void DeferredFinalize(nsISupports* aSupports);
+
+} // namespace mozilla
+
+#endif // mozilla_DeferredFinalize_h
diff --git a/xpcom/base/ErrorList.h b/xpcom/base/ErrorList.h
new file mode 100644
index 0000000000..cfa461fe40
--- /dev/null
+++ b/xpcom/base/ErrorList.h
@@ -0,0 +1,1026 @@
+// IWYU pragma: private, include "nsError.h"
+/* Helper file for nsError.h, via preprocessor magic */
+ /* Standard "it worked" return value */
+ ERROR(NS_OK, 0),
+
+ /* ======================================================================= */
+ /* Core errors, not part of any modules */
+ /* ======================================================================= */
+ ERROR(NS_ERROR_BASE, 0xC1F30000),
+ /* Returned when an instance is not initialized */
+ ERROR(NS_ERROR_NOT_INITIALIZED, NS_ERROR_BASE + 1),
+ /* Returned when an instance is already initialized */
+ ERROR(NS_ERROR_ALREADY_INITIALIZED, NS_ERROR_BASE + 2),
+ /* Returned by a not implemented function */
+ ERROR(NS_ERROR_NOT_IMPLEMENTED, 0x80004001),
+ /* Returned when a given interface is not supported. */
+ ERROR(NS_NOINTERFACE, 0x80004002),
+ ERROR(NS_ERROR_NO_INTERFACE, NS_NOINTERFACE),
+ /* Returned when a function aborts */
+ ERROR(NS_ERROR_ABORT, 0x80004004),
+ /* Returned when a function fails */
+ ERROR(NS_ERROR_FAILURE, 0x80004005),
+ /* Returned when an unexpected error occurs */
+ ERROR(NS_ERROR_UNEXPECTED, 0x8000ffff),
+ /* Returned when a memory allocation fails */
+ ERROR(NS_ERROR_OUT_OF_MEMORY, 0x8007000e),
+ /* Returned when an illegal value is passed */
+ ERROR(NS_ERROR_ILLEGAL_VALUE, 0x80070057),
+ ERROR(NS_ERROR_INVALID_ARG, NS_ERROR_ILLEGAL_VALUE),
+ ERROR(NS_ERROR_INVALID_POINTER, NS_ERROR_INVALID_ARG),
+ ERROR(NS_ERROR_NULL_POINTER, NS_ERROR_INVALID_ARG),
+ /* Returned when a class doesn't allow aggregation */
+ ERROR(NS_ERROR_NO_AGGREGATION, 0x80040110),
+ /* Returned when an operation can't complete due to an unavailable resource */
+ ERROR(NS_ERROR_NOT_AVAILABLE, 0x80040111),
+ /* Returned when a class is not registered */
+ ERROR(NS_ERROR_FACTORY_NOT_REGISTERED, 0x80040154),
+ /* Returned when a class cannot be registered, but may be tried again later */
+ ERROR(NS_ERROR_FACTORY_REGISTER_AGAIN, 0x80040155),
+ /* Returned when a dynamically loaded factory couldn't be found */
+ ERROR(NS_ERROR_FACTORY_NOT_LOADED, 0x800401f8),
+ /* Returned when a factory doesn't support signatures */
+ ERROR(NS_ERROR_FACTORY_NO_SIGNATURE_SUPPORT, NS_ERROR_BASE + 0x101),
+ /* Returned when a factory already is registered */
+ ERROR(NS_ERROR_FACTORY_EXISTS, NS_ERROR_BASE + 0x100),
+
+
+ /* ======================================================================= */
+ /* 1: NS_ERROR_MODULE_XPCOM */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_XPCOM
+ /* Result codes used by nsIVariant */
+ ERROR(NS_ERROR_CANNOT_CONVERT_DATA, FAILURE(1)),
+ ERROR(NS_ERROR_OBJECT_IS_IMMUTABLE, FAILURE(2)),
+ ERROR(NS_ERROR_LOSS_OF_SIGNIFICANT_DATA, FAILURE(3)),
+ /* Result code used by nsIThreadManager */
+ ERROR(NS_ERROR_NOT_SAME_THREAD, FAILURE(4)),
+ /* Various operations are not permitted during XPCOM shutdown and will fail
+ * with this exception. */
+ ERROR(NS_ERROR_ILLEGAL_DURING_SHUTDOWN, FAILURE(30)),
+ ERROR(NS_ERROR_SERVICE_NOT_AVAILABLE, FAILURE(22)),
+
+ ERROR(NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA, SUCCESS(1)),
+ /* Used by nsCycleCollectionParticipant */
+ ERROR(NS_SUCCESS_INTERRUPTED_TRAVERSE, SUCCESS(2)),
+ /* DEPRECATED */
+ ERROR(NS_ERROR_SERVICE_NOT_FOUND, SUCCESS(22)),
+ /* DEPRECATED */
+ ERROR(NS_ERROR_SERVICE_IN_USE, SUCCESS(23)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 2: NS_ERROR_MODULE_BASE */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_BASE
+ /* I/O Errors */
+
+ /* Stream closed */
+ ERROR(NS_BASE_STREAM_CLOSED, FAILURE(2)),
+ /* Error from the operating system */
+ ERROR(NS_BASE_STREAM_OSERROR, FAILURE(3)),
+ /* Illegal arguments */
+ ERROR(NS_BASE_STREAM_ILLEGAL_ARGS, FAILURE(4)),
+ /* For unichar streams */
+ ERROR(NS_BASE_STREAM_NO_CONVERTER, FAILURE(5)),
+ /* For unichar streams */
+ ERROR(NS_BASE_STREAM_BAD_CONVERSION, FAILURE(6)),
+ ERROR(NS_BASE_STREAM_WOULD_BLOCK, FAILURE(7)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 3: NS_ERROR_MODULE_GFX */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_GFX
+ /* no printer available (e.g. cannot find _any_ printer) */
+ ERROR(NS_ERROR_GFX_PRINTER_NO_PRINTER_AVAILABLE, FAILURE(1)),
+ /* _specified_ (by name) printer not found */
+ ERROR(NS_ERROR_GFX_PRINTER_NAME_NOT_FOUND, FAILURE(2)),
+ /* print-to-file: could not open output file */
+ ERROR(NS_ERROR_GFX_PRINTER_COULD_NOT_OPEN_FILE, FAILURE(3)),
+ /* print: starting document */
+ ERROR(NS_ERROR_GFX_PRINTER_STARTDOC, FAILURE(4)),
+ /* print: ending document */
+ ERROR(NS_ERROR_GFX_PRINTER_ENDDOC, FAILURE(5)),
+ /* print: starting page */
+ ERROR(NS_ERROR_GFX_PRINTER_STARTPAGE, FAILURE(6)),
+ /* The document is still being loaded */
+ ERROR(NS_ERROR_GFX_PRINTER_DOC_IS_BUSY, FAILURE(7)),
+
+ /* Font cmap is strangely structured - avoid this font! */
+ ERROR(NS_ERROR_GFX_CMAP_MALFORMED, FAILURE(51)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 4: NS_ERROR_MODULE_WIDGET */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_WIDGET
+ /* Used by:
+ * - nsIWidget::NotifyIME()
+ * - nsIWidget::OnWindowedPluginKeyEvent()
+ * Returned when the notification or the event is handled and it's consumed
+ * by somebody. */
+ ERROR(NS_SUCCESS_EVENT_CONSUMED, SUCCESS(1)),
+ /* Used by:
+ * - nsIWidget::OnWindowedPluginKeyEvent()
+ * Returned when the event is handled correctly but the result will be
+ * notified asynchronously. */
+ ERROR(NS_SUCCESS_EVENT_HANDLED_ASYNCHRONOUSLY, SUCCESS(2)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 6: NS_ERROR_MODULE_NETWORK */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_NETWORK
+ /* General async request error codes:
+ *
+ * These error codes are commonly passed through callback methods to indicate
+ * the status of some requested async request.
+ *
+ * For example, see nsIRequestObserver::onStopRequest.
+ */
+
+ /* The async request completed successfully. */
+ ERROR(NS_BINDING_SUCCEEDED, NS_OK),
+
+ /* The async request failed for some unknown reason. */
+ ERROR(NS_BINDING_FAILED, FAILURE(1)),
+ /* The async request failed because it was aborted by some user action. */
+ ERROR(NS_BINDING_ABORTED, FAILURE(2)),
+ /* The async request has been "redirected" to a different async request.
+ * (e.g., an HTTP redirect occurred).
+ *
+ * This error code is used with load groups to notify the load group observer
+ * when a request in the load group is redirected to another request. */
+ ERROR(NS_BINDING_REDIRECTED, FAILURE(3)),
+ /* The async request has been "retargeted" to a different "handler."
+ *
+ * This error code is used with load groups to notify the load group observer
+ * when a request in the load group is removed from the load group and added
+ * to a different load group. */
+ ERROR(NS_BINDING_RETARGETED, FAILURE(4)),
+
+ /* Miscellaneous error codes: These errors are not typically passed via
+ * onStopRequest. */
+
+ /* The URI is malformed. */
+ ERROR(NS_ERROR_MALFORMED_URI, FAILURE(10)),
+ /* The requested action could not be completed while the object is busy.
+ * Implementations of nsIChannel::asyncOpen will commonly return this error
+ * if the channel has already been opened (and has not yet been closed). */
+ ERROR(NS_ERROR_IN_PROGRESS, FAILURE(15)),
+ /* Returned from nsIChannel::asyncOpen to indicate that OnDataAvailable will
+ * not be called because there is no content available. This is used by
+ * helper app style protocols (e.g., mailto). XXX perhaps this should be a
+ * success code. */
+ ERROR(NS_ERROR_NO_CONTENT, FAILURE(17)),
+ /* The URI scheme corresponds to an unknown protocol handler. */
+ ERROR(NS_ERROR_UNKNOWN_PROTOCOL, FAILURE(18)),
+ /* The content encoding of the source document was incorrect, for example
+ * returning a plain HTML document advertised as Content-Encoding: gzip */
+ ERROR(NS_ERROR_INVALID_CONTENT_ENCODING, FAILURE(27)),
+ /* A transport level corruption was found in the source document. for example
+ * a document with a calculated checksum that does not match the Content-MD5
+ * http header. */
+ ERROR(NS_ERROR_CORRUPTED_CONTENT, FAILURE(29)),
+ /* A content signature verification failed for some reason. This can be either
+ * an actual verification error, or any other error that led to the fact that
+ * a content signature that was expected couldn't be verified. */
+ ERROR(NS_ERROR_INVALID_SIGNATURE, FAILURE(58)),
+ /* While parsing for the first component of a header field using syntax as in
+ * Content-Disposition or Content-Type, the first component was found to be
+ * empty, such as in: Content-Disposition: ; filename=foo */
+ ERROR(NS_ERROR_FIRST_HEADER_FIELD_COMPONENT_EMPTY, FAILURE(34)),
+ /* Returned from nsIChannel::asyncOpen when trying to open the channel again
+ * (reopening is not supported). */
+ ERROR(NS_ERROR_ALREADY_OPENED, FAILURE(73)),
+
+ /* Connectivity error codes: */
+
+ /* The connection is already established. XXX unused - consider removing. */
+ ERROR(NS_ERROR_ALREADY_CONNECTED, FAILURE(11)),
+ /* The connection does not exist. XXX unused - consider removing. */
+ ERROR(NS_ERROR_NOT_CONNECTED, FAILURE(12)),
+ /* The connection attempt failed, for example, because no server was
+ * listening at specified host:port. */
+ ERROR(NS_ERROR_CONNECTION_REFUSED, FAILURE(13)),
+ /* The connection was lost due to a timeout error. */
+ ERROR(NS_ERROR_NET_TIMEOUT, FAILURE(14)),
+ /* The requested action could not be completed while the networking library
+ * is in the offline state. */
+ ERROR(NS_ERROR_OFFLINE, FAILURE(16)),
+ /* The requested action was prohibited because it would have caused the
+ * networking library to establish a connection to an unsafe or otherwise
+ * banned port. */
+ ERROR(NS_ERROR_PORT_ACCESS_NOT_ALLOWED, FAILURE(19)),
+ /* The connection was established, but no data was ever received. */
+ ERROR(NS_ERROR_NET_RESET, FAILURE(20)),
+ /* The connection was established, but the data transfer was interrupted. */
+ ERROR(NS_ERROR_NET_INTERRUPT, FAILURE(71)),
+ /* The connection attempt to a proxy failed. */
+ ERROR(NS_ERROR_PROXY_CONNECTION_REFUSED, FAILURE(72)),
+ /* A transfer was only partially done when it completed. */
+ ERROR(NS_ERROR_NET_PARTIAL_TRANSFER, FAILURE(76)),
+ /* HTTP/2 detected invalid TLS configuration */
+ ERROR(NS_ERROR_NET_INADEQUATE_SECURITY, FAILURE(82)),
+
+ /* XXX really need to better rationalize these error codes. are consumers of
+ * necko really expected to know how to discern the meaning of these?? */
+ /* This request is not resumable, but it was tried to resume it, or to
+ * request resume-specific data. */
+ ERROR(NS_ERROR_NOT_RESUMABLE, FAILURE(25)),
+ /* The request failed as a result of a detected redirection loop. */
+ ERROR(NS_ERROR_REDIRECT_LOOP, FAILURE(31)),
+ /* It was attempted to resume the request, but the entity has changed in the
+ * meantime. */
+ ERROR(NS_ERROR_ENTITY_CHANGED, FAILURE(32)),
+ /* The request failed because the content type returned by the server was not
+ * a type expected by the channel (for nested channels such as the JAR
+ * channel). */
+ ERROR(NS_ERROR_UNSAFE_CONTENT_TYPE, FAILURE(74)),
+ /* The request failed because the user tried to access to a remote XUL
+ * document from a website that is not in its white-list. */
+ ERROR(NS_ERROR_REMOTE_XUL, FAILURE(75)),
+ /* The request resulted in an error page being displayed. */
+ ERROR(NS_ERROR_LOAD_SHOWED_ERRORPAGE, FAILURE(77)),
+
+
+ /* FTP specific error codes: */
+
+ ERROR(NS_ERROR_FTP_LOGIN, FAILURE(21)),
+ ERROR(NS_ERROR_FTP_CWD, FAILURE(22)),
+ ERROR(NS_ERROR_FTP_PASV, FAILURE(23)),
+ ERROR(NS_ERROR_FTP_PWD, FAILURE(24)),
+ ERROR(NS_ERROR_FTP_LIST, FAILURE(28)),
+
+ /* DNS specific error codes: */
+
+ /* The lookup of a hostname failed. This generally refers to the hostname
+ * from the URL being loaded. */
+ ERROR(NS_ERROR_UNKNOWN_HOST, FAILURE(30)),
+ /* A low or medium priority DNS lookup failed because the pending queue was
+ * already full. High priorty (the default) always makes room */
+ ERROR(NS_ERROR_DNS_LOOKUP_QUEUE_FULL, FAILURE(33)),
+ /* The lookup of a proxy hostname failed. If a channel is configured to
+ * speak to a proxy server, then it will generate this error if the proxy
+ * hostname cannot be resolved. */
+ ERROR(NS_ERROR_UNKNOWN_PROXY_HOST, FAILURE(42)),
+
+
+ /* Socket specific error codes: */
+
+ /* The specified socket type does not exist. */
+ ERROR(NS_ERROR_UNKNOWN_SOCKET_TYPE, FAILURE(51)),
+ /* The specified socket type could not be created. */
+ ERROR(NS_ERROR_SOCKET_CREATE_FAILED, FAILURE(52)),
+ /* The operating system doesn't support the given type of address. */
+ ERROR(NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED, FAILURE(53)),
+ /* The address to which we tried to bind the socket was busy. */
+ ERROR(NS_ERROR_SOCKET_ADDRESS_IN_USE, FAILURE(54)),
+
+ /* Cache specific error codes: */
+ ERROR(NS_ERROR_CACHE_KEY_NOT_FOUND, FAILURE(61)),
+ ERROR(NS_ERROR_CACHE_DATA_IS_STREAM, FAILURE(62)),
+ ERROR(NS_ERROR_CACHE_DATA_IS_NOT_STREAM, FAILURE(63)),
+ ERROR(NS_ERROR_CACHE_WAIT_FOR_VALIDATION, FAILURE(64)),
+ ERROR(NS_ERROR_CACHE_ENTRY_DOOMED, FAILURE(65)),
+ ERROR(NS_ERROR_CACHE_READ_ACCESS_DENIED, FAILURE(66)),
+ ERROR(NS_ERROR_CACHE_WRITE_ACCESS_DENIED, FAILURE(67)),
+ ERROR(NS_ERROR_CACHE_IN_USE, FAILURE(68)),
+ /* Error passed through onStopRequest if the document could not be fetched
+ * from the cache. */
+ ERROR(NS_ERROR_DOCUMENT_NOT_CACHED, FAILURE(70)),
+
+ /* Effective TLD Service specific error codes: */
+
+ /* The requested number of domain levels exceeds those present in the host
+ * string. */
+ ERROR(NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS, FAILURE(80)),
+ /* The host string is an IP address. */
+ ERROR(NS_ERROR_HOST_IS_IP_ADDRESS, FAILURE(81)),
+
+
+ /* StreamLoader specific result codes: */
+
+ /* Result code returned by nsIStreamLoaderObserver to indicate that the
+ * observer is taking over responsibility for the data buffer, and the loader
+ * should NOT free it. */
+ ERROR(NS_SUCCESS_ADOPTED_DATA, SUCCESS(90)),
+
+ /* FTP */
+ ERROR(NS_NET_STATUS_BEGIN_FTP_TRANSACTION, SUCCESS(27)),
+ ERROR(NS_NET_STATUS_END_FTP_TRANSACTION, SUCCESS(28)),
+
+ /* This success code may be returned by nsIAuthModule::getNextToken to
+ * indicate that the authentication is finished and thus there's no need
+ * to call getNextToken again. */
+ ERROR(NS_SUCCESS_AUTH_FINISHED, SUCCESS(40)),
+
+ /* These are really not "results", they're statuses, used by nsITransport and
+ * friends. This is abuse of nsresult, but we'll put up with it for now. */
+ /* nsITransport */
+ ERROR(NS_NET_STATUS_READING, FAILURE(8)),
+ ERROR(NS_NET_STATUS_WRITING, FAILURE(9)),
+
+ /* nsISocketTransport */
+ ERROR(NS_NET_STATUS_RESOLVING_HOST, FAILURE(3)),
+ ERROR(NS_NET_STATUS_RESOLVED_HOST, FAILURE(11)),
+ ERROR(NS_NET_STATUS_CONNECTING_TO, FAILURE(7)),
+ ERROR(NS_NET_STATUS_CONNECTED_TO, FAILURE(4)),
+ ERROR(NS_NET_STATUS_SENDING_TO, FAILURE(5)),
+ ERROR(NS_NET_STATUS_WAITING_FOR, FAILURE(10)),
+ ERROR(NS_NET_STATUS_RECEIVING_FROM, FAILURE(6)),
+
+ /* nsIInterceptedChannel */
+ /* Generic error for non-specific failures during service worker interception */
+ ERROR(NS_ERROR_INTERCEPTION_FAILED, FAILURE(100)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 7: NS_ERROR_MODULE_PLUGINS */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_PLUGINS
+ ERROR(NS_ERROR_PLUGINS_PLUGINSNOTCHANGED, FAILURE(1000)),
+ ERROR(NS_ERROR_PLUGIN_DISABLED, FAILURE(1001)),
+ ERROR(NS_ERROR_PLUGIN_BLOCKLISTED, FAILURE(1002)),
+ ERROR(NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED, FAILURE(1003)),
+ ERROR(NS_ERROR_PLUGIN_CLICKTOPLAY, FAILURE(1004)),
+ ERROR(NS_PLUGIN_INIT_PENDING, SUCCESS(1005)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 8: NS_ERROR_MODULE_LAYOUT */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_LAYOUT
+ /* Return code for nsITableLayout */
+ ERROR(NS_TABLELAYOUT_CELL_NOT_FOUND, SUCCESS(0)),
+ /* Return code for nsFrame::GetNextPrevLineFromeBlockFrame */
+ ERROR(NS_POSITION_BEFORE_TABLE, SUCCESS(3)),
+ /** Return codes for nsPresState::GetProperty() */
+ /* Returned if the property exists */
+ ERROR(NS_STATE_PROPERTY_EXISTS, NS_OK),
+ /* Returned if the property does not exist */
+ ERROR(NS_STATE_PROPERTY_NOT_THERE, SUCCESS(5)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 9: NS_ERROR_MODULE_HTMLPARSER */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_HTMLPARSER
+ ERROR(NS_ERROR_HTMLPARSER_CONTINUE, NS_OK),
+
+ ERROR(NS_ERROR_HTMLPARSER_EOF, FAILURE(1000)),
+ ERROR(NS_ERROR_HTMLPARSER_UNKNOWN, FAILURE(1001)),
+ ERROR(NS_ERROR_HTMLPARSER_CANTPROPAGATE, FAILURE(1002)),
+ ERROR(NS_ERROR_HTMLPARSER_CONTEXTMISMATCH, FAILURE(1003)),
+ ERROR(NS_ERROR_HTMLPARSER_BADFILENAME, FAILURE(1004)),
+ ERROR(NS_ERROR_HTMLPARSER_BADURL, FAILURE(1005)),
+ ERROR(NS_ERROR_HTMLPARSER_INVALIDPARSERCONTEXT, FAILURE(1006)),
+ ERROR(NS_ERROR_HTMLPARSER_INTERRUPTED, FAILURE(1007)),
+ ERROR(NS_ERROR_HTMLPARSER_BLOCK, FAILURE(1008)),
+ ERROR(NS_ERROR_HTMLPARSER_BADTOKENIZER, FAILURE(1009)),
+ ERROR(NS_ERROR_HTMLPARSER_BADATTRIBUTE, FAILURE(1010)),
+ ERROR(NS_ERROR_HTMLPARSER_UNRESOLVEDDTD, FAILURE(1011)),
+ ERROR(NS_ERROR_HTMLPARSER_MISPLACEDTABLECONTENT, FAILURE(1012)),
+ ERROR(NS_ERROR_HTMLPARSER_BADDTD, FAILURE(1013)),
+ ERROR(NS_ERROR_HTMLPARSER_BADCONTEXT, FAILURE(1014)),
+ ERROR(NS_ERROR_HTMLPARSER_STOPPARSING, FAILURE(1015)),
+ ERROR(NS_ERROR_HTMLPARSER_UNTERMINATEDSTRINGLITERAL, FAILURE(1016)),
+ ERROR(NS_ERROR_HTMLPARSER_HIERARCHYTOODEEP, FAILURE(1017)),
+ ERROR(NS_ERROR_HTMLPARSER_FAKE_ENDTAG, FAILURE(1018)),
+ ERROR(NS_ERROR_HTMLPARSER_INVALID_COMMENT, FAILURE(1019)),
+
+ ERROR(NS_HTMLTOKENS_NOT_AN_ENTITY, SUCCESS(2000)),
+ ERROR(NS_HTMLPARSER_VALID_META_CHARSET, SUCCESS(3000)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 10: NS_ERROR_MODULE_RDF */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_RDF
+ /* Returned from nsIRDFDataSource::Assert() and Unassert() if the assertion
+ * (or unassertion was accepted by the datasource */
+ ERROR(NS_RDF_ASSERTION_ACCEPTED, NS_OK),
+ /* Returned from nsIRDFCursor::Advance() if the cursor has no more
+ * elements to enumerate */
+ ERROR(NS_RDF_CURSOR_EMPTY, SUCCESS(1)),
+ /* Returned from nsIRDFDataSource::GetSource() and GetTarget() if the
+ * source/target has no value */
+ ERROR(NS_RDF_NO_VALUE, SUCCESS(2)),
+ /* Returned from nsIRDFDataSource::Assert() and Unassert() if the assertion
+ * (or unassertion) was rejected by the datasource; i.e., the datasource was
+ * not willing to record the statement. */
+ ERROR(NS_RDF_ASSERTION_REJECTED, SUCCESS(3)),
+ /* Return this from rdfITripleVisitor to stop cycling */
+ ERROR(NS_RDF_STOP_VISIT, SUCCESS(4)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 11: NS_ERROR_MODULE_UCONV */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_UCONV
+ ERROR(NS_ERROR_UCONV_NOCONV, FAILURE(1)),
+ ERROR(NS_ERROR_UDEC_ILLEGALINPUT, FAILURE(14)),
+
+ ERROR(NS_SUCCESS_USING_FALLBACK_LOCALE, SUCCESS(2)),
+ ERROR(NS_OK_UDEC_EXACTLENGTH, SUCCESS(11)),
+ ERROR(NS_OK_UDEC_MOREINPUT, SUCCESS(12)),
+ ERROR(NS_OK_UDEC_MOREOUTPUT, SUCCESS(13)),
+ ERROR(NS_OK_UDEC_NOBOMFOUND, SUCCESS(14)),
+ ERROR(NS_OK_UENC_EXACTLENGTH, SUCCESS(33)),
+ ERROR(NS_OK_UENC_MOREOUTPUT, SUCCESS(34)),
+ ERROR(NS_ERROR_UENC_NOMAPPING, SUCCESS(35)),
+ ERROR(NS_OK_UENC_MOREINPUT, SUCCESS(36)),
+
+ /* BEGIN DEPRECATED */
+ ERROR(NS_EXACT_LENGTH, NS_OK_UDEC_EXACTLENGTH),
+ ERROR(NS_PARTIAL_MORE_INPUT, NS_OK_UDEC_MOREINPUT),
+ ERROR(NS_PARTIAL_MORE_OUTPUT, NS_OK_UDEC_MOREOUTPUT),
+ ERROR(NS_ERROR_ILLEGAL_INPUT, NS_ERROR_UDEC_ILLEGALINPUT),
+ /* END DEPRECATED */
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 13: NS_ERROR_MODULE_FILES */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_FILES
+ ERROR(NS_ERROR_FILE_UNRECOGNIZED_PATH, FAILURE(1)),
+ ERROR(NS_ERROR_FILE_UNRESOLVABLE_SYMLINK, FAILURE(2)),
+ ERROR(NS_ERROR_FILE_EXECUTION_FAILED, FAILURE(3)),
+ ERROR(NS_ERROR_FILE_UNKNOWN_TYPE, FAILURE(4)),
+ ERROR(NS_ERROR_FILE_DESTINATION_NOT_DIR, FAILURE(5)),
+ ERROR(NS_ERROR_FILE_TARGET_DOES_NOT_EXIST, FAILURE(6)),
+ ERROR(NS_ERROR_FILE_COPY_OR_MOVE_FAILED, FAILURE(7)),
+ ERROR(NS_ERROR_FILE_ALREADY_EXISTS, FAILURE(8)),
+ ERROR(NS_ERROR_FILE_INVALID_PATH, FAILURE(9)),
+ ERROR(NS_ERROR_FILE_DISK_FULL, FAILURE(10)),
+ ERROR(NS_ERROR_FILE_CORRUPTED, FAILURE(11)),
+ ERROR(NS_ERROR_FILE_NOT_DIRECTORY, FAILURE(12)),
+ ERROR(NS_ERROR_FILE_IS_DIRECTORY, FAILURE(13)),
+ ERROR(NS_ERROR_FILE_IS_LOCKED, FAILURE(14)),
+ ERROR(NS_ERROR_FILE_TOO_BIG, FAILURE(15)),
+ ERROR(NS_ERROR_FILE_NO_DEVICE_SPACE, FAILURE(16)),
+ ERROR(NS_ERROR_FILE_NAME_TOO_LONG, FAILURE(17)),
+ ERROR(NS_ERROR_FILE_NOT_FOUND, FAILURE(18)),
+ ERROR(NS_ERROR_FILE_READ_ONLY, FAILURE(19)),
+ ERROR(NS_ERROR_FILE_DIR_NOT_EMPTY, FAILURE(20)),
+ ERROR(NS_ERROR_FILE_ACCESS_DENIED, FAILURE(21)),
+
+ ERROR(NS_SUCCESS_FILE_DIRECTORY_EMPTY, SUCCESS(1)),
+ /* Result codes used by nsIDirectoryServiceProvider2 */
+ ERROR(NS_SUCCESS_AGGREGATE_RESULT, SUCCESS(2)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 14: NS_ERROR_MODULE_DOM */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_DOM
+ /* XXX If you add a new DOM error code, also add an error string to
+ * dom/base/domerr.msg */
+
+ /* Standard DOM error codes: http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html */
+ ERROR(NS_ERROR_DOM_INDEX_SIZE_ERR, FAILURE(1)),
+ ERROR(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR, FAILURE(3)),
+ ERROR(NS_ERROR_DOM_WRONG_DOCUMENT_ERR, FAILURE(4)),
+ ERROR(NS_ERROR_DOM_INVALID_CHARACTER_ERR, FAILURE(5)),
+ ERROR(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR, FAILURE(7)),
+ ERROR(NS_ERROR_DOM_NOT_FOUND_ERR, FAILURE(8)),
+ ERROR(NS_ERROR_DOM_NOT_SUPPORTED_ERR, FAILURE(9)),
+ ERROR(NS_ERROR_DOM_INUSE_ATTRIBUTE_ERR, FAILURE(10)),
+ ERROR(NS_ERROR_DOM_INVALID_STATE_ERR, FAILURE(11)),
+ ERROR(NS_ERROR_DOM_SYNTAX_ERR, FAILURE(12)),
+ ERROR(NS_ERROR_DOM_INVALID_MODIFICATION_ERR, FAILURE(13)),
+ ERROR(NS_ERROR_DOM_NAMESPACE_ERR, FAILURE(14)),
+ ERROR(NS_ERROR_DOM_INVALID_ACCESS_ERR, FAILURE(15)),
+ ERROR(NS_ERROR_DOM_TYPE_MISMATCH_ERR, FAILURE(17)),
+ ERROR(NS_ERROR_DOM_SECURITY_ERR, FAILURE(18)),
+ ERROR(NS_ERROR_DOM_NETWORK_ERR, FAILURE(19)),
+ ERROR(NS_ERROR_DOM_ABORT_ERR, FAILURE(20)),
+ ERROR(NS_ERROR_DOM_URL_MISMATCH_ERR, FAILURE(21)),
+ ERROR(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR, FAILURE(22)),
+ ERROR(NS_ERROR_DOM_TIMEOUT_ERR, FAILURE(23)),
+ ERROR(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR, FAILURE(24)),
+ ERROR(NS_ERROR_DOM_DATA_CLONE_ERR, FAILURE(25)),
+ /* XXX Should be JavaScript native errors */
+ ERROR(NS_ERROR_TYPE_ERR, FAILURE(26)),
+ ERROR(NS_ERROR_RANGE_ERR, FAILURE(27)),
+ /* StringEncoding API errors from http://wiki.whatwg.org/wiki/StringEncoding */
+ ERROR(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR, FAILURE(28)),
+ ERROR(NS_ERROR_DOM_INVALID_POINTER_ERR, FAILURE(29)),
+ /* WebCrypto API errors from http://www.w3.org/TR/WebCryptoAPI/ */
+ ERROR(NS_ERROR_DOM_UNKNOWN_ERR, FAILURE(30)),
+ ERROR(NS_ERROR_DOM_DATA_ERR, FAILURE(31)),
+ ERROR(NS_ERROR_DOM_OPERATION_ERR, FAILURE(32)),
+ /* https://heycam.github.io/webidl/#notallowederror */
+ ERROR(NS_ERROR_DOM_NOT_ALLOWED_ERR, FAILURE(33)),
+ /* DOM error codes defined by us */
+ ERROR(NS_ERROR_DOM_SECMAN_ERR, FAILURE(1001)),
+ ERROR(NS_ERROR_DOM_WRONG_TYPE_ERR, FAILURE(1002)),
+ ERROR(NS_ERROR_DOM_NOT_OBJECT_ERR, FAILURE(1003)),
+ ERROR(NS_ERROR_DOM_NOT_XPC_OBJECT_ERR, FAILURE(1004)),
+ ERROR(NS_ERROR_DOM_NOT_NUMBER_ERR, FAILURE(1005)),
+ ERROR(NS_ERROR_DOM_NOT_BOOLEAN_ERR, FAILURE(1006)),
+ ERROR(NS_ERROR_DOM_NOT_FUNCTION_ERR, FAILURE(1007)),
+ ERROR(NS_ERROR_DOM_TOO_FEW_PARAMETERS_ERR, FAILURE(1008)),
+ ERROR(NS_ERROR_DOM_BAD_DOCUMENT_DOMAIN, FAILURE(1009)),
+ ERROR(NS_ERROR_DOM_PROP_ACCESS_DENIED, FAILURE(1010)),
+ ERROR(NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED, FAILURE(1011)),
+ ERROR(NS_ERROR_DOM_BAD_URI, FAILURE(1012)),
+ ERROR(NS_ERROR_DOM_RETVAL_UNDEFINED, FAILURE(1013)),
+ ERROR(NS_ERROR_DOM_QUOTA_REACHED, FAILURE(1014)),
+ ERROR(NS_ERROR_DOM_JS_EXCEPTION, FAILURE(1015)),
+
+ /* A way to represent uncatchable exceptions */
+ ERROR(NS_ERROR_UNCATCHABLE_EXCEPTION, FAILURE(1016)),
+
+ /* An nsresult value to use in ErrorResult to indicate that we want to throw
+ a DOMException */
+ ERROR(NS_ERROR_DOM_DOMEXCEPTION, FAILURE(1017)),
+
+ /* An nsresult value to use in ErrorResult to indicate that we
+ * should just rethrow whatever is on the JSContext (which might be
+ * nothing if an uncatchable exception was thrown).
+ */
+ ERROR(NS_ERROR_DOM_EXCEPTION_ON_JSCONTEXT, FAILURE(1018)),
+
+ ERROR(NS_ERROR_DOM_MALFORMED_URI, FAILURE(1019)),
+ ERROR(NS_ERROR_DOM_INVALID_HEADER_NAME, FAILURE(1020)),
+
+ ERROR(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT, FAILURE(1021)),
+ ERROR(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_BE_OPENED, FAILURE(1022)),
+ ERROR(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING, FAILURE(1023)),
+ ERROR(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_LOADING_OR_DONE, FAILURE(1024)),
+ ERROR(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSEXML, FAILURE(1025)),
+ ERROR(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSETEXT, FAILURE(1026)),
+ ERROR(NS_ERROR_DOM_INVALID_STATE_XHR_CHUNKED_RESPONSETYPES_UNSUPPORTED_FOR_SYNC, FAILURE(1027)),
+ ERROR(NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC, FAILURE(1028)),
+
+ /* May be used to indicate when e.g. setting a property value didn't
+ * actually change the value, like for obj.foo = "bar"; obj.foo = "bar";
+ * the second assignment throws NS_SUCCESS_DOM_NO_OPERATION.
+ */
+ ERROR(NS_SUCCESS_DOM_NO_OPERATION, SUCCESS(1)),
+
+ /*
+ * A success code that indicates that evaluating a string of JS went
+ * just fine except it threw an exception. Only for legacy use by
+ * nsJSUtils.
+ */
+ ERROR(NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW, SUCCESS(2)),
+
+ /*
+ * A success code that indicates that evaluating a string of JS went
+ * just fine except it was killed by an uncatchable exception.
+ * Only for legacy use by nsJSUtils.
+ */
+ ERROR(NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE, SUCCESS(3)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 15: NS_ERROR_MODULE_IMGLIB */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_IMGLIB
+ ERROR(NS_IMAGELIB_SUCCESS_LOAD_FINISHED, SUCCESS(0)),
+ ERROR(NS_IMAGELIB_CHANGING_OWNER, SUCCESS(1)),
+
+ ERROR(NS_IMAGELIB_ERROR_FAILURE, FAILURE(5)),
+ ERROR(NS_IMAGELIB_ERROR_NO_DECODER, FAILURE(6)),
+ ERROR(NS_IMAGELIB_ERROR_NOT_FINISHED, FAILURE(7)),
+ ERROR(NS_IMAGELIB_ERROR_NO_ENCODER, FAILURE(9)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 17: NS_ERROR_MODULE_EDITOR */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_EDITOR
+ ERROR(NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND, SUCCESS(1)),
+ ERROR(NS_SUCCESS_EDITOR_FOUND_TARGET, SUCCESS(2)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 18: NS_ERROR_MODULE_XPCONNECT */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_XPCONNECT
+ ERROR(NS_ERROR_XPC_NOT_ENOUGH_ARGS, FAILURE(1)),
+ ERROR(NS_ERROR_XPC_NEED_OUT_OBJECT, FAILURE(2)),
+ ERROR(NS_ERROR_XPC_CANT_SET_OUT_VAL, FAILURE(3)),
+ ERROR(NS_ERROR_XPC_NATIVE_RETURNED_FAILURE, FAILURE(4)),
+ ERROR(NS_ERROR_XPC_CANT_GET_INTERFACE_INFO, FAILURE(5)),
+ ERROR(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO, FAILURE(6)),
+ ERROR(NS_ERROR_XPC_CANT_GET_METHOD_INFO, FAILURE(7)),
+ ERROR(NS_ERROR_XPC_UNEXPECTED, FAILURE(8)),
+ ERROR(NS_ERROR_XPC_BAD_CONVERT_JS, FAILURE(9)),
+ ERROR(NS_ERROR_XPC_BAD_CONVERT_NATIVE, FAILURE(10)),
+ ERROR(NS_ERROR_XPC_BAD_CONVERT_JS_NULL_REF, FAILURE(11)),
+ ERROR(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, FAILURE(12)),
+ ERROR(NS_ERROR_XPC_CANT_CONVERT_WN_TO_FUN, FAILURE(13)),
+ ERROR(NS_ERROR_XPC_CANT_DEFINE_PROP_ON_WN, FAILURE(14)),
+ ERROR(NS_ERROR_XPC_CANT_WATCH_WN_STATIC, FAILURE(15)),
+ ERROR(NS_ERROR_XPC_CANT_EXPORT_WN_STATIC, FAILURE(16)),
+ ERROR(NS_ERROR_XPC_SCRIPTABLE_CALL_FAILED, FAILURE(17)),
+ ERROR(NS_ERROR_XPC_SCRIPTABLE_CTOR_FAILED, FAILURE(18)),
+ ERROR(NS_ERROR_XPC_CANT_CALL_WO_SCRIPTABLE, FAILURE(19)),
+ ERROR(NS_ERROR_XPC_CANT_CTOR_WO_SCRIPTABLE, FAILURE(20)),
+ ERROR(NS_ERROR_XPC_CI_RETURNED_FAILURE, FAILURE(21)),
+ ERROR(NS_ERROR_XPC_GS_RETURNED_FAILURE, FAILURE(22)),
+ ERROR(NS_ERROR_XPC_BAD_CID, FAILURE(23)),
+ ERROR(NS_ERROR_XPC_BAD_IID, FAILURE(24)),
+ ERROR(NS_ERROR_XPC_CANT_CREATE_WN, FAILURE(25)),
+ ERROR(NS_ERROR_XPC_JS_THREW_EXCEPTION, FAILURE(26)),
+ ERROR(NS_ERROR_XPC_JS_THREW_NATIVE_OBJECT, FAILURE(27)),
+ ERROR(NS_ERROR_XPC_JS_THREW_JS_OBJECT, FAILURE(28)),
+ ERROR(NS_ERROR_XPC_JS_THREW_NULL, FAILURE(29)),
+ ERROR(NS_ERROR_XPC_JS_THREW_STRING, FAILURE(30)),
+ ERROR(NS_ERROR_XPC_JS_THREW_NUMBER, FAILURE(31)),
+ ERROR(NS_ERROR_XPC_JAVASCRIPT_ERROR, FAILURE(32)),
+ ERROR(NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS, FAILURE(33)),
+ ERROR(NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY, FAILURE(34)),
+ ERROR(NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY, FAILURE(35)),
+ ERROR(NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY, FAILURE(36)),
+ ERROR(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, FAILURE(37)),
+ ERROR(NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING, FAILURE(38)),
+ ERROR(NS_ERROR_XPC_SECURITY_MANAGER_VETO, FAILURE(39)),
+ ERROR(NS_ERROR_XPC_INTERFACE_NOT_SCRIPTABLE, FAILURE(40)),
+ ERROR(NS_ERROR_XPC_INTERFACE_NOT_FROM_NSISUPPORTS, FAILURE(41)),
+ ERROR(NS_ERROR_XPC_CANT_GET_JSOBJECT_OF_DOM_OBJECT, FAILURE(42)),
+ ERROR(NS_ERROR_XPC_CANT_SET_READ_ONLY_CONSTANT, FAILURE(43)),
+ ERROR(NS_ERROR_XPC_CANT_SET_READ_ONLY_ATTRIBUTE, FAILURE(44)),
+ ERROR(NS_ERROR_XPC_CANT_SET_READ_ONLY_METHOD, FAILURE(45)),
+ ERROR(NS_ERROR_XPC_CANT_ADD_PROP_TO_WRAPPED_NATIVE, FAILURE(46)),
+ ERROR(NS_ERROR_XPC_CALL_TO_SCRIPTABLE_FAILED, FAILURE(47)),
+ ERROR(NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED, FAILURE(48)),
+ ERROR(NS_ERROR_XPC_BAD_ID_STRING, FAILURE(49)),
+ ERROR(NS_ERROR_XPC_BAD_INITIALIZER_NAME, FAILURE(50)),
+ ERROR(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN, FAILURE(51)),
+ ERROR(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, FAILURE(52)),
+ ERROR(NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL, FAILURE(53)),
+ ERROR(NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE, FAILURE(54)),
+ /* any new errors here should have an associated entry added in xpc.msg */
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 19: NS_ERROR_MODULE_PROFILE */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_PROFILE
+ ERROR(NS_ERROR_LAUNCHED_CHILD_PROCESS, FAILURE(200)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 21: NS_ERROR_MODULE_SECURITY */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_SECURITY
+ /* Error code for CSP */
+ ERROR(NS_ERROR_CSP_FORM_ACTION_VIOLATION, FAILURE(98)),
+ ERROR(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION, FAILURE(99)),
+
+ /* Error code for Sub-Resource Integrity */
+ ERROR(NS_ERROR_SRI_CORRUPT, FAILURE(200)),
+ ERROR(NS_ERROR_SRI_DISABLED, FAILURE(201)),
+ ERROR(NS_ERROR_SRI_NOT_ELIGIBLE, FAILURE(202)),
+ ERROR(NS_ERROR_SRI_UNEXPECTED_HASH_TYPE, FAILURE(203)),
+ ERROR(NS_ERROR_SRI_IMPORT, FAILURE(204)),
+
+ /* CMS specific nsresult error codes. Note: the numbers used here correspond
+ * to the values in nsICMSMessageErrors.idl. */
+ ERROR(NS_ERROR_CMS_VERIFY_NOT_SIGNED, FAILURE(1024)),
+ ERROR(NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO, FAILURE(1025)),
+ ERROR(NS_ERROR_CMS_VERIFY_BAD_DIGEST, FAILURE(1026)),
+ ERROR(NS_ERROR_CMS_VERIFY_NOCERT, FAILURE(1028)),
+ ERROR(NS_ERROR_CMS_VERIFY_UNTRUSTED, FAILURE(1029)),
+ ERROR(NS_ERROR_CMS_VERIFY_ERROR_UNVERIFIED, FAILURE(1031)),
+ ERROR(NS_ERROR_CMS_VERIFY_ERROR_PROCESSING, FAILURE(1032)),
+ ERROR(NS_ERROR_CMS_VERIFY_BAD_SIGNATURE, FAILURE(1033)),
+ ERROR(NS_ERROR_CMS_VERIFY_DIGEST_MISMATCH, FAILURE(1034)),
+ ERROR(NS_ERROR_CMS_VERIFY_UNKNOWN_ALGO, FAILURE(1035)),
+ ERROR(NS_ERROR_CMS_VERIFY_UNSUPPORTED_ALGO, FAILURE(1036)),
+ ERROR(NS_ERROR_CMS_VERIFY_MALFORMED_SIGNATURE, FAILURE(1037)),
+ ERROR(NS_ERROR_CMS_VERIFY_HEADER_MISMATCH, FAILURE(1038)),
+ ERROR(NS_ERROR_CMS_VERIFY_NOT_YET_ATTEMPTED, FAILURE(1039)),
+ ERROR(NS_ERROR_CMS_VERIFY_CERT_WITHOUT_ADDRESS, FAILURE(1040)),
+ ERROR(NS_ERROR_CMS_ENCRYPT_NO_BULK_ALG, FAILURE(1056)),
+ ERROR(NS_ERROR_CMS_ENCRYPT_INCOMPLETE, FAILURE(1057)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 22: NS_ERROR_MODULE_DOM_XPATH */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_DOM_XPATH
+ /* DOM error codes from http://www.w3.org/TR/DOM-Level-3-XPath/ */
+ ERROR(NS_ERROR_DOM_INVALID_EXPRESSION_ERR, FAILURE(51)),
+ ERROR(NS_ERROR_DOM_TYPE_ERR, FAILURE(52)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 24: NS_ERROR_MODULE_URILOADER */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_URILOADER
+ ERROR(NS_ERROR_WONT_HANDLE_CONTENT, FAILURE(1)),
+ /* The load has been cancelled because it was found on a malware or phishing
+ * blacklist. */
+ ERROR(NS_ERROR_MALWARE_URI, FAILURE(30)),
+ ERROR(NS_ERROR_PHISHING_URI, FAILURE(31)),
+ ERROR(NS_ERROR_TRACKING_URI, FAILURE(34)),
+ ERROR(NS_ERROR_UNWANTED_URI, FAILURE(35)),
+ ERROR(NS_ERROR_BLOCKED_URI, FAILURE(37)),
+ /* Used when "Save Link As..." doesn't see the headers quickly enough to
+ * choose a filename. See nsContextMenu.js. */
+ ERROR(NS_ERROR_SAVE_LINK_AS_TIMEOUT, FAILURE(32)),
+ /* Used when the data from a channel has already been parsed and cached so it
+ * doesn't need to be reparsed from the original source. */
+ ERROR(NS_ERROR_PARSED_DATA_CACHED, FAILURE(33)),
+
+ /* This success code indicates that a refresh header was found and
+ * successfully setup. */
+ ERROR(NS_REFRESHURI_HEADER_FOUND, SUCCESS(2)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 25: NS_ERROR_MODULE_CONTENT */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_CONTENT
+ /* Error codes for image loading */
+ ERROR(NS_ERROR_IMAGE_SRC_CHANGED, FAILURE(4)),
+ ERROR(NS_ERROR_IMAGE_BLOCKED, FAILURE(5)),
+ /* Error codes for content policy blocking */
+ ERROR(NS_ERROR_CONTENT_BLOCKED, FAILURE(6)),
+ ERROR(NS_ERROR_CONTENT_BLOCKED_SHOW_ALT, FAILURE(7)),
+ /* Success variations of content policy blocking */
+ ERROR(NS_PROPTABLE_PROP_NOT_THERE, FAILURE(10)),
+ /* Error code for XBL */
+ ERROR(NS_ERROR_XBL_BLOCKED, FAILURE(15)),
+ /* Error code for when the content process crashed */
+ ERROR(NS_ERROR_CONTENT_CRASHED, FAILURE(16)),
+
+ /* XXX this is not really used */
+ ERROR(NS_HTML_STYLE_PROPERTY_NOT_THERE, SUCCESS(2)),
+ ERROR(NS_CONTENT_BLOCKED, SUCCESS(8)),
+ ERROR(NS_CONTENT_BLOCKED_SHOW_ALT, SUCCESS(9)),
+ ERROR(NS_PROPTABLE_PROP_OVERWRITTEN, SUCCESS(11)),
+ /* Error codes for FindBroadcaster in XULDocument.cpp */
+ ERROR(NS_FINDBROADCASTER_NOT_FOUND, SUCCESS(12)),
+ ERROR(NS_FINDBROADCASTER_FOUND, SUCCESS(13)),
+ ERROR(NS_FINDBROADCASTER_AWAIT_OVERLAYS, SUCCESS(14)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 27: NS_ERROR_MODULE_XSLT */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_XSLT
+ ERROR(NS_ERROR_XPATH_INVALID_ARG, NS_ERROR_INVALID_ARG),
+
+ ERROR(NS_ERROR_XSLT_PARSE_FAILURE, FAILURE(1)),
+ ERROR(NS_ERROR_XPATH_PARSE_FAILURE, FAILURE(2)),
+ ERROR(NS_ERROR_XSLT_ALREADY_SET, FAILURE(3)),
+ ERROR(NS_ERROR_XSLT_EXECUTION_FAILURE, FAILURE(4)),
+ ERROR(NS_ERROR_XPATH_UNKNOWN_FUNCTION, FAILURE(5)),
+ ERROR(NS_ERROR_XSLT_BAD_RECURSION, FAILURE(6)),
+ ERROR(NS_ERROR_XSLT_BAD_VALUE, FAILURE(7)),
+ ERROR(NS_ERROR_XSLT_NODESET_EXPECTED, FAILURE(8)),
+ ERROR(NS_ERROR_XSLT_ABORTED, FAILURE(9)),
+ ERROR(NS_ERROR_XSLT_NETWORK_ERROR, FAILURE(10)),
+ ERROR(NS_ERROR_XSLT_WRONG_MIME_TYPE, FAILURE(11)),
+ ERROR(NS_ERROR_XSLT_LOAD_RECURSION, FAILURE(12)),
+ ERROR(NS_ERROR_XPATH_BAD_ARGUMENT_COUNT, FAILURE(13)),
+ ERROR(NS_ERROR_XPATH_BAD_EXTENSION_FUNCTION, FAILURE(14)),
+ ERROR(NS_ERROR_XPATH_PAREN_EXPECTED, FAILURE(15)),
+ ERROR(NS_ERROR_XPATH_INVALID_AXIS, FAILURE(16)),
+ ERROR(NS_ERROR_XPATH_NO_NODE_TYPE_TEST, FAILURE(17)),
+ ERROR(NS_ERROR_XPATH_BRACKET_EXPECTED, FAILURE(18)),
+ ERROR(NS_ERROR_XPATH_INVALID_VAR_NAME, FAILURE(19)),
+ ERROR(NS_ERROR_XPATH_UNEXPECTED_END, FAILURE(20)),
+ ERROR(NS_ERROR_XPATH_OPERATOR_EXPECTED, FAILURE(21)),
+ ERROR(NS_ERROR_XPATH_UNCLOSED_LITERAL, FAILURE(22)),
+ ERROR(NS_ERROR_XPATH_BAD_COLON, FAILURE(23)),
+ ERROR(NS_ERROR_XPATH_BAD_BANG, FAILURE(24)),
+ ERROR(NS_ERROR_XPATH_ILLEGAL_CHAR, FAILURE(25)),
+ ERROR(NS_ERROR_XPATH_BINARY_EXPECTED, FAILURE(26)),
+ ERROR(NS_ERROR_XSLT_LOAD_BLOCKED_ERROR, FAILURE(27)),
+ ERROR(NS_ERROR_XPATH_INVALID_EXPRESSION_EVALUATED, FAILURE(28)),
+ ERROR(NS_ERROR_XPATH_UNBALANCED_CURLY_BRACE, FAILURE(29)),
+ ERROR(NS_ERROR_XSLT_BAD_NODE_NAME, FAILURE(30)),
+ ERROR(NS_ERROR_XSLT_VAR_ALREADY_SET, FAILURE(31)),
+ ERROR(NS_ERROR_XSLT_CALL_TO_KEY_NOT_ALLOWED, FAILURE(32)),
+
+ ERROR(NS_XSLT_GET_NEW_HANDLER, SUCCESS(1)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 28: NS_ERROR_MODULE_IPC */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_IPC
+ // Initial creation of a Transport object failed internally for unknown reasons.
+ ERROR(NS_ERROR_TRANSPORT_INIT, FAILURE(1)),
+ // Generic error related to duplicating handle failures.
+ ERROR(NS_ERROR_DUPLICATE_HANDLE, FAILURE(2)),
+ // Bridging: failure trying to open the connection to the parent
+ ERROR(NS_ERROR_BRIDGE_OPEN_PARENT, FAILURE(3)),
+ // Bridging: failure trying to open the connection to the child
+ ERROR(NS_ERROR_BRIDGE_OPEN_CHILD, FAILURE(4)),
+#undef MODULE
+
+ /* ======================================================================= */
+ /* 29: NS_ERROR_MODULE_SVG */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_SVG
+ /* SVG DOM error codes from http://www.w3.org/TR/SVG11/svgdom.html */
+ ERROR(NS_ERROR_DOM_SVG_WRONG_TYPE_ERR, FAILURE(0)),
+ /* Yes, the spec says "INVERTABLE", not "INVERTIBLE" */
+ ERROR(NS_ERROR_DOM_SVG_MATRIX_NOT_INVERTABLE, FAILURE(2)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 30: NS_ERROR_MODULE_STORAGE */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_STORAGE
+ /* To add additional errors to Storage, please append entries to the bottom
+ * of the list in the following format:
+ * NS_ERROR_STORAGE_YOUR_ERR, FAILURE(n)
+ * where n is the next unique positive integer. You must also add an entry
+ * to js/xpconnect/src/xpc.msg under the code block beginning with the
+ * comment 'storage related codes (from mozStorage.h)', in the following
+ * format: 'XPC_MSG_DEF(NS_ERROR_STORAGE_YOUR_ERR, "brief description of your
+ * error")' */
+ ERROR(NS_ERROR_STORAGE_BUSY, FAILURE(1)),
+ ERROR(NS_ERROR_STORAGE_IOERR, FAILURE(2)),
+ ERROR(NS_ERROR_STORAGE_CONSTRAINT, FAILURE(3)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 32: NS_ERROR_MODULE_DOM_FILE */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_DOM_FILE
+ ERROR(NS_ERROR_DOM_FILE_NOT_FOUND_ERR, FAILURE(0)),
+ ERROR(NS_ERROR_DOM_FILE_NOT_READABLE_ERR, FAILURE(1)),
+ ERROR(NS_ERROR_DOM_FILE_ABORT_ERR, FAILURE(2)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 33: NS_ERROR_MODULE_DOM_INDEXEDDB */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_DOM_INDEXEDDB
+ /* IndexedDB error codes http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html */
+ ERROR(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, FAILURE(1)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR, FAILURE(3)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR, FAILURE(4)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_DATA_ERR, FAILURE(5)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR, FAILURE(6)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR, FAILURE(7)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR, FAILURE(8)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR, FAILURE(9)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_TIMEOUT_ERR, FAILURE(10)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR, FAILURE(11)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_VERSION_ERR, FAILURE(12)),
+ ERROR(NS_ERROR_DOM_INDEXEDDB_RECOVERABLE_ERR, FAILURE(1001)),
+#undef MODULE
+
+
+ /* ======================================================================= */
+ /* 34: NS_ERROR_MODULE_DOM_FILEHANDLE */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_DOM_FILEHANDLE
+ ERROR(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR, FAILURE(1)),
+ ERROR(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR, FAILURE(2)),
+ ERROR(NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR, FAILURE(3)),
+ ERROR(NS_ERROR_DOM_FILEHANDLE_ABORT_ERR, FAILURE(4)),
+ ERROR(NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR, FAILURE(5)),
+ ERROR(NS_ERROR_DOM_FILEHANDLE_QUOTA_ERR, FAILURE(6)),
+#undef MODULE
+
+ /* ======================================================================= */
+ /* 35: NS_ERROR_MODULE_SIGNED_JAR */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_SIGNED_JAR
+ ERROR(NS_ERROR_SIGNED_JAR_NOT_SIGNED, FAILURE(1)),
+ ERROR(NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY, FAILURE(2)),
+ ERROR(NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY, FAILURE(3)),
+ ERROR(NS_ERROR_SIGNED_JAR_ENTRY_MISSING, FAILURE(4)),
+ ERROR(NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE, FAILURE(5)),
+ ERROR(NS_ERROR_SIGNED_JAR_ENTRY_TOO_LARGE, FAILURE(6)),
+ ERROR(NS_ERROR_SIGNED_JAR_ENTRY_INVALID, FAILURE(7)),
+ ERROR(NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, FAILURE(8)),
+#undef MODULE
+
+ /* ======================================================================= */
+ /* 36: NS_ERROR_MODULE_DOM_FILESYSTEM */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_DOM_FILESYSTEM
+ ERROR(NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR, FAILURE(1)),
+ ERROR(NS_ERROR_DOM_FILESYSTEM_INVALID_MODIFICATION_ERR, FAILURE(2)),
+ ERROR(NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR, FAILURE(3)),
+ ERROR(NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR, FAILURE(4)),
+ ERROR(NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR, FAILURE(5)),
+ ERROR(NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR, FAILURE(6)),
+#undef MODULE
+
+ /* ======================================================================= */
+ /* 38: NS_ERROR_MODULE_SIGNED_APP */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_SIGNED_APP
+ ERROR(NS_ERROR_SIGNED_APP_MANIFEST_INVALID, FAILURE(1)),
+#undef MODULE
+
+ /* ======================================================================= */
+ /* 39: NS_ERROR_MODULE_DOM_ANIM */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_DOM_ANIM
+ ERROR(NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR, FAILURE(1)),
+#undef MODULE
+
+ /* ======================================================================= */
+ /* 40: NS_ERROR_MODULE_DOM_PUSH */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_DOM_PUSH
+ ERROR(NS_ERROR_DOM_PUSH_INVALID_REGISTRATION_ERR, FAILURE(1)),
+ ERROR(NS_ERROR_DOM_PUSH_DENIED_ERR, FAILURE(2)),
+ ERROR(NS_ERROR_DOM_PUSH_ABORT_ERR, FAILURE(3)),
+ ERROR(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE, FAILURE(4)),
+ ERROR(NS_ERROR_DOM_PUSH_INVALID_KEY_ERR, FAILURE(5)),
+ ERROR(NS_ERROR_DOM_PUSH_MISMATCHED_KEY_ERR, FAILURE(6)),
+#undef MODULE
+
+ /* ======================================================================= */
+ /* 41: NS_ERROR_MODULE_DOM_MEDIA */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_DOM_MEDIA
+ /* HTMLMediaElement API errors from https://html.spec.whatwg.org/multipage/embedded-content.html#media-elements */
+ ERROR(NS_ERROR_DOM_MEDIA_ABORT_ERR, FAILURE(1)),
+ ERROR(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR, FAILURE(2)),
+ ERROR(NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR, FAILURE(3)),
+
+ /* HTMLMediaElement internal decoding error */
+ ERROR(NS_ERROR_DOM_MEDIA_DECODE_ERR, FAILURE(4)),
+ ERROR(NS_ERROR_DOM_MEDIA_FATAL_ERR, FAILURE(5)),
+ ERROR(NS_ERROR_DOM_MEDIA_METADATA_ERR, FAILURE(6)),
+ ERROR(NS_ERROR_DOM_MEDIA_OVERFLOW_ERR, FAILURE(7)),
+ ERROR(NS_ERROR_DOM_MEDIA_END_OF_STREAM, FAILURE(8)),
+ ERROR(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, FAILURE(9)),
+ ERROR(NS_ERROR_DOM_MEDIA_CANCELED, FAILURE(10)),
+ ERROR(NS_ERROR_DOM_MEDIA_MEDIASINK_ERR, FAILURE(11)),
+ ERROR(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, FAILURE(12)),
+ ERROR(NS_ERROR_DOM_MEDIA_CDM_ERR, FAILURE(13)),
+ ERROR(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER, FAILURE(14)),
+
+ /* Internal platform-related errors */
+ ERROR(NS_ERROR_DOM_MEDIA_CUBEB_INITIALIZATION_ERR, FAILURE(101)),
+#undef MODULE
+
+ /* ======================================================================= */
+ /* 51: NS_ERROR_MODULE_GENERAL */
+ /* ======================================================================= */
+#define MODULE NS_ERROR_MODULE_GENERAL
+ /* Error code used internally by the incremental downloader to cancel the
+ * network channel when the download is already complete. */
+ ERROR(NS_ERROR_DOWNLOAD_COMPLETE, FAILURE(1)),
+ /* Error code used internally by the incremental downloader to cancel the
+ * network channel when the response to a range request is 200 instead of
+ * 206. */
+ ERROR(NS_ERROR_DOWNLOAD_NOT_PARTIAL, FAILURE(2)),
+ ERROR(NS_ERROR_UNORM_MOREOUTPUT, FAILURE(33)),
+
+ ERROR(NS_ERROR_DOCSHELL_REQUEST_REJECTED, FAILURE(1001)),
+ /* This is needed for displaying an error message when navigation is
+ * attempted on a document when printing The value arbitrary as long as it
+ * doesn't conflict with any of the other values in the errors in
+ * DisplayLoadError */
+ ERROR(NS_ERROR_DOCUMENT_IS_PRINTMODE, FAILURE(2001)),
+
+ ERROR(NS_SUCCESS_DONT_FIXUP, SUCCESS(1)),
+ /* This success code may be returned by nsIAppStartup::Run to indicate that
+ * the application should be restarted. This condition corresponds to the
+ * case in which nsIAppStartup::Quit was called with the eRestart flag. */
+ ERROR(NS_SUCCESS_RESTART_APP, SUCCESS(1)),
+ ERROR(NS_SUCCESS_RESTART_APP_NOT_SAME_PROFILE, SUCCESS(3)),
+ ERROR(NS_SUCCESS_UNORM_NOTFOUND, SUCCESS(17)),
+
+
+ /* a11y */
+ /* raised when current pivot's position is needed but it's not in the tree */
+ ERROR(NS_ERROR_NOT_IN_TREE, FAILURE(38)),
+
+ /* see nsTextEquivUtils */
+ ERROR(NS_OK_NO_NAME_CLAUSE_HANDLED, SUCCESS(34))
+#undef MODULE
diff --git a/xpcom/base/ErrorNames.cpp b/xpcom/base/ErrorNames.cpp
new file mode 100644
index 0000000000..165a1a0fc3
--- /dev/null
+++ b/xpcom/base/ErrorNames.cpp
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/ErrorNames.h"
+#include "nsString.h"
+#include "prerror.h"
+
+namespace {
+
+struct ErrorEntry
+{
+ nsresult value;
+ const char * name;
+};
+
+#undef ERROR
+#define ERROR(key, val) {key, #key}
+
+const ErrorEntry errors[] = {
+ #include "ErrorList.h"
+};
+
+#undef ERROR
+
+} // unnamed namespace
+
+namespace mozilla {
+
+void
+GetErrorName(nsresult rv, nsACString& name)
+{
+ for (size_t i = 0; i < ArrayLength(errors); ++i) {
+ if (errors[i].value == rv) {
+ name.AssignASCII(errors[i].name);
+ return;
+ }
+ }
+
+ bool isSecurityError = NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_SECURITY;
+
+ // NS_ERROR_MODULE_SECURITY is the only module that is "allowed" to
+ // synthesize nsresult error codes that are not listed in ErrorList.h. (The
+ // NS_ERROR_MODULE_SECURITY error codes are synthesized from NSPR error
+ // codes.)
+ MOZ_ASSERT(isSecurityError);
+
+ name.AssignASCII(NS_SUCCEEDED(rv) ? "NS_ERROR_GENERATE_SUCCESS("
+ : "NS_ERROR_GENERATE_FAILURE(");
+
+ if (isSecurityError) {
+ name.AppendASCII("NS_ERROR_MODULE_SECURITY");
+ } else {
+ // This should never happen given the assertion above, so we don't bother
+ // trying to print a symbolic name for the module here.
+ name.AppendInt(NS_ERROR_GET_MODULE(rv));
+ }
+
+ name.AppendASCII(", ");
+
+ const char * nsprName = nullptr;
+ if (isSecurityError) {
+ // Invert the logic from NSSErrorsService::GetXPCOMFromNSSError
+ PRErrorCode nsprCode
+ = -1 * static_cast<PRErrorCode>(NS_ERROR_GET_CODE(rv));
+ nsprName = PR_ErrorToName(nsprCode);
+
+ // All NSPR error codes defined by NSPR or NSS should have a name mapping.
+ MOZ_ASSERT(nsprName);
+ }
+
+ if (nsprName) {
+ name.AppendASCII(nsprName);
+ } else {
+ name.AppendInt(NS_ERROR_GET_CODE(rv));
+ }
+
+ name.AppendASCII(")");
+}
+
+} // namespace mozilla
diff --git a/xpcom/base/ErrorNames.h b/xpcom/base/ErrorNames.h
new file mode 100644
index 0000000000..9fdba7ace2
--- /dev/null
+++ b/xpcom/base/ErrorNames.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ErrorNames_h
+#define mozilla_ErrorNames_h
+
+#include "nsError.h"
+
+class nsACString;
+
+namespace mozilla {
+
+// Maps the given nsresult to its symbolic name. For example,
+// GetErrorName(NS_OK, name) will result in name == "NS_OK".
+// When the symbolic name is unknown, name will be of the form
+// "NS_ERROR_GENERATE_SUCCESS(<module>, <code>)" or
+// "NS_ERROR_GENERATE_FAILURE(<module>, <code>)".
+void GetErrorName(nsresult rv, nsACString& name);
+
+} // namespace mozilla
+
+#endif // mozilla_ErrorNames_h
diff --git a/xpcom/base/HoldDropJSObjects.cpp b/xpcom/base/HoldDropJSObjects.cpp
new file mode 100644
index 0000000000..eeecc7121b
--- /dev/null
+++ b/xpcom/base/HoldDropJSObjects.cpp
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/HoldDropJSObjects.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/CycleCollectedJSContext.h"
+
+namespace mozilla {
+namespace cyclecollector {
+
+void
+HoldJSObjectsImpl(void* aHolder, nsScriptObjectTracer* aTracer)
+{
+ CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
+ MOZ_ASSERT(cx, "Should have a CycleCollectedJSContext by now");
+ cx->AddJSHolder(aHolder, aTracer);
+}
+
+void
+HoldJSObjectsImpl(nsISupports* aHolder)
+{
+ nsXPCOMCycleCollectionParticipant* participant = nullptr;
+ CallQueryInterface(aHolder, &participant);
+ MOZ_ASSERT(participant, "Failed to QI to nsXPCOMCycleCollectionParticipant!");
+ MOZ_ASSERT(participant->CheckForRightISupports(aHolder),
+ "The result of QIing a JS holder should be the same as ToSupports");
+
+ HoldJSObjectsImpl(aHolder, participant);
+}
+
+void
+DropJSObjectsImpl(void* aHolder)
+{
+ CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
+ MOZ_ASSERT(cx, "Should have a CycleCollectedJSContext by now");
+ cx->RemoveJSHolder(aHolder);
+}
+
+void
+DropJSObjectsImpl(nsISupports* aHolder)
+{
+#ifdef DEBUG
+ nsXPCOMCycleCollectionParticipant* participant = nullptr;
+ CallQueryInterface(aHolder, &participant);
+ MOZ_ASSERT(participant, "Failed to QI to nsXPCOMCycleCollectionParticipant!");
+ MOZ_ASSERT(participant->CheckForRightISupports(aHolder),
+ "The result of QIing a JS holder should be the same as ToSupports");
+#endif
+ DropJSObjectsImpl(static_cast<void*>(aHolder));
+}
+
+} // namespace cyclecollector
+
+#ifdef DEBUG
+bool
+IsJSHolder(void* aHolder)
+{
+ CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
+ MOZ_ASSERT(cx, "Should have a CycleCollectedJSContext by now");
+ return cx->IsJSHolder(aHolder);
+}
+#endif
+
+} // namespace mozilla
diff --git a/xpcom/base/HoldDropJSObjects.h b/xpcom/base/HoldDropJSObjects.h
new file mode 100644
index 0000000000..1a500a94af
--- /dev/null
+++ b/xpcom/base/HoldDropJSObjects.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_HoldDropJSObjects_h
+#define mozilla_HoldDropJSObjects_h
+
+#include "mozilla/TypeTraits.h"
+#include "nsCycleCollectionParticipant.h"
+
+class nsISupports;
+class nsScriptObjectTracer;
+
+// Only HoldJSObjects and DropJSObjects should be called directly.
+
+namespace mozilla {
+namespace cyclecollector {
+
+void HoldJSObjectsImpl(void* aHolder, nsScriptObjectTracer* aTracer);
+void HoldJSObjectsImpl(nsISupports* aHolder);
+void DropJSObjectsImpl(void* aHolder);
+void DropJSObjectsImpl(nsISupports* aHolder);
+
+} // namespace cyclecollector
+
+
+template<class T, bool isISupports = IsBaseOf<nsISupports, T>::value>
+struct HoldDropJSObjectsHelper
+{
+ static void Hold(T* aHolder)
+ {
+ cyclecollector::HoldJSObjectsImpl(aHolder,
+ NS_CYCLE_COLLECTION_PARTICIPANT(T));
+ }
+ static void Drop(T* aHolder)
+ {
+ cyclecollector::DropJSObjectsImpl(aHolder);
+ }
+};
+
+template<class T>
+struct HoldDropJSObjectsHelper<T, true>
+{
+ static void Hold(T* aHolder)
+ {
+ cyclecollector::HoldJSObjectsImpl(ToSupports(aHolder));
+ }
+ static void Drop(T* aHolder)
+ {
+ cyclecollector::DropJSObjectsImpl(ToSupports(aHolder));
+ }
+};
+
+
+template<class T>
+void
+HoldJSObjects(T* aHolder)
+{
+ HoldDropJSObjectsHelper<T>::Hold(aHolder);
+}
+
+template<class T>
+void
+DropJSObjects(T* aHolder)
+{
+ HoldDropJSObjectsHelper<T>::Drop(aHolder);
+}
+
+#ifdef DEBUG
+bool IsJSHolder(void* aHolder);
+#endif
+
+} // namespace mozilla
+
+#endif // mozilla_HoldDropJSObjects_h
diff --git a/xpcom/base/JSObjectHolder.cpp b/xpcom/base/JSObjectHolder.cpp
new file mode 100644
index 0000000000..5bcc3cabbf
--- /dev/null
+++ b/xpcom/base/JSObjectHolder.cpp
@@ -0,0 +1,9 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "JSObjectHolder.h"
+
+NS_IMPL_ISUPPORTS(mozilla::JSObjectHolder, nsISupports)
diff --git a/xpcom/base/JSObjectHolder.h b/xpcom/base/JSObjectHolder.h
new file mode 100644
index 0000000000..7b83b813db
--- /dev/null
+++ b/xpcom/base/JSObjectHolder.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 mozilla_JSObjectHolder_h
+#define mozilla_JSObjectHolder_h
+
+#include "js/RootingAPI.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+
+// This class is useful when something on one thread needs to keep alive
+// a JS Object from another thread. If they are both on the same thread, the
+// owning class should instead be made a cycle collected SCRIPT_HOLDER class.
+// This object should only be AddRefed and Released on the same thread as
+// mJSObject.
+//
+// Note that this keeps alive the JS object until it goes away, so be sure not to
+// create cycles that keep alive the holder.
+//
+// JSObjectHolder is ISupports to make it usable with NS_ReleaseOnMainThread.
+class JSObjectHolder final : public nsISupports
+{
+public:
+ JSObjectHolder(JSContext* aCx, JSObject* aObject) : mJSObject(aCx, aObject) {}
+
+ NS_DECL_ISUPPORTS
+
+ JSObject* GetJSObject() { return mJSObject; }
+
+private:
+ ~JSObjectHolder() {}
+
+ JS::PersistentRooted<JSObject*> mJSObject;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_JSObjectHolder_h
diff --git a/xpcom/base/LinuxUtils.cpp b/xpcom/base/LinuxUtils.cpp
new file mode 100644
index 0000000000..331c82be97
--- /dev/null
+++ b/xpcom/base/LinuxUtils.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "LinuxUtils.h"
+
+#if defined(XP_LINUX)
+
+#include <ctype.h>
+#include <stdio.h>
+
+#include "nsPrintfCString.h"
+
+namespace mozilla {
+
+void
+LinuxUtils::GetThreadName(pid_t aTid, nsACString& aName)
+{
+ aName.Truncate();
+ if (aTid <= 0) {
+ return;
+ }
+
+ const size_t kBuffSize = 16; // 15 chars max + '\n'
+ char buf[kBuffSize];
+ nsPrintfCString path("/proc/%d/comm", aTid);
+ FILE* fp = fopen(path.get(), "r");
+ if (!fp) {
+ // The fopen could also fail if the thread exited before we got here.
+ return;
+ }
+
+ size_t len = fread(buf, 1, kBuffSize, fp);
+ fclose(fp);
+
+ // No need to strip the '\n', since isspace() includes it.
+ while (len > 0 &&
+ (isspace(buf[len - 1]) || isdigit(buf[len - 1]) ||
+ buf[len - 1] == '#' || buf[len - 1] == '_')) {
+ --len;
+ }
+
+ aName.Assign(buf, len);
+}
+
+}
+
+#endif // XP_LINUX
diff --git a/xpcom/base/LinuxUtils.h b/xpcom/base/LinuxUtils.h
new file mode 100644
index 0000000000..e82c15e08c
--- /dev/null
+++ b/xpcom/base/LinuxUtils.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_LinuxUtils_h
+#define mozilla_LinuxUtils_h
+
+#if defined(XP_LINUX)
+
+#include <unistd.h>
+#include "nsString.h"
+
+namespace mozilla {
+
+class LinuxUtils
+{
+public:
+ // Obtain the name of a thread, omitting any numeric suffix added by a
+ // thread pool library (as in, e.g., "Binder_2" or "mozStorage #1").
+ // The empty string is returned on error.
+ //
+ // Note: if this is ever needed on kernels older than 2.6.33 (early 2010),
+ // it will have to parse /proc/<pid>/status instead, because
+ // /proc/<pid>/comm didn't exist before then.
+ static void GetThreadName(pid_t aTid, nsACString& aName);
+};
+
+}
+
+#endif // XP_LINUX
+
+#endif
diff --git a/xpcom/base/LogModulePrefWatcher.cpp b/xpcom/base/LogModulePrefWatcher.cpp
new file mode 100644
index 0000000000..bd04eda98b
--- /dev/null
+++ b/xpcom/base/LogModulePrefWatcher.cpp
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "LogModulePrefWatcher.h"
+
+#include "mozilla/Logging.h"
+#include "mozilla/Preferences.h"
+#include "nsString.h"
+#include "nsXULAppAPI.h"
+#include "base/process_util.h"
+
+static const char kLoggingPrefPrefix[] = "logging.";
+static const char kLoggingConfigPrefPrefix[] = "logging.config";
+static const int kLoggingConfigPrefixLen = sizeof(kLoggingConfigPrefPrefix) - 1;
+static const char kLoggingPrefClearOnStartup[] = "logging.config.clear_on_startup";
+static const char kLoggingPrefLogFile[] = "logging.config.LOG_FILE";
+static const char kLoggingPrefAddTimestamp[] = "logging.config.add_timestamp";
+static const char kLoggingPrefSync[] = "logging.config.sync";
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(LogModulePrefWatcher, nsIObserver)
+
+/**
+ * Resets all the preferences in the logging. branch
+ * This is needed because we may crash while logging, and this would cause us
+ * to log after restarting as well.
+ *
+ * If logging after restart is desired, set the logging.config.clear_on_startup
+ * pref to false, or use the MOZ_LOG_FILE and MOZ_LOG_MODULES env vars.
+ */
+void ResetExistingPrefs()
+{
+ uint32_t count;
+ char** names;
+ nsresult rv = Preferences::GetRootBranch()->
+ GetChildList(kLoggingPrefPrefix, &count, &names);
+ if (NS_SUCCEEDED(rv) && count) {
+ for (size_t i = 0; i < count; i++) {
+ // Clearing the pref will cause it to reload, thus resetting the log level
+ Preferences::ClearUser(names[i]);
+ }
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, names);
+ }
+}
+
+/**
+ * Loads the log level from the given pref and updates the corresponding
+ * LogModule.
+ */
+static void
+LoadPrefValue(const char* aName)
+{
+ LogLevel logLevel = LogLevel::Disabled;
+
+ nsresult rv;
+ int32_t prefLevel = 0;
+ nsAutoCString prefValue;
+
+ if (strncmp(aName, kLoggingConfigPrefPrefix, kLoggingConfigPrefixLen) == 0) {
+ nsAutoCString prefName(aName);
+
+ if (prefName.EqualsLiteral(kLoggingPrefLogFile)) {
+ rv = Preferences::GetCString(aName, &prefValue);
+ // The pref was reset. Clear the user file.
+ if (NS_FAILED(rv) || prefValue.IsEmpty()) {
+ LogModule::SetLogFile(nullptr);
+ return;
+ }
+
+ // If the pref value doesn't have a PID placeholder, append it to the end.
+ if (!strstr(prefValue.get(), "%PID")) {
+ prefValue.Append("%PID");
+ }
+
+ LogModule::SetLogFile(prefValue.BeginReading());
+ } else if (prefName.EqualsLiteral(kLoggingPrefAddTimestamp)) {
+ bool addTimestamp = Preferences::GetBool(aName, false);
+ LogModule::SetAddTimestamp(addTimestamp);
+ } else if (prefName.EqualsLiteral(kLoggingPrefSync)) {
+ bool sync = Preferences::GetBool(aName, false);
+ LogModule::SetIsSync(sync);
+ }
+ return;
+ }
+
+ if (Preferences::GetInt(aName, &prefLevel) == NS_OK) {
+ logLevel = ToLogLevel(prefLevel);
+ } else if (Preferences::GetCString(aName, &prefValue) == NS_OK) {
+ if (prefValue.LowerCaseEqualsLiteral("error")) {
+ logLevel = LogLevel::Error;
+ } else if (prefValue.LowerCaseEqualsLiteral("warning")) {
+ logLevel = LogLevel::Warning;
+ } else if (prefValue.LowerCaseEqualsLiteral("info")) {
+ logLevel = LogLevel::Info;
+ } else if (prefValue.LowerCaseEqualsLiteral("debug")) {
+ logLevel = LogLevel::Debug;
+ } else if (prefValue.LowerCaseEqualsLiteral("verbose")) {
+ logLevel = LogLevel::Verbose;
+ }
+ }
+
+ const char* moduleName = aName + strlen(kLoggingPrefPrefix);
+ LogModule::Get(moduleName)->SetLevel(logLevel);
+}
+
+void
+LoadExistingPrefs()
+{
+ nsIPrefBranch* root = Preferences::GetRootBranch();
+ if (!root) {
+ return;
+ }
+
+ uint32_t count;
+ char** names;
+ nsresult rv = root->GetChildList(kLoggingPrefPrefix, &count, &names);
+ if (NS_SUCCEEDED(rv) && count) {
+ for (size_t i = 0; i < count; i++) {
+ LoadPrefValue(names[i]);
+ }
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, names);
+ }
+}
+
+LogModulePrefWatcher::LogModulePrefWatcher()
+{
+}
+
+void
+LogModulePrefWatcher::RegisterPrefWatcher()
+{
+ RefPtr<LogModulePrefWatcher> prefWatcher = new LogModulePrefWatcher();
+ Preferences::AddStrongObserver(prefWatcher, kLoggingPrefPrefix);
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (observerService && XRE_IsParentProcess()) {
+ observerService->AddObserver(prefWatcher, "browser-delayed-startup-finished", false);
+ }
+
+ LoadExistingPrefs();
+}
+
+NS_IMETHODIMP
+LogModulePrefWatcher::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ if (strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic) == 0) {
+ NS_LossyConvertUTF16toASCII prefName(aData);
+ LoadPrefValue(prefName.get());
+ } else if (strcmp("browser-delayed-startup-finished", aTopic) == 0) {
+ bool clear = Preferences::GetBool(kLoggingPrefClearOnStartup, true);
+ if (clear) {
+ ResetExistingPrefs();
+ }
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (observerService) {
+ observerService->RemoveObserver(this, "browser-delayed-startup-finished");
+ }
+ }
+
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/xpcom/base/LogModulePrefWatcher.h b/xpcom/base/LogModulePrefWatcher.h
new file mode 100644
index 0000000000..657e54f011
--- /dev/null
+++ b/xpcom/base/LogModulePrefWatcher.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 LogModulePrefWatcher_h
+#define LogModulePrefWatcher_h
+
+#include "nsIObserver.h"
+
+namespace mozilla {
+
+/**
+ * Watches for changes to "logging.*" prefs and then updates the appropriate
+ * LogModule's log level. Both the integer and string versions of the LogLevel
+ * enum are supported.
+ *
+ * For example setting the pref "logging.Foo" to "Verbose" will set the
+ * LogModule for "Foo" to the LogLevel::Verbose level. Setting "logging.Bar" to
+ * 4 would set the LogModule for "Bar" to the LogLevel::Debug level.
+ */
+class LogModulePrefWatcher : public nsIObserver
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ /**
+ * Starts observing logging pref changes.
+ */
+ static void RegisterPrefWatcher();
+
+private:
+ LogModulePrefWatcher();
+ virtual ~LogModulePrefWatcher()
+ {
+ }
+};
+} // namespace mozilla
+
+#endif // LogModulePrefWatcher_h
diff --git a/xpcom/base/Logging.cpp b/xpcom/base/Logging.cpp
new file mode 100644
index 0000000000..e87df91e49
--- /dev/null
+++ b/xpcom/base/Logging.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 "mozilla/Logging.h"
+
+#include <algorithm>
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/FileUtils.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "nsClassHashtable.h"
+#include "nsDebug.h"
+#include "NSPRLogModulesParser.h"
+
+#include "prenv.h"
+#include "prprf.h"
+#ifdef XP_WIN
+#include <process.h>
+#else
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+// NB: Initial amount determined by auditing the codebase for the total amount
+// of unique module names and padding up to the next power of 2.
+const uint32_t kInitialModuleCount = 256;
+// When rotate option is added to the modules list, this is the hardcoded
+// number of files we create and rotate. When there is rotate:40,
+// we will keep four files per process, each limited to 10MB. Sum is 40MB,
+// the given limit.
+const uint32_t kRotateFilesNumber = 4;
+
+namespace mozilla {
+
+namespace detail {
+
+void log_print(const PRLogModuleInfo* aModule,
+ LogLevel aLevel,
+ const char* aFmt, ...)
+{
+ va_list ap;
+ va_start(ap, aFmt);
+ char* buff = PR_vsmprintf(aFmt, ap);
+ PR_LogPrint("%s", buff);
+ PR_smprintf_free(buff);
+ va_end(ap);
+}
+
+void log_print(const LogModule* aModule,
+ LogLevel aLevel,
+ const char* aFmt, ...)
+{
+ va_list ap;
+ va_start(ap, aFmt);
+ aModule->Printv(aLevel, aFmt, ap);
+ va_end(ap);
+}
+
+} // detail
+
+LogLevel
+ToLogLevel(int32_t aLevel)
+{
+ aLevel = std::min(aLevel, static_cast<int32_t>(LogLevel::Verbose));
+ aLevel = std::max(aLevel, static_cast<int32_t>(LogLevel::Disabled));
+ return static_cast<LogLevel>(aLevel);
+}
+
+const char*
+ToLogStr(LogLevel aLevel) {
+ switch (aLevel) {
+ case LogLevel::Error:
+ return "E";
+ case LogLevel::Warning:
+ return "W";
+ case LogLevel::Info:
+ return "I";
+ case LogLevel::Debug:
+ return "D";
+ case LogLevel::Verbose:
+ return "V";
+ case LogLevel::Disabled:
+ default:
+ MOZ_CRASH("Invalid log level.");
+ return "";
+ }
+}
+
+namespace detail {
+
+/**
+ * A helper class providing reference counting for FILE*.
+ * It encapsulates the following:
+ * - the FILE handle
+ * - the order number it was created for when rotating (actual path)
+ * - number of active references
+ */
+class LogFile
+{
+ FILE* mFile;
+ uint32_t mFileNum;
+
+public:
+ LogFile(FILE* aFile, uint32_t aFileNum)
+ : mFile(aFile)
+ , mFileNum(aFileNum)
+ , mNextToRelease(nullptr)
+ {
+ }
+
+ ~LogFile()
+ {
+ fclose(mFile);
+ delete mNextToRelease;
+ }
+
+ FILE* File() const { return mFile; }
+ uint32_t Num() const { return mFileNum; }
+
+ LogFile* mNextToRelease;
+};
+
+const char*
+ExpandPIDMarker(const char* aFilename, char (&buffer)[2048])
+{
+ MOZ_ASSERT(aFilename);
+ static const char kPIDToken[] = "%PID";
+ const char* pidTokenPtr = strstr(aFilename, kPIDToken);
+ if (pidTokenPtr &&
+ SprintfLiteral(buffer, "%.*s%s%d%s",
+ static_cast<int>(pidTokenPtr - aFilename), aFilename,
+ XRE_IsParentProcess() ? "-main." : "-child.",
+ base::GetCurrentProcId(),
+ pidTokenPtr + strlen(kPIDToken)) > 0)
+ {
+ return buffer;
+ }
+
+ return aFilename;
+}
+
+} // detail
+
+namespace {
+ // Helper method that initializes an empty va_list to be empty.
+ void empty_va(va_list *va, ...)
+ {
+ va_start(*va, va);
+ va_end(*va);
+ }
+}
+
+class LogModuleManager
+{
+public:
+ LogModuleManager()
+ : mModulesLock("logmodules")
+ , mModules(kInitialModuleCount)
+ , mPrintEntryCount(0)
+ , mOutFile(nullptr)
+ , mToReleaseFile(nullptr)
+ , mOutFileNum(0)
+ , mOutFilePath(strdup(""))
+ , mMainThread(PR_GetCurrentThread())
+ , mSetFromEnv(false)
+ , mAddTimestamp(false)
+ , mIsSync(false)
+ , mRotate(0)
+ {
+ }
+
+ ~LogModuleManager()
+ {
+ detail::LogFile* logFile = mOutFile.exchange(nullptr);
+ delete logFile;
+ }
+
+ /**
+ * Loads config from env vars if present.
+ */
+ void Init()
+ {
+ bool shouldAppend = false;
+ bool addTimestamp = false;
+ bool isSync = false;
+ int32_t rotate = 0;
+ const char* modules = PR_GetEnv("MOZ_LOG");
+ if (!modules || !modules[0]) {
+ modules = PR_GetEnv("MOZ_LOG_MODULES");
+ if (modules) {
+ NS_WARNING("MOZ_LOG_MODULES is deprecated."
+ "\nPlease use MOZ_LOG instead.");
+ }
+ }
+ if (!modules || !modules[0]) {
+ modules = PR_GetEnv("NSPR_LOG_MODULES");
+ if (modules) {
+ NS_WARNING("NSPR_LOG_MODULES is deprecated."
+ "\nPlease use MOZ_LOG instead.");
+ }
+ }
+
+ NSPRLogModulesParser(modules,
+ [&shouldAppend, &addTimestamp, &isSync, &rotate]
+ (const char* aName, LogLevel aLevel, int32_t aValue) mutable {
+ if (strcmp(aName, "append") == 0) {
+ shouldAppend = true;
+ } else if (strcmp(aName, "timestamp") == 0) {
+ addTimestamp = true;
+ } else if (strcmp(aName, "sync") == 0) {
+ isSync = true;
+ } else if (strcmp(aName, "rotate") == 0) {
+ rotate = (aValue << 20) / kRotateFilesNumber;
+ } else {
+ LogModule::Get(aName)->SetLevel(aLevel);
+ }
+ });
+
+ // Rotate implies timestamp to make the files readable
+ mAddTimestamp = addTimestamp || rotate > 0;
+ mIsSync = isSync;
+ mRotate = rotate;
+
+ if (rotate > 0 && shouldAppend) {
+ NS_WARNING("MOZ_LOG: when you rotate the log, you cannot use append!");
+ }
+
+ const char* logFile = PR_GetEnv("MOZ_LOG_FILE");
+ if (!logFile || !logFile[0]) {
+ logFile = PR_GetEnv("NSPR_LOG_FILE");
+ }
+
+ if (logFile && logFile[0]) {
+ char buf[2048];
+ logFile = detail::ExpandPIDMarker(logFile, buf);
+ mOutFilePath.reset(strdup(logFile));
+
+ if (mRotate > 0) {
+ // Delete all the previously captured files, including non-rotated
+ // log files, so that users don't complain our logs eat space even
+ // after the rotate option has been added and don't happen to send
+ // us old large logs along with the rotated files.
+ remove(mOutFilePath.get());
+ for (uint32_t i = 0; i < kRotateFilesNumber; ++i) {
+ RemoveFile(i);
+ }
+ }
+
+ mOutFile = OpenFile(shouldAppend, mOutFileNum);
+ mSetFromEnv = true;
+ }
+ }
+
+ void SetLogFile(const char* aFilename)
+ {
+ // For now we don't allow you to change the file at runtime.
+ if (mSetFromEnv) {
+ NS_WARNING("LogModuleManager::SetLogFile - Log file was set from the "
+ "MOZ_LOG_FILE environment variable.");
+ return;
+ }
+
+ const char * filename = aFilename ? aFilename : "";
+ char buf[2048];
+ filename = detail::ExpandPIDMarker(filename, buf);
+
+ // Can't use rotate at runtime yet.
+ MOZ_ASSERT(mRotate == 0, "We don't allow rotate for runtime logfile changes");
+ mOutFilePath.reset(strdup(filename));
+
+ // Exchange mOutFile and set it to be released once all the writes are done.
+ detail::LogFile* newFile = OpenFile(false, 0);
+ detail::LogFile* oldFile = mOutFile.exchange(newFile);
+
+ // Since we don't allow changing the logfile if MOZ_LOG_FILE is already set,
+ // and we don't allow log rotation when setting it at runtime, mToReleaseFile
+ // will be null, so we're not leaking.
+ DebugOnly<detail::LogFile*> prevFile = mToReleaseFile.exchange(oldFile);
+ MOZ_ASSERT(!prevFile, "Should be null because rotation is not allowed");
+
+ // If we just need to release a file, we must force print, in order to
+ // trigger the closing and release of mToReleaseFile.
+ if (oldFile) {
+ va_list va;
+ empty_va(&va);
+ Print("Logger", LogLevel::Info, "Flushing old log files\n", va);
+ }
+ }
+
+ uint32_t GetLogFile(char *aBuffer, size_t aLength)
+ {
+ uint32_t len = strlen(mOutFilePath.get());
+ if (len + 1 > aLength) {
+ return 0;
+ }
+ snprintf(aBuffer, aLength, "%s", mOutFilePath.get());
+ return len;
+ }
+
+ void SetIsSync(bool aIsSync)
+ {
+ mIsSync = aIsSync;
+ }
+
+ void SetAddTimestamp(bool aAddTimestamp)
+ {
+ mAddTimestamp = aAddTimestamp;
+ }
+
+ detail::LogFile* OpenFile(bool aShouldAppend, uint32_t aFileNum)
+ {
+ FILE* file;
+
+ if (mRotate > 0) {
+ char buf[2048];
+ SprintfLiteral(buf, "%s.%d", mOutFilePath.get(), aFileNum);
+
+ // rotate doesn't support append.
+ file = fopen(buf, "w");
+ } else {
+ file = fopen(mOutFilePath.get(), aShouldAppend ? "a" : "w");
+ }
+
+ if (!file) {
+ return nullptr;
+ }
+
+ return new detail::LogFile(file, aFileNum);
+ }
+
+ void RemoveFile(uint32_t aFileNum)
+ {
+ char buf[2048];
+ SprintfLiteral(buf, "%s.%d", mOutFilePath.get(), aFileNum);
+ remove(buf);
+ }
+
+ LogModule* CreateOrGetModule(const char* aName)
+ {
+ OffTheBooksMutexAutoLock guard(mModulesLock);
+ LogModule* module = nullptr;
+ if (!mModules.Get(aName, &module)) {
+ module = new LogModule(aName, LogLevel::Disabled);
+ mModules.Put(aName, module);
+ }
+
+ return module;
+ }
+
+ void Print(const char* aName, LogLevel aLevel, const char* aFmt, va_list aArgs)
+ {
+ const size_t kBuffSize = 1024;
+ char buff[kBuffSize];
+
+ char* buffToWrite = buff;
+
+ // For backwards compat we need to use the NSPR format string versions
+ // of sprintf and friends and then hand off to printf.
+ va_list argsCopy;
+ va_copy(argsCopy, aArgs);
+ size_t charsWritten = PR_vsnprintf(buff, kBuffSize, aFmt, argsCopy);
+ va_end(argsCopy);
+
+ if (charsWritten == kBuffSize - 1) {
+ // We may have maxed out, allocate a buffer instead.
+ buffToWrite = PR_vsmprintf(aFmt, aArgs);
+ charsWritten = strlen(buffToWrite);
+ }
+
+ // Determine if a newline needs to be appended to the message.
+ const char* newline = "";
+ if (charsWritten == 0 || buffToWrite[charsWritten - 1] != '\n') {
+ newline = "\n";
+ }
+
+ FILE* out = stderr;
+
+ // In case we use rotate, this ensures the FILE is kept alive during
+ // its use. Increased before we load mOutFile.
+ ++mPrintEntryCount;
+
+ detail::LogFile* outFile = mOutFile;
+ if (outFile) {
+ out = outFile->File();
+ }
+
+ // This differs from the NSPR format in that we do not output the
+ // opaque system specific thread pointer (ie pthread_t) cast
+ // to a long. The address of the current PR_Thread continues to be
+ // prefixed.
+ //
+ // Additionally we prefix the output with the abbreviated log level
+ // and the module name.
+ PRThread *currentThread = PR_GetCurrentThread();
+ const char *currentThreadName = (mMainThread == currentThread)
+ ? "Main Thread"
+ : PR_GetThreadName(currentThread);
+
+ char noNameThread[40];
+ if (!currentThreadName) {
+ SprintfLiteral(noNameThread, "Unnamed thread %p", currentThread);
+ currentThreadName = noNameThread;
+ }
+
+ if (!mAddTimestamp) {
+ fprintf_stderr(out,
+ "[%s]: %s/%s %s%s",
+ currentThreadName, ToLogStr(aLevel),
+ aName, buffToWrite, newline);
+ } else {
+ PRExplodedTime now;
+ PR_ExplodeTime(PR_Now(), PR_GMTParameters, &now);
+ fprintf_stderr(
+ out,
+ "%04d-%02d-%02d %02d:%02d:%02d.%06d UTC - [%s]: %s/%s %s%s",
+ now.tm_year, now.tm_month + 1, now.tm_mday,
+ now.tm_hour, now.tm_min, now.tm_sec, now.tm_usec,
+ currentThreadName, ToLogStr(aLevel),
+ aName, buffToWrite, newline);
+ }
+
+ if (mIsSync) {
+ fflush(out);
+ }
+
+ if (buffToWrite != buff) {
+ PR_smprintf_free(buffToWrite);
+ }
+
+ if (mRotate > 0 && outFile) {
+ int32_t fileSize = ftell(out);
+ if (fileSize > mRotate) {
+ uint32_t fileNum = outFile->Num();
+
+ uint32_t nextFileNum = fileNum + 1;
+ if (nextFileNum >= kRotateFilesNumber) {
+ nextFileNum = 0;
+ }
+
+ // And here is the trick. The current out-file remembers its order
+ // number. When no other thread shifted the global file number yet,
+ // we are the thread to open the next file.
+ if (mOutFileNum.compareExchange(fileNum, nextFileNum)) {
+ // We can work with mToReleaseFile because we are sure the
+ // mPrintEntryCount can't drop to zero now - the condition
+ // to actually delete what's stored in that member.
+ // And also, no other thread can enter this piece of code
+ // because mOutFile is still holding the current file with
+ // the non-shifted number. The compareExchange() above is
+ // a no-op for other threads.
+ outFile->mNextToRelease = mToReleaseFile;
+ mToReleaseFile = outFile;
+
+ mOutFile = OpenFile(false, nextFileNum);
+ }
+ }
+ }
+
+ if (--mPrintEntryCount == 0 && mToReleaseFile) {
+ // We were the last Print() entered, if there is a file to release
+ // do it now. exchange() is atomic and makes sure we release the file
+ // only once on one thread.
+ detail::LogFile* release = mToReleaseFile.exchange(nullptr);
+ delete release;
+ }
+ }
+
+private:
+ OffTheBooksMutex mModulesLock;
+ nsClassHashtable<nsCharPtrHashKey, LogModule> mModules;
+
+ // Print() entry counter, actually reflects concurrent use of the current
+ // output file. ReleaseAcquire ensures that manipulation with mOutFile
+ // and mToReleaseFile is synchronized by manipulation with this value.
+ Atomic<uint32_t, ReleaseAcquire> mPrintEntryCount;
+ // File to write to. ReleaseAcquire because we need to sync mToReleaseFile
+ // with this.
+ Atomic<detail::LogFile*, ReleaseAcquire> mOutFile;
+ // File to be released when reference counter drops to zero. This member
+ // is assigned mOutFile when the current file has reached the limit.
+ // It can be Relaxed, since it's synchronized with mPrintEntryCount
+ // manipulation and we do atomic exchange() on it.
+ Atomic<detail::LogFile*, Relaxed> mToReleaseFile;
+ // The next file number. This is mostly only for synchronization sake.
+ // Can have relaxed ordering, since we only do compareExchange on it which
+ // is atomic regardless ordering.
+ Atomic<uint32_t, Relaxed> mOutFileNum;
+ // Just keeps the actual file path for further use.
+ UniqueFreePtr<char[]> mOutFilePath;
+
+ PRThread *mMainThread;
+ bool mSetFromEnv;
+ Atomic<bool, Relaxed> mAddTimestamp;
+ Atomic<bool, Relaxed> mIsSync;
+ int32_t mRotate;
+};
+
+StaticAutoPtr<LogModuleManager> sLogModuleManager;
+
+LogModule*
+LogModule::Get(const char* aName)
+{
+ // This is just a pass through to the LogModuleManager so
+ // that the LogModuleManager implementation can be kept internal.
+ MOZ_ASSERT(sLogModuleManager != nullptr);
+ return sLogModuleManager->CreateOrGetModule(aName);
+}
+
+void
+LogModule::SetLogFile(const char* aFilename)
+{
+ MOZ_ASSERT(sLogModuleManager);
+ sLogModuleManager->SetLogFile(aFilename);
+}
+
+uint32_t
+LogModule::GetLogFile(char *aBuffer, size_t aLength)
+{
+ MOZ_ASSERT(sLogModuleManager);
+ return sLogModuleManager->GetLogFile(aBuffer, aLength);
+}
+
+void
+LogModule::SetAddTimestamp(bool aAddTimestamp)
+{
+ sLogModuleManager->SetAddTimestamp(aAddTimestamp);
+}
+
+void
+LogModule::SetIsSync(bool aIsSync)
+{
+ sLogModuleManager->SetIsSync(aIsSync);
+}
+
+void
+LogModule::Init()
+{
+ // NB: This method is not threadsafe; it is expected to be called very early
+ // in startup prior to any other threads being run.
+ if (sLogModuleManager) {
+ // Already initialized.
+ return;
+ }
+
+ // NB: We intentionally do not register for ClearOnShutdown as that happens
+ // before all logging is complete. And, yes, that means we leak, but
+ // we're doing that intentionally.
+ sLogModuleManager = new LogModuleManager();
+ sLogModuleManager->Init();
+}
+
+void
+LogModule::Printv(LogLevel aLevel, const char* aFmt, va_list aArgs) const
+{
+ MOZ_ASSERT(sLogModuleManager != nullptr);
+
+ // Forward to LogModule manager w/ level and name
+ sLogModuleManager->Print(Name(), aLevel, aFmt, aArgs);
+}
+
+} // namespace mozilla
diff --git a/xpcom/base/Logging.h b/xpcom/base/Logging.h
new file mode 100644
index 0000000000..040fb9c497
--- /dev/null
+++ b/xpcom/base/Logging.h
@@ -0,0 +1,255 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_logging_h
+#define mozilla_logging_h
+
+#include <string.h>
+#include <stdarg.h>
+
+#include "prlog.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Likely.h"
+#include "mozilla/MacroForEach.h"
+
+// This file is a placeholder for a replacement to the NSPR logging framework
+// that is defined in prlog.h. Currently it is just a pass through, but as
+// work progresses more functionality will be swapped out in favor of
+// mozilla logging implementations.
+
+// We normally have logging enabled everywhere, but measurements showed that
+// having logging enabled on Android is quite expensive (hundreds of kilobytes
+// for both the format strings for logging and the code to perform all the
+// logging calls). Because retrieving logs from a mobile device is
+// comparatively more difficult for Android than it is for desktop and because
+// desktop machines tend to be less space/bandwidth-constrained than Android
+// devices, we've chosen to leave logging enabled on desktop, but disabled on
+// Android. Given that logging can still be useful for development purposes,
+// however, we leave logging enabled on Android developer builds.
+#if !defined(ANDROID) || !defined(RELEASE_OR_BETA)
+#define MOZ_LOGGING_ENABLED 1
+#else
+#define MOZ_LOGGING_ENABLED 0
+#endif
+
+namespace mozilla {
+
+// While not a 100% mapping to PR_LOG's numeric values, mozilla::LogLevel does
+// maintain a direct mapping for the Disabled, Debug and Verbose levels.
+//
+// Mappings of LogLevel to PR_LOG's numeric values:
+//
+// +---------+------------------+-----------------+
+// | Numeric | NSPR Logging | Mozilla Logging |
+// +---------+------------------+-----------------+
+// | 0 | PR_LOG_NONE | Disabled |
+// | 1 | PR_LOG_ALWAYS | Error |
+// | 2 | PR_LOG_ERROR | Warning |
+// | 3 | PR_LOG_WARNING | Info |
+// | 4 | PR_LOG_DEBUG | Debug |
+// | 5 | PR_LOG_DEBUG + 1 | Verbose |
+// +---------+------------------+-----------------+
+//
+enum class LogLevel {
+ Disabled = 0,
+ Error,
+ Warning,
+ Info,
+ Debug,
+ Verbose,
+};
+
+/**
+ * Safely converts an integer into a valid LogLevel.
+ */
+LogLevel ToLogLevel(int32_t aLevel);
+
+class LogModule
+{
+public:
+ ~LogModule() { ::free(mName); }
+
+ /**
+ * Retrieves the module with the given name. If it does not already exist
+ * it will be created.
+ *
+ * @param aName The name of the module.
+ * @return A log module for the given name. This may be shared.
+ */
+ static LogModule* Get(const char* aName);
+
+ static void Init();
+
+ /**
+ * Sets the log file to the given filename.
+ */
+ static void SetLogFile(const char* aFilename);
+
+ /**
+ * @param aBuffer - pointer to a buffer
+ * @param aLength - the length of the buffer
+ *
+ * @return the actual length of the filepath.
+ */
+ static uint32_t GetLogFile(char *aBuffer, size_t aLength);
+
+ /**
+ * @param aAddTimestamp If we should log a time stamp with every message.
+ */
+ static void SetAddTimestamp(bool aAddTimestamp);
+
+ /**
+ * @param aIsSync If we should flush the file after every logged message.
+ */
+ static void SetIsSync(bool aIsSync);
+
+ /**
+ * Indicates whether or not the given log level is enabled.
+ */
+ bool ShouldLog(LogLevel aLevel) const { return mLevel >= aLevel; }
+
+ /**
+ * Retrieves the log module's current level.
+ */
+ LogLevel Level() const { return mLevel; }
+
+ /**
+ * Sets the log module's level.
+ */
+ void SetLevel(LogLevel level) { mLevel = level; }
+
+ /**
+ * Print a log message for this module.
+ */
+ void Printv(LogLevel aLevel, const char* aFmt, va_list aArgs) const;
+
+ /**
+ * Retrieves the module name.
+ */
+ const char* Name() const { return mName; }
+
+private:
+ friend class LogModuleManager;
+
+ explicit LogModule(const char* aName, LogLevel aLevel)
+ : mName(strdup(aName)), mLevel(aLevel)
+ {
+ }
+
+ LogModule(LogModule&) = delete;
+ LogModule& operator=(const LogModule&) = delete;
+
+ char* mName;
+ Atomic<LogLevel, Relaxed> mLevel;
+};
+
+/**
+ * Helper class that lazy loads the given log module. This is safe to use for
+ * declaring static references to log modules and can be used as a replacement
+ * for accessing a LogModule directly.
+ *
+ * Example usage:
+ * static LazyLogModule sLayoutLog("layout");
+ *
+ * void Foo() {
+ * MOZ_LOG(sLayoutLog, LogLevel::Verbose, ("Entering foo"));
+ * }
+ */
+class LazyLogModule final
+{
+public:
+ explicit constexpr LazyLogModule(const char* aLogName)
+ : mLogName(aLogName)
+ , mLog(nullptr)
+ {
+ }
+
+ operator LogModule*()
+ {
+ // NB: The use of an atomic makes the reading and assignment of mLog
+ // thread-safe. There is a small chance that mLog will be set more
+ // than once, but that's okay as it will be set to the same LogModule
+ // instance each time. Also note LogModule::Get is thread-safe.
+ LogModule* tmp = mLog;
+ if (MOZ_UNLIKELY(!tmp)) {
+ tmp = LogModule::Get(mLogName);
+ mLog = tmp;
+ }
+
+ return tmp;
+ }
+
+private:
+ const char* const mLogName;
+ Atomic<LogModule*, ReleaseAcquire> mLog;
+};
+
+namespace detail {
+
+inline bool log_test(const PRLogModuleInfo* module, LogLevel level) {
+ MOZ_ASSERT(level != LogLevel::Disabled);
+ return module && module->level >= static_cast<int>(level);
+}
+
+/**
+ * A rather inefficient wrapper for PR_LogPrint that always allocates.
+ * PR_LogModuleInfo is deprecated so it's not worth the effort to do
+ * any better.
+ */
+void log_print(const PRLogModuleInfo* aModule,
+ LogLevel aLevel,
+ const char* aFmt, ...);
+
+inline bool log_test(const LogModule* module, LogLevel level) {
+ MOZ_ASSERT(level != LogLevel::Disabled);
+ return module && module->ShouldLog(level);
+}
+
+void log_print(const LogModule* aModule,
+ LogLevel aLevel,
+ const char* aFmt, ...);
+} // namespace detail
+
+} // namespace mozilla
+
+
+// Helper macro used convert MOZ_LOG's third parameter, |_args|, from a
+// parenthesized form to a varargs form. For example:
+// ("%s", "a message") => "%s", "a message"
+#define MOZ_LOG_EXPAND_ARGS(...) __VA_ARGS__
+
+#if MOZ_LOGGING_ENABLED
+#define MOZ_LOG_TEST(_module,_level) mozilla::detail::log_test(_module, _level)
+#else
+// Define away MOZ_LOG_TEST here so the compiler will fold away entire
+// logging blocks via dead code elimination, e.g.:
+//
+// if (MOZ_LOG_TEST(...)) {
+// ...compute things to log and log them...
+// }
+//
+// This also has the nice property that no special definition of MOZ_LOG is
+// required when logging is disabled.
+#define MOZ_LOG_TEST(_module,_level) false
+#endif
+
+#define MOZ_LOG(_module,_level,_args) \
+ PR_BEGIN_MACRO \
+ if (MOZ_LOG_TEST(_module,_level)) { \
+ mozilla::detail::log_print(_module, _level, MOZ_LOG_EXPAND_ARGS _args); \
+ } \
+ PR_END_MACRO
+
+#undef PR_LOG
+#undef PR_LOG_TEST
+
+// This #define is a Logging.h-only knob! Don't encourage people to get fancy
+// with their log definitions by exporting it outside of Logging.h.
+#undef MOZ_LOGGING_ENABLED
+
+#endif // mozilla_logging_h
diff --git a/xpcom/base/MacHelpers.h b/xpcom/base/MacHelpers.h
new file mode 100644
index 0000000000..9716ae3f2f
--- /dev/null
+++ b/xpcom/base/MacHelpers.h
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_MacHelpers_h
+#define mozilla_MacHelpers_h
+
+#include "nsString.h"
+
+namespace mozilla {
+
+nsresult GetSelectedCityInfo(nsAString& aCountryCode);
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/base/MacHelpers.mm b/xpcom/base/MacHelpers.mm
new file mode 100644
index 0000000000..19d0d89007
--- /dev/null
+++ b/xpcom/base/MacHelpers.mm
@@ -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/. */
+
+#include "nsString.h"
+#include "MacHelpers.h"
+#include "nsObjCExceptions.h"
+
+#import <Foundation/Foundation.h>
+
+namespace mozilla {
+
+nsresult
+GetSelectedCityInfo(nsAString& aCountryCode)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ // Can be replaced with [[NSLocale currentLocale] countryCode] once we build
+ // with the 10.12 SDK.
+ id countryCode = [[NSLocale currentLocale] objectForKey:NSLocaleCountryCode];
+
+ if (![countryCode isKindOfClass:[NSString class]]) {
+ return NS_ERROR_FAILURE;
+ }
+
+ const char* countryCodeUTF8 = [(NSString*)countryCode UTF8String];
+
+ if (!countryCodeUTF8) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AppendUTF8toUTF16(countryCodeUTF8, aCountryCode);
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+} // namespace Mozilla
+
diff --git a/xpcom/base/NSPRLogModulesParser.cpp b/xpcom/base/NSPRLogModulesParser.cpp
new file mode 100644
index 0000000000..21090925c2
--- /dev/null
+++ b/xpcom/base/NSPRLogModulesParser.cpp
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "NSPRLogModulesParser.h"
+
+#include "mozilla/Tokenizer.h"
+
+const char kDelimiters[] = ", ";
+const char kAdditionalWordChars[] = "_-";
+
+namespace mozilla {
+
+void
+NSPRLogModulesParser(const char* aLogModules,
+ function<void(const char*, LogLevel, int32_t)> aCallback)
+{
+ if (!aLogModules) {
+ return;
+ }
+
+ Tokenizer parser(aLogModules, kDelimiters, kAdditionalWordChars);
+ nsAutoCString moduleName;
+
+ // Format: LOG_MODULES="Foo:2,Bar, Baz:5"
+ while (parser.ReadWord(moduleName)) {
+ // Next should be :<level>, default to Error if not provided.
+ LogLevel logLevel = LogLevel::Error;
+ int32_t levelValue = 0;
+ if (parser.CheckChar(':')) {
+ // Check if a negative value is provided.
+ int32_t multiplier = 1;
+ if (parser.CheckChar([](const char aChar) { return aChar == '-'; })) {
+ multiplier = -1;
+ }
+
+ // NB: If a level isn't provided after the ':' we assume the default
+ // Error level is desired. This differs from NSPR which will stop
+ // processing the log module string in this case.
+ if (parser.ReadInteger(&levelValue)) {
+ logLevel = ToLogLevel(levelValue * multiplier);
+ }
+ }
+
+ aCallback(moduleName.get(), logLevel, levelValue);
+
+ // Skip ahead to the next token.
+ parser.SkipWhites();
+ }
+}
+
+} // namespace mozilla
diff --git a/xpcom/base/NSPRLogModulesParser.h b/xpcom/base/NSPRLogModulesParser.h
new file mode 100644
index 0000000000..38aab14a34
--- /dev/null
+++ b/xpcom/base/NSPRLogModulesParser.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Logging.h"
+#include "mozilla/Function.h"
+
+namespace mozilla {
+
+/**
+ * Helper function that parses the legacy NSPR_LOG_MODULES env var format
+ * for specifying log levels and logging options.
+ *
+ * @param aLogModules The log modules configuration string.
+ * @param aCallback The callback to invoke for each log module config entry.
+ */
+void NSPRLogModulesParser(const char* aLogModules,
+ function<void(const char*, LogLevel, int32_t)> aCallback);
+
+} // namespace mozilla
diff --git a/xpcom/base/OwningNonNull.h b/xpcom/base/OwningNonNull.h
new file mode 100644
index 0000000000..b72a250c40
--- /dev/null
+++ b/xpcom/base/OwningNonNull.h
@@ -0,0 +1,198 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* A class for non-null strong pointers to reference-counted objects. */
+
+#ifndef mozilla_OwningNonNull_h
+#define mozilla_OwningNonNull_h
+
+#include "nsAutoPtr.h"
+#include "nsCycleCollectionNoteChild.h"
+
+namespace mozilla {
+
+template<class T>
+class OwningNonNull
+{
+public:
+ OwningNonNull() {}
+
+ MOZ_IMPLICIT OwningNonNull(T& aValue)
+ {
+ init(&aValue);
+ }
+
+ template<class U>
+ MOZ_IMPLICIT OwningNonNull(already_AddRefed<U>&& aValue)
+ {
+ init(aValue);
+ }
+
+ template<class U>
+ MOZ_IMPLICIT OwningNonNull(const OwningNonNull<U>& aValue)
+ {
+ init(aValue);
+ }
+
+ // This is no worse than get() in terms of const handling.
+ operator T&() const
+ {
+ MOZ_ASSERT(mInited);
+ MOZ_ASSERT(mPtr, "OwningNonNull<T> was set to null");
+ return *mPtr;
+ }
+
+ operator T*() const
+ {
+ MOZ_ASSERT(mInited);
+ MOZ_ASSERT(mPtr, "OwningNonNull<T> was set to null");
+ return mPtr;
+ }
+
+ // Conversion to bool is always true, so delete to catch errors
+ explicit operator bool() const = delete;
+
+ T*
+ operator->() const
+ {
+ MOZ_ASSERT(mInited);
+ MOZ_ASSERT(mPtr, "OwningNonNull<T> was set to null");
+ return mPtr;
+ }
+
+ OwningNonNull<T>&
+ operator=(T* aValue)
+ {
+ init(aValue);
+ return *this;
+ }
+
+ OwningNonNull<T>&
+ operator=(T& aValue)
+ {
+ init(&aValue);
+ return *this;
+ }
+
+ template<class U>
+ OwningNonNull<T>&
+ operator=(already_AddRefed<U>&& aValue)
+ {
+ init(aValue);
+ return *this;
+ }
+
+ template<class U>
+ OwningNonNull<T>&
+ operator=(const OwningNonNull<U>& aValue)
+ {
+ init(aValue);
+ return *this;
+ }
+
+ // Don't allow assigning nullptr, it makes no sense
+ void operator=(decltype(nullptr)) = delete;
+
+ already_AddRefed<T> forget()
+ {
+#ifdef DEBUG
+ mInited = false;
+#endif
+ return mPtr.forget();
+ }
+
+ template<class U>
+ void
+ forget(U** aOther)
+ {
+#ifdef DEBUG
+ mInited = false;
+#endif
+ mPtr.forget(aOther);
+ }
+
+ // Make us work with smart pointer helpers that expect a get().
+ T* get() const
+ {
+ MOZ_ASSERT(mInited);
+ MOZ_ASSERT(mPtr);
+ return mPtr;
+ }
+
+ template<typename U>
+ void swap(U& aOther)
+ {
+ mPtr.swap(aOther);
+#ifdef DEBUG
+ mInited = mPtr;
+#endif
+ }
+
+ // We have some consumers who want to check whether we're inited in non-debug
+ // builds as well. Luckily, we have the invariant that we're inited precisely
+ // when mPtr is non-null.
+ bool isInitialized() const
+ {
+ MOZ_ASSERT(!!mPtr == mInited, "mInited out of sync with mPtr?");
+ return mPtr;
+ }
+
+protected:
+ template<typename U>
+ void init(U&& aValue)
+ {
+ mPtr = aValue;
+ MOZ_ASSERT(mPtr);
+#ifdef DEBUG
+ mInited = true;
+#endif
+ }
+
+ RefPtr<T> mPtr;
+#ifdef DEBUG
+ bool mInited = false;
+#endif
+};
+
+template <typename T>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ OwningNonNull<T>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ CycleCollectionNoteChild(aCallback, aField.get(), aName, aFlags);
+}
+
+} // namespace mozilla
+
+// Declared in nsCOMPtr.h
+template<class T> template<class U>
+nsCOMPtr<T>::nsCOMPtr(const mozilla::OwningNonNull<U>& aOther)
+ : nsCOMPtr(aOther.get())
+{}
+
+template<class T> template<class U>
+nsCOMPtr<T>&
+nsCOMPtr<T>::operator=(const mozilla::OwningNonNull<U>& aOther)
+{
+ return operator=(aOther.get());
+}
+
+// Declared in mozilla/RefPtr.h
+template<class T> template<class U>
+RefPtr<T>::RefPtr(const mozilla::OwningNonNull<U>& aOther)
+ : RefPtr(aOther.get())
+{}
+
+template<class T> template<class U>
+RefPtr<T>&
+RefPtr<T>::operator=(const mozilla::OwningNonNull<U>& aOther)
+{
+ return operator=(aOther.get());
+}
+
+#endif // mozilla_OwningNonNull_h
diff --git a/xpcom/base/StaticMutex.h b/xpcom/base/StaticMutex.h
new file mode 100644
index 0000000000..731e694058
--- /dev/null
+++ b/xpcom/base/StaticMutex.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_StaticMutex_h
+#define mozilla_StaticMutex_h
+
+#include "mozilla/Atomics.h"
+#include "mozilla/Mutex.h"
+
+namespace mozilla {
+
+/**
+ * StaticMutex is a Mutex that can (and in fact, must) be used as a
+ * global/static variable.
+ *
+ * The main reason to use StaticMutex as opposed to
+ * StaticAutoPtr<OffTheBooksMutex> is that we instantiate the StaticMutex in a
+ * thread-safe manner the first time it's used.
+ *
+ * The same caveats that apply to StaticAutoPtr apply to StaticMutex. In
+ * particular, do not use StaticMutex as a stack variable or a class instance
+ * variable, because this class relies on the fact that global variablies are
+ * initialized to 0 in order to initialize mMutex. It is only safe to use
+ * StaticMutex as a global or static variable.
+ */
+class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticMutex
+{
+public:
+ // In debug builds, check that mMutex is initialized for us as we expect by
+ // the compiler. In non-debug builds, don't declare a constructor so that
+ // the compiler can see that the constructor is trivial.
+#ifdef DEBUG
+ StaticMutex()
+ {
+ MOZ_ASSERT(!mMutex);
+ }
+#endif
+
+ void Lock()
+ {
+ Mutex()->Lock();
+ }
+
+ void Unlock()
+ {
+ Mutex()->Unlock();
+ }
+
+ void AssertCurrentThreadOwns()
+ {
+#ifdef DEBUG
+ Mutex()->AssertCurrentThreadOwns();
+#endif
+ }
+
+private:
+ OffTheBooksMutex* Mutex()
+ {
+ if (mMutex) {
+ return mMutex;
+ }
+
+ OffTheBooksMutex* mutex = new OffTheBooksMutex("StaticMutex");
+ if (!mMutex.compareExchange(nullptr, mutex)) {
+ delete mutex;
+ }
+
+ return mMutex;
+ }
+
+ Atomic<OffTheBooksMutex*> mMutex;
+
+
+ // Disallow copy constructor, but only in debug mode. We only define
+ // a default constructor in debug mode (see above); if we declared
+ // this constructor always, the compiler wouldn't generate a trivial
+ // default constructor for us in non-debug mode.
+#ifdef DEBUG
+ StaticMutex(StaticMutex& aOther);
+#endif
+
+ // Disallow these operators.
+ StaticMutex& operator=(StaticMutex* aRhs);
+ static void* operator new(size_t) CPP_THROW_NEW;
+ static void operator delete(void*);
+};
+
+typedef BaseAutoLock<StaticMutex> StaticMutexAutoLock;
+typedef BaseAutoUnlock<StaticMutex> StaticMutexAutoUnlock;
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/base/StaticPtr.h b/xpcom/base/StaticPtr.h
new file mode 100644
index 0000000000..f2c820a939
--- /dev/null
+++ b/xpcom/base/StaticPtr.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 mozilla_StaticPtr_h
+#define mozilla_StaticPtr_h
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+
+/**
+ * StaticAutoPtr and StaticRefPtr are like nsAutoPtr and nsRefPtr, except they
+ * are suitable for use as global variables.
+ *
+ * In particular, a global instance of Static{Auto,Ref}Ptr doesn't cause the
+ * compiler to emit a static initializer (in release builds, anyway).
+ *
+ * In order to accomplish this, Static{Auto,Ref}Ptr must have a trivial
+ * constructor and destructor. As a consequence, it cannot initialize its raw
+ * pointer to 0 on construction, and it cannot delete/release its raw pointer
+ * upon destruction.
+ *
+ * Since the compiler guarantees that all global variables are initialized to
+ * 0, these trivial constructors are safe. Since we rely on this, the clang
+ * plugin, run as part of our "static analysis" builds, makes it a compile-time
+ * error to use Static{Auto,Ref}Ptr as anything except a global variable.
+ *
+ * Static{Auto,Ref}Ptr have a limited interface as compared to ns{Auto,Ref}Ptr;
+ * this is intentional, since their range of acceptable uses is smaller.
+ */
+
+template<class T>
+class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticAutoPtr
+{
+public:
+ // In debug builds, check that mRawPtr is initialized for us as we expect
+ // by the compiler. In non-debug builds, don't declare a constructor
+ // so that the compiler can see that the constructor is trivial.
+#ifdef DEBUG
+ StaticAutoPtr()
+ {
+ MOZ_ASSERT(!mRawPtr);
+ }
+#endif
+
+ StaticAutoPtr<T>& operator=(T* aRhs)
+ {
+ Assign(aRhs);
+ return *this;
+ }
+
+ T* get() const { return mRawPtr; }
+
+ operator T*() const { return get(); }
+
+ T* operator->() const
+ {
+ MOZ_ASSERT(mRawPtr);
+ return get();
+ }
+
+ T& operator*() const { return *get(); }
+
+private:
+ // Disallow copy constructor, but only in debug mode. We only define
+ // a default constructor in debug mode (see above); if we declared
+ // this constructor always, the compiler wouldn't generate a trivial
+ // default constructor for us in non-debug mode.
+#ifdef DEBUG
+ StaticAutoPtr(StaticAutoPtr<T>& aOther);
+#endif
+
+ void Assign(T* aNewPtr)
+ {
+ MOZ_ASSERT(!aNewPtr || mRawPtr != aNewPtr);
+ T* oldPtr = mRawPtr;
+ mRawPtr = aNewPtr;
+ delete oldPtr;
+ }
+
+ T* mRawPtr;
+};
+
+template<class T>
+class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticRefPtr
+{
+public:
+ // In debug builds, check that mRawPtr is initialized for us as we expect
+ // by the compiler. In non-debug builds, don't declare a constructor
+ // so that the compiler can see that the constructor is trivial.
+#ifdef DEBUG
+ StaticRefPtr()
+ {
+ MOZ_ASSERT(!mRawPtr);
+ }
+#endif
+
+ StaticRefPtr<T>& operator=(T* aRhs)
+ {
+ AssignWithAddref(aRhs);
+ return *this;
+ }
+
+ StaticRefPtr<T>& operator=(const StaticRefPtr<T>& aRhs)
+ {
+ return (this = aRhs.mRawPtr);
+ }
+
+ StaticRefPtr<T>& operator=(already_AddRefed<T>& aRhs)
+ {
+ AssignAssumingAddRef(aRhs.take());
+ return *this;
+ }
+
+ StaticRefPtr<T>& operator=(already_AddRefed<T>&& aRhs)
+ {
+ AssignAssumingAddRef(aRhs.take());
+ return *this;
+ }
+
+ already_AddRefed<T>
+ forget()
+ {
+ T* temp = mRawPtr;
+ mRawPtr = nullptr;
+ return already_AddRefed<T>(temp);
+ }
+
+ T* get() const { return mRawPtr; }
+
+ operator T*() const { return get(); }
+
+ T* operator->() const
+ {
+ MOZ_ASSERT(mRawPtr);
+ return get();
+ }
+
+ T& operator*() const { return *get(); }
+
+private:
+ void AssignWithAddref(T* aNewPtr)
+ {
+ if (aNewPtr) {
+ aNewPtr->AddRef();
+ }
+ AssignAssumingAddRef(aNewPtr);
+ }
+
+ void AssignAssumingAddRef(T* aNewPtr)
+ {
+ T* oldPtr = mRawPtr;
+ mRawPtr = aNewPtr;
+ if (oldPtr) {
+ oldPtr->Release();
+ }
+ }
+
+ T* MOZ_OWNING_REF mRawPtr;
+};
+
+namespace StaticPtr_internal {
+class Zero;
+} // namespace StaticPtr_internal
+
+#define REFLEXIVE_EQUALITY_OPERATORS(type1, type2, eq_fn, ...) \
+ template<__VA_ARGS__> \
+ inline bool \
+ operator==(type1 lhs, type2 rhs) \
+ { \
+ return eq_fn; \
+ } \
+ \
+ template<__VA_ARGS__> \
+ inline bool \
+ operator==(type2 lhs, type1 rhs) \
+ { \
+ return rhs == lhs; \
+ } \
+ \
+ template<__VA_ARGS__> \
+ inline bool \
+ operator!=(type1 lhs, type2 rhs) \
+ { \
+ return !(lhs == rhs); \
+ } \
+ \
+ template<__VA_ARGS__> \
+ inline bool \
+ operator!=(type2 lhs, type1 rhs) \
+ { \
+ return !(lhs == rhs); \
+ }
+
+// StaticAutoPtr (in)equality operators
+
+template<class T, class U>
+inline bool
+operator==(const StaticAutoPtr<T>& aLhs, const StaticAutoPtr<U>& aRhs)
+{
+ return aLhs.get() == aRhs.get();
+}
+
+template<class T, class U>
+inline bool
+operator!=(const StaticAutoPtr<T>& aLhs, const StaticAutoPtr<U>& aRhs)
+{
+ return !(aLhs == aRhs);
+}
+
+REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr<T>&, const U*,
+ lhs.get() == rhs, class T, class U)
+
+REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr<T>&, U*,
+ lhs.get() == rhs, class T, class U)
+
+// Let us compare StaticAutoPtr to 0.
+REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr<T>&, StaticPtr_internal::Zero*,
+ lhs.get() == nullptr, class T)
+
+// StaticRefPtr (in)equality operators
+
+template<class T, class U>
+inline bool
+operator==(const StaticRefPtr<T>& aLhs, const StaticRefPtr<U>& aRhs)
+{
+ return aLhs.get() == aRhs.get();
+}
+
+template<class T, class U>
+inline bool
+operator!=(const StaticRefPtr<T>& aLhs, const StaticRefPtr<U>& aRhs)
+{
+ return !(aLhs == aRhs);
+}
+
+REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr<T>&, const U*,
+ lhs.get() == rhs, class T, class U)
+
+REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr<T>&, U*,
+ lhs.get() == rhs, class T, class U)
+
+// Let us compare StaticRefPtr to 0.
+REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr<T>&, StaticPtr_internal::Zero*,
+ lhs.get() == nullptr, class T)
+
+#undef REFLEXIVE_EQUALITY_OPERATORS
+
+} // namespace mozilla
+
+// Declared in mozilla/RefPtr.h
+template<class T> template<class U>
+RefPtr<T>::RefPtr(const mozilla::StaticRefPtr<U>& aOther)
+ : RefPtr(aOther.get())
+{}
+
+template<class T> template<class U>
+RefPtr<T>&
+RefPtr<T>::operator=(const mozilla::StaticRefPtr<U>& aOther)
+{
+ return operator=(aOther.get());
+}
+
+#endif
diff --git a/xpcom/base/SystemMemoryReporter.cpp b/xpcom/base/SystemMemoryReporter.cpp
new file mode 100644
index 0000000000..105b9c8cf1
--- /dev/null
+++ b/xpcom/base/SystemMemoryReporter.cpp
@@ -0,0 +1,989 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/SystemMemoryReporter.h"
+
+#include "mozilla/Attributes.h"
+#include "mozilla/LinuxUtils.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/TaggedAnonymousMemory.h"
+#include "mozilla/Unused.h"
+
+#include "nsDataHashtable.h"
+#include "nsIMemoryReporter.h"
+#include "nsPrintfCString.h"
+#include "nsString.h"
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
+
+#include <dirent.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+// This file implements a Linux-specific, system-wide memory reporter. It
+// gathers all the useful memory measurements obtainable from the OS in a
+// single place, giving a high-level view of memory consumption for the entire
+// machine/device.
+//
+// Other memory reporters measure part of a single process's memory consumption.
+// This reporter is different in that it measures memory consumption of many
+// processes, and they end up in a single reports tree. This is a slight abuse
+// of the memory reporting infrastructure, and therefore the results are given
+// their own "process" called "System", which means they show up in about:memory
+// in their own section, distinct from the per-process sections.
+
+namespace mozilla {
+namespace SystemMemoryReporter {
+
+#if !defined(XP_LINUX)
+#error "This won't work if we're not on Linux."
+#endif
+
+/**
+ * RAII helper that will close an open DIR handle.
+ */
+struct MOZ_STACK_CLASS AutoDir
+{
+ explicit AutoDir(DIR* aDir) : mDir(aDir) {}
+ ~AutoDir() { if (mDir) closedir(mDir); };
+ DIR* mDir;
+};
+
+/**
+ * RAII helper that will close an open FILE handle.
+ */
+struct MOZ_STACK_CLASS AutoFile
+{
+ explicit AutoFile(FILE* aFile) : mFile(aFile) {}
+ ~AutoFile() { if (mFile) fclose(mFile); }
+ FILE* mFile;
+};
+
+static bool
+EndsWithLiteral(const nsCString& aHaystack, const char* aNeedle)
+{
+ int32_t idx = aHaystack.RFind(aNeedle);
+ return idx != -1 && idx + strlen(aNeedle) == aHaystack.Length();
+}
+
+static void
+GetDirname(const nsCString& aPath, nsACString& aOut)
+{
+ int32_t idx = aPath.RFind("/");
+ if (idx == -1) {
+ aOut.Truncate();
+ } else {
+ aOut.Assign(Substring(aPath, 0, idx));
+ }
+}
+
+static void
+GetBasename(const nsCString& aPath, nsACString& aOut)
+{
+ nsCString out;
+ int32_t idx = aPath.RFind("/");
+ if (idx == -1) {
+ out.Assign(aPath);
+ } else {
+ out.Assign(Substring(aPath, idx + 1));
+ }
+
+ // On Android, some entries in /dev/ashmem end with "(deleted)" (e.g.
+ // "/dev/ashmem/libxul.so(deleted)"). We don't care about this modifier, so
+ // cut it off when getting the entry's basename.
+ if (EndsWithLiteral(out, "(deleted)")) {
+ out.Assign(Substring(out, 0, out.RFind("(deleted)")));
+ }
+ out.StripChars(" ");
+
+ aOut.Assign(out);
+}
+
+static bool
+IsNumeric(const char* aStr)
+{
+ MOZ_ASSERT(*aStr); // shouldn't see empty strings
+ while (*aStr) {
+ if (!isdigit(*aStr)) {
+ return false;
+ }
+ ++aStr;
+ }
+ return true;
+}
+
+static bool
+IsAnonymous(const nsACString& aName)
+{
+ // Recent kernels have multiple [stack:nnnn] entries, where |nnnn| is a
+ // thread ID. However, the entire virtual memory area containing a thread's
+ // stack pointer is considered the stack for that thread, even if it was
+ // merged with an adjacent area containing non-stack data. So we treat them
+ // as regular anonymous memory. However, see below about tagged anonymous
+ // memory.
+ return aName.IsEmpty() ||
+ StringBeginsWith(aName, NS_LITERAL_CSTRING("[stack:"));
+}
+
+class SystemReporter final : public nsIMemoryReporter
+{
+ ~SystemReporter() {}
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+#define REPORT(_path, _units, _amount, _desc) \
+ do { \
+ size_t __amount = _amount; /* evaluate _amount only once */ \
+ if (__amount > 0) { \
+ aHandleReport->Callback(NS_LITERAL_CSTRING("System"), _path, \
+ KIND_OTHER, _units, __amount, _desc, aData); \
+ } \
+ } while (0)
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ // There is lots of privacy-sensitive data in /proc. Just skip this
+ // reporter entirely when anonymization is required.
+ if (aAnonymize) {
+ return NS_OK;
+ }
+
+ if (!Preferences::GetBool("memory.system_memory_reporter")) {
+ return NS_OK;
+ }
+
+ // Read relevant fields from /proc/meminfo.
+ int64_t memTotal = 0, memFree = 0;
+ nsresult rv1 = ReadMemInfo(&memTotal, &memFree);
+
+ // Collect per-process reports from /proc/<pid>/smaps.
+ int64_t totalPss = 0;
+ nsresult rv2 = CollectProcessReports(aHandleReport, aData, &totalPss);
+
+ // Report the non-process numbers.
+ if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
+ int64_t other = memTotal - memFree - totalPss;
+ REPORT(NS_LITERAL_CSTRING("mem/other"), UNITS_BYTES, other,
+ NS_LITERAL_CSTRING(
+"Memory which is neither owned by any user-space process nor free. Note that "
+"this includes memory holding cached files from the disk which can be "
+"reclaimed by the OS at any time."));
+
+ REPORT(NS_LITERAL_CSTRING("mem/free"), UNITS_BYTES, memFree,
+ NS_LITERAL_CSTRING(
+"Memory which is free and not being used for any purpose."));
+ }
+
+ // Report reserved memory not included in memTotal.
+ CollectPmemReports(aHandleReport, aData);
+
+ // Report zram usage statistics.
+ CollectZramReports(aHandleReport, aData);
+
+ // Report kgsl graphics memory usage.
+ CollectKgslReports(aHandleReport, aData);
+
+ // Report ION memory usage.
+ CollectIonReports(aHandleReport, aData);
+
+ return NS_OK;
+ }
+
+private:
+ // These are the cross-cutting measurements across all processes.
+ class ProcessSizes
+ {
+ public:
+ void Add(const nsACString& aKey, size_t aSize)
+ {
+ mTagged.Put(aKey, mTagged.Get(aKey) + aSize);
+ }
+
+ void Report(nsIHandleReportCallback* aHandleReport, nsISupports* aData)
+ {
+ for (auto iter = mTagged.Iter(); !iter.Done(); iter.Next()) {
+ nsCStringHashKey::KeyType key = iter.Key();
+ size_t amount = iter.UserData();
+
+ nsAutoCString path("processes/");
+ path.Append(key);
+
+ nsAutoCString desc("This is the sum of all processes' '");
+ desc.Append(key);
+ desc.AppendLiteral("' numbers.");
+
+ REPORT(path, UNITS_BYTES, amount, desc);
+ }
+ }
+
+ private:
+ nsDataHashtable<nsCStringHashKey, size_t> mTagged;
+ };
+
+ nsresult ReadMemInfo(int64_t* aMemTotal, int64_t* aMemFree)
+ {
+ FILE* f = fopen("/proc/meminfo", "r");
+ if (!f) {
+ return NS_ERROR_FAILURE;
+ }
+
+ int n1 = fscanf(f, "MemTotal: %" SCNd64 " kB\n", aMemTotal);
+ int n2 = fscanf(f, "MemFree: %" SCNd64 " kB\n", aMemFree);
+
+ fclose(f);
+
+ if (n1 != 1 || n2 != 1) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Convert from KB to B.
+ *aMemTotal *= 1024;
+ *aMemFree *= 1024;
+
+ return NS_OK;
+ }
+
+ nsresult CollectProcessReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData,
+ int64_t* aTotalPss)
+ {
+ *aTotalPss = 0;
+ ProcessSizes processSizes;
+
+ DIR* d = opendir("/proc");
+ if (NS_WARN_IF(!d)) {
+ return NS_ERROR_FAILURE;
+ }
+ struct dirent* ent;
+ while ((ent = readdir(d))) {
+ struct stat statbuf;
+ const char* pidStr = ent->d_name;
+ // Don't check the return value of stat() -- it can return -1 for these
+ // directories even when it has succeeded, apparently.
+ stat(pidStr, &statbuf);
+ if (S_ISDIR(statbuf.st_mode) && IsNumeric(pidStr)) {
+ nsCString processName("process(");
+
+ // Get the command name from cmdline. If that fails, the pid is still
+ // shown.
+ nsPrintfCString cmdlinePath("/proc/%s/cmdline", pidStr);
+ FILE* f = fopen(cmdlinePath.get(), "r");
+ if (f) {
+ static const size_t len = 256;
+ char buf[len];
+ if (fgets(buf, len, f)) {
+ processName.Append(buf);
+ // A hack: replace forward slashes with '\\' so they aren't treated
+ // as path separators. Consumers of this reporter (such as
+ // about:memory) have to undo this change.
+ processName.ReplaceChar('/', '\\');
+ processName.AppendLiteral(", ");
+ }
+ fclose(f);
+ }
+ processName.AppendLiteral("pid=");
+ processName.Append(pidStr);
+ processName.Append(')');
+
+ // Read the PSS values from the smaps file.
+ nsPrintfCString smapsPath("/proc/%s/smaps", pidStr);
+ f = fopen(smapsPath.get(), "r");
+ if (!f) {
+ // Processes can terminate between the readdir() call above and now,
+ // so just skip if we can't open the file.
+ continue;
+ }
+ ParseMappings(f, processName, aHandleReport, aData, &processSizes,
+ aTotalPss);
+ fclose(f);
+
+ // Report the open file descriptors for this process.
+ nsPrintfCString procFdPath("/proc/%s/fd", pidStr);
+ CollectOpenFileReports(aHandleReport, aData, procFdPath, processName);
+ }
+ }
+ closedir(d);
+
+ // Report the "processes/" tree.
+ processSizes.Report(aHandleReport, aData);
+
+ return NS_OK;
+ }
+
+ void ParseMappings(FILE* aFile,
+ const nsACString& aProcessName,
+ nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData,
+ ProcessSizes* aProcessSizes,
+ int64_t* aTotalPss)
+ {
+ // The first line of an entry in /proc/<pid>/smaps looks just like an entry
+ // in /proc/<pid>/maps:
+ //
+ // address perms offset dev inode pathname
+ // 02366000-025d8000 rw-p 00000000 00:00 0 [heap]
+ //
+ // Each of the following lines contains a key and a value, separated
+ // by ": ", where the key does not contain either of those characters.
+ // Assuming more than this about the structure of those lines has
+ // failed to be future-proof in the past, so we avoid doing so.
+ //
+ // This makes it difficult to detect the start of a new entry
+ // until it's been removed from the stdio buffer, so we just loop
+ // over all lines in the file in this routine.
+
+ const int argCount = 8;
+
+ unsigned long long addrStart, addrEnd;
+ char perms[5];
+ unsigned long long offset;
+ // The 2.6 and 3.0 kernels allocate 12 bits for the major device number and
+ // 20 bits for the minor device number. Future kernels might allocate more.
+ // 64 bits ought to be enough for anybody.
+ char devMajor[17];
+ char devMinor[17];
+ unsigned int inode;
+ char line[1025];
+
+ // This variable holds the path of the current entry, or is void
+ // if we're scanning for the start of a new entry.
+ nsAutoCString currentPath;
+ int pathOffset;
+
+ currentPath.SetIsVoid(true);
+ while (fgets(line, sizeof(line), aFile)) {
+ if (currentPath.IsVoid()) {
+ int n = sscanf(line,
+ "%llx-%llx %4s %llx "
+ "%16[0-9a-fA-F]:%16[0-9a-fA-F] %u %n",
+ &addrStart, &addrEnd, perms, &offset, devMajor,
+ devMinor, &inode, &pathOffset);
+
+ if (n >= argCount - 1) {
+ currentPath.Assign(line + pathOffset);
+ currentPath.StripChars("\n");
+ }
+ continue;
+ }
+
+ // Now that we have a name and other metadata, scan for the PSS.
+ size_t pss_kb;
+ int n = sscanf(line, "Pss: %zu", &pss_kb);
+ if (n < 1) {
+ continue;
+ }
+
+ size_t pss = pss_kb * 1024;
+ if (pss > 0) {
+ nsAutoCString name, description, tag;
+ GetReporterNameAndDescription(currentPath.get(), perms, name, description, tag);
+
+ nsAutoCString processMemPath("mem/processes/");
+ processMemPath.Append(aProcessName);
+ processMemPath.Append('/');
+ processMemPath.Append(name);
+
+ REPORT(processMemPath, UNITS_BYTES, pss, description);
+
+ // Increment the appropriate aProcessSizes values, and the total.
+ aProcessSizes->Add(tag, pss);
+ *aTotalPss += pss;
+ }
+
+ // Now that we've seen the PSS, we're done with this entry.
+ currentPath.SetIsVoid(true);
+ }
+ }
+
+ void GetReporterNameAndDescription(const char* aPath,
+ const char* aPerms,
+ nsACString& aName,
+ nsACString& aDesc,
+ nsACString& aTag)
+ {
+ aName.Truncate();
+ aDesc.Truncate();
+ aTag.Truncate();
+
+ // If aPath points to a file, we have its absolute path; it might
+ // also be a bracketed pseudo-name (see below). In either case
+ // there is also some whitespace to trim.
+ nsAutoCString absPath;
+ absPath.Append(aPath);
+ absPath.StripChars(" ");
+
+ if (absPath.EqualsLiteral("[heap]")) {
+ aName.AppendLiteral("anonymous/brk-heap");
+ aDesc.AppendLiteral(
+ "Memory in anonymous mappings within the boundaries defined by "
+ "brk() / sbrk(). This is likely to be just a portion of the "
+ "application's heap; the remainder lives in other anonymous mappings. "
+ "This corresponds to '[heap]' in /proc/<pid>/smaps.");
+ aTag = aName;
+ } else if (absPath.EqualsLiteral("[stack]")) {
+ aName.AppendLiteral("stack/main-thread");
+ aDesc.AppendPrintf(
+ "The stack size of the process's main thread. This corresponds to "
+ "'[stack]' in /proc/<pid>/smaps.");
+ aTag = aName;
+ } else if (MozTaggedMemoryIsSupported() &&
+ StringBeginsWith(absPath, NS_LITERAL_CSTRING("[stack:"))) {
+ // If tagged memory is supported, we can be reasonably sure that
+ // the virtual memory area containing the stack hasn't been
+ // merged with unrelated heap memory. (This prevents the
+ // "[stack:" entries from reaching the IsAnonymous case below.)
+ pid_t tid = atoi(absPath.get() + 7);
+ nsAutoCString threadName, escapedThreadName;
+ LinuxUtils::GetThreadName(tid, threadName);
+ if (threadName.IsEmpty()) {
+ threadName.AssignLiteral("<unknown>");
+ }
+ escapedThreadName.Assign(threadName);
+ escapedThreadName.StripChars("()");
+ escapedThreadName.ReplaceChar('/', '\\');
+
+ aName.AppendLiteral("stack/non-main-thread");
+ aName.AppendLiteral("/name(");
+ aName.Append(escapedThreadName);
+ aName.Append(')');
+ aTag = aName;
+ aName.AppendPrintf("/thread(%d)", tid);
+
+ aDesc.AppendPrintf("The stack size of a non-main thread named '%s' with "
+ "thread ID %d. This corresponds to '[stack:%d]' "
+ "in /proc/%d/smaps.", threadName.get(), tid, tid);
+ } else if (absPath.EqualsLiteral("[vdso]")) {
+ aName.AppendLiteral("vdso");
+ aDesc.AppendLiteral(
+ "The virtual dynamically-linked shared object, also known as the "
+ "'vsyscall page'. This is a memory region mapped by the operating "
+ "system for the purpose of allowing processes to perform some "
+ "privileged actions without the overhead of a syscall.");
+ aTag = aName;
+ } else if (StringBeginsWith(absPath, NS_LITERAL_CSTRING("[anon:")) &&
+ EndsWithLiteral(absPath, "]")) {
+ // It's tagged memory; see also "mfbt/TaggedAnonymousMemory.h".
+ nsAutoCString tag(Substring(absPath, 6, absPath.Length() - 7));
+
+ aName.AppendLiteral("anonymous/");
+ aName.Append(tag);
+ aTag = aName;
+ aDesc.AppendLiteral("Memory in anonymous mappings tagged with '");
+ aDesc.Append(tag);
+ aDesc.Append('\'');
+ } else if (!IsAnonymous(absPath)) {
+ // We now know it's an actual file. Truncate this to its
+ // basename, and put the absolute path in the description.
+ nsAutoCString basename, dirname;
+ GetBasename(absPath, basename);
+ GetDirname(absPath, dirname);
+
+ // Hack: A file is a shared library if the basename contains ".so" and
+ // its dirname contains "/lib", or if the basename ends with ".so".
+ if (EndsWithLiteral(basename, ".so") ||
+ (basename.Find(".so") != -1 && dirname.Find("/lib") != -1)) {
+ aName.AppendLiteral("shared-libraries/");
+ aTag = aName;
+
+ if (strncmp(aPerms, "r-x", 3) == 0) {
+ aTag.AppendLiteral("read-executable");
+ } else if (strncmp(aPerms, "rw-", 3) == 0) {
+ aTag.AppendLiteral("read-write");
+ } else if (strncmp(aPerms, "r--", 3) == 0) {
+ aTag.AppendLiteral("read-only");
+ } else {
+ aTag.AppendLiteral("other");
+ }
+
+ } else {
+ aName.AppendLiteral("other-files");
+ if (EndsWithLiteral(basename, ".xpi")) {
+ aName.AppendLiteral("/extensions");
+ } else if (dirname.Find("/fontconfig") != -1) {
+ aName.AppendLiteral("/fontconfig");
+ } else {
+ aName.AppendLiteral("/misc");
+ }
+ aTag = aName;
+ aName.Append('/');
+ }
+
+ aName.Append(basename);
+ aDesc.Append(absPath);
+ } else {
+ if (MozTaggedMemoryIsSupported()) {
+ aName.AppendLiteral("anonymous/untagged");
+ aDesc.AppendLiteral("Memory in untagged anonymous mappings.");
+ aTag = aName;
+ } else {
+ aName.AppendLiteral("anonymous/outside-brk");
+ aDesc.AppendLiteral("Memory in anonymous mappings outside the "
+ "boundaries defined by brk() / sbrk().");
+ aTag = aName;
+ }
+ }
+
+ aName.AppendLiteral("/[");
+ aName.Append(aPerms);
+ aName.Append(']');
+
+ // Append the permissions. This is useful for non-verbose mode in
+ // about:memory when the filename is long and goes of the right side of the
+ // window.
+ aDesc.AppendLiteral(" [");
+ aDesc.Append(aPerms);
+ aDesc.Append(']');
+ }
+
+ void CollectPmemReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData)
+ {
+ // The pmem subsystem allocates physically contiguous memory for
+ // interfacing with hardware. In order to ensure availability,
+ // this memory is reserved during boot, and allocations are made
+ // within these regions at runtime.
+ //
+ // There are typically several of these pools allocated at boot.
+ // The /sys/kernel/pmem_regions directory contains a subdirectory
+ // for each one. Within each subdirectory, the files we care
+ // about are "size" (the total amount of physical memory) and
+ // "mapped_regions" (a list of the current allocations within that
+ // area).
+ DIR* d = opendir("/sys/kernel/pmem_regions");
+ if (!d) {
+ return;
+ }
+
+ struct dirent* ent;
+ while ((ent = readdir(d))) {
+ const char* name = ent->d_name;
+ uint64_t size;
+ int scanned;
+
+ // Skip "." and ".." (and any other dotfiles).
+ if (name[0] == '.') {
+ continue;
+ }
+
+ // Read the total size. The file gives the size in decimal and
+ // hex, in the form "13631488(0xd00000)"; we parse the former.
+ nsPrintfCString sizePath("/sys/kernel/pmem_regions/%s/size", name);
+ FILE* sizeFile = fopen(sizePath.get(), "r");
+ if (NS_WARN_IF(!sizeFile)) {
+ continue;
+ }
+ scanned = fscanf(sizeFile, "%" SCNu64, &size);
+ fclose(sizeFile);
+ if (NS_WARN_IF(scanned != 1)) {
+ continue;
+ }
+
+ // Read mapped regions; format described below.
+ uint64_t freeSize = size;
+ nsPrintfCString regionsPath("/sys/kernel/pmem_regions/%s/mapped_regions",
+ name);
+ FILE* regionsFile = fopen(regionsPath.get(), "r");
+ if (regionsFile) {
+ static const size_t bufLen = 4096;
+ char buf[bufLen];
+ while (fgets(buf, bufLen, regionsFile)) {
+ int pid;
+
+ // Skip header line.
+ if (strncmp(buf, "pid #", 5) == 0) {
+ continue;
+ }
+ // Line format: "pid N:" + zero or more "(Start,Len) ".
+ // N is decimal; Start and Len are in hex.
+ scanned = sscanf(buf, "pid %d", &pid);
+ if (NS_WARN_IF(scanned != 1)) {
+ continue;
+ }
+ for (const char* nextParen = strchr(buf, '(');
+ nextParen != nullptr;
+ nextParen = strchr(nextParen + 1, '(')) {
+ uint64_t mapStart, mapLen;
+
+ scanned = sscanf(nextParen + 1, "%" SCNx64 ",%" SCNx64,
+ &mapStart, &mapLen);
+ if (NS_WARN_IF(scanned != 2)) {
+ break;
+ }
+
+ nsPrintfCString path("mem/pmem/used/%s/segment(pid=%d, "
+ "offset=0x%" PRIx64 ")", name, pid, mapStart);
+ nsPrintfCString desc("Physical memory reserved for the \"%s\" pool "
+ "and allocated to a buffer.", name);
+ REPORT(path, UNITS_BYTES, mapLen, desc);
+ freeSize -= mapLen;
+ }
+ }
+ fclose(regionsFile);
+ }
+
+ nsPrintfCString path("mem/pmem/free/%s", name);
+ nsPrintfCString desc("Physical memory reserved for the \"%s\" pool and "
+ "unavailable to the rest of the system, but not "
+ "currently allocated.", name);
+ REPORT(path, UNITS_BYTES, freeSize, desc);
+ }
+ closedir(d);
+ }
+
+ void
+ CollectIonReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData)
+ {
+ // ION is a replacement for PMEM (and other similar allocators).
+ //
+ // More details from http://lwn.net/Articles/480055/
+ // "Like its PMEM-like predecessors, ION manages one or more memory pools,
+ // some of which are set aside at boot time to combat fragmentation or to
+ // serve special hardware needs. GPUs, display controllers, and cameras
+ // are some of the hardware blocks that may have special memory
+ // requirements."
+ //
+ // The file format starts as follows:
+ // client pid size
+ // ----------------------------------------------------
+ // adsprpc-smd 1 4096
+ // fd900000.qcom,mdss_mdp 1 1658880
+ // ----------------------------------------------------
+ // orphaned allocations (info is from last known client):
+ // Homescreen 24100 294912 0 1
+ // b2g 23987 1658880 0 1
+ // mdss_fb0 401 1658880 0 1
+ // b2g 23987 4096 0 1
+ // Built-in Keyboa 24205 61440 0 1
+ // ----------------------------------------------------
+ // <other stuff>
+ //
+ // For our purposes we only care about the first portion of the file noted
+ // above which contains memory alloations (both sections). The term
+ // "orphaned" is misleading, it appears that every allocation not by the
+ // first process is considered orphaned on FxOS devices.
+
+ // The first three fields of each entry interest us:
+ // 1) client - Essentially the process name. We limit client names to 63
+ // characters, in theory they should never be greater than 15
+ // due to thread name length limitations.
+ // 2) pid - The ID of the allocating process, read as a uint32_t.
+ // 3) size - The size of the allocation in bytes, read as as a uint64_t.
+ const char* const kFormatString = "%63s %" SCNu32 " %" SCNu64;
+ const int kNumFields = 3;
+ const size_t kStringSize = 64;
+ const char* const kIonIommuPath = "/sys/kernel/debug/ion/iommu";
+
+ FILE* iommu = fopen(kIonIommuPath, "r");
+ if (!iommu) {
+ return;
+ }
+
+ AutoFile iommuGuard(iommu);
+
+ const size_t kBufferLen = 256;
+ char buffer[kBufferLen];
+ char client[kStringSize];
+ uint32_t pid;
+ uint64_t size;
+
+ // Ignore the header line.
+ Unused << fgets(buffer, kBufferLen, iommu);
+
+ // Ignore the separator line.
+ Unused << fgets(buffer, kBufferLen, iommu);
+
+ const char* const kSep = "----";
+ const size_t kSepLen = 4;
+
+ // Read non-orphaned entries.
+ while (fgets(buffer, kBufferLen, iommu) &&
+ strncmp(kSep, buffer, kSepLen) != 0) {
+ if (sscanf(buffer, kFormatString, client, &pid, &size) == kNumFields) {
+ nsPrintfCString entryPath("ion-memory/%s (pid=%d)", client, pid);
+ REPORT(entryPath, UNITS_BYTES, size,
+ NS_LITERAL_CSTRING("An ION kernel memory allocation."));
+ }
+ }
+
+ // Ignore the orphaned header.
+ Unused << fgets(buffer, kBufferLen, iommu);
+
+ // Read orphaned entries.
+ while (fgets(buffer, kBufferLen, iommu) &&
+ strncmp(kSep, buffer, kSepLen) != 0) {
+ if (sscanf(buffer, kFormatString, client, &pid, &size) == kNumFields) {
+ nsPrintfCString entryPath("ion-memory/%s (pid=%d)", client, pid);
+ REPORT(entryPath, UNITS_BYTES, size,
+ NS_LITERAL_CSTRING("An ION kernel memory allocation."));
+ }
+ }
+
+ // Ignore the rest of the file.
+ }
+
+ uint64_t
+ ReadSizeFromFile(const char* aFilename)
+ {
+ FILE* sizeFile = fopen(aFilename, "r");
+ if (NS_WARN_IF(!sizeFile)) {
+ return 0;
+ }
+
+ uint64_t size = 0;
+ Unused << fscanf(sizeFile, "%" SCNu64, &size);
+ fclose(sizeFile);
+
+ return size;
+ }
+
+ void
+ CollectZramReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData)
+ {
+ // zram usage stats files can be found under:
+ // /sys/block/zram<id>
+ // |--> disksize - Maximum amount of uncompressed data that can be
+ // stored on the disk (bytes)
+ // |--> orig_data_size - Uncompressed size of data in the disk (bytes)
+ // |--> compr_data_size - Compressed size of the data in the disk (bytes)
+ // |--> num_reads - Number of attempted reads to the disk (count)
+ // |--> num_writes - Number of attempted writes to the disk (count)
+ //
+ // Each file contains a single integer value in decimal form.
+
+ DIR* d = opendir("/sys/block");
+ if (!d) {
+ return;
+ }
+
+ struct dirent* ent;
+ while ((ent = readdir(d))) {
+ const char* name = ent->d_name;
+
+ // Skip non-zram entries.
+ if (strncmp("zram", name, 4) != 0) {
+ continue;
+ }
+
+ // Report disk size statistics.
+ nsPrintfCString diskSizeFile("/sys/block/%s/disksize", name);
+ nsPrintfCString origSizeFile("/sys/block/%s/orig_data_size", name);
+
+ uint64_t diskSize = ReadSizeFromFile(diskSizeFile.get());
+ uint64_t origSize = ReadSizeFromFile(origSizeFile.get());
+ uint64_t unusedSize = diskSize - origSize;
+
+ nsPrintfCString diskUsedPath("zram-disksize/%s/used", name);
+ nsPrintfCString diskUsedDesc(
+ "The uncompressed size of data stored in \"%s.\" "
+ "This excludes zero-filled pages since "
+ "no memory is allocated for them.", name);
+ REPORT(diskUsedPath, UNITS_BYTES, origSize, diskUsedDesc);
+
+ nsPrintfCString diskUnusedPath("zram-disksize/%s/unused", name);
+ nsPrintfCString diskUnusedDesc(
+ "The amount of uncompressed data that can still be "
+ "be stored in \"%s\"", name);
+ REPORT(diskUnusedPath, UNITS_BYTES, unusedSize, diskUnusedDesc);
+
+ // Report disk accesses.
+ nsPrintfCString readsFile("/sys/block/%s/num_reads", name);
+ nsPrintfCString writesFile("/sys/block/%s/num_writes", name);
+
+ uint64_t reads = ReadSizeFromFile(readsFile.get());
+ uint64_t writes = ReadSizeFromFile(writesFile.get());
+
+ nsPrintfCString readsDesc(
+ "The number of reads (failed or successful) done on "
+ "\"%s\"", name);
+ nsPrintfCString readsPath("zram-accesses/%s/reads", name);
+ REPORT(readsPath, UNITS_COUNT_CUMULATIVE, reads, readsDesc);
+
+ nsPrintfCString writesDesc(
+ "The number of writes (failed or successful) done "
+ "on \"%s\"", name);
+ nsPrintfCString writesPath("zram-accesses/%s/writes", name);
+ REPORT(writesPath, UNITS_COUNT_CUMULATIVE, writes, writesDesc);
+
+ // Report compressed data size.
+ nsPrintfCString comprSizeFile("/sys/block/%s/compr_data_size", name);
+ uint64_t comprSize = ReadSizeFromFile(comprSizeFile.get());
+
+ nsPrintfCString comprSizeDesc(
+ "The compressed size of data stored in \"%s\"",
+ name);
+ nsPrintfCString comprSizePath("zram-compr-data-size/%s", name);
+ REPORT(comprSizePath, UNITS_BYTES, comprSize, comprSizeDesc);
+ }
+
+ closedir(d);
+ }
+
+ void
+ CollectOpenFileReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData,
+ const nsACString& aProcPath,
+ const nsACString& aProcessName)
+ {
+ // All file descriptors opened by a process are listed under
+ // /proc/<pid>/fd/<numerical_fd>. Each entry is a symlink that points to the
+ // path that was opened. This can be an actual file, a socket, a pipe, an
+ // anon_inode, or possibly an uncategorized device.
+ const char kFilePrefix[] = "/";
+ const char kSocketPrefix[] = "socket:";
+ const char kPipePrefix[] = "pipe:";
+ const char kAnonInodePrefix[] = "anon_inode:";
+
+ const nsCString procPath(aProcPath);
+ DIR* d = opendir(procPath.get());
+ if (!d) {
+ return;
+ }
+
+ char linkPath[PATH_MAX + 1];
+ struct dirent* ent;
+ while ((ent = readdir(d))) {
+ const char* fd = ent->d_name;
+
+ // Skip "." and ".." (and any other dotfiles).
+ if (fd[0] == '.') {
+ continue;
+ }
+
+ nsPrintfCString fullPath("%s/%s", procPath.get(), fd);
+ ssize_t linkPathSize = readlink(fullPath.get(), linkPath, PATH_MAX);
+ if (linkPathSize > 0) {
+ linkPath[linkPathSize] = '\0';
+
+#define CHECK_PREFIX(prefix) \
+ (strncmp(linkPath, prefix, sizeof(prefix) - 1) == 0)
+
+ const char* category = nullptr;
+ const char* descriptionPrefix = nullptr;
+
+ if (CHECK_PREFIX(kFilePrefix)) {
+ category = "files"; // No trailing slash, the file path will have one
+ descriptionPrefix = "An open";
+ } else if (CHECK_PREFIX(kSocketPrefix)) {
+ category = "sockets/";
+ descriptionPrefix = "A socket";
+ } else if (CHECK_PREFIX(kPipePrefix)) {
+ category = "pipes/";
+ descriptionPrefix = "A pipe";
+ } else if (CHECK_PREFIX(kAnonInodePrefix)) {
+ category = "anon_inodes/";
+ descriptionPrefix = "An anon_inode";
+ } else {
+ category = "";
+ descriptionPrefix = "An uncategorized";
+ }
+
+#undef CHECK_PREFIX
+
+ const nsCString processName(aProcessName);
+ nsPrintfCString entryPath("open-fds/%s/%s%s/%s",
+ processName.get(), category, linkPath, fd);
+ nsPrintfCString entryDescription("%s file descriptor opened by the process",
+ descriptionPrefix);
+ REPORT(entryPath, UNITS_COUNT, 1, entryDescription);
+ }
+ }
+
+ closedir(d);
+ }
+
+ void
+ CollectKgslReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData)
+ {
+ // Each process that uses kgsl memory will have an entry under
+ // /sys/kernel/debug/kgsl/proc/<pid>/mem. This file format includes a
+ // header and then entries with types as follows:
+ // gpuaddr useraddr size id flags type usage sglen
+ // hexaddr hexaddr int int str str str int
+ // We care primarily about the usage and size.
+
+ // For simplicity numbers will be uint64_t, strings 63 chars max.
+ const char* const kScanFormat =
+ "%" SCNx64 " %" SCNx64 " %" SCNu64 " %" SCNu64
+ " %63s %63s %63s %" SCNu64;
+ const int kNumFields = 8;
+ const size_t kStringSize = 64;
+
+ DIR* d = opendir("/sys/kernel/debug/kgsl/proc/");
+ if (!d) {
+ return;
+ }
+
+ AutoDir dirGuard(d);
+
+ struct dirent* ent;
+ while ((ent = readdir(d))) {
+ const char* pid = ent->d_name;
+
+ // Skip "." and ".." (and any other dotfiles).
+ if (pid[0] == '.') {
+ continue;
+ }
+
+ nsPrintfCString memPath("/sys/kernel/debug/kgsl/proc/%s/mem", pid);
+ FILE* memFile = fopen(memPath.get(), "r");
+ if (NS_WARN_IF(!memFile)) {
+ continue;
+ }
+
+ AutoFile fileGuard(memFile);
+
+ // Attempt to map the pid to a more useful name.
+ nsAutoCString procName;
+ LinuxUtils::GetThreadName(atoi(pid), procName);
+
+ if (procName.IsEmpty()) {
+ procName.Append("pid=");
+ procName.Append(pid);
+ } else {
+ procName.Append(" (pid=");
+ procName.Append(pid);
+ procName.Append(")");
+ }
+
+ uint64_t gpuaddr, useraddr, size, id, sglen;
+ char flags[kStringSize];
+ char type[kStringSize];
+ char usage[kStringSize];
+
+ // Bypass the header line.
+ char buff[1024];
+ Unused << fgets(buff, 1024, memFile);
+
+ while (fscanf(memFile, kScanFormat, &gpuaddr, &useraddr, &size, &id,
+ flags, type, usage, &sglen) == kNumFields) {
+ nsPrintfCString entryPath("kgsl-memory/%s/%s", procName.get(), usage);
+ REPORT(entryPath, UNITS_BYTES, size,
+ NS_LITERAL_CSTRING("A kgsl graphics memory allocation."));
+ }
+ }
+ }
+};
+
+NS_IMPL_ISUPPORTS(SystemReporter, nsIMemoryReporter)
+
+void
+Init()
+{
+ RegisterStrongMemoryReporter(new SystemReporter());
+}
+
+} // namespace SystemMemoryReporter
+} // namespace mozilla
diff --git a/xpcom/base/SystemMemoryReporter.h b/xpcom/base/SystemMemoryReporter.h
new file mode 100644
index 0000000000..e524876133
--- /dev/null
+++ b/xpcom/base/SystemMemoryReporter.h
@@ -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/. */
+
+#ifndef mozilla_SystemMemoryReporter_h_
+#define mozilla_SystemMemoryReporter_h_
+
+namespace mozilla {
+namespace SystemMemoryReporter {
+
+// This only works on Linux, but to make callers' lives easier, we stub out
+// empty functions on other platforms.
+
+#if defined(XP_LINUX)
+void
+Init();
+#else
+void
+Init()
+{
+}
+#endif
+
+} // namespace SystemMemoryReporter
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/base/moz.build b/xpcom/base/moz.build
new file mode 100644
index 0000000000..d6a336b409
--- /dev/null
+++ b/xpcom/base/moz.build
@@ -0,0 +1,159 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_SOURCES += [
+ 'nsIConsoleListener.idl',
+ 'nsIConsoleMessage.idl',
+ 'nsIConsoleService.idl',
+ 'nsICycleCollectorListener.idl',
+ 'nsIDebug2.idl',
+ 'nsIErrorService.idl',
+ 'nsIException.idl',
+ 'nsIGZFileWriter.idl',
+ 'nsIInterfaceRequestor.idl',
+ 'nsIMemory.idl',
+ 'nsIMemoryInfoDumper.idl',
+ 'nsIMemoryReporter.idl',
+ 'nsIMessageLoop.idl',
+ 'nsIMutable.idl',
+ 'nsIProgrammingLanguage.idl',
+ 'nsISecurityConsoleMessage.idl',
+ 'nsIStatusReporter.idl',
+ 'nsISupports.idl',
+ 'nsIUUIDGenerator.idl',
+ 'nsIVersionComparator.idl',
+ 'nsIWeakReference.idl',
+ 'nsrootidl.idl',
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ XPIDL_SOURCES += [
+ 'nsIMacUtils.idl',
+ ]
+ EXPORTS.mozilla += [
+ 'MacHelpers.h',
+ ]
+ UNIFIED_SOURCES += [
+ 'MacHelpers.mm',
+ ]
+
+XPIDL_MODULE = 'xpcom_base'
+
+EXPORTS += [
+ 'CodeAddressService.h',
+ 'ErrorList.h',
+ 'nsAgg.h',
+ 'nsAlgorithm.h',
+ 'nsAutoPtr.h',
+ 'nsAutoRef.h',
+ 'nsCom.h',
+ 'nscore.h',
+ 'nsCycleCollector.h',
+ 'nsDebugImpl.h',
+ 'nsDumpUtils.h',
+ 'nsError.h',
+ 'nsGZFileWriter.h',
+ 'nsIID.h',
+ 'nsInterfaceRequestorAgg.h',
+ 'nsISizeOf.h',
+ 'nsISupportsBase.h',
+ 'nsObjCExceptions.h',
+ 'nsQueryObject.h',
+ 'nsTraceRefcnt.h',
+ 'nsWeakPtr.h',
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ EXPORTS += [
+ 'nsWindowsHelpers.h',
+ ]
+
+EXPORTS.mozilla += [
+ 'AvailableMemoryTracker.h',
+ 'ClearOnShutdown.h',
+ 'CountingAllocatorBase.h',
+ 'CycleCollectedJSContext.h',
+ 'Debug.h',
+ 'DebuggerOnGCRunnable.h',
+ 'DeferredFinalize.h',
+ 'ErrorNames.h',
+ 'HoldDropJSObjects.h',
+ 'JSObjectHolder.h',
+ 'LinuxUtils.h',
+ 'Logging.h',
+ 'nsMemoryInfoDumper.h',
+ 'OwningNonNull.h',
+ 'StaticMutex.h',
+ 'StaticPtr.h',
+ 'SystemMemoryReporter.h',
+]
+
+# nsDebugImpl isn't unified because we disable PGO so that NS_ABORT_OOM isn't
+# optimized away oddly.
+SOURCES += [
+ 'nsDebugImpl.cpp',
+]
+SOURCES['nsDebugImpl.cpp'].no_pgo = True
+
+UNIFIED_SOURCES += [
+ 'AvailableMemoryTracker.cpp',
+ 'ClearOnShutdown.cpp',
+ 'CycleCollectedJSContext.cpp',
+ 'Debug.cpp',
+ 'DebuggerOnGCRunnable.cpp',
+ 'DeferredFinalize.cpp',
+ 'ErrorNames.cpp',
+ 'HoldDropJSObjects.cpp',
+ 'JSObjectHolder.cpp',
+ 'Logging.cpp',
+ 'LogModulePrefWatcher.cpp',
+ 'nsConsoleMessage.cpp',
+ 'nsConsoleService.cpp',
+ 'nsCycleCollector.cpp',
+ 'nsCycleCollectorTraceJSHelpers.cpp',
+ 'nsDumpUtils.cpp',
+ 'nsErrorService.cpp',
+ 'nsGZFileWriter.cpp',
+ 'nsInterfaceRequestorAgg.cpp',
+ 'nsMemoryImpl.cpp',
+ 'nsMemoryInfoDumper.cpp',
+ 'nsMemoryReporterManager.cpp',
+ 'nsMessageLoop.cpp',
+ 'NSPRLogModulesParser.cpp',
+ 'nsSecurityConsoleMessage.cpp',
+ 'nsStatusReporterManager.cpp',
+ 'nsSystemInfo.cpp',
+ 'nsTraceRefcnt.cpp',
+ 'nsUUIDGenerator.cpp',
+ 'nsVersionComparatorImpl.cpp',
+]
+
+if CONFIG['OS_ARCH'] == 'Linux':
+ SOURCES += [
+ 'LinuxUtils.cpp',
+ 'SystemMemoryReporter.cpp',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ SOURCES += [
+ 'nsMacUtilsImpl.cpp',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ SOURCES += [
+ 'nsCrashOnException.cpp',
+ ]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+ '../build',
+ '/xpcom/ds',
+]
+
+if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
+ CXXFLAGS += CONFIG['TK_CFLAGS']
diff --git a/xpcom/base/nsAgg.h b/xpcom/base/nsAgg.h
new file mode 100644
index 0000000000..8dcf8067ec
--- /dev/null
+++ b/xpcom/base/nsAgg.h
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 nsAgg_h___
+#define nsAgg_h___
+
+#include "nsISupports.h"
+#include "nsCycleCollectionParticipant.h"
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Put NS_DECL_AGGREGATED or NS_DECL_CYCLE_COLLECTING_AGGREGATED in your class's
+// declaration.
+#define NS_DECL_AGGREGATED \
+ NS_DECL_ISUPPORTS \
+ NS_DECL_AGGREGATED_HELPER
+
+#define NS_DECL_CYCLE_COLLECTING_AGGREGATED \
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS \
+ NS_DECL_AGGREGATED_HELPER
+
+#define NS_DECL_AGGREGATED_HELPER \
+public: \
+ \
+ /** \
+ * Returns the nsISupports pointer of the inner object (aka the \
+ * aggregatee). This pointer is really only useful to the outer object \
+ * (aka the aggregator), which can use it to hold on to the inner \
+ * object. Anything else wants the nsISupports pointer of the outer \
+ * object (gotten by QI'ing inner or outer to nsISupports). This method \
+ * returns a non-addrefed pointer. \
+ * @return the nsISupports pointer of the inner object \
+ */ \
+ nsISupports* InnerObject(void) { return &fAggregated; } \
+ \
+ /** \
+ * Returns true if this object is part of an aggregated object. \
+ */ \
+ bool IsPartOfAggregated(void) { return fOuter != InnerObject(); } \
+ \
+private: \
+ \
+ /* You must implement this operation instead of the nsISupports */ \
+ /* methods. */ \
+ nsresult \
+ AggregatedQueryInterface(const nsIID& aIID, void** aInstancePtr); \
+ \
+ class Internal : public nsISupports { \
+ public: \
+ \
+ Internal() {} \
+ \
+ NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); \
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void); \
+ NS_IMETHOD_(MozExternalRefCountType) Release(void); \
+ \
+ NS_DECL_OWNINGTHREAD \
+ }; \
+ \
+ friend class Internal; \
+ \
+ nsISupports* MOZ_UNSAFE_REF("fOuter can either point to fAggregated " \
+ "or to an outer object, and the safety " \
+ "of this reference depends on the exact " \
+ "lifetime semantics of the AddRef/Release " \
+ "functions created by these macros.") \
+ fOuter; \
+ Internal fAggregated; \
+ \
+public: \
+
+#define NS_DECL_AGGREGATED_CYCLE_COLLECTION_CLASS(_class) \
+class NS_CYCLE_COLLECTION_INNERCLASS \
+ : public nsXPCOMCycleCollectionParticipant \
+{ \
+public: \
+ NS_IMETHOD_(void) Unlink(void *p) override; \
+ NS_IMETHOD Traverse(void *p, nsCycleCollectionTraversalCallback &cb) \
+ override; \
+ NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(_class) \
+ NS_IMETHOD_(void) DeleteCycleCollectable(void* p) override \
+ { \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class):: \
+ Downcast(static_cast<nsISupports*>(p))->DeleteCycleCollectable(); \
+ } \
+ static _class* Downcast(nsISupports* s) \
+ { \
+ return (_class*)((char*)(s) - offsetof(_class, fAggregated)); \
+ } \
+ static nsISupports* Upcast(_class *p) \
+ { \
+ return p->InnerObject(); \
+ } \
+ static nsXPCOMCycleCollectionParticipant* GetParticipant() \
+ { \
+ return &_class::NS_CYCLE_COLLECTION_INNERNAME; \
+ } \
+}; \
+NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class); \
+static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
+
+// Put this in your class's constructor:
+#define NS_INIT_AGGREGATED(outer) \
+ PR_BEGIN_MACRO \
+ fOuter = outer ? outer : &fAggregated; \
+ PR_END_MACRO
+
+
+// Put this in your class's implementation file:
+#define NS_IMPL_AGGREGATED(_class) \
+ \
+NS_IMPL_AGGREGATED_HELPER(_class) \
+ \
+NS_IMETHODIMP_(MozExternalRefCountType) \
+_class::Internal::AddRef(void) \
+{ \
+ _class* agg = (_class*)((char*)(this) - offsetof(_class, fAggregated)); \
+ MOZ_ASSERT(int32_t(agg->mRefCnt) >= 0, "illegal refcnt"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ ++agg->mRefCnt; \
+ NS_LOG_ADDREF(this, agg->mRefCnt, #_class, sizeof(*this)); \
+ return agg->mRefCnt; \
+} \
+ \
+NS_IMETHODIMP_(MozExternalRefCountType) \
+_class::Internal::Release(void) \
+{ \
+ _class* agg = (_class*)((char*)(this) - offsetof(_class, fAggregated)); \
+ MOZ_ASSERT(int32_t(agg->mRefCnt) > 0, "dup release"); \
+ NS_ASSERT_OWNINGTHREAD(_class); \
+ --agg->mRefCnt; \
+ NS_LOG_RELEASE(this, agg->mRefCnt, #_class); \
+ if (agg->mRefCnt == 0) { \
+ agg->mRefCnt = 1; /* stabilize */ \
+ delete agg; \
+ return 0; \
+ } \
+ return agg->mRefCnt; \
+} \
+
+#define NS_IMPL_CYCLE_COLLECTING_AGGREGATED(_class) \
+ \
+NS_IMPL_AGGREGATED_HELPER(_class) \
+ \
+NS_IMETHODIMP_(MozExternalRefCountType) \
+_class::Internal::AddRef(void) \
+{ \
+ _class* agg = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Downcast(this); \
+ MOZ_ASSERT(int32_t(agg->mRefCnt) >= 0, "illegal refcnt"); \
+ NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class); \
+ nsrefcnt count = agg->mRefCnt.incr(this); \
+ NS_LOG_ADDREF(this, count, #_class, sizeof(*agg)); \
+ return count; \
+} \
+NS_IMETHODIMP_(MozExternalRefCountType) \
+_class::Internal::Release(void) \
+{ \
+ _class* agg = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Downcast(this); \
+ MOZ_ASSERT(int32_t(agg->mRefCnt) > 0, "dup release"); \
+ NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class); \
+ nsrefcnt count = agg->mRefCnt.decr(this); \
+ NS_LOG_RELEASE(this, count, #_class); \
+ return count; \
+} \
+NS_IMETHODIMP_(void) \
+_class::DeleteCycleCollectable(void) \
+{ \
+ delete this; \
+}
+
+#define NS_IMPL_AGGREGATED_HELPER(_class) \
+NS_IMETHODIMP \
+_class::QueryInterface(const nsIID& aIID, void** aInstancePtr) \
+{ \
+ return fOuter->QueryInterface(aIID, aInstancePtr); \
+} \
+ \
+NS_IMETHODIMP_(MozExternalRefCountType) \
+_class::AddRef(void) \
+{ \
+ return fOuter->AddRef(); \
+} \
+ \
+NS_IMETHODIMP_(MozExternalRefCountType) \
+_class::Release(void) \
+{ \
+ return fOuter->Release(); \
+} \
+ \
+NS_IMETHODIMP \
+_class::Internal::QueryInterface(const nsIID& aIID, void** aInstancePtr) \
+{ \
+ _class* agg = (_class*)((char*)(this) - offsetof(_class, fAggregated)); \
+ return agg->AggregatedQueryInterface(aIID, aInstancePtr); \
+} \
+
+/**
+ * To make aggregated objects participate in cycle collection we need to enable
+ * the outer object (aggregator) to traverse/unlink the objects held by the
+ * inner object (the aggregatee). We can't just make the inner object QI'able to
+ * NS_CYCLECOLLECTIONPARTICIPANT_IID, we don't want to return the inner object's
+ * nsCycleCollectionParticipant for the outer object (which will happen if the
+ * outer object doesn't participate in cycle collection itself).
+ * NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID enables the outer object to get
+ * the inner objects nsCycleCollectionParticipant.
+ *
+ * There are three cases:
+ * - No aggregation
+ * QI'ing to NS_CYCLECOLLECTIONPARTICIPANT_IID will return the inner
+ * object's nsCycleCollectionParticipant.
+ *
+ * - Aggregation and outer object does not participate in cycle collection
+ * QI'ing to NS_CYCLECOLLECTIONPARTICIPANT_IID will not return anything.
+ *
+ * - Aggregation and outer object does participate in cycle collection
+ * QI'ing to NS_CYCLECOLLECTIONPARTICIPANT_IID will return the outer
+ * object's nsCycleCollectionParticipant. The outer object's
+ * nsCycleCollectionParticipant can then QI the inner object to
+ * NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID to get the inner object's
+ * nsCycleCollectionParticipant, which it can use to traverse/unlink the
+ * objects reachable from the inner object.
+ */
+#define NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID \
+{ \
+ 0x32889b7e, \
+ 0xe4fe, \
+ 0x43f4, \
+ { 0x85, 0x31, 0xb5, 0x28, 0x23, 0xa2, 0xe9, 0xfc } \
+}
+
+/**
+ * Just holds the IID so NS_GET_IID works.
+ */
+class nsAggregatedCycleCollectionParticipant
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID)
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsAggregatedCycleCollectionParticipant,
+ NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID)
+
+// for use with QI macros in nsISupportsUtils.h:
+
+#define NS_INTERFACE_MAP_BEGIN_AGGREGATED(_class) \
+ NS_IMPL_AGGREGATED_QUERY_HEAD(_class)
+
+#define NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_AGGREGATED(_class) \
+ NS_IMPL_QUERY_CYCLE_COLLECTION(_class)
+
+#define NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION_AGGREGATED(_class) \
+ NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_AGGREGATED(_class) \
+ NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_ISUPPORTS(_class)
+
+#define NS_IMPL_AGGREGATED_QUERY_HEAD(_class) \
+nsresult \
+_class::AggregatedQueryInterface(REFNSIID aIID, void** aInstancePtr) \
+{ \
+ NS_ASSERTION(aInstancePtr, \
+ "AggregatedQueryInterface requires a non-NULL result ptr!"); \
+ if ( !aInstancePtr ) \
+ return NS_ERROR_NULL_POINTER; \
+ nsISupports* foundInterface; \
+ if ( aIID.Equals(NS_GET_IID(nsISupports)) ) \
+ foundInterface = InnerObject(); \
+ else
+
+#define NS_IMPL_AGGREGATED_QUERY_CYCLE_COLLECTION(_class) \
+ if (aIID.Equals(IsPartOfAggregated() ? \
+ NS_GET_IID(nsCycleCollectionParticipant) : \
+ NS_GET_IID(nsAggregatedCycleCollectionParticipant))) \
+ foundInterface = NS_CYCLE_COLLECTION_PARTICIPANT(_class); \
+ else
+
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_AGGREGATED(_class) \
+ NS_IMETHODIMP \
+ NS_CYCLE_COLLECTION_CLASSNAME(_class)::Traverse \
+ (void *p, nsCycleCollectionTraversalCallback &cb) \
+ { \
+ nsISupports *s = static_cast<nsISupports*>(p); \
+ MOZ_ASSERT(CheckForRightISupports(s), \
+ "not the nsISupports pointer we expect"); \
+ _class *tmp = static_cast<_class*>(Downcast(s)); \
+ if (!tmp->IsPartOfAggregated()) \
+ NS_IMPL_CYCLE_COLLECTION_DESCRIBE(_class, tmp->mRefCnt.get())
+
+#define NS_GENERIC_AGGREGATED_CONSTRUCTOR(_InstanceClass) \
+static nsresult \
+_InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \
+ void **aResult) \
+{ \
+ *aResult = nullptr; \
+ if (NS_WARN_IF(aOuter && !aIID.Equals(NS_GET_IID(nsISupports)))) \
+ return NS_ERROR_INVALID_ARG; \
+ \
+ RefPtr<_InstanceClass> inst = new _InstanceClass(aOuter); \
+ if (!inst) { \
+ return NS_ERROR_OUT_OF_MEMORY; \
+ } \
+ \
+ nsISupports* inner = inst->InnerObject(); \
+ nsresult rv = inner->QueryInterface(aIID, aResult); \
+ \
+ return rv; \
+} \
+
+#define NS_GENERIC_AGGREGATED_CONSTRUCTOR_INIT(_InstanceClass, _InitMethod) \
+static nsresult \
+_InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \
+ void **aResult) \
+{ \
+ *aResult = nullptr; \
+ if (NS_WARN_IF(aOuter && !aIID.Equals(NS_GET_IID(nsISupports)))) \
+ return NS_ERROR_INVALID_ARG; \
+ \
+ RefPtr<_InstanceClass> inst = new _InstanceClass(aOuter); \
+ if (!inst) { \
+ return NS_ERROR_OUT_OF_MEMORY; \
+ } \
+ \
+ nsISupports* inner = inst->InnerObject(); \
+ NS_ADDREF(inner); \
+ nsresult rv = inst->_InitMethod(); \
+ if (NS_SUCCEEDED(rv)) { \
+ rv = inner->QueryInterface(aIID, aResult); \
+ } \
+ NS_RELEASE(inner); \
+ \
+ return rv; \
+} \
+
+#endif /* nsAgg_h___ */
diff --git a/xpcom/base/nsAlgorithm.h b/xpcom/base/nsAlgorithm.h
new file mode 100644
index 0000000000..ceaa3ace33
--- /dev/null
+++ b/xpcom/base/nsAlgorithm.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAlgorithm_h___
+#define nsAlgorithm_h___
+
+#include "nsCharTraits.h" // for |nsCharSourceTraits|, |nsCharSinkTraits|
+
+template <class T>
+inline T
+NS_ROUNDUP(const T& aA, const T& aB)
+{
+ return ((aA + (aB - 1)) / aB) * aB;
+}
+
+// We use these instead of std::min/max because we can't include the algorithm
+// header in all of XPCOM because the stl wrappers will error out when included
+// in parts of XPCOM. These functions should never be used outside of XPCOM.
+template <class T>
+inline const T&
+XPCOM_MIN(const T& aA, const T& aB)
+{
+ return aB < aA ? aB : aA;
+}
+
+// Must return b when a == b in case a is -0
+template <class T>
+inline const T&
+XPCOM_MAX(const T& aA, const T& aB)
+{
+ return aA > aB ? aA : aB;
+}
+
+namespace mozilla {
+
+template <class T>
+inline const T&
+clamped(const T& aA, const T& aMin, const T& aMax)
+{
+ MOZ_ASSERT(aMax >= aMin,
+ "clamped(): aMax must be greater than or equal to aMin");
+ return XPCOM_MIN(XPCOM_MAX(aA, aMin), aMax);
+}
+
+} // namespace mozilla
+
+template <class InputIterator, class T>
+inline uint32_t
+NS_COUNT(InputIterator& aFirst, const InputIterator& aLast, const T& aValue)
+{
+ uint32_t result = 0;
+ for (; aFirst != aLast; ++aFirst)
+ if (*aFirst == aValue) {
+ ++result;
+ }
+ return result;
+}
+
+template <class InputIterator, class OutputIterator>
+inline OutputIterator&
+copy_string(const InputIterator& aFirst, const InputIterator& aLast,
+ OutputIterator& aResult)
+{
+ typedef nsCharSourceTraits<InputIterator> source_traits;
+ typedef nsCharSinkTraits<OutputIterator> sink_traits;
+
+ sink_traits::write(aResult, source_traits::read(aFirst),
+ source_traits::readable_distance(aFirst, aLast));
+ return aResult;
+}
+
+#endif // !defined(nsAlgorithm_h___)
diff --git a/xpcom/base/nsAllocator.h b/xpcom/base/nsAllocator.h
new file mode 100644
index 0000000000..a9ad1c70a3
--- /dev/null
+++ b/xpcom/base/nsAllocator.h
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+////////////////////////////////////////////////////////////////////////////////
+// obsolete
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef nsAllocator_h__
+#define nsAllocator_h__
+
+#include "nsAgg.h"
+#include "nsIFactory.h"
+
+#endif // nsAllocator_h__
diff --git a/xpcom/base/nsAutoPtr.h b/xpcom/base/nsAutoPtr.h
new file mode 100644
index 0000000000..b5a15000ca
--- /dev/null
+++ b/xpcom/base/nsAutoPtr.h
@@ -0,0 +1,454 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 nsAutoPtr_h
+#define nsAutoPtr_h
+
+#include "nsCOMPtr.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TypeTraits.h"
+
+#include "nsCycleCollectionNoteChild.h"
+#include "mozilla/MemoryReporting.h"
+
+/*****************************************************************************/
+
+// template <class T> class nsAutoPtrGetterTransfers;
+
+template <class T>
+class nsAutoPtr
+{
+private:
+ static_assert(!mozilla::IsScalar<T>::value, "If you are using "
+ "nsAutoPtr to hold an array, use UniquePtr<T[]> instead");
+
+ void**
+ begin_assignment()
+ {
+ assign(0);
+ return reinterpret_cast<void**>(&mRawPtr);
+ }
+
+ void
+ assign(T* aNewPtr)
+ {
+ T* oldPtr = mRawPtr;
+
+ if (aNewPtr && aNewPtr == oldPtr) {
+ NS_RUNTIMEABORT("Logic flaw in the caller");
+ }
+
+ mRawPtr = aNewPtr;
+ delete oldPtr;
+ }
+
+ // |class Ptr| helps us prevent implicit "copy construction"
+ // through |operator T*() const| from a |const nsAutoPtr<T>|
+ // because two implicit conversions in a row aren't allowed.
+ // It still allows assignment from T* through implicit conversion
+ // from |T*| to |nsAutoPtr<T>::Ptr|
+ class Ptr
+ {
+ public:
+ MOZ_IMPLICIT Ptr(T* aPtr)
+ : mPtr(aPtr)
+ {
+ }
+
+ operator T*() const
+ {
+ return mPtr;
+ }
+
+ private:
+ T* MOZ_NON_OWNING_REF mPtr;
+ };
+
+private:
+ T* MOZ_OWNING_REF mRawPtr;
+
+public:
+ typedef T element_type;
+
+ ~nsAutoPtr()
+ {
+ delete mRawPtr;
+ }
+
+ // Constructors
+
+ nsAutoPtr()
+ : mRawPtr(0)
+ // default constructor
+ {
+ }
+
+ MOZ_IMPLICIT nsAutoPtr(Ptr aRawPtr)
+ : mRawPtr(aRawPtr)
+ // construct from a raw pointer (of the right type)
+ {
+ }
+
+ // This constructor shouldn't exist; we should just use the &&
+ // constructor.
+ nsAutoPtr(nsAutoPtr<T>& aSmartPtr)
+ : mRawPtr(aSmartPtr.forget())
+ // Construct by transferring ownership from another smart pointer.
+ {
+ }
+
+ template <typename I>
+ MOZ_IMPLICIT nsAutoPtr(nsAutoPtr<I>& aSmartPtr)
+ : mRawPtr(aSmartPtr.forget())
+ // Construct by transferring ownership from another smart pointer.
+ {
+ }
+
+ nsAutoPtr(nsAutoPtr<T>&& aSmartPtr)
+ : mRawPtr(aSmartPtr.forget())
+ // Construct by transferring ownership from another smart pointer.
+ {
+ }
+
+ template <typename I>
+ MOZ_IMPLICIT nsAutoPtr(nsAutoPtr<I>&& aSmartPtr)
+ : mRawPtr(aSmartPtr.forget())
+ // Construct by transferring ownership from another smart pointer.
+ {
+ }
+
+ // Assignment operators
+
+ nsAutoPtr<T>&
+ operator=(T* aRhs)
+ // assign from a raw pointer (of the right type)
+ {
+ assign(aRhs);
+ return *this;
+ }
+
+ nsAutoPtr<T>& operator=(nsAutoPtr<T>& aRhs)
+ // assign by transferring ownership from another smart pointer.
+ {
+ assign(aRhs.forget());
+ return *this;
+ }
+
+ template <typename I>
+ nsAutoPtr<T>& operator=(nsAutoPtr<I>& aRhs)
+ // assign by transferring ownership from another smart pointer.
+ {
+ assign(aRhs.forget());
+ return *this;
+ }
+
+ nsAutoPtr<T>& operator=(nsAutoPtr<T>&& aRhs)
+ {
+ assign(aRhs.forget());
+ return *this;
+ }
+
+ template <typename I>
+ nsAutoPtr<T>& operator=(nsAutoPtr<I>&& aRhs)
+ {
+ assign(aRhs.forget());
+ return *this;
+ }
+
+ // Other pointer operators
+
+ T*
+ get() const
+ /*
+ Prefer the implicit conversion provided automatically by
+ |operator T*() const|. Use |get()| _only_ to resolve
+ ambiguity.
+ */
+ {
+ return mRawPtr;
+ }
+
+ operator T*() const
+ /*
+ ...makes an |nsAutoPtr| 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 |nsAutoPtr|
+ substitutable for a raw pointer.
+
+ Prefer the implicit use of this operator to calling |get()|,
+ except where necessary to resolve ambiguity.
+ */
+ {
+ return get();
+ }
+
+ T*
+ forget()
+ {
+ T* temp = mRawPtr;
+ mRawPtr = 0;
+ return temp;
+ }
+
+ T*
+ operator->() const
+ {
+ NS_PRECONDITION(mRawPtr != 0,
+ "You can't dereference a NULL nsAutoPtr with operator->().");
+ return get();
+ }
+
+ template <typename R, typename... Args>
+ class Proxy
+ {
+ typedef R (T::*member_function)(Args...);
+ T* mRawPtr;
+ member_function mFunction;
+ public:
+ Proxy(T* aRawPtr, member_function aFunction)
+ : mRawPtr(aRawPtr),
+ mFunction(aFunction)
+ {
+ }
+ template<typename... ActualArgs>
+ R operator()(ActualArgs&&... aArgs)
+ {
+ return ((*mRawPtr).*mFunction)(mozilla::Forward<ActualArgs>(aArgs)...);
+ }
+ };
+
+ template <typename R, typename C, typename... Args>
+ Proxy<R, Args...> operator->*(R (C::*aFptr)(Args...)) const
+ {
+ NS_PRECONDITION(mRawPtr != 0,
+ "You can't dereference a NULL nsAutoPtr with operator->*().");
+ return Proxy<R, Args...>(get(), aFptr);
+ }
+
+ nsAutoPtr<T>*
+ get_address()
+ // This is not intended to be used by clients. See |address_of|
+ // below.
+ {
+ return this;
+ }
+
+ const nsAutoPtr<T>*
+ get_address() const
+ // This is not intended to be used by clients. See |address_of|
+ // below.
+ {
+ return this;
+ }
+
+public:
+ T&
+ operator*() const
+ {
+ NS_PRECONDITION(mRawPtr != 0,
+ "You can't dereference a NULL nsAutoPtr with operator*().");
+ return *get();
+ }
+
+ T**
+ StartAssignment()
+ {
+#ifndef NSCAP_FEATURE_INLINE_STARTASSIGNMENT
+ return reinterpret_cast<T**>(begin_assignment());
+#else
+ assign(0);
+ return reinterpret_cast<T**>(&mRawPtr);
+#endif
+ }
+};
+
+template <class T>
+inline nsAutoPtr<T>*
+address_of(nsAutoPtr<T>& aPtr)
+{
+ return aPtr.get_address();
+}
+
+template <class T>
+inline const nsAutoPtr<T>*
+address_of(const nsAutoPtr<T>& aPtr)
+{
+ return aPtr.get_address();
+}
+
+template <class T>
+class nsAutoPtrGetterTransfers
+/*
+ ...
+
+ This class is designed to be used for anonymous temporary objects in the
+ argument list of calls that return COM interface pointers, e.g.,
+
+ nsAutoPtr<IFoo> fooP;
+ ...->GetTransferedPointer(getter_Transfers(fooP))
+
+ DO NOT USE THIS TYPE DIRECTLY IN YOUR CODE. Use |getter_Transfers()| instead.
+
+ When initialized with a |nsAutoPtr|, as in the example above, it returns
+ a |void**|, a |T**|, or an |nsISupports**| as needed, that the
+ outer call (|GetTransferedPointer| in this case) can fill in.
+
+ This type should be a nested class inside |nsAutoPtr<T>|.
+*/
+{
+public:
+ explicit
+ nsAutoPtrGetterTransfers(nsAutoPtr<T>& aSmartPtr)
+ : mTargetSmartPtr(aSmartPtr)
+ {
+ // nothing else to do
+ }
+
+ operator void**()
+ {
+ return reinterpret_cast<void**>(mTargetSmartPtr.StartAssignment());
+ }
+
+ operator T**()
+ {
+ return mTargetSmartPtr.StartAssignment();
+ }
+
+ T*&
+ operator*()
+ {
+ return *(mTargetSmartPtr.StartAssignment());
+ }
+
+private:
+ nsAutoPtr<T>& mTargetSmartPtr;
+};
+
+template <class T>
+inline nsAutoPtrGetterTransfers<T>
+getter_Transfers(nsAutoPtr<T>& aSmartPtr)
+/*
+ Used around a |nsAutoPtr| when
+ ...makes the class |nsAutoPtrGetterTransfers<T>| invisible.
+*/
+{
+ return nsAutoPtrGetterTransfers<T>(aSmartPtr);
+}
+
+
+
+// Comparing two |nsAutoPtr|s
+
+template <class T, class U>
+inline bool
+operator==(const nsAutoPtr<T>& aLhs, const nsAutoPtr<U>& aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) == static_cast<const U*>(aRhs.get());
+}
+
+
+template <class T, class U>
+inline bool
+operator!=(const nsAutoPtr<T>& aLhs, const nsAutoPtr<U>& aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) != static_cast<const U*>(aRhs.get());
+}
+
+
+// Comparing an |nsAutoPtr| to a raw pointer
+
+template <class T, class U>
+inline bool
+operator==(const nsAutoPtr<T>& aLhs, const U* aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) == static_cast<const U*>(aRhs);
+}
+
+template <class T, class U>
+inline bool
+operator==(const U* aLhs, const nsAutoPtr<T>& aRhs)
+{
+ return static_cast<const U*>(aLhs) == static_cast<const T*>(aRhs.get());
+}
+
+template <class T, class U>
+inline bool
+operator!=(const nsAutoPtr<T>& aLhs, const U* aRhs)
+{
+ return static_cast<const T*>(aLhs.get()) != static_cast<const U*>(aRhs);
+}
+
+template <class T, class U>
+inline bool
+operator!=(const U* aLhs, const nsAutoPtr<T>& aRhs)
+{
+ return static_cast<const U*>(aLhs) != static_cast<const T*>(aRhs.get());
+}
+
+template <class T, class U>
+inline bool
+operator==(const nsAutoPtr<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 nsAutoPtr<T>& aRhs)
+{
+ return const_cast<const U*>(aLhs) == static_cast<const T*>(aRhs.get());
+}
+
+template <class T, class U>
+inline bool
+operator!=(const nsAutoPtr<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 nsAutoPtr<T>& aRhs)
+{
+ return const_cast<const U*>(aLhs) != static_cast<const T*>(aRhs.get());
+}
+
+
+
+// Comparing an |nsAutoPtr| to |nullptr|
+
+template <class T>
+inline bool
+operator==(const nsAutoPtr<T>& aLhs, decltype(nullptr))
+{
+ return aLhs.get() == nullptr;
+}
+
+template <class T>
+inline bool
+operator==(decltype(nullptr), const nsAutoPtr<T>& aRhs)
+{
+ return nullptr == aRhs.get();
+}
+
+template <class T>
+inline bool
+operator!=(const nsAutoPtr<T>& aLhs, decltype(nullptr))
+{
+ return aLhs.get() != nullptr;
+}
+
+template <class T>
+inline bool
+operator!=(decltype(nullptr), const nsAutoPtr<T>& aRhs)
+{
+ return nullptr != aRhs.get();
+}
+
+
+/*****************************************************************************/
+
+#endif // !defined(nsAutoPtr_h)
diff --git a/xpcom/base/nsAutoRef.h b/xpcom/base/nsAutoRef.h
new file mode 100644
index 0000000000..9986c8a561
--- /dev/null
+++ b/xpcom/base/nsAutoRef.h
@@ -0,0 +1,672 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 nsAutoRef_h_
+#define nsAutoRef_h_
+
+#include "mozilla/Attributes.h"
+
+#include "nscore.h" // for nullptr, bool
+
+template <class T> class nsSimpleRef;
+template <class T> class nsAutoRefBase;
+template <class T> class nsReturnRef;
+template <class T> class nsReturningRef;
+
+/**
+ * template <class T> class nsAutoRef
+ *
+ * A class that holds a handle to a resource that must be released.
+ * No reference is added on construction.
+ *
+ * No copy constructor nor copy assignment operators are available, so the
+ * resource will be held until released on destruction or explicitly
+ * |reset()| or transferred through provided methods.
+ *
+ * The publicly available methods are the public methods on this class and its
+ * public base classes |nsAutoRefBase<T>| and |nsSimpleRef<T>|.
+ *
+ * For ref-counted resources see also |nsCountedRef<T>|.
+ * For function return values see |nsReturnRef<T>|.
+ *
+ * For each class |T|, |nsAutoRefTraits<T>| or |nsSimpleRef<T>| must be
+ * specialized to use |nsAutoRef<T>| and |nsCountedRef<T>|.
+ *
+ * @param T A class identifying the type of reference held by the
+ * |nsAutoRef<T>| and the unique set methods for managing references
+ * to the resource (defined by |nsAutoRefTraits<T>| or
+ * |nsSimpleRef<T>|).
+ *
+ * Often this is the class representing the resource. Sometimes a
+ * new possibly-incomplete class may need to be declared.
+ *
+ *
+ * Example: An Automatically closing file descriptor
+ *
+ * // References that are simple integral types (as file-descriptors are)
+ * // usually need a new class to represent the resource and how to handle its
+ * // references.
+ * class nsRawFD;
+ *
+ * // Specializing nsAutoRefTraits<nsRawFD> describes how to manage file
+ * // descriptors, so that nsAutoRef<nsRawFD> provides automatic closing of
+ * // its file descriptor on destruction.
+ * template <>
+ * class nsAutoRefTraits<nsRawFD> {
+ * public:
+ * // The file descriptor is held in an int.
+ * typedef int RawRef;
+ * // -1 means that there is no file associated with the handle.
+ * static int Void() { return -1; }
+ * // The file associated with a file descriptor is released with close().
+ * static void Release(RawRef aFD) { close(aFD); }
+ * };
+ *
+ * // A function returning a file descriptor that must be closed.
+ * nsReturnRef<nsRawFD> get_file(const char *filename) {
+ * // Constructing from a raw file descriptor assumes ownership.
+ * nsAutoRef<nsRawFD> fd(open(filename, O_RDONLY));
+ * fcntl(fd, F_SETFD, FD_CLOEXEC);
+ * return fd.out();
+ * }
+ *
+ * void f() {
+ * unsigned char buf[1024];
+ *
+ * // Hold a file descriptor for /etc/hosts in fd1.
+ * nsAutoRef<nsRawFD> fd1(get_file("/etc/hosts"));
+ *
+ * nsAutoRef<nsRawFD> fd2;
+ * fd2.steal(fd1); // fd2 takes the file descriptor from fd1
+ * ssize_t count = read(fd1, buf, 1024); // error fd1 has no file
+ * count = read(fd2, buf, 1024); // reads from /etc/hosts
+ *
+ * // If the file descriptor is not stored then it is closed.
+ * get_file("/etc/login.defs"); // login.defs is closed
+ *
+ * // Now use fd1 to hold a file descriptor for /etc/passwd.
+ * fd1 = get_file("/etc/passwd");
+ *
+ * // The nsAutoRef<nsRawFD> can give up the file descriptor if explicitly
+ * // instructed, but the caller must then ensure that the file is closed.
+ * int rawfd = fd1.disown();
+ *
+ * // Assume ownership of another file descriptor.
+ * fd1.own(open("/proc/1/maps");
+ *
+ * // On destruction, fd1 closes /proc/1/maps and fd2 closes /etc/hosts,
+ * // but /etc/passwd is not closed.
+ * }
+ *
+ */
+
+
+template <class T>
+class nsAutoRef : public nsAutoRefBase<T>
+{
+protected:
+ typedef nsAutoRef<T> ThisClass;
+ typedef nsAutoRefBase<T> BaseClass;
+ typedef nsSimpleRef<T> SimpleRef;
+ typedef typename BaseClass::RawRefOnly RawRefOnly;
+ typedef typename BaseClass::LocalSimpleRef LocalSimpleRef;
+
+public:
+ nsAutoRef()
+ {
+ }
+
+ // Explicit construction is required so as not to risk unintentionally
+ // releasing the resource associated with a raw ref.
+ explicit nsAutoRef(RawRefOnly aRefToRelease)
+ : BaseClass(aRefToRelease)
+ {
+ }
+
+ // Construction from a nsReturnRef<T> function return value, which expects
+ // to give up ownership, transfers ownership.
+ // (nsReturnRef<T> is converted to const nsReturningRef<T>.)
+ explicit nsAutoRef(const nsReturningRef<T>& aReturning)
+ : BaseClass(aReturning)
+ {
+ }
+
+ // The only assignment operator provided is for transferring from an
+ // nsReturnRef smart reference, which expects to pass its ownership to
+ // another object.
+ //
+ // With raw references and other smart references, the type of the lhs and
+ // its taking and releasing nature is often not obvious from an assignment
+ // statement. Assignment from a raw ptr especially is not normally
+ // expected to release the reference.
+ //
+ // Use |steal| for taking ownership from other smart refs.
+ //
+ // For raw references, use |own| to indicate intention to have the
+ // resource released.
+ //
+ // Or, to create another owner of the same reference, use an nsCountedRef.
+
+ ThisClass& operator=(const nsReturningRef<T>& aReturning)
+ {
+ BaseClass::steal(aReturning.mReturnRef);
+ return *this;
+ }
+
+ // Conversion to a raw reference allow the nsAutoRef<T> to often be used
+ // like a raw reference.
+ operator typename SimpleRef::RawRef() const
+ {
+ return this->get();
+ }
+
+ // Transfer ownership from another smart reference.
+ void steal(ThisClass& aOtherRef)
+ {
+ BaseClass::steal(aOtherRef);
+ }
+
+ // Assume ownership of a raw ref.
+ //
+ // |own| has similar function to |steal|, and is useful for receiving
+ // ownership from a return value of a function. It is named differently
+ // because |own| requires more care to ensure that the function intends to
+ // give away ownership, and so that |steal| can be safely used, knowing
+ // that it won't steal ownership from any methods returning raw ptrs to
+ // data owned by a foreign object.
+ void own(RawRefOnly aRefToRelease)
+ {
+ BaseClass::own(aRefToRelease);
+ }
+
+ // Exchange ownership with |aOther|
+ void swap(ThisClass& aOther)
+ {
+ LocalSimpleRef temp;
+ temp.SimpleRef::operator=(*this);
+ SimpleRef::operator=(aOther);
+ aOther.SimpleRef::operator=(temp);
+ }
+
+ // Release the reference now.
+ void reset()
+ {
+ this->SafeRelease();
+ LocalSimpleRef empty;
+ SimpleRef::operator=(empty);
+ }
+
+ // Pass out the reference for a function return values.
+ nsReturnRef<T> out()
+ {
+ return nsReturnRef<T>(this->disown());
+ }
+
+ // operator->() and disown() are provided by nsAutoRefBase<T>.
+ // The default nsSimpleRef<T> provides get().
+
+private:
+ // No copy constructor
+ explicit nsAutoRef(ThisClass& aRefToSteal);
+};
+
+/**
+ * template <class T> class nsCountedRef
+ *
+ * A class that creates (adds) a new reference to a resource on construction
+ * or assignment and releases on destruction.
+ *
+ * This class is similar to nsAutoRef<T> and inherits its methods, but also
+ * provides copy construction and assignment operators that enable more than
+ * one concurrent reference to the same resource.
+ *
+ * Specialize |nsAutoRefTraits<T>| or |nsSimpleRef<T>| to use this. This
+ * class assumes that the resource itself counts references and so can only be
+ * used when |T| represents a reference-counting resource.
+ */
+
+template <class T>
+class nsCountedRef : public nsAutoRef<T>
+{
+protected:
+ typedef nsCountedRef<T> ThisClass;
+ typedef nsAutoRef<T> BaseClass;
+ typedef nsSimpleRef<T> SimpleRef;
+ typedef typename BaseClass::RawRef RawRef;
+
+public:
+ nsCountedRef()
+ {
+ }
+
+ // Construction and assignment from a another nsCountedRef
+ // or a raw ref copies and increments the ref count.
+ nsCountedRef(const ThisClass& aRefToCopy)
+ {
+ SimpleRef::operator=(aRefToCopy);
+ SafeAddRef();
+ }
+ ThisClass& operator=(const ThisClass& aRefToCopy)
+ {
+ if (this == &aRefToCopy) {
+ return *this;
+ }
+
+ this->SafeRelease();
+ SimpleRef::operator=(aRefToCopy);
+ SafeAddRef();
+ return *this;
+ }
+
+ // Implicit conversion from another smart ref argument (to a raw ref) is
+ // accepted here because construction and assignment safely creates a new
+ // reference without interfering with the reference to copy.
+ explicit nsCountedRef(RawRef aRefToCopy)
+ : BaseClass(aRefToCopy)
+ {
+ SafeAddRef();
+ }
+ ThisClass& operator=(RawRef aRefToCopy)
+ {
+ this->own(aRefToCopy);
+ SafeAddRef();
+ return *this;
+ }
+
+ // Construction and assignment from an nsReturnRef function return value,
+ // which expects to give up ownership, transfers ownership.
+ explicit nsCountedRef(const nsReturningRef<T>& aReturning)
+ : BaseClass(aReturning)
+ {
+ }
+ ThisClass& operator=(const nsReturningRef<T>& aReturning)
+ {
+ BaseClass::operator=(aReturning);
+ return *this;
+ }
+
+protected:
+ // Increase the reference count if there is a resource.
+ void SafeAddRef()
+ {
+ if (this->HaveResource()) {
+ this->AddRef(this->get());
+ }
+ }
+};
+
+/**
+ * template <class T> class nsReturnRef
+ *
+ * A type for function return values that hold a reference to a resource that
+ * must be released. See also |nsAutoRef<T>::out()|.
+ */
+
+template <class T>
+class nsReturnRef : public nsAutoRefBase<T>
+{
+protected:
+ typedef nsAutoRefBase<T> BaseClass;
+ typedef typename BaseClass::RawRefOnly RawRefOnly;
+
+public:
+ // For constructing a return value with no resource
+ nsReturnRef()
+ {
+ }
+
+ // For returning a smart reference from a raw reference that must be
+ // released. Explicit construction is required so as not to risk
+ // unintentionally releasing the resource associated with a raw ref.
+ MOZ_IMPLICIT nsReturnRef(RawRefOnly aRefToRelease)
+ : BaseClass(aRefToRelease)
+ {
+ }
+
+ // Copy construction transfers ownership
+ nsReturnRef(nsReturnRef<T>& aRefToSteal)
+ : BaseClass(aRefToSteal)
+ {
+ }
+
+ MOZ_IMPLICIT nsReturnRef(const nsReturningRef<T>& aReturning)
+ : BaseClass(aReturning)
+ {
+ }
+
+ // Conversion to a temporary (const) object referring to this object so
+ // that the reference may be passed from a function return value
+ // (temporary) to another smart reference. There is no need to use this
+ // explicitly. Simply assign a nsReturnRef<T> function return value to a
+ // smart reference.
+ operator nsReturningRef<T>()
+ {
+ return nsReturningRef<T>(*this);
+ }
+
+ // No conversion to RawRef operator is provided on nsReturnRef, to ensure
+ // that the return value is not carelessly assigned to a raw ptr (and the
+ // resource then released). If passing to a function that takes a raw
+ // ptr, use get or disown as appropriate.
+};
+
+/**
+ * template <class T> class nsReturningRef
+ *
+ * A class to allow ownership to be transferred from nsReturnRef function
+ * return values.
+ *
+ * It should not be necessary for clients to reference this
+ * class directly. Simply pass an nsReturnRef<T> to a parameter taking an
+ * |nsReturningRef<T>|.
+ *
+ * The conversion operator on nsReturnRef constructs a temporary wrapper of
+ * class nsReturningRef<T> around a non-const reference to the nsReturnRef.
+ * The wrapper can then be passed as an rvalue parameter.
+ */
+
+template <class T>
+class nsReturningRef
+{
+private:
+ friend class nsReturnRef<T>;
+
+ explicit nsReturningRef(nsReturnRef<T>& aReturnRef)
+ : mReturnRef(aReturnRef)
+ {
+ }
+public:
+ nsReturnRef<T>& mReturnRef;
+};
+
+/**
+ * template <class T> class nsAutoRefTraits
+ *
+ * A class describing traits of references managed by the default
+ * |nsSimpleRef<T>| implementation and thus |nsAutoRef<T>| and |nsCountedRef|.
+ * The default |nsSimpleRef<T> is suitable for resources with handles that
+ * have a void value. (If there is no such void value for a handle,
+ * specialize |nsSimpleRef<T>|.)
+ *
+ * Specializations must be provided for each class |T| according to the
+ * following pattern:
+ *
+ * // The template parameter |T| should be a class such that the set of fields
+ * // in class nsAutoRefTraits<T> is unique for class |T|. Usually the
+ * // resource object class is sufficient. For handles that are simple
+ * // integral typedefs, a new unique possibly-incomplete class may need to be
+ * // declared.
+ *
+ * template <>
+ * class nsAutoRefTraits<T>
+ * {
+ * // Specializations must provide a typedef for RawRef, describing the
+ * // type of the handle to the resource.
+ * typedef <handle-type> RawRef;
+ *
+ * // Specializations should define Void(), a function returning a value
+ * // suitable for a handle that does not have an associated resource.
+ * //
+ * // The return type must be a suitable as the parameter to a RawRef
+ * // constructor and operator==.
+ * //
+ * // If this method is not accessible then some limited nsAutoRef
+ * // functionality will still be available, but the default constructor,
+ * // |reset|, and most transfer of ownership methods will not be available.
+ * static <return-type> Void();
+ *
+ * // Specializations must define Release() to properly finalize the
+ * // handle to a non-void custom-deleted or reference-counted resource.
+ * static void Release(RawRef aRawRef);
+ *
+ * // For reference-counted resources, if |nsCountedRef<T>| is to be used,
+ * // specializations must define AddRef to increment the reference count
+ * // held by a non-void handle.
+ * // (AddRef() is not necessary for |nsAutoRef<T>|.)
+ * static void AddRef(RawRef aRawRef);
+ * };
+ *
+ * See nsPointerRefTraits for example specializations for simple pointer
+ * references. See nsAutoRef for an example specialization for a non-pointer
+ * reference.
+ */
+
+template <class T> class nsAutoRefTraits;
+
+/**
+ * template <class T> class nsPointerRefTraits
+ *
+ * A convenience class useful as a base class for specializations of
+ * |nsAutoRefTraits<T>| where the handle to the resource is a pointer to |T|.
+ * By inheriting from this class, definitions of only Release(RawRef) and
+ * possibly AddRef(RawRef) need to be added.
+ *
+ * Examples of use:
+ *
+ * template <>
+ * class nsAutoRefTraits<PRFileDesc> : public nsPointerRefTraits<PRFileDesc>
+ * {
+ * public:
+ * static void Release(PRFileDesc *ptr) { PR_Close(ptr); }
+ * };
+ *
+ * template <>
+ * class nsAutoRefTraits<FcPattern> : public nsPointerRefTraits<FcPattern>
+ * {
+ * public:
+ * static void Release(FcPattern *ptr) { FcPatternDestroy(ptr); }
+ * static void AddRef(FcPattern *ptr) { FcPatternReference(ptr); }
+ * };
+ */
+
+template <class T>
+class nsPointerRefTraits
+{
+public:
+ // The handle is a pointer to T.
+ typedef T* RawRef;
+ // A nullptr does not have a resource.
+ static RawRef Void()
+ {
+ return nullptr;
+ }
+};
+
+/**
+ * template <class T> class nsSimpleRef
+ *
+ * Constructs a non-smart reference, and provides methods to test whether
+ * there is an associated resource and (if so) get its raw handle.
+ *
+ * A default implementation is suitable for resources with handles that have a
+ * void value. This is not intended for direct use but used by |nsAutoRef<T>|
+ * and thus |nsCountedRef<T>|.
+ *
+ * Specialize this class if there is no particular void value for the resource
+ * handle. A specialized implementation must also provide Release(RawRef),
+ * and, if |nsCountedRef<T>| is required, AddRef(RawRef), as described in
+ * nsAutoRefTraits<T>.
+ */
+
+template <class T>
+class nsSimpleRef : protected nsAutoRefTraits<T>
+{
+protected:
+ // The default implementation uses nsAutoRefTrait<T>.
+ // Specializations need not define this typedef.
+ typedef nsAutoRefTraits<T> Traits;
+ // The type of the handle to the resource.
+ // A specialization must provide a typedef for RawRef.
+ typedef typename Traits::RawRef RawRef;
+
+ // Construct with no resource.
+ //
+ // If this constructor is not accessible then some limited nsAutoRef
+ // functionality will still be available, but the default constructor,
+ // |reset|, and most transfer of ownership methods will not be available.
+ nsSimpleRef()
+ : mRawRef(Traits::Void())
+ {
+ }
+ // Construct with a handle to a resource.
+ // A specialization must provide this.
+ explicit nsSimpleRef(RawRef aRawRef)
+ : mRawRef(aRawRef)
+ {
+ }
+
+ // Test whether there is an associated resource. A specialization must
+ // provide this. The function is permitted to always return true if the
+ // default constructor is not accessible, or if Release (and AddRef) can
+ // deal with void handles.
+ bool HaveResource() const
+ {
+ return mRawRef != Traits::Void();
+ }
+
+public:
+ // A specialization must provide get() or loose some functionality. This
+ // is inherited by derived classes and the specialization may choose
+ // whether it is public or protected.
+ RawRef get() const
+ {
+ return mRawRef;
+ }
+
+private:
+ RawRef mRawRef;
+};
+
+
+/**
+ * template <class T> class nsAutoRefBase
+ *
+ * Internal base class for |nsAutoRef<T>| and |nsReturnRef<T>|.
+ * Adds release on destruction to a |nsSimpleRef<T>|.
+ */
+
+template <class T>
+class nsAutoRefBase : public nsSimpleRef<T>
+{
+protected:
+ typedef nsAutoRefBase<T> ThisClass;
+ typedef nsSimpleRef<T> SimpleRef;
+ typedef typename SimpleRef::RawRef RawRef;
+
+ nsAutoRefBase()
+ {
+ }
+
+ // A type for parameters that should be passed a raw ref but should not
+ // accept implicit conversions (from another smart ref). (The only
+ // conversion to this type is from a raw ref so only raw refs will be
+ // accepted.)
+ class RawRefOnly
+ {
+ public:
+ MOZ_IMPLICIT RawRefOnly(RawRef aRawRef)
+ : mRawRef(aRawRef)
+ {
+ }
+ operator RawRef() const
+ {
+ return mRawRef;
+ }
+ private:
+ RawRef mRawRef;
+ };
+
+ // Construction from a raw ref assumes ownership
+ explicit nsAutoRefBase(RawRefOnly aRefToRelease)
+ : SimpleRef(aRefToRelease)
+ {
+ }
+
+ // Constructors that steal ownership
+ explicit nsAutoRefBase(ThisClass& aRefToSteal)
+ : SimpleRef(aRefToSteal.disown())
+ {
+ }
+ explicit nsAutoRefBase(const nsReturningRef<T>& aReturning)
+ : SimpleRef(aReturning.mReturnRef.disown())
+ {
+ }
+
+ ~nsAutoRefBase()
+ {
+ SafeRelease();
+ }
+
+ // An internal class providing access to protected nsSimpleRef<T>
+ // constructors for construction of temporary simple references (that are
+ // not ThisClass).
+ class LocalSimpleRef : public SimpleRef
+ {
+ public:
+ LocalSimpleRef()
+ {
+ }
+ explicit LocalSimpleRef(RawRef aRawRef)
+ : SimpleRef(aRawRef)
+ {
+ }
+ };
+
+private:
+ ThisClass& operator=(const ThisClass& aSmartRef) = delete;
+
+public:
+ RawRef operator->() const
+ {
+ return this->get();
+ }
+
+ // Transfer ownership to a raw reference.
+ //
+ // THE CALLER MUST ENSURE THAT THE REFERENCE IS EXPLICITLY RELEASED.
+ //
+ // Is this really what you want to use? Using this removes any guarantee
+ // of release. Use nsAutoRef<T>::out() for return values, or an
+ // nsAutoRef<T> modifiable lvalue for an out parameter. Use disown() when
+ // the reference must be stored in a POD type object, such as may be
+ // preferred for a namespace-scope object with static storage duration,
+ // for example.
+ RawRef disown()
+ {
+ RawRef temp = this->get();
+ LocalSimpleRef empty;
+ SimpleRef::operator=(empty);
+ return temp;
+ }
+
+protected:
+ // steal and own are protected because they make no sense on nsReturnRef,
+ // but steal is implemented on this class for access to aOtherRef.disown()
+ // when aOtherRef is an nsReturnRef;
+
+ // Transfer ownership from another smart reference.
+ void steal(ThisClass& aOtherRef)
+ {
+ own(aOtherRef.disown());
+ }
+ // Assume ownership of a raw ref.
+ void own(RawRefOnly aRefToRelease)
+ {
+ SafeRelease();
+ LocalSimpleRef ref(aRefToRelease);
+ SimpleRef::operator=(ref);
+ }
+
+ // Release a resource if there is one.
+ void SafeRelease()
+ {
+ if (this->HaveResource()) {
+ this->Release(this->get());
+ }
+ }
+};
+
+#endif // !defined(nsAutoRef_h_)
diff --git a/xpcom/base/nsCom.h b/xpcom/base/nsCom.h
new file mode 100644
index 0000000000..20353a266d
--- /dev/null
+++ b/xpcom/base/nsCom.h
@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 nsCom_h__
+#define nsCom_h__
+#include "nscore.h"
+#endif
+
+
diff --git a/xpcom/base/nsConsoleMessage.cpp b/xpcom/base/nsConsoleMessage.cpp
new file mode 100644
index 0000000000..e3530745b7
--- /dev/null
+++ b/xpcom/base/nsConsoleMessage.cpp
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Base implementation for console messages.
+ */
+
+#include "nsConsoleMessage.h"
+#include "jsapi.h"
+
+NS_IMPL_ISUPPORTS(nsConsoleMessage, nsIConsoleMessage)
+
+nsConsoleMessage::nsConsoleMessage()
+ : mTimeStamp(0)
+ , mMessage()
+{
+}
+
+nsConsoleMessage::nsConsoleMessage(const char16_t* aMessage)
+{
+ mTimeStamp = JS_Now() / 1000;
+ mMessage.Assign(aMessage);
+}
+
+NS_IMETHODIMP
+nsConsoleMessage::GetMessageMoz(char16_t** aResult)
+{
+ *aResult = ToNewUnicode(mMessage);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsConsoleMessage::GetLogLevel(uint32_t* aLogLevel)
+{
+ *aLogLevel = nsConsoleMessage::info;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsConsoleMessage::GetTimeStamp(int64_t* aTimeStamp)
+{
+ *aTimeStamp = mTimeStamp;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsConsoleMessage::ToString(nsACString& /*UTF8*/ aResult)
+{
+ CopyUTF16toUTF8(mMessage, aResult);
+
+ return NS_OK;
+}
diff --git a/xpcom/base/nsConsoleMessage.h b/xpcom/base/nsConsoleMessage.h
new file mode 100644
index 0000000000..64ad921a5d
--- /dev/null
+++ b/xpcom/base/nsConsoleMessage.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nsconsolemessage_h__
+#define __nsconsolemessage_h__
+
+#include "mozilla/Attributes.h"
+
+#include "nsIConsoleMessage.h"
+#include "nsString.h"
+
+class nsConsoleMessage final : public nsIConsoleMessage
+{
+public:
+ nsConsoleMessage();
+ explicit nsConsoleMessage(const char16_t* aMessage);
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSICONSOLEMESSAGE
+
+private:
+ ~nsConsoleMessage()
+ {
+ }
+
+ int64_t mTimeStamp;
+ nsString mMessage;
+};
+
+#endif /* __nsconsolemessage_h__ */
diff --git a/xpcom/base/nsConsoleService.cpp b/xpcom/base/nsConsoleService.cpp
new file mode 100644
index 0000000000..3862a02c4a
--- /dev/null
+++ b/xpcom/base/nsConsoleService.cpp
@@ -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/. */
+
+/*
+ * Maintains a circular buffer of recent messages, and notifies
+ * listeners when new messages are logged.
+ */
+
+/* Threadsafe. */
+
+#include "nsMemory.h"
+#include "nsCOMArray.h"
+#include "nsThreadUtils.h"
+
+#include "nsConsoleService.h"
+#include "nsConsoleMessage.h"
+#include "nsIClassInfoImpl.h"
+#include "nsIConsoleListener.h"
+#include "nsPrintfCString.h"
+#include "nsProxyRelease.h"
+#include "nsIScriptError.h"
+#include "nsISupportsPrimitives.h"
+
+#include "mozilla/Preferences.h"
+
+#if defined(ANDROID)
+#include <android/log.h>
+#include "mozilla/dom/ContentChild.h"
+#endif
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+#ifdef MOZ_TASK_TRACER
+#include "GeckoTaskTracer.h"
+using namespace mozilla::tasktracer;
+#endif
+
+using namespace mozilla;
+
+NS_IMPL_ADDREF(nsConsoleService)
+NS_IMPL_RELEASE(nsConsoleService)
+NS_IMPL_CLASSINFO(nsConsoleService, nullptr,
+ nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON,
+ NS_CONSOLESERVICE_CID)
+NS_IMPL_QUERY_INTERFACE_CI(nsConsoleService, nsIConsoleService, nsIObserver)
+NS_IMPL_CI_INTERFACE_GETTER(nsConsoleService, nsIConsoleService, nsIObserver)
+
+static bool sLoggingEnabled = true;
+static bool sLoggingBuffered = true;
+#if defined(ANDROID)
+static bool sLoggingLogcat = true;
+#endif // defined(ANDROID)
+
+nsConsoleService::MessageElement::~MessageElement()
+{
+}
+
+nsConsoleService::nsConsoleService()
+ : mCurrentSize(0)
+ , mDeliveringMessage(false)
+ , mLock("nsConsoleService.mLock")
+{
+ // XXX grab this from a pref!
+ // hm, but worry about circularity, bc we want to be able to report
+ // prefs errs...
+ mMaximumSize = 250;
+}
+
+
+void
+nsConsoleService::ClearMessagesForWindowID(const uint64_t innerID)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ MutexAutoLock lock(mLock);
+
+ for (MessageElement* e = mMessages.getFirst(); e != nullptr; ) {
+ // Only messages implementing nsIScriptError interface expose the
+ // inner window ID.
+ nsCOMPtr<nsIScriptError> scriptError = do_QueryInterface(e->Get());
+ if (!scriptError) {
+ e = e->getNext();
+ continue;
+ }
+ uint64_t innerWindowID;
+ nsresult rv = scriptError->GetInnerWindowID(&innerWindowID);
+ if (NS_FAILED(rv) || innerWindowID != innerID) {
+ e = e->getNext();
+ continue;
+ }
+
+ MessageElement* next = e->getNext();
+ e->remove();
+ delete e;
+ mCurrentSize--;
+ MOZ_ASSERT(mCurrentSize < mMaximumSize);
+
+ e = next;
+ }
+}
+
+void
+nsConsoleService::ClearMessages()
+{
+ // NB: A lock is not required here as it's only called from |Reset| which
+ // locks for us and from the dtor.
+ while (!mMessages.isEmpty()) {
+ MessageElement* e = mMessages.popFirst();
+ delete e;
+ }
+ mCurrentSize = 0;
+}
+
+nsConsoleService::~nsConsoleService()
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ ClearMessages();
+}
+
+class AddConsolePrefWatchers : public Runnable
+{
+public:
+ explicit AddConsolePrefWatchers(nsConsoleService* aConsole) : mConsole(aConsole)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ Preferences::AddBoolVarCache(&sLoggingEnabled, "consoleservice.enabled", true);
+ Preferences::AddBoolVarCache(&sLoggingBuffered, "consoleservice.buffered", true);
+#if defined(ANDROID)
+ Preferences::AddBoolVarCache(&sLoggingLogcat, "consoleservice.logcat", true);
+#endif // defined(ANDROID)
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ MOZ_ASSERT(obs);
+ obs->AddObserver(mConsole, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+ obs->AddObserver(mConsole, "inner-window-destroyed", false);
+
+ if (!sLoggingBuffered) {
+ mConsole->Reset();
+ }
+ return NS_OK;
+ }
+
+private:
+ RefPtr<nsConsoleService> mConsole;
+};
+
+nsresult
+nsConsoleService::Init()
+{
+ NS_DispatchToMainThread(new AddConsolePrefWatchers(this));
+
+ return NS_OK;
+}
+
+namespace {
+
+class LogMessageRunnable : public Runnable
+{
+public:
+ LogMessageRunnable(nsIConsoleMessage* aMessage, nsConsoleService* aService)
+ : mMessage(aMessage)
+ , mService(aService)
+ { }
+
+ NS_DECL_NSIRUNNABLE
+
+private:
+ nsCOMPtr<nsIConsoleMessage> mMessage;
+ RefPtr<nsConsoleService> mService;
+};
+
+NS_IMETHODIMP
+LogMessageRunnable::Run()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Snapshot of listeners so that we don't reenter this hash during
+ // enumeration.
+ nsCOMArray<nsIConsoleListener> listeners;
+ mService->CollectCurrentListeners(listeners);
+
+ mService->SetIsDelivering();
+
+ for (int32_t i = 0; i < listeners.Count(); ++i) {
+ listeners[i]->Observe(mMessage);
+ }
+
+ mService->SetDoneDelivering();
+
+ return NS_OK;
+}
+
+} // namespace
+
+// nsIConsoleService methods
+NS_IMETHODIMP
+nsConsoleService::LogMessage(nsIConsoleMessage* aMessage)
+{
+ return LogMessageWithMode(aMessage, OutputToLog);
+}
+
+// This can be called off the main thread.
+nsresult
+nsConsoleService::LogMessageWithMode(nsIConsoleMessage* aMessage,
+ nsConsoleService::OutputMode aOutputMode)
+{
+ if (!aMessage) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (!sLoggingEnabled) {
+ return NS_OK;
+ }
+
+ if (NS_IsMainThread() && mDeliveringMessage) {
+ nsCString msg;
+ aMessage->ToString(msg);
+ NS_WARNING(nsPrintfCString("Reentrancy error: some client attempted "
+ "to display a message to the console while in a console listener. "
+ "The following message was discarded: \"%s\"", msg.get()).get());
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<LogMessageRunnable> r;
+ nsCOMPtr<nsIConsoleMessage> retiredMessage;
+
+ /*
+ * Lock while updating buffer, and while taking snapshot of
+ * listeners array.
+ */
+ {
+ MutexAutoLock lock(mLock);
+
+#if defined(ANDROID)
+ if (sLoggingLogcat && aOutputMode == OutputToLog) {
+ nsCString msg;
+ aMessage->ToString(msg);
+
+ /** Attempt to use the process name as the log tag. */
+ mozilla::dom::ContentChild* child =
+ mozilla::dom::ContentChild::GetSingleton();
+ nsCString appName;
+ if (child) {
+ child->GetProcessName(appName);
+ } else {
+ appName = "GeckoConsole";
+ }
+
+ uint32_t logLevel = 0;
+ aMessage->GetLogLevel(&logLevel);
+
+ android_LogPriority logPriority = ANDROID_LOG_INFO;
+ switch (logLevel) {
+ case nsIConsoleMessage::debug:
+ logPriority = ANDROID_LOG_DEBUG;
+ break;
+ case nsIConsoleMessage::info:
+ logPriority = ANDROID_LOG_INFO;
+ break;
+ case nsIConsoleMessage::warn:
+ logPriority = ANDROID_LOG_WARN;
+ break;
+ case nsIConsoleMessage::error:
+ logPriority = ANDROID_LOG_ERROR;
+ break;
+ }
+
+ __android_log_print(logPriority, appName.get(), "%s", msg.get());
+ }
+#endif
+#ifdef XP_WIN
+ if (IsDebuggerPresent()) {
+ nsString msg;
+ aMessage->GetMessageMoz(getter_Copies(msg));
+ msg.Append('\n');
+ OutputDebugStringW(msg.get());
+ }
+#endif
+#ifdef MOZ_TASK_TRACER
+ {
+ nsCString msg;
+ aMessage->ToString(msg);
+ int prefixPos = msg.Find(GetJSLabelPrefix());
+ if (prefixPos >= 0) {
+ nsDependentCSubstring submsg(msg, prefixPos);
+ AddLabel("%s", submsg.BeginReading());
+ }
+ }
+#endif
+
+ if (sLoggingBuffered) {
+ MessageElement* e = new MessageElement(aMessage);
+ mMessages.insertBack(e);
+ if (mCurrentSize != mMaximumSize) {
+ mCurrentSize++;
+ } else {
+ MessageElement* p = mMessages.popFirst();
+ MOZ_ASSERT(p);
+ p->swapMessage(retiredMessage);
+ delete p;
+ }
+ }
+
+ if (mListeners.Count() > 0) {
+ r = new LogMessageRunnable(aMessage, this);
+ }
+ }
+
+ if (retiredMessage) {
+ // Release |retiredMessage| on the main thread in case it is an instance of
+ // a mainthread-only class like nsScriptErrorWithStack and we're off the
+ // main thread.
+ NS_ReleaseOnMainThread(retiredMessage.forget());
+ }
+
+ if (r) {
+ // avoid failing in XPCShell tests
+ nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+ if (mainThread) {
+ NS_DispatchToMainThread(r.forget());
+ }
+ }
+
+ return NS_OK;
+}
+
+void
+nsConsoleService::CollectCurrentListeners(
+ nsCOMArray<nsIConsoleListener>& aListeners)
+{
+ MutexAutoLock lock(mLock);
+ for (auto iter = mListeners.Iter(); !iter.Done(); iter.Next()) {
+ nsIConsoleListener* value = iter.UserData();
+ aListeners.AppendObject(value);
+ }
+}
+
+NS_IMETHODIMP
+nsConsoleService::LogStringMessage(const char16_t* aMessage)
+{
+ if (!sLoggingEnabled) {
+ return NS_OK;
+ }
+
+ RefPtr<nsConsoleMessage> msg(new nsConsoleMessage(aMessage));
+ return this->LogMessage(msg);
+}
+
+NS_IMETHODIMP
+nsConsoleService::GetMessageArray(uint32_t* aCount,
+ nsIConsoleMessage*** aMessages)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ MutexAutoLock lock(mLock);
+
+ if (mMessages.isEmpty()) {
+ /*
+ * Make a 1-length output array so that nobody gets confused,
+ * and return a count of 0. This should result in a 0-length
+ * array object when called from script.
+ */
+ nsIConsoleMessage** messageArray = (nsIConsoleMessage**)
+ moz_xmalloc(sizeof(nsIConsoleMessage*));
+ *messageArray = nullptr;
+ *aMessages = messageArray;
+ *aCount = 0;
+
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(mCurrentSize <= mMaximumSize);
+ nsIConsoleMessage** messageArray =
+ static_cast<nsIConsoleMessage**>(moz_xmalloc(sizeof(nsIConsoleMessage*)
+ * mCurrentSize));
+
+ uint32_t i = 0;
+ for (MessageElement* e = mMessages.getFirst(); e != nullptr; e = e->getNext()) {
+ nsCOMPtr<nsIConsoleMessage> m = e->Get();
+ m.forget(&messageArray[i]);
+ i++;
+ }
+
+ MOZ_ASSERT(i == mCurrentSize);
+
+ *aCount = i;
+ *aMessages = messageArray;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsConsoleService::RegisterListener(nsIConsoleListener* aListener)
+{
+ if (!NS_IsMainThread()) {
+ NS_ERROR("nsConsoleService::RegisterListener is main thread only.");
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ nsCOMPtr<nsISupports> canonical = do_QueryInterface(aListener);
+
+ MutexAutoLock lock(mLock);
+ if (mListeners.GetWeak(canonical)) {
+ // Reregistering a listener isn't good
+ return NS_ERROR_FAILURE;
+ }
+ mListeners.Put(canonical, aListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsConsoleService::UnregisterListener(nsIConsoleListener* aListener)
+{
+ if (!NS_IsMainThread()) {
+ NS_ERROR("nsConsoleService::UnregisterListener is main thread only.");
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ nsCOMPtr<nsISupports> canonical = do_QueryInterface(aListener);
+
+ MutexAutoLock lock(mLock);
+
+ if (!mListeners.GetWeak(canonical)) {
+ // Unregistering a listener that was never registered?
+ return NS_ERROR_FAILURE;
+ }
+ mListeners.Remove(canonical);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsConsoleService::Reset()
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ /*
+ * Make sure nobody trips into the buffer while it's being reset
+ */
+ MutexAutoLock lock(mLock);
+
+ ClearMessages();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsConsoleService::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+ // Dump all our messages, in case any are cycle collected.
+ Reset();
+ // We could remove ourselves from the observer service, but it is about to
+ // drop all observers anyways, so why bother.
+ } else if (!strcmp(aTopic, "inner-window-destroyed")) {
+ nsCOMPtr<nsISupportsPRUint64> supportsInt = do_QueryInterface(aSubject);
+ MOZ_ASSERT(supportsInt);
+
+ uint64_t windowId;
+ MOZ_ALWAYS_SUCCEEDS(supportsInt->GetData(&windowId));
+
+ ClearMessagesForWindowID(windowId);
+ } else {
+ MOZ_CRASH();
+ }
+ return NS_OK;
+}
diff --git a/xpcom/base/nsConsoleService.h b/xpcom/base/nsConsoleService.h
new file mode 100644
index 0000000000..089de81069
--- /dev/null
+++ b/xpcom/base/nsConsoleService.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/. */
+
+/*
+ * nsConsoleService class declaration.
+ */
+
+#ifndef __nsconsoleservice_h__
+#define __nsconsoleservice_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Mutex.h"
+
+#include "nsInterfaceHashtable.h"
+#include "nsHashKeys.h"
+
+#include "nsIConsoleService.h"
+
+class nsConsoleService final : public nsIConsoleService,
+ public nsIObserver
+{
+public:
+ nsConsoleService();
+ nsresult Init();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSICONSOLESERVICE
+ NS_DECL_NSIOBSERVER
+
+ void SetIsDelivering()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!mDeliveringMessage);
+ mDeliveringMessage = true;
+ }
+
+ void SetDoneDelivering()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mDeliveringMessage);
+ mDeliveringMessage = false;
+ }
+
+ // This is a variant of LogMessage which allows the caller to determine
+ // if the message should be output to an OS-specific log. This is used on
+ // B2G to control whether the message is logged to the android log or not.
+
+ enum OutputMode {
+ SuppressLog,
+ OutputToLog
+ };
+ virtual nsresult LogMessageWithMode(nsIConsoleMessage* aMessage,
+ OutputMode aOutputMode);
+
+ typedef nsInterfaceHashtable<nsISupportsHashKey,
+ nsIConsoleListener> ListenerHash;
+ void CollectCurrentListeners(nsCOMArray<nsIConsoleListener>& aListeners);
+
+private:
+ class MessageElement : public mozilla::LinkedListElement<MessageElement>
+ {
+ public:
+ explicit MessageElement(nsIConsoleMessage* aMessage) : mMessage(aMessage)
+ {}
+
+ nsIConsoleMessage* Get()
+ {
+ return mMessage.get();
+ }
+
+ // Swap directly into an nsCOMPtr to avoid spurious refcount
+ // traffic off the main thread in debug builds from
+ // NSCAP_ASSERT_NO_QUERY_NEEDED().
+ void swapMessage(nsCOMPtr<nsIConsoleMessage>& aRetVal)
+ {
+ mMessage.swap(aRetVal);
+ }
+
+ ~MessageElement();
+
+ private:
+ nsCOMPtr<nsIConsoleMessage> mMessage;
+
+ MessageElement(const MessageElement&) = delete;
+ MessageElement& operator=(const MessageElement&) = delete;
+ MessageElement(MessageElement&&) = delete;
+ MessageElement& operator=(MessageElement&&) = delete;
+ };
+
+ ~nsConsoleService();
+
+ void ClearMessagesForWindowID(const uint64_t innerID);
+ void ClearMessages();
+
+ mozilla::LinkedList<MessageElement> mMessages;
+
+ // The current size of mMessages.
+ uint32_t mCurrentSize;
+
+ // The maximum size of mMessages.
+ uint32_t mMaximumSize;
+
+ // Are we currently delivering a console message on the main thread? If
+ // so, we suppress incoming messages on the main thread only, to avoid
+ // infinite repitition.
+ bool mDeliveringMessage;
+
+ // Listeners to notify whenever a new message is logged.
+ ListenerHash mListeners;
+
+ // To serialize interesting methods.
+ mozilla::Mutex mLock;
+};
+
+#endif /* __nsconsoleservice_h__ */
diff --git a/xpcom/base/nsCrashOnException.cpp b/xpcom/base/nsCrashOnException.cpp
new file mode 100644
index 0000000000..0f8042531f
--- /dev/null
+++ b/xpcom/base/nsCrashOnException.cpp
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "nsCrashOnException.h"
+#include "nsCOMPtr.h"
+#include "nsServiceManagerUtils.h"
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsICrashReporter.h"
+#endif
+
+namespace mozilla {
+
+static int
+ReportException(EXCEPTION_POINTERS* aExceptionInfo)
+{
+#ifdef MOZ_CRASHREPORTER
+ nsCOMPtr<nsICrashReporter> cr =
+ do_GetService("@mozilla.org/toolkit/crash-reporter;1");
+ if (cr) {
+ cr->WriteMinidumpForException(aExceptionInfo);
+ }
+#endif
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+XPCOM_API(LRESULT)
+CallWindowProcCrashProtected(WNDPROC aWndProc, HWND aHWnd, UINT aMsg,
+ WPARAM aWParam, LPARAM aLParam)
+{
+ MOZ_SEH_TRY {
+ return aWndProc(aHWnd, aMsg, aWParam, aLParam);
+ }
+ MOZ_SEH_EXCEPT(ReportException(GetExceptionInformation())) {
+ ::TerminateProcess(::GetCurrentProcess(), 253);
+ }
+ return 0; // not reached
+}
+
+}
+
diff --git a/xpcom/base/nsCrashOnException.h b/xpcom/base/nsCrashOnException.h
new file mode 100644
index 0000000000..cdf3fdf092
--- /dev/null
+++ b/xpcom/base/nsCrashOnException.h
@@ -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/. */
+
+#ifndef nsCrashOnException_h
+#define nsCrashOnException_h
+
+#include <nscore.h>
+#include <windows.h>
+
+namespace mozilla {
+
+// Call a given window procedure, and catch any Win32 exceptions raised from it,
+// and report them as crashes.
+XPCOM_API(LRESULT) CallWindowProcCrashProtected(WNDPROC aWndProc, HWND aHWnd,
+ UINT aMsg, WPARAM aWParam,
+ LPARAM aLParam);
+
+}
+
+#endif
diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp
new file mode 100644
index 0000000000..ca7057628c
--- /dev/null
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -0,0 +1,4213 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 implements a garbage-cycle collector based on the paper
+//
+// Concurrent Cycle Collection in Reference Counted Systems
+// Bacon & Rajan (2001), ECOOP 2001 / Springer LNCS vol 2072
+//
+// We are not using the concurrent or acyclic cases of that paper; so
+// the green, red and orange colors are not used.
+//
+// The collector is based on tracking pointers of four colors:
+//
+// Black nodes are definitely live. If we ever determine a node is
+// black, it's ok to forget about, drop from our records.
+//
+// White nodes are definitely garbage cycles. Once we finish with our
+// scanning, we unlink all the white nodes and expect that by
+// unlinking them they will self-destruct (since a garbage cycle is
+// only keeping itself alive with internal links, by definition).
+//
+// Snow-white is an addition to the original algorithm. Snow-white object
+// has reference count zero and is just waiting for deletion.
+//
+// Grey nodes are being scanned. Nodes that turn grey will turn
+// either black if we determine that they're live, or white if we
+// determine that they're a garbage cycle. After the main collection
+// algorithm there should be no grey nodes.
+//
+// Purple nodes are *candidates* for being scanned. They are nodes we
+// haven't begun scanning yet because they're not old enough, or we're
+// still partway through the algorithm.
+//
+// XPCOM objects participating in garbage-cycle collection are obliged
+// to inform us when they ought to turn purple; that is, when their
+// refcount transitions from N+1 -> N, for nonzero N. Furthermore we
+// require that *after* an XPCOM object has informed us of turning
+// purple, they will tell us when they either transition back to being
+// black (incremented refcount) or are ultimately deleted.
+
+// Incremental cycle collection
+//
+// Beyond the simple state machine required to implement incremental
+// collection, the CC needs to be able to compensate for things the browser
+// is doing during the collection. There are two kinds of problems. For each
+// of these, there are two cases to deal with: purple-buffered C++ objects
+// and JS objects.
+
+// The first problem is that an object in the CC's graph can become garbage.
+// This is bad because the CC touches the objects in its graph at every
+// stage of its operation.
+//
+// All cycle collected C++ objects that die during a cycle collection
+// will end up actually getting deleted by the SnowWhiteKiller. Before
+// the SWK deletes an object, it checks if an ICC is running, and if so,
+// if the object is in the graph. If it is, the CC clears mPointer and
+// mParticipant so it does not point to the raw object any more. Because
+// objects could die any time the CC returns to the mutator, any time the CC
+// accesses a PtrInfo it must perform a null check on mParticipant to
+// ensure the object has not gone away.
+//
+// JS objects don't always run finalizers, so the CC can't remove them from
+// the graph when they die. Fortunately, JS objects can only die during a GC,
+// so if a GC is begun during an ICC, the browser synchronously finishes off
+// the ICC, which clears the entire CC graph. If the GC and CC are scheduled
+// properly, this should be rare.
+//
+// The second problem is that objects in the graph can be changed, say by
+// being addrefed or released, or by having a field updated, after the object
+// has been added to the graph. The problem is that ICC can miss a newly
+// created reference to an object, and end up unlinking an object that is
+// actually alive.
+//
+// The basic idea of the solution, from "An on-the-fly Reference Counting
+// Garbage Collector for Java" by Levanoni and Petrank, is to notice if an
+// object has had an additional reference to it created during the collection,
+// and if so, don't collect it during the current collection. This avoids having
+// to rerun the scan as in Bacon & Rajan 2001.
+//
+// For cycle collected C++ objects, we modify AddRef to place the object in
+// the purple buffer, in addition to Release. Then, in the CC, we treat any
+// objects in the purple buffer as being alive, after graph building has
+// completed. Because they are in the purple buffer, they will be suspected
+// in the next CC, so there's no danger of leaks. This is imprecise, because
+// we will treat as live an object that has been Released but not AddRefed
+// during graph building, but that's probably rare enough that the additional
+// bookkeeping overhead is not worthwhile.
+//
+// For JS objects, the cycle collector is only looking at gray objects. If a
+// gray object is touched during ICC, it will be made black by UnmarkGray.
+// Thus, if a JS object has become black during the ICC, we treat it as live.
+// Merged JS zones have to be handled specially: we scan all zone globals.
+// If any are black, we treat the zone as being black.
+
+
+// Safety
+//
+// An XPCOM object is either scan-safe or scan-unsafe, purple-safe or
+// purple-unsafe.
+//
+// An nsISupports object is scan-safe if:
+//
+// - It can be QI'ed to |nsXPCOMCycleCollectionParticipant|, though
+// this operation loses ISupports identity (like nsIClassInfo).
+// - Additionally, the operation |traverse| on the resulting
+// nsXPCOMCycleCollectionParticipant does not cause *any* refcount
+// adjustment to occur (no AddRef / Release calls).
+//
+// A non-nsISupports ("native") object is scan-safe by explicitly
+// providing its nsCycleCollectionParticipant.
+//
+// An object is purple-safe if it satisfies the following properties:
+//
+// - The object is scan-safe.
+//
+// When we receive a pointer |ptr| via
+// |nsCycleCollector::suspect(ptr)|, we assume it is purple-safe. We
+// can check the scan-safety, but have no way to ensure the
+// purple-safety; objects must obey, or else the entire system falls
+// apart. Don't involve an object in this scheme if you can't
+// guarantee its purple-safety. The easiest way to ensure that an
+// object is purple-safe is to use nsCycleCollectingAutoRefCnt.
+//
+// When we have a scannable set of purple nodes ready, we begin
+// our walks. During the walks, the nodes we |traverse| should only
+// feed us more scan-safe nodes, and should not adjust the refcounts
+// of those nodes.
+//
+// We do not |AddRef| or |Release| any objects during scanning. We
+// rely on the purple-safety of the roots that call |suspect| to
+// hold, such that we will clear the pointer from the purple buffer
+// entry to the object before it is destroyed. The pointers that are
+// merely scan-safe we hold only for the duration of scanning, and
+// there should be no objects released from the scan-safe set during
+// the scan.
+//
+// We *do* call |Root| and |Unroot| on every white object, on
+// either side of the calls to |Unlink|. This keeps the set of white
+// objects alive during the unlinking.
+//
+
+#if !defined(__MINGW32__)
+#ifdef WIN32
+#include <crtdbg.h>
+#include <errno.h>
+#endif
+#endif
+
+#include "base/process_util.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/CycleCollectedJSContext.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/HoldDropJSObjects.h"
+/* This must occur *after* base/process_util.h to avoid typedefs conflicts. */
+#include "mozilla/LinkedList.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/SegmentedVector.h"
+
+#include "nsCycleCollectionParticipant.h"
+#include "nsCycleCollectionNoteRootCallback.h"
+#include "nsDeque.h"
+#include "nsCycleCollector.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+#include "prenv.h"
+#include "nsPrintfCString.h"
+#include "nsTArray.h"
+#include "nsIConsoleService.h"
+#include "mozilla/Attributes.h"
+#include "nsICycleCollectorListener.h"
+#include "nsIMemoryReporter.h"
+#include "nsIFile.h"
+#include "nsDumpUtils.h"
+#include "xpcpublic.h"
+#include "GeckoProfiler.h"
+#include <stdint.h>
+#include <stdio.h>
+
+#include "mozilla/AutoGlobalTimelineMarker.h"
+#include "mozilla/Likely.h"
+#include "mozilla/PoisonIOInterposer.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/ThreadLocal.h"
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+
+using namespace mozilla;
+
+//#define COLLECT_TIME_DEBUG
+
+// Enable assertions that are useful for diagnosing errors in graph construction.
+//#define DEBUG_CC_GRAPH
+
+#define DEFAULT_SHUTDOWN_COLLECTIONS 5
+
+// One to do the freeing, then another to detect there is no more work to do.
+#define NORMAL_SHUTDOWN_COLLECTIONS 2
+
+// Cycle collector environment variables
+//
+// MOZ_CC_LOG_ALL: If defined, always log cycle collector heaps.
+//
+// MOZ_CC_LOG_SHUTDOWN: If defined, log cycle collector heaps at shutdown.
+//
+// MOZ_CC_LOG_THREAD: If set to "main", only automatically log main thread
+// CCs. If set to "worker", only automatically log worker CCs. If set to "all",
+// log either. The default value is "all". This must be used with either
+// MOZ_CC_LOG_ALL or MOZ_CC_LOG_SHUTDOWN for it to do anything.
+//
+// MOZ_CC_LOG_PROCESS: If set to "main", only automatically log main process
+// CCs. If set to "content", only automatically log tab CCs. If set to
+// "plugins", only automatically log plugin CCs. If set to "all", log
+// everything. The default value is "all". This must be used with either
+// MOZ_CC_LOG_ALL or MOZ_CC_LOG_SHUTDOWN for it to do anything.
+//
+// MOZ_CC_ALL_TRACES: If set to "all", any cycle collector
+// logging done will be WantAllTraces, which disables
+// various cycle collector optimizations to give a fuller picture of
+// the heap. If set to "shutdown", only shutdown logging will be WantAllTraces.
+// The default is none.
+//
+// MOZ_CC_RUN_DURING_SHUTDOWN: In non-DEBUG or builds, if this is set,
+// run cycle collections at shutdown.
+//
+// MOZ_CC_LOG_DIRECTORY: The directory in which logs are placed (such as
+// logs from MOZ_CC_LOG_ALL and MOZ_CC_LOG_SHUTDOWN, or other uses
+// of nsICycleCollectorListener)
+
+// Various parameters of this collector can be tuned using environment
+// variables.
+
+struct nsCycleCollectorParams
+{
+ bool mLogAll;
+ bool mLogShutdown;
+ bool mAllTracesAll;
+ bool mAllTracesShutdown;
+ bool mLogThisThread;
+
+ nsCycleCollectorParams() :
+ mLogAll(PR_GetEnv("MOZ_CC_LOG_ALL") != nullptr),
+ mLogShutdown(PR_GetEnv("MOZ_CC_LOG_SHUTDOWN") != nullptr),
+ mAllTracesAll(false),
+ mAllTracesShutdown(false)
+ {
+ const char* logThreadEnv = PR_GetEnv("MOZ_CC_LOG_THREAD");
+ bool threadLogging = true;
+ if (logThreadEnv && !!strcmp(logThreadEnv, "all")) {
+ if (NS_IsMainThread()) {
+ threadLogging = !strcmp(logThreadEnv, "main");
+ } else {
+ threadLogging = !strcmp(logThreadEnv, "worker");
+ }
+ }
+
+ const char* logProcessEnv = PR_GetEnv("MOZ_CC_LOG_PROCESS");
+ bool processLogging = true;
+ if (logProcessEnv && !!strcmp(logProcessEnv, "all")) {
+ switch (XRE_GetProcessType()) {
+ case GeckoProcessType_Default:
+ processLogging = !strcmp(logProcessEnv, "main");
+ break;
+ case GeckoProcessType_Plugin:
+ processLogging = !strcmp(logProcessEnv, "plugins");
+ break;
+ case GeckoProcessType_Content:
+ processLogging = !strcmp(logProcessEnv, "content");
+ break;
+ default:
+ processLogging = false;
+ break;
+ }
+ }
+ mLogThisThread = threadLogging && processLogging;
+
+ const char* allTracesEnv = PR_GetEnv("MOZ_CC_ALL_TRACES");
+ if (allTracesEnv) {
+ if (!strcmp(allTracesEnv, "all")) {
+ mAllTracesAll = true;
+ } else if (!strcmp(allTracesEnv, "shutdown")) {
+ mAllTracesShutdown = true;
+ }
+ }
+ }
+
+ bool LogThisCC(bool aIsShutdown)
+ {
+ return (mLogAll || (aIsShutdown && mLogShutdown)) && mLogThisThread;
+ }
+
+ bool AllTracesThisCC(bool aIsShutdown)
+ {
+ return mAllTracesAll || (aIsShutdown && mAllTracesShutdown);
+ }
+};
+
+#ifdef COLLECT_TIME_DEBUG
+class TimeLog
+{
+public:
+ TimeLog() : mLastCheckpoint(TimeStamp::Now())
+ {
+ }
+
+ void
+ Checkpoint(const char* aEvent)
+ {
+ TimeStamp now = TimeStamp::Now();
+ double dur = (now - mLastCheckpoint).ToMilliseconds();
+ if (dur >= 0.5) {
+ printf("cc: %s took %.1fms\n", aEvent, dur);
+ }
+ mLastCheckpoint = now;
+ }
+
+private:
+ TimeStamp mLastCheckpoint;
+};
+#else
+class TimeLog
+{
+public:
+ TimeLog()
+ {
+ }
+ void Checkpoint(const char* aEvent)
+ {
+ }
+};
+#endif
+
+
+////////////////////////////////////////////////////////////////////////
+// Base types
+////////////////////////////////////////////////////////////////////////
+
+struct PtrInfo;
+
+class EdgePool
+{
+public:
+ // EdgePool allocates arrays of void*, primarily to hold PtrInfo*.
+ // However, at the end of a block, the last two pointers are a null
+ // and then a void** pointing to the next block. This allows
+ // EdgePool::Iterators to be a single word but still capable of crossing
+ // block boundaries.
+
+ EdgePool()
+ {
+ mSentinelAndBlocks[0].block = nullptr;
+ mSentinelAndBlocks[1].block = nullptr;
+ }
+
+ ~EdgePool()
+ {
+ MOZ_ASSERT(!mSentinelAndBlocks[0].block &&
+ !mSentinelAndBlocks[1].block,
+ "Didn't call Clear()?");
+ }
+
+ void Clear()
+ {
+ EdgeBlock* b = EdgeBlocks();
+ while (b) {
+ EdgeBlock* next = b->Next();
+ delete b;
+ b = next;
+ }
+
+ mSentinelAndBlocks[0].block = nullptr;
+ mSentinelAndBlocks[1].block = nullptr;
+ }
+
+#ifdef DEBUG
+ bool IsEmpty()
+ {
+ return !mSentinelAndBlocks[0].block &&
+ !mSentinelAndBlocks[1].block;
+ }
+#endif
+
+private:
+ struct EdgeBlock;
+ union PtrInfoOrBlock
+ {
+ // Use a union to avoid reinterpret_cast and the ensuing
+ // potential aliasing bugs.
+ PtrInfo* ptrInfo;
+ EdgeBlock* block;
+ };
+ struct EdgeBlock
+ {
+ enum { EdgeBlockSize = 16 * 1024 };
+
+ PtrInfoOrBlock mPointers[EdgeBlockSize];
+ EdgeBlock()
+ {
+ mPointers[EdgeBlockSize - 2].block = nullptr; // sentinel
+ mPointers[EdgeBlockSize - 1].block = nullptr; // next block pointer
+ }
+ EdgeBlock*& Next()
+ {
+ return mPointers[EdgeBlockSize - 1].block;
+ }
+ PtrInfoOrBlock* Start()
+ {
+ return &mPointers[0];
+ }
+ PtrInfoOrBlock* End()
+ {
+ return &mPointers[EdgeBlockSize - 2];
+ }
+ };
+
+ // Store the null sentinel so that we can have valid iterators
+ // before adding any edges and without adding any blocks.
+ PtrInfoOrBlock mSentinelAndBlocks[2];
+
+ EdgeBlock*& EdgeBlocks()
+ {
+ return mSentinelAndBlocks[1].block;
+ }
+ EdgeBlock* EdgeBlocks() const
+ {
+ return mSentinelAndBlocks[1].block;
+ }
+
+public:
+ class Iterator
+ {
+ public:
+ Iterator() : mPointer(nullptr) {}
+ explicit Iterator(PtrInfoOrBlock* aPointer) : mPointer(aPointer) {}
+ Iterator(const Iterator& aOther) : mPointer(aOther.mPointer) {}
+
+ Iterator& operator++()
+ {
+ if (!mPointer->ptrInfo) {
+ // Null pointer is a sentinel for link to the next block.
+ mPointer = (mPointer + 1)->block->mPointers;
+ }
+ ++mPointer;
+ return *this;
+ }
+
+ PtrInfo* operator*() const
+ {
+ if (!mPointer->ptrInfo) {
+ // Null pointer is a sentinel for link to the next block.
+ return (mPointer + 1)->block->mPointers->ptrInfo;
+ }
+ return mPointer->ptrInfo;
+ }
+ bool operator==(const Iterator& aOther) const
+ {
+ return mPointer == aOther.mPointer;
+ }
+ bool operator!=(const Iterator& aOther) const
+ {
+ return mPointer != aOther.mPointer;
+ }
+
+#ifdef DEBUG_CC_GRAPH
+ bool Initialized() const
+ {
+ return mPointer != nullptr;
+ }
+#endif
+
+ private:
+ PtrInfoOrBlock* mPointer;
+ };
+
+ class Builder;
+ friend class Builder;
+ class Builder
+ {
+ public:
+ explicit Builder(EdgePool& aPool)
+ : mCurrent(&aPool.mSentinelAndBlocks[0])
+ , mBlockEnd(&aPool.mSentinelAndBlocks[0])
+ , mNextBlockPtr(&aPool.EdgeBlocks())
+ {
+ }
+
+ Iterator Mark()
+ {
+ return Iterator(mCurrent);
+ }
+
+ void Add(PtrInfo* aEdge)
+ {
+ if (mCurrent == mBlockEnd) {
+ EdgeBlock* b = new EdgeBlock();
+ *mNextBlockPtr = b;
+ mCurrent = b->Start();
+ mBlockEnd = b->End();
+ mNextBlockPtr = &b->Next();
+ }
+ (mCurrent++)->ptrInfo = aEdge;
+ }
+ private:
+ // mBlockEnd points to space for null sentinel
+ PtrInfoOrBlock* mCurrent;
+ PtrInfoOrBlock* mBlockEnd;
+ EdgeBlock** mNextBlockPtr;
+ };
+
+ size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+ {
+ size_t n = 0;
+ EdgeBlock* b = EdgeBlocks();
+ while (b) {
+ n += aMallocSizeOf(b);
+ b = b->Next();
+ }
+ return n;
+ }
+};
+
+#ifdef DEBUG_CC_GRAPH
+#define CC_GRAPH_ASSERT(b) MOZ_ASSERT(b)
+#else
+#define CC_GRAPH_ASSERT(b)
+#endif
+
+#define CC_TELEMETRY(_name, _value) \
+ PR_BEGIN_MACRO \
+ if (NS_IsMainThread()) { \
+ Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR##_name, _value); \
+ } else { \
+ Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_WORKER##_name, _value); \
+ } \
+ PR_END_MACRO
+
+enum NodeColor { black, white, grey };
+
+// This structure should be kept as small as possible; we may expect
+// hundreds of thousands of them to be allocated and touched
+// repeatedly during each cycle collection.
+
+struct PtrInfo
+{
+ void* mPointer;
+ nsCycleCollectionParticipant* mParticipant;
+ uint32_t mColor : 2;
+ uint32_t mInternalRefs : 30;
+ uint32_t mRefCount;
+private:
+ EdgePool::Iterator mFirstChild;
+
+ static const uint32_t kInitialRefCount = UINT32_MAX - 1;
+
+public:
+
+ PtrInfo(void* aPointer, nsCycleCollectionParticipant* aParticipant)
+ : mPointer(aPointer),
+ mParticipant(aParticipant),
+ mColor(grey),
+ mInternalRefs(0),
+ mRefCount(kInitialRefCount),
+ mFirstChild()
+ {
+ MOZ_ASSERT(aParticipant);
+
+ // We initialize mRefCount to a large non-zero value so
+ // that it doesn't look like a JS object to the cycle collector
+ // in the case where the object dies before being traversed.
+ MOZ_ASSERT(!IsGrayJS() && !IsBlackJS());
+ }
+
+ // Allow NodePool::NodeBlock's constructor to compile.
+ PtrInfo()
+ {
+ NS_NOTREACHED("should never be called");
+ }
+
+ bool IsGrayJS() const
+ {
+ return mRefCount == 0;
+ }
+
+ bool IsBlackJS() const
+ {
+ return mRefCount == UINT32_MAX;
+ }
+
+ bool WasTraversed() const
+ {
+ return mRefCount != kInitialRefCount;
+ }
+
+ EdgePool::Iterator FirstChild() const
+ {
+ CC_GRAPH_ASSERT(mFirstChild.Initialized());
+ return mFirstChild;
+ }
+
+ // this PtrInfo must be part of a NodePool
+ EdgePool::Iterator LastChild() const
+ {
+ CC_GRAPH_ASSERT((this + 1)->mFirstChild.Initialized());
+ return (this + 1)->mFirstChild;
+ }
+
+ void SetFirstChild(EdgePool::Iterator aFirstChild)
+ {
+ CC_GRAPH_ASSERT(aFirstChild.Initialized());
+ mFirstChild = aFirstChild;
+ }
+
+ // this PtrInfo must be part of a NodePool
+ void SetLastChild(EdgePool::Iterator aLastChild)
+ {
+ CC_GRAPH_ASSERT(aLastChild.Initialized());
+ (this + 1)->mFirstChild = aLastChild;
+ }
+};
+
+/**
+ * A structure designed to be used like a linked list of PtrInfo, except
+ * it allocates many PtrInfos at a time.
+ */
+class NodePool
+{
+private:
+ // The -2 allows us to use |NodeBlockSize + 1| for |mEntries|, and fit
+ // |mNext|, all without causing slop.
+ enum { NodeBlockSize = 4 * 1024 - 2 };
+
+ struct NodeBlock
+ {
+ // We create and destroy NodeBlock using moz_xmalloc/free rather than new
+ // and delete to avoid calling its constructor and destructor.
+ NodeBlock()
+ {
+ NS_NOTREACHED("should never be called");
+
+ // Ensure NodeBlock is the right size (see the comment on NodeBlockSize
+ // above).
+ static_assert(
+ sizeof(NodeBlock) == 81904 || // 32-bit; equals 19.996 x 4 KiB pages
+ sizeof(NodeBlock) == 131048, // 64-bit; equals 31.994 x 4 KiB pages
+ "ill-sized NodeBlock"
+ );
+ }
+ ~NodeBlock()
+ {
+ NS_NOTREACHED("should never be called");
+ }
+
+ NodeBlock* mNext;
+ PtrInfo mEntries[NodeBlockSize + 1]; // +1 to store last child of last node
+ };
+
+public:
+ NodePool()
+ : mBlocks(nullptr)
+ , mLast(nullptr)
+ {
+ }
+
+ ~NodePool()
+ {
+ MOZ_ASSERT(!mBlocks, "Didn't call Clear()?");
+ }
+
+ void Clear()
+ {
+ NodeBlock* b = mBlocks;
+ while (b) {
+ NodeBlock* n = b->mNext;
+ free(b);
+ b = n;
+ }
+
+ mBlocks = nullptr;
+ mLast = nullptr;
+ }
+
+#ifdef DEBUG
+ bool IsEmpty()
+ {
+ return !mBlocks && !mLast;
+ }
+#endif
+
+ class Builder;
+ friend class Builder;
+ class Builder
+ {
+ public:
+ explicit Builder(NodePool& aPool)
+ : mNextBlock(&aPool.mBlocks)
+ , mNext(aPool.mLast)
+ , mBlockEnd(nullptr)
+ {
+ MOZ_ASSERT(!aPool.mBlocks && !aPool.mLast, "pool not empty");
+ }
+ PtrInfo* Add(void* aPointer, nsCycleCollectionParticipant* aParticipant)
+ {
+ if (mNext == mBlockEnd) {
+ NodeBlock* block = static_cast<NodeBlock*>(malloc(sizeof(NodeBlock)));
+ if (!block) {
+ return nullptr;
+ }
+
+ *mNextBlock = block;
+ mNext = block->mEntries;
+ mBlockEnd = block->mEntries + NodeBlockSize;
+ block->mNext = nullptr;
+ mNextBlock = &block->mNext;
+ }
+ return new (mozilla::KnownNotNull, mNext++) PtrInfo(aPointer, aParticipant);
+ }
+ private:
+ NodeBlock** mNextBlock;
+ PtrInfo*& mNext;
+ PtrInfo* mBlockEnd;
+ };
+
+ class Enumerator;
+ friend class Enumerator;
+ class Enumerator
+ {
+ public:
+ explicit Enumerator(NodePool& aPool)
+ : mFirstBlock(aPool.mBlocks)
+ , mCurBlock(nullptr)
+ , mNext(nullptr)
+ , mBlockEnd(nullptr)
+ , mLast(aPool.mLast)
+ {
+ }
+
+ bool IsDone() const
+ {
+ return mNext == mLast;
+ }
+
+ bool AtBlockEnd() const
+ {
+ return mNext == mBlockEnd;
+ }
+
+ PtrInfo* GetNext()
+ {
+ MOZ_ASSERT(!IsDone(), "calling GetNext when done");
+ if (mNext == mBlockEnd) {
+ NodeBlock* nextBlock = mCurBlock ? mCurBlock->mNext : mFirstBlock;
+ mNext = nextBlock->mEntries;
+ mBlockEnd = mNext + NodeBlockSize;
+ mCurBlock = nextBlock;
+ }
+ return mNext++;
+ }
+ private:
+ // mFirstBlock is a reference to allow an Enumerator to be constructed
+ // for an empty graph.
+ NodeBlock*& mFirstBlock;
+ NodeBlock* mCurBlock;
+ // mNext is the next value we want to return, unless mNext == mBlockEnd
+ // NB: mLast is a reference to allow enumerating while building!
+ PtrInfo* mNext;
+ PtrInfo* mBlockEnd;
+ PtrInfo*& mLast;
+ };
+
+ size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+ {
+ // We don't measure the things pointed to by mEntries[] because those
+ // pointers are non-owning.
+ size_t n = 0;
+ NodeBlock* b = mBlocks;
+ while (b) {
+ n += aMallocSizeOf(b);
+ b = b->mNext;
+ }
+ return n;
+ }
+
+private:
+ NodeBlock* mBlocks;
+ PtrInfo* mLast;
+};
+
+
+// Declarations for mPtrToNodeMap.
+
+struct PtrToNodeEntry : public PLDHashEntryHdr
+{
+ // The key is mNode->mPointer
+ PtrInfo* mNode;
+};
+
+static bool
+PtrToNodeMatchEntry(const PLDHashEntryHdr* aEntry, const void* aKey)
+{
+ const PtrToNodeEntry* n = static_cast<const PtrToNodeEntry*>(aEntry);
+ return n->mNode->mPointer == aKey;
+}
+
+static PLDHashTableOps PtrNodeOps = {
+ PLDHashTable::HashVoidPtrKeyStub,
+ PtrToNodeMatchEntry,
+ PLDHashTable::MoveEntryStub,
+ PLDHashTable::ClearEntryStub,
+ nullptr
+};
+
+
+struct WeakMapping
+{
+ // map and key will be null if the corresponding objects are GC marked
+ PtrInfo* mMap;
+ PtrInfo* mKey;
+ PtrInfo* mKeyDelegate;
+ PtrInfo* mVal;
+};
+
+class CCGraphBuilder;
+
+struct CCGraph
+{
+ NodePool mNodes;
+ EdgePool mEdges;
+ nsTArray<WeakMapping> mWeakMaps;
+ uint32_t mRootCount;
+
+private:
+ PLDHashTable mPtrToNodeMap;
+ bool mOutOfMemory;
+
+ static const uint32_t kInitialMapLength = 16384;
+
+public:
+ CCGraph()
+ : mRootCount(0)
+ , mPtrToNodeMap(&PtrNodeOps, sizeof(PtrToNodeEntry), kInitialMapLength)
+ , mOutOfMemory(false)
+ {}
+
+ ~CCGraph() {}
+
+ void Init()
+ {
+ MOZ_ASSERT(IsEmpty(), "Failed to call CCGraph::Clear");
+ }
+
+ void Clear()
+ {
+ mNodes.Clear();
+ mEdges.Clear();
+ mWeakMaps.Clear();
+ mRootCount = 0;
+ mPtrToNodeMap.ClearAndPrepareForLength(kInitialMapLength);
+ mOutOfMemory = false;
+ }
+
+#ifdef DEBUG
+ bool IsEmpty()
+ {
+ return mNodes.IsEmpty() && mEdges.IsEmpty() &&
+ mWeakMaps.IsEmpty() && mRootCount == 0 &&
+ mPtrToNodeMap.EntryCount() == 0;
+ }
+#endif
+
+ PtrInfo* FindNode(void* aPtr);
+ PtrToNodeEntry* AddNodeToMap(void* aPtr);
+ void RemoveObjectFromMap(void* aObject);
+
+ uint32_t MapCount() const
+ {
+ return mPtrToNodeMap.EntryCount();
+ }
+
+ size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+ {
+ size_t n = 0;
+
+ n += mNodes.SizeOfExcludingThis(aMallocSizeOf);
+ n += mEdges.SizeOfExcludingThis(aMallocSizeOf);
+
+ // We don't measure what the WeakMappings point to, because the
+ // pointers are non-owning.
+ n += mWeakMaps.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ n += mPtrToNodeMap.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ return n;
+ }
+
+private:
+ PtrToNodeEntry* FindNodeEntry(void* aPtr)
+ {
+ return static_cast<PtrToNodeEntry*>(mPtrToNodeMap.Search(aPtr));
+ }
+};
+
+PtrInfo*
+CCGraph::FindNode(void* aPtr)
+{
+ PtrToNodeEntry* e = FindNodeEntry(aPtr);
+ return e ? e->mNode : nullptr;
+}
+
+PtrToNodeEntry*
+CCGraph::AddNodeToMap(void* aPtr)
+{
+ JS::AutoSuppressGCAnalysis suppress;
+ if (mOutOfMemory) {
+ return nullptr;
+ }
+
+ auto e = static_cast<PtrToNodeEntry*>(mPtrToNodeMap.Add(aPtr, fallible));
+ if (!e) {
+ mOutOfMemory = true;
+ MOZ_ASSERT(false, "Ran out of memory while building cycle collector graph");
+ return nullptr;
+ }
+ return e;
+}
+
+void
+CCGraph::RemoveObjectFromMap(void* aObj)
+{
+ PtrToNodeEntry* e = FindNodeEntry(aObj);
+ PtrInfo* pinfo = e ? e->mNode : nullptr;
+ if (pinfo) {
+ mPtrToNodeMap.RemoveEntry(e);
+
+ pinfo->mPointer = nullptr;
+ pinfo->mParticipant = nullptr;
+ }
+}
+
+
+static nsISupports*
+CanonicalizeXPCOMParticipant(nsISupports* aIn)
+{
+ nsISupports* out = nullptr;
+ aIn->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
+ reinterpret_cast<void**>(&out));
+ return out;
+}
+
+static inline void
+ToParticipant(nsISupports* aPtr, nsXPCOMCycleCollectionParticipant** aCp);
+
+static void
+CanonicalizeParticipant(void** aParti, nsCycleCollectionParticipant** aCp)
+{
+ // If the participant is null, this is an nsISupports participant,
+ // so we must QI to get the real participant.
+
+ if (!*aCp) {
+ nsISupports* nsparti = static_cast<nsISupports*>(*aParti);
+ nsparti = CanonicalizeXPCOMParticipant(nsparti);
+ NS_ASSERTION(nsparti,
+ "Don't add objects that don't participate in collection!");
+ nsXPCOMCycleCollectionParticipant* xcp;
+ ToParticipant(nsparti, &xcp);
+ *aParti = nsparti;
+ *aCp = xcp;
+ }
+}
+
+struct nsPurpleBufferEntry
+{
+ union
+ {
+ void* mObject; // when low bit unset
+ nsPurpleBufferEntry* mNextInFreeList; // when low bit set
+ };
+
+ nsCycleCollectingAutoRefCnt* mRefCnt;
+
+ nsCycleCollectionParticipant* mParticipant; // nullptr for nsISupports
+};
+
+class nsCycleCollector;
+
+struct nsPurpleBuffer
+{
+private:
+ struct PurpleBlock
+ {
+ PurpleBlock* mNext;
+ // Try to match the size of a jemalloc bucket, to minimize slop bytes.
+ // - On 32-bit platforms sizeof(nsPurpleBufferEntry) is 12, so mEntries
+ // is 16,380 bytes, which leaves 4 bytes for mNext.
+ // - On 64-bit platforms sizeof(nsPurpleBufferEntry) is 24, so mEntries
+ // is 32,544 bytes, which leaves 8 bytes for mNext.
+ nsPurpleBufferEntry mEntries[1365];
+
+ PurpleBlock() : mNext(nullptr)
+ {
+ // Ensure PurpleBlock is the right size (see above).
+ static_assert(
+ sizeof(PurpleBlock) == 16384 || // 32-bit
+ sizeof(PurpleBlock) == 32768, // 64-bit
+ "ill-sized nsPurpleBuffer::PurpleBlock"
+ );
+
+ InitNextPointers();
+ }
+
+ // Put all the entries in the block on the free list.
+ void InitNextPointers()
+ {
+ for (uint32_t i = 1; i < ArrayLength(mEntries); ++i) {
+ mEntries[i - 1].mNextInFreeList =
+ (nsPurpleBufferEntry*)(uintptr_t(mEntries + i) | 1);
+ }
+ mEntries[ArrayLength(mEntries) - 1].mNextInFreeList =
+ (nsPurpleBufferEntry*)1;
+ }
+
+ template<class PurpleVisitor>
+ void VisitEntries(nsPurpleBuffer& aBuffer, PurpleVisitor& aVisitor)
+ {
+ nsPurpleBufferEntry* eEnd = ArrayEnd(mEntries);
+ for (nsPurpleBufferEntry* e = mEntries; e != eEnd; ++e) {
+ MOZ_ASSERT(e->mObject, "There should be no null mObject when we iterate over the purple buffer");
+ if (!(uintptr_t(e->mObject) & uintptr_t(1)) && e->mObject) {
+ aVisitor.Visit(aBuffer, e);
+ }
+ }
+ }
+ };
+ // This class wraps a linked list of the elements in the purple
+ // buffer.
+
+ uint32_t mCount;
+ PurpleBlock mFirstBlock;
+ nsPurpleBufferEntry* mFreeList;
+
+public:
+ nsPurpleBuffer()
+ {
+ InitBlocks();
+ }
+
+ ~nsPurpleBuffer()
+ {
+ FreeBlocks();
+ }
+
+ template<class PurpleVisitor>
+ void VisitEntries(PurpleVisitor& aVisitor)
+ {
+ for (PurpleBlock* b = &mFirstBlock; b; b = b->mNext) {
+ b->VisitEntries(*this, aVisitor);
+ }
+ }
+
+ void InitBlocks()
+ {
+ mCount = 0;
+ mFreeList = mFirstBlock.mEntries;
+ }
+
+ void FreeBlocks()
+ {
+ if (mCount > 0) {
+ UnmarkRemainingPurple(&mFirstBlock);
+ }
+ PurpleBlock* b = mFirstBlock.mNext;
+ while (b) {
+ if (mCount > 0) {
+ UnmarkRemainingPurple(b);
+ }
+ PurpleBlock* next = b->mNext;
+ delete b;
+ b = next;
+ }
+ mFirstBlock.mNext = nullptr;
+ }
+
+ struct UnmarkRemainingPurpleVisitor
+ {
+ void
+ Visit(nsPurpleBuffer& aBuffer, nsPurpleBufferEntry* aEntry)
+ {
+ if (aEntry->mRefCnt) {
+ aEntry->mRefCnt->RemoveFromPurpleBuffer();
+ aEntry->mRefCnt = nullptr;
+ }
+ aEntry->mObject = nullptr;
+ --aBuffer.mCount;
+ }
+ };
+
+ void UnmarkRemainingPurple(PurpleBlock* aBlock)
+ {
+ UnmarkRemainingPurpleVisitor visitor;
+ aBlock->VisitEntries(*this, visitor);
+ }
+
+ void SelectPointers(CCGraphBuilder& aBuilder);
+
+ // RemoveSkippable removes entries from the purple buffer synchronously
+ // (1) if aAsyncSnowWhiteFreeing is false and nsPurpleBufferEntry::mRefCnt is 0 or
+ // (2) if the object's nsXPCOMCycleCollectionParticipant::CanSkip() returns true or
+ // (3) if nsPurpleBufferEntry::mRefCnt->IsPurple() is false.
+ // (4) If removeChildlessNodes is true, then any nodes in the purple buffer
+ // that will have no children in the cycle collector graph will also be
+ // removed. CanSkip() may be run on these children.
+ void RemoveSkippable(nsCycleCollector* aCollector,
+ bool aRemoveChildlessNodes,
+ bool aAsyncSnowWhiteFreeing,
+ CC_ForgetSkippableCallback aCb);
+
+ MOZ_ALWAYS_INLINE nsPurpleBufferEntry* NewEntry()
+ {
+ if (MOZ_UNLIKELY(!mFreeList)) {
+ PurpleBlock* b = new PurpleBlock;
+ mFreeList = b->mEntries;
+
+ // Add the new block as the second block in the list.
+ b->mNext = mFirstBlock.mNext;
+ mFirstBlock.mNext = b;
+ }
+
+ nsPurpleBufferEntry* e = mFreeList;
+ mFreeList = (nsPurpleBufferEntry*)
+ (uintptr_t(mFreeList->mNextInFreeList) & ~uintptr_t(1));
+ return e;
+ }
+
+ MOZ_ALWAYS_INLINE void Put(void* aObject, nsCycleCollectionParticipant* aCp,
+ nsCycleCollectingAutoRefCnt* aRefCnt)
+ {
+ nsPurpleBufferEntry* e = NewEntry();
+
+ ++mCount;
+
+ e->mObject = aObject;
+ e->mRefCnt = aRefCnt;
+ e->mParticipant = aCp;
+ }
+
+ void Remove(nsPurpleBufferEntry* aEntry)
+ {
+ MOZ_ASSERT(mCount != 0, "must have entries");
+
+ if (aEntry->mRefCnt) {
+ aEntry->mRefCnt->RemoveFromPurpleBuffer();
+ aEntry->mRefCnt = nullptr;
+ }
+ aEntry->mNextInFreeList =
+ (nsPurpleBufferEntry*)(uintptr_t(mFreeList) | uintptr_t(1));
+ mFreeList = aEntry;
+
+ --mCount;
+ }
+
+ uint32_t Count() const
+ {
+ return mCount;
+ }
+
+ size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+ {
+ size_t n = 0;
+
+ // Don't measure mFirstBlock because it's within |this|.
+ const PurpleBlock* block = mFirstBlock.mNext;
+ while (block) {
+ n += aMallocSizeOf(block);
+ block = block->mNext;
+ }
+
+ // mFreeList is deliberately not measured because it points into
+ // the purple buffer, which is within mFirstBlock and thus within |this|.
+ //
+ // We also don't measure the things pointed to by mEntries[] because
+ // those pointers are non-owning.
+
+ return n;
+ }
+};
+
+static bool
+AddPurpleRoot(CCGraphBuilder& aBuilder, void* aRoot,
+ nsCycleCollectionParticipant* aParti);
+
+struct SelectPointersVisitor
+{
+ explicit SelectPointersVisitor(CCGraphBuilder& aBuilder)
+ : mBuilder(aBuilder)
+ {
+ }
+
+ void
+ Visit(nsPurpleBuffer& aBuffer, nsPurpleBufferEntry* aEntry)
+ {
+ MOZ_ASSERT(aEntry->mObject, "Null object in purple buffer");
+ MOZ_ASSERT(aEntry->mRefCnt->get() != 0,
+ "SelectPointersVisitor: snow-white object in the purple buffer");
+ if (!aEntry->mRefCnt->IsPurple() ||
+ AddPurpleRoot(mBuilder, aEntry->mObject, aEntry->mParticipant)) {
+ aBuffer.Remove(aEntry);
+ }
+ }
+
+private:
+ CCGraphBuilder& mBuilder;
+};
+
+void
+nsPurpleBuffer::SelectPointers(CCGraphBuilder& aBuilder)
+{
+ SelectPointersVisitor visitor(aBuilder);
+ VisitEntries(visitor);
+
+ NS_ASSERTION(mCount == 0, "AddPurpleRoot failed");
+ if (mCount == 0) {
+ FreeBlocks();
+ InitBlocks();
+ mFirstBlock.InitNextPointers();
+ }
+}
+
+enum ccPhase
+{
+ IdlePhase,
+ GraphBuildingPhase,
+ ScanAndCollectWhitePhase,
+ CleanupPhase
+};
+
+enum ccType
+{
+ SliceCC, /* If a CC is in progress, continue it. Otherwise, start a new one. */
+ ManualCC, /* Explicitly triggered. */
+ ShutdownCC /* Shutdown CC, used for finding leaks. */
+};
+
+////////////////////////////////////////////////////////////////////////
+// Top level structure for the cycle collector.
+////////////////////////////////////////////////////////////////////////
+
+using js::SliceBudget;
+
+class JSPurpleBuffer;
+
+class nsCycleCollector : public nsIMemoryReporter
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMEMORYREPORTER
+
+private:
+ bool mActivelyCollecting;
+ bool mFreeingSnowWhite;
+ // mScanInProgress should be false when we're collecting white objects.
+ bool mScanInProgress;
+ CycleCollectorResults mResults;
+ TimeStamp mCollectionStart;
+
+ CycleCollectedJSContext* mJSContext;
+
+ ccPhase mIncrementalPhase;
+ CCGraph mGraph;
+ nsAutoPtr<CCGraphBuilder> mBuilder;
+ RefPtr<nsCycleCollectorLogger> mLogger;
+
+#ifdef DEBUG
+ void* mThread;
+#endif
+
+ nsCycleCollectorParams mParams;
+
+ uint32_t mWhiteNodeCount;
+
+ CC_BeforeUnlinkCallback mBeforeUnlinkCB;
+ CC_ForgetSkippableCallback mForgetSkippableCB;
+
+ nsPurpleBuffer mPurpleBuf;
+
+ uint32_t mUnmergedNeeded;
+ uint32_t mMergedInARow;
+
+ RefPtr<JSPurpleBuffer> mJSPurpleBuffer;
+
+private:
+ virtual ~nsCycleCollector();
+
+public:
+ nsCycleCollector();
+
+ void RegisterJSContext(CycleCollectedJSContext* aJSContext);
+ void ForgetJSContext();
+
+ void SetBeforeUnlinkCallback(CC_BeforeUnlinkCallback aBeforeUnlinkCB)
+ {
+ CheckThreadSafety();
+ mBeforeUnlinkCB = aBeforeUnlinkCB;
+ }
+
+ void SetForgetSkippableCallback(CC_ForgetSkippableCallback aForgetSkippableCB)
+ {
+ CheckThreadSafety();
+ mForgetSkippableCB = aForgetSkippableCB;
+ }
+
+ void Suspect(void* aPtr, nsCycleCollectionParticipant* aCp,
+ nsCycleCollectingAutoRefCnt* aRefCnt);
+ uint32_t SuspectedCount();
+ void ForgetSkippable(bool aRemoveChildlessNodes, bool aAsyncSnowWhiteFreeing);
+ bool FreeSnowWhite(bool aUntilNoSWInPurpleBuffer);
+
+ // This method assumes its argument is already canonicalized.
+ void RemoveObjectFromGraph(void* aPtr);
+
+ void PrepareForGarbageCollection();
+ void FinishAnyCurrentCollection();
+
+ bool Collect(ccType aCCType,
+ SliceBudget& aBudget,
+ nsICycleCollectorListener* aManualListener,
+ bool aPreferShorterSlices = false);
+ void Shutdown(bool aDoCollect);
+
+ bool IsIdle() const { return mIncrementalPhase == IdlePhase; }
+
+ void SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ size_t* aObjectSize,
+ size_t* aGraphSize,
+ size_t* aPurpleBufferSize) const;
+
+ JSPurpleBuffer* GetJSPurpleBuffer();
+
+ CycleCollectedJSContext* Context() { return mJSContext; }
+
+private:
+ void CheckThreadSafety();
+ void ShutdownCollect();
+
+ void FixGrayBits(bool aForceGC, TimeLog& aTimeLog);
+ bool IsIncrementalGCInProgress();
+ void FinishAnyIncrementalGCInProgress();
+ bool ShouldMergeZones(ccType aCCType);
+
+ void BeginCollection(ccType aCCType, nsICycleCollectorListener* aManualListener);
+ void MarkRoots(SliceBudget& aBudget);
+ void ScanRoots(bool aFullySynchGraphBuild);
+ void ScanIncrementalRoots();
+ void ScanWhiteNodes(bool aFullySynchGraphBuild);
+ void ScanBlackNodes();
+ void ScanWeakMaps();
+
+ // returns whether anything was collected
+ bool CollectWhite();
+
+ void CleanupAfterCollection();
+};
+
+NS_IMPL_ISUPPORTS(nsCycleCollector, nsIMemoryReporter)
+
+/**
+ * GraphWalker is templatized over a Visitor class that must provide
+ * the following two methods:
+ *
+ * bool ShouldVisitNode(PtrInfo const *pi);
+ * void VisitNode(PtrInfo *pi);
+ */
+template<class Visitor>
+class GraphWalker
+{
+private:
+ Visitor mVisitor;
+
+ void DoWalk(nsDeque& aQueue);
+
+ void CheckedPush(nsDeque& aQueue, PtrInfo* aPi)
+ {
+ if (!aPi) {
+ MOZ_CRASH();
+ }
+ if (!aQueue.Push(aPi, fallible)) {
+ mVisitor.Failed();
+ }
+ }
+
+public:
+ void Walk(PtrInfo* aPi);
+ void WalkFromRoots(CCGraph& aGraph);
+ // copy-constructing the visitor should be cheap, and less
+ // indirection than using a reference
+ explicit GraphWalker(const Visitor aVisitor) : mVisitor(aVisitor)
+ {
+ }
+};
+
+
+////////////////////////////////////////////////////////////////////////
+// The static collector struct
+////////////////////////////////////////////////////////////////////////
+
+struct CollectorData
+{
+ RefPtr<nsCycleCollector> mCollector;
+ CycleCollectedJSContext* mContext;
+};
+
+static MOZ_THREAD_LOCAL(CollectorData*) sCollectorData;
+
+////////////////////////////////////////////////////////////////////////
+// Utility functions
+////////////////////////////////////////////////////////////////////////
+
+static inline void
+ToParticipant(nsISupports* aPtr, nsXPCOMCycleCollectionParticipant** aCp)
+{
+ // We use QI to move from an nsISupports to an
+ // nsXPCOMCycleCollectionParticipant, which is a per-class singleton helper
+ // object that implements traversal and unlinking logic for the nsISupports
+ // in question.
+ *aCp = nullptr;
+ CallQueryInterface(aPtr, aCp);
+}
+
+template<class Visitor>
+MOZ_NEVER_INLINE void
+GraphWalker<Visitor>::Walk(PtrInfo* aPi)
+{
+ nsDeque queue;
+ CheckedPush(queue, aPi);
+ DoWalk(queue);
+}
+
+template<class Visitor>
+MOZ_NEVER_INLINE void
+GraphWalker<Visitor>::WalkFromRoots(CCGraph& aGraph)
+{
+ nsDeque queue;
+ NodePool::Enumerator etor(aGraph.mNodes);
+ for (uint32_t i = 0; i < aGraph.mRootCount; ++i) {
+ CheckedPush(queue, etor.GetNext());
+ }
+ DoWalk(queue);
+}
+
+template<class Visitor>
+MOZ_NEVER_INLINE void
+GraphWalker<Visitor>::DoWalk(nsDeque& aQueue)
+{
+ // Use a aQueue to match the breadth-first traversal used when we
+ // built the graph, for hopefully-better locality.
+ while (aQueue.GetSize() > 0) {
+ PtrInfo* pi = static_cast<PtrInfo*>(aQueue.PopFront());
+
+ if (pi->WasTraversed() && mVisitor.ShouldVisitNode(pi)) {
+ mVisitor.VisitNode(pi);
+ for (EdgePool::Iterator child = pi->FirstChild(),
+ child_end = pi->LastChild();
+ child != child_end; ++child) {
+ CheckedPush(aQueue, *child);
+ }
+ }
+ }
+}
+
+struct CCGraphDescriber : public LinkedListElement<CCGraphDescriber>
+{
+ CCGraphDescriber()
+ : mAddress("0x"), mCnt(0), mType(eUnknown)
+ {
+ }
+
+ enum Type
+ {
+ eRefCountedObject,
+ eGCedObject,
+ eGCMarkedObject,
+ eEdge,
+ eRoot,
+ eGarbage,
+ eUnknown
+ };
+
+ nsCString mAddress;
+ nsCString mName;
+ nsCString mCompartmentOrToAddress;
+ uint32_t mCnt;
+ Type mType;
+};
+
+class LogStringMessageAsync : public CancelableRunnable
+{
+public:
+ explicit LogStringMessageAsync(const nsAString& aMsg) : mMsg(aMsg)
+ {}
+
+ NS_IMETHOD Run() override
+ {
+ nsCOMPtr<nsIConsoleService> cs =
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+ if (cs) {
+ cs->LogStringMessage(mMsg.get());
+ }
+ return NS_OK;
+ }
+
+private:
+ nsString mMsg;
+};
+
+class nsCycleCollectorLogSinkToFile final : public nsICycleCollectorLogSink
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ nsCycleCollectorLogSinkToFile() :
+ mProcessIdentifier(base::GetCurrentProcId()),
+ mGCLog("gc-edges"), mCCLog("cc-edges")
+ {
+ }
+
+ NS_IMETHOD GetFilenameIdentifier(nsAString& aIdentifier) override
+ {
+ aIdentifier = mFilenameIdentifier;
+ return NS_OK;
+ }
+
+ NS_IMETHOD SetFilenameIdentifier(const nsAString& aIdentifier) override
+ {
+ mFilenameIdentifier = aIdentifier;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetProcessIdentifier(int32_t* aIdentifier) override
+ {
+ *aIdentifier = mProcessIdentifier;
+ return NS_OK;
+ }
+
+ NS_IMETHOD SetProcessIdentifier(int32_t aIdentifier) override
+ {
+ mProcessIdentifier = aIdentifier;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetGcLog(nsIFile** aPath) override
+ {
+ NS_IF_ADDREF(*aPath = mGCLog.mFile);
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetCcLog(nsIFile** aPath) override
+ {
+ NS_IF_ADDREF(*aPath = mCCLog.mFile);
+ return NS_OK;
+ }
+
+ NS_IMETHOD Open(FILE** aGCLog, FILE** aCCLog) override
+ {
+ nsresult rv;
+
+ if (mGCLog.mStream || mCCLog.mStream) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ rv = OpenLog(&mGCLog);
+ NS_ENSURE_SUCCESS(rv, rv);
+ *aGCLog = mGCLog.mStream;
+
+ rv = OpenLog(&mCCLog);
+ NS_ENSURE_SUCCESS(rv, rv);
+ *aCCLog = mCCLog.mStream;
+
+ return NS_OK;
+ }
+
+ NS_IMETHOD CloseGCLog() override
+ {
+ if (!mGCLog.mStream) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ CloseLog(&mGCLog, NS_LITERAL_STRING("Garbage"));
+ return NS_OK;
+ }
+
+ NS_IMETHOD CloseCCLog() override
+ {
+ if (!mCCLog.mStream) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ CloseLog(&mCCLog, NS_LITERAL_STRING("Cycle"));
+ return NS_OK;
+ }
+
+private:
+ ~nsCycleCollectorLogSinkToFile()
+ {
+ if (mGCLog.mStream) {
+ MozillaUnRegisterDebugFILE(mGCLog.mStream);
+ fclose(mGCLog.mStream);
+ }
+ if (mCCLog.mStream) {
+ MozillaUnRegisterDebugFILE(mCCLog.mStream);
+ fclose(mCCLog.mStream);
+ }
+ }
+
+ struct FileInfo
+ {
+ const char* const mPrefix;
+ nsCOMPtr<nsIFile> mFile;
+ FILE* mStream;
+
+ explicit FileInfo(const char* aPrefix) : mPrefix(aPrefix), mStream(nullptr) { }
+ };
+
+ /**
+ * Create a new file named something like aPrefix.$PID.$IDENTIFIER.log in
+ * $MOZ_CC_LOG_DIRECTORY or in the system's temp directory. No existing
+ * file will be overwritten; if aPrefix.$PID.$IDENTIFIER.log exists, we'll
+ * try a file named something like aPrefix.$PID.$IDENTIFIER-1.log, and so
+ * on.
+ */
+ already_AddRefed<nsIFile> CreateTempFile(const char* aPrefix)
+ {
+ nsPrintfCString filename("%s.%d%s%s.log",
+ aPrefix,
+ mProcessIdentifier,
+ mFilenameIdentifier.IsEmpty() ? "" : ".",
+ NS_ConvertUTF16toUTF8(mFilenameIdentifier).get());
+
+ // Get the log directory either from $MOZ_CC_LOG_DIRECTORY or from
+ // the fallback directories in OpenTempFile. We don't use an nsCOMPtr
+ // here because OpenTempFile uses an in/out param and getter_AddRefs
+ // wouldn't work.
+ nsIFile* logFile = nullptr;
+ if (char* env = PR_GetEnv("MOZ_CC_LOG_DIRECTORY")) {
+ NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true,
+ &logFile);
+ }
+
+ // On Android or B2G, this function will open a file named
+ // aFilename under a memory-reporting-specific folder
+ // (/data/local/tmp/memory-reports). Otherwise, it will open a
+ // file named aFilename under "NS_OS_TEMP_DIR".
+ nsresult rv = nsDumpUtils::OpenTempFile(filename, &logFile,
+ NS_LITERAL_CSTRING("memory-reports"));
+ if (NS_FAILED(rv)) {
+ NS_IF_RELEASE(logFile);
+ return nullptr;
+ }
+
+ return dont_AddRef(logFile);
+ }
+
+ nsresult OpenLog(FileInfo* aLog)
+ {
+ // Initially create the log in a file starting with "incomplete-".
+ // We'll move the file and strip off the "incomplete-" once the dump
+ // completes. (We do this because we don't want scripts which poll
+ // the filesystem looking for GC/CC dumps to grab a file before we're
+ // finished writing to it.)
+ nsAutoCString incomplete;
+ incomplete += "incomplete-";
+ incomplete += aLog->mPrefix;
+ MOZ_ASSERT(!aLog->mFile);
+ aLog->mFile = CreateTempFile(incomplete.get());
+ if (NS_WARN_IF(!aLog->mFile)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ MOZ_ASSERT(!aLog->mStream);
+ nsresult rv = aLog->mFile->OpenANSIFileDesc("w", &aLog->mStream);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ MozillaRegisterDebugFILE(aLog->mStream);
+ return NS_OK;
+ }
+
+ nsresult CloseLog(FileInfo* aLog, const nsAString& aCollectorKind)
+ {
+ MOZ_ASSERT(aLog->mStream);
+ MOZ_ASSERT(aLog->mFile);
+
+ MozillaUnRegisterDebugFILE(aLog->mStream);
+ fclose(aLog->mStream);
+ aLog->mStream = nullptr;
+
+ // Strip off "incomplete-".
+ nsCOMPtr<nsIFile> logFileFinalDestination =
+ CreateTempFile(aLog->mPrefix);
+ if (NS_WARN_IF(!logFileFinalDestination)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsAutoString logFileFinalDestinationName;
+ logFileFinalDestination->GetLeafName(logFileFinalDestinationName);
+ if (NS_WARN_IF(logFileFinalDestinationName.IsEmpty())) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ aLog->mFile->MoveTo(/* directory */ nullptr, logFileFinalDestinationName);
+
+ // Save the file path.
+ aLog->mFile = logFileFinalDestination;
+
+ // Log to the error console.
+ nsAutoString logPath;
+ logFileFinalDestination->GetPath(logPath);
+ nsAutoString msg = aCollectorKind +
+ NS_LITERAL_STRING(" Collector log dumped to ") + logPath;
+
+ // We don't want any JS to run between ScanRoots and CollectWhite calls,
+ // and since ScanRoots calls this method, better to log the message
+ // asynchronously.
+ RefPtr<LogStringMessageAsync> log = new LogStringMessageAsync(msg);
+ NS_DispatchToCurrentThread(log);
+ return NS_OK;
+ }
+
+ int32_t mProcessIdentifier;
+ nsString mFilenameIdentifier;
+ FileInfo mGCLog;
+ FileInfo mCCLog;
+};
+
+NS_IMPL_ISUPPORTS(nsCycleCollectorLogSinkToFile, nsICycleCollectorLogSink)
+
+
+class nsCycleCollectorLogger final : public nsICycleCollectorListener
+{
+ ~nsCycleCollectorLogger()
+ {
+ ClearDescribers();
+ }
+
+public:
+ nsCycleCollectorLogger()
+ : mLogSink(nsCycleCollector_createLogSink())
+ , mWantAllTraces(false)
+ , mDisableLog(false)
+ , mWantAfterProcessing(false)
+ , mCCLog(nullptr)
+ {
+ }
+
+ NS_DECL_ISUPPORTS
+
+ void SetAllTraces()
+ {
+ mWantAllTraces = true;
+ }
+
+ bool IsAllTraces()
+ {
+ return mWantAllTraces;
+ }
+
+ NS_IMETHOD AllTraces(nsICycleCollectorListener** aListener) override
+ {
+ SetAllTraces();
+ NS_ADDREF(*aListener = this);
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetWantAllTraces(bool* aAllTraces) override
+ {
+ *aAllTraces = mWantAllTraces;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetDisableLog(bool* aDisableLog) override
+ {
+ *aDisableLog = mDisableLog;
+ return NS_OK;
+ }
+
+ NS_IMETHOD SetDisableLog(bool aDisableLog) override
+ {
+ mDisableLog = aDisableLog;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetWantAfterProcessing(bool* aWantAfterProcessing) override
+ {
+ *aWantAfterProcessing = mWantAfterProcessing;
+ return NS_OK;
+ }
+
+ NS_IMETHOD SetWantAfterProcessing(bool aWantAfterProcessing) override
+ {
+ mWantAfterProcessing = aWantAfterProcessing;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetLogSink(nsICycleCollectorLogSink** aLogSink) override
+ {
+ NS_ADDREF(*aLogSink = mLogSink);
+ return NS_OK;
+ }
+
+ NS_IMETHOD SetLogSink(nsICycleCollectorLogSink* aLogSink) override
+ {
+ if (!aLogSink) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ mLogSink = aLogSink;
+ return NS_OK;
+ }
+
+ nsresult Begin()
+ {
+ nsresult rv;
+
+ mCurrentAddress.AssignLiteral("0x");
+ ClearDescribers();
+ if (mDisableLog) {
+ return NS_OK;
+ }
+
+ FILE* gcLog;
+ rv = mLogSink->Open(&gcLog, &mCCLog);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // Dump the JS heap.
+ CollectorData* data = sCollectorData.get();
+ if (data && data->mContext) {
+ data->mContext->DumpJSHeap(gcLog);
+ }
+ rv = mLogSink->CloseGCLog();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ fprintf(mCCLog, "# WantAllTraces=%s\n", mWantAllTraces ? "true" : "false");
+ return NS_OK;
+ }
+ void NoteRefCountedObject(uint64_t aAddress, uint32_t aRefCount,
+ const char* aObjectDescription)
+ {
+ if (!mDisableLog) {
+ fprintf(mCCLog, "%p [rc=%u] %s\n", (void*)aAddress, aRefCount,
+ aObjectDescription);
+ }
+ if (mWantAfterProcessing) {
+ CCGraphDescriber* d = new CCGraphDescriber();
+ mDescribers.insertBack(d);
+ mCurrentAddress.AssignLiteral("0x");
+ mCurrentAddress.AppendInt(aAddress, 16);
+ d->mType = CCGraphDescriber::eRefCountedObject;
+ d->mAddress = mCurrentAddress;
+ d->mCnt = aRefCount;
+ d->mName.Append(aObjectDescription);
+ }
+ }
+ void NoteGCedObject(uint64_t aAddress, bool aMarked,
+ const char* aObjectDescription,
+ uint64_t aCompartmentAddress)
+ {
+ if (!mDisableLog) {
+ fprintf(mCCLog, "%p [gc%s] %s\n", (void*)aAddress,
+ aMarked ? ".marked" : "", aObjectDescription);
+ }
+ if (mWantAfterProcessing) {
+ CCGraphDescriber* d = new CCGraphDescriber();
+ mDescribers.insertBack(d);
+ mCurrentAddress.AssignLiteral("0x");
+ mCurrentAddress.AppendInt(aAddress, 16);
+ d->mType = aMarked ? CCGraphDescriber::eGCMarkedObject :
+ CCGraphDescriber::eGCedObject;
+ d->mAddress = mCurrentAddress;
+ d->mName.Append(aObjectDescription);
+ if (aCompartmentAddress) {
+ d->mCompartmentOrToAddress.AssignLiteral("0x");
+ d->mCompartmentOrToAddress.AppendInt(aCompartmentAddress, 16);
+ } else {
+ d->mCompartmentOrToAddress.SetIsVoid(true);
+ }
+ }
+ }
+ void NoteEdge(uint64_t aToAddress, const char* aEdgeName)
+ {
+ if (!mDisableLog) {
+ fprintf(mCCLog, "> %p %s\n", (void*)aToAddress, aEdgeName);
+ }
+ if (mWantAfterProcessing) {
+ CCGraphDescriber* d = new CCGraphDescriber();
+ mDescribers.insertBack(d);
+ d->mType = CCGraphDescriber::eEdge;
+ d->mAddress = mCurrentAddress;
+ d->mCompartmentOrToAddress.AssignLiteral("0x");
+ d->mCompartmentOrToAddress.AppendInt(aToAddress, 16);
+ d->mName.Append(aEdgeName);
+ }
+ }
+ void NoteWeakMapEntry(uint64_t aMap, uint64_t aKey,
+ uint64_t aKeyDelegate, uint64_t aValue)
+ {
+ if (!mDisableLog) {
+ fprintf(mCCLog, "WeakMapEntry map=%p key=%p keyDelegate=%p value=%p\n",
+ (void*)aMap, (void*)aKey, (void*)aKeyDelegate, (void*)aValue);
+ }
+ // We don't support after-processing for weak map entries.
+ }
+ void NoteIncrementalRoot(uint64_t aAddress)
+ {
+ if (!mDisableLog) {
+ fprintf(mCCLog, "IncrementalRoot %p\n", (void*)aAddress);
+ }
+ // We don't support after-processing for incremental roots.
+ }
+ void BeginResults()
+ {
+ if (!mDisableLog) {
+ fputs("==========\n", mCCLog);
+ }
+ }
+ void DescribeRoot(uint64_t aAddress, uint32_t aKnownEdges)
+ {
+ if (!mDisableLog) {
+ fprintf(mCCLog, "%p [known=%u]\n", (void*)aAddress, aKnownEdges);
+ }
+ if (mWantAfterProcessing) {
+ CCGraphDescriber* d = new CCGraphDescriber();
+ mDescribers.insertBack(d);
+ d->mType = CCGraphDescriber::eRoot;
+ d->mAddress.AppendInt(aAddress, 16);
+ d->mCnt = aKnownEdges;
+ }
+ }
+ void DescribeGarbage(uint64_t aAddress)
+ {
+ if (!mDisableLog) {
+ fprintf(mCCLog, "%p [garbage]\n", (void*)aAddress);
+ }
+ if (mWantAfterProcessing) {
+ CCGraphDescriber* d = new CCGraphDescriber();
+ mDescribers.insertBack(d);
+ d->mType = CCGraphDescriber::eGarbage;
+ d->mAddress.AppendInt(aAddress, 16);
+ }
+ }
+ void End()
+ {
+ if (!mDisableLog) {
+ mCCLog = nullptr;
+ Unused << NS_WARN_IF(NS_FAILED(mLogSink->CloseCCLog()));
+ }
+ }
+ NS_IMETHOD ProcessNext(nsICycleCollectorHandler* aHandler,
+ bool* aCanContinue) override
+ {
+ if (NS_WARN_IF(!aHandler) || NS_WARN_IF(!mWantAfterProcessing)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ CCGraphDescriber* d = mDescribers.popFirst();
+ if (d) {
+ switch (d->mType) {
+ case CCGraphDescriber::eRefCountedObject:
+ aHandler->NoteRefCountedObject(d->mAddress,
+ d->mCnt,
+ d->mName);
+ break;
+ case CCGraphDescriber::eGCedObject:
+ case CCGraphDescriber::eGCMarkedObject:
+ aHandler->NoteGCedObject(d->mAddress,
+ d->mType ==
+ CCGraphDescriber::eGCMarkedObject,
+ d->mName,
+ d->mCompartmentOrToAddress);
+ break;
+ case CCGraphDescriber::eEdge:
+ aHandler->NoteEdge(d->mAddress,
+ d->mCompartmentOrToAddress,
+ d->mName);
+ break;
+ case CCGraphDescriber::eRoot:
+ aHandler->DescribeRoot(d->mAddress,
+ d->mCnt);
+ break;
+ case CCGraphDescriber::eGarbage:
+ aHandler->DescribeGarbage(d->mAddress);
+ break;
+ case CCGraphDescriber::eUnknown:
+ NS_NOTREACHED("CCGraphDescriber::eUnknown");
+ break;
+ }
+ delete d;
+ }
+ if (!(*aCanContinue = !mDescribers.isEmpty())) {
+ mCurrentAddress.AssignLiteral("0x");
+ }
+ return NS_OK;
+ }
+ NS_IMETHOD AsLogger(nsCycleCollectorLogger** aRetVal) override
+ {
+ RefPtr<nsCycleCollectorLogger> rval = this;
+ rval.forget(aRetVal);
+ return NS_OK;
+ }
+private:
+ void ClearDescribers()
+ {
+ CCGraphDescriber* d;
+ while ((d = mDescribers.popFirst())) {
+ delete d;
+ }
+ }
+
+ nsCOMPtr<nsICycleCollectorLogSink> mLogSink;
+ bool mWantAllTraces;
+ bool mDisableLog;
+ bool mWantAfterProcessing;
+ nsCString mCurrentAddress;
+ mozilla::LinkedList<CCGraphDescriber> mDescribers;
+ FILE* mCCLog;
+};
+
+NS_IMPL_ISUPPORTS(nsCycleCollectorLogger, nsICycleCollectorListener)
+
+nsresult
+nsCycleCollectorLoggerConstructor(nsISupports* aOuter,
+ const nsIID& aIID,
+ void** aInstancePtr)
+{
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ nsISupports* logger = new nsCycleCollectorLogger();
+
+ return logger->QueryInterface(aIID, aInstancePtr);
+}
+
+static bool
+GCThingIsGrayCCThing(JS::GCCellPtr thing)
+{
+ return AddToCCKind(thing.kind()) &&
+ JS::GCThingIsMarkedGray(thing);
+}
+
+static bool
+ValueIsGrayCCThing(const JS::Value& value)
+{
+ return AddToCCKind(value.traceKind()) &&
+ JS::GCThingIsMarkedGray(value.toGCCellPtr());
+}
+
+////////////////////////////////////////////////////////////////////////
+// Bacon & Rajan's |MarkRoots| routine.
+////////////////////////////////////////////////////////////////////////
+
+class CCGraphBuilder final : public nsCycleCollectionTraversalCallback,
+ public nsCycleCollectionNoteRootCallback
+{
+private:
+ CCGraph& mGraph;
+ CycleCollectorResults& mResults;
+ NodePool::Builder mNodeBuilder;
+ EdgePool::Builder mEdgeBuilder;
+ MOZ_INIT_OUTSIDE_CTOR PtrInfo* mCurrPi;
+ nsCycleCollectionParticipant* mJSParticipant;
+ nsCycleCollectionParticipant* mJSZoneParticipant;
+ nsCString mNextEdgeName;
+ RefPtr<nsCycleCollectorLogger> mLogger;
+ bool mMergeZones;
+ nsAutoPtr<NodePool::Enumerator> mCurrNode;
+
+public:
+ CCGraphBuilder(CCGraph& aGraph,
+ CycleCollectorResults& aResults,
+ CycleCollectedJSContext* aJSContext,
+ nsCycleCollectorLogger* aLogger,
+ bool aMergeZones);
+ virtual ~CCGraphBuilder();
+
+ bool WantAllTraces() const
+ {
+ return nsCycleCollectionNoteRootCallback::WantAllTraces();
+ }
+
+ bool AddPurpleRoot(void* aRoot, nsCycleCollectionParticipant* aParti);
+
+ // This is called when all roots have been added to the graph, to prepare for BuildGraph().
+ void DoneAddingRoots();
+
+ // Do some work traversing nodes in the graph. Returns true if this graph building is finished.
+ bool BuildGraph(SliceBudget& aBudget);
+
+private:
+ PtrInfo* AddNode(void* aPtr, nsCycleCollectionParticipant* aParticipant);
+ PtrInfo* AddWeakMapNode(JS::GCCellPtr aThing);
+ PtrInfo* AddWeakMapNode(JSObject* aObject);
+
+ void SetFirstChild()
+ {
+ mCurrPi->SetFirstChild(mEdgeBuilder.Mark());
+ }
+
+ void SetLastChild()
+ {
+ mCurrPi->SetLastChild(mEdgeBuilder.Mark());
+ }
+
+public:
+ // nsCycleCollectionNoteRootCallback methods.
+ NS_IMETHOD_(void) NoteXPCOMRoot(nsISupports* aRoot);
+ NS_IMETHOD_(void) NoteJSRoot(JSObject* aRoot);
+ NS_IMETHOD_(void) NoteNativeRoot(void* aRoot,
+ nsCycleCollectionParticipant* aParticipant);
+ NS_IMETHOD_(void) NoteWeakMapping(JSObject* aMap, JS::GCCellPtr aKey,
+ JSObject* aKdelegate, JS::GCCellPtr aVal);
+
+ // nsCycleCollectionTraversalCallback methods.
+ NS_IMETHOD_(void) DescribeRefCountedNode(nsrefcnt aRefCount,
+ const char* aObjName);
+ NS_IMETHOD_(void) DescribeGCedNode(bool aIsMarked, const char* aObjName,
+ uint64_t aCompartmentAddress);
+
+ NS_IMETHOD_(void) NoteXPCOMChild(nsISupports* aChild);
+ NS_IMETHOD_(void) NoteJSChild(const JS::GCCellPtr& aThing);
+ NS_IMETHOD_(void) NoteNativeChild(void* aChild,
+ nsCycleCollectionParticipant* aParticipant);
+ NS_IMETHOD_(void) NoteNextEdgeName(const char* aName);
+
+private:
+ void NoteJSChild(JS::GCCellPtr aChild);
+
+ NS_IMETHOD_(void) NoteRoot(void* aRoot,
+ nsCycleCollectionParticipant* aParticipant)
+ {
+ MOZ_ASSERT(aRoot);
+ MOZ_ASSERT(aParticipant);
+
+ if (!aParticipant->CanSkipInCC(aRoot) || MOZ_UNLIKELY(WantAllTraces())) {
+ AddNode(aRoot, aParticipant);
+ }
+ }
+
+ NS_IMETHOD_(void) NoteChild(void* aChild, nsCycleCollectionParticipant* aCp,
+ nsCString& aEdgeName)
+ {
+ PtrInfo* childPi = AddNode(aChild, aCp);
+ if (!childPi) {
+ return;
+ }
+ mEdgeBuilder.Add(childPi);
+ if (mLogger) {
+ mLogger->NoteEdge((uint64_t)aChild, aEdgeName.get());
+ }
+ ++childPi->mInternalRefs;
+ }
+
+ JS::Zone* MergeZone(JS::GCCellPtr aGcthing)
+ {
+ if (!mMergeZones) {
+ return nullptr;
+ }
+ JS::Zone* zone = JS::GetTenuredGCThingZone(aGcthing);
+ if (js::IsSystemZone(zone)) {
+ return nullptr;
+ }
+ return zone;
+ }
+};
+
+CCGraphBuilder::CCGraphBuilder(CCGraph& aGraph,
+ CycleCollectorResults& aResults,
+ CycleCollectedJSContext* aJSContext,
+ nsCycleCollectorLogger* aLogger,
+ bool aMergeZones)
+ : mGraph(aGraph)
+ , mResults(aResults)
+ , mNodeBuilder(aGraph.mNodes)
+ , mEdgeBuilder(aGraph.mEdges)
+ , mJSParticipant(nullptr)
+ , mJSZoneParticipant(nullptr)
+ , mLogger(aLogger)
+ , mMergeZones(aMergeZones)
+{
+ if (aJSContext) {
+ mJSParticipant = aJSContext->GCThingParticipant();
+ mJSZoneParticipant = aJSContext->ZoneParticipant();
+ }
+
+ if (mLogger) {
+ mFlags |= nsCycleCollectionTraversalCallback::WANT_DEBUG_INFO;
+ if (mLogger->IsAllTraces()) {
+ mFlags |= nsCycleCollectionTraversalCallback::WANT_ALL_TRACES;
+ mWantAllTraces = true; // for nsCycleCollectionNoteRootCallback
+ }
+ }
+
+ mMergeZones = mMergeZones && MOZ_LIKELY(!WantAllTraces());
+
+ MOZ_ASSERT(nsCycleCollectionNoteRootCallback::WantAllTraces() ==
+ nsCycleCollectionTraversalCallback::WantAllTraces());
+}
+
+CCGraphBuilder::~CCGraphBuilder()
+{
+}
+
+PtrInfo*
+CCGraphBuilder::AddNode(void* aPtr, nsCycleCollectionParticipant* aParticipant)
+{
+ PtrToNodeEntry* e = mGraph.AddNodeToMap(aPtr);
+ if (!e) {
+ return nullptr;
+ }
+
+ PtrInfo* result;
+ if (!e->mNode) {
+ // New entry.
+ result = mNodeBuilder.Add(aPtr, aParticipant);
+ if (!result) {
+ return nullptr;
+ }
+
+ e->mNode = result;
+ NS_ASSERTION(result, "mNodeBuilder.Add returned null");
+ } else {
+ result = e->mNode;
+ MOZ_ASSERT(result->mParticipant == aParticipant,
+ "nsCycleCollectionParticipant shouldn't change!");
+ }
+ return result;
+}
+
+bool
+CCGraphBuilder::AddPurpleRoot(void* aRoot, nsCycleCollectionParticipant* aParti)
+{
+ CanonicalizeParticipant(&aRoot, &aParti);
+
+ if (WantAllTraces() || !aParti->CanSkipInCC(aRoot)) {
+ PtrInfo* pinfo = AddNode(aRoot, aParti);
+ if (!pinfo) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void
+CCGraphBuilder::DoneAddingRoots()
+{
+ // We've finished adding roots, and everything in the graph is a root.
+ mGraph.mRootCount = mGraph.MapCount();
+
+ mCurrNode = new NodePool::Enumerator(mGraph.mNodes);
+}
+
+MOZ_NEVER_INLINE bool
+CCGraphBuilder::BuildGraph(SliceBudget& aBudget)
+{
+ const intptr_t kNumNodesBetweenTimeChecks = 1000;
+ const intptr_t kStep = SliceBudget::CounterReset / kNumNodesBetweenTimeChecks;
+
+ MOZ_ASSERT(mCurrNode);
+
+ while (!aBudget.isOverBudget() && !mCurrNode->IsDone()) {
+ PtrInfo* pi = mCurrNode->GetNext();
+ if (!pi) {
+ MOZ_CRASH();
+ }
+
+ mCurrPi = pi;
+
+ // We need to call SetFirstChild() even on deleted nodes, to set their
+ // firstChild() that may be read by a prior non-deleted neighbor.
+ SetFirstChild();
+
+ if (pi->mParticipant) {
+ nsresult rv = pi->mParticipant->Traverse(pi->mPointer, *this);
+ MOZ_RELEASE_ASSERT(!NS_FAILED(rv), "Cycle collector Traverse method failed");
+ }
+
+ if (mCurrNode->AtBlockEnd()) {
+ SetLastChild();
+ }
+
+ aBudget.step(kStep);
+ }
+
+ if (!mCurrNode->IsDone()) {
+ return false;
+ }
+
+ if (mGraph.mRootCount > 0) {
+ SetLastChild();
+ }
+
+ mCurrNode = nullptr;
+
+ return true;
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::NoteXPCOMRoot(nsISupports* aRoot)
+{
+ aRoot = CanonicalizeXPCOMParticipant(aRoot);
+ NS_ASSERTION(aRoot,
+ "Don't add objects that don't participate in collection!");
+
+ nsXPCOMCycleCollectionParticipant* cp;
+ ToParticipant(aRoot, &cp);
+
+ NoteRoot(aRoot, cp);
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::NoteJSRoot(JSObject* aRoot)
+{
+ if (JS::Zone* zone = MergeZone(JS::GCCellPtr(aRoot))) {
+ NoteRoot(zone, mJSZoneParticipant);
+ } else {
+ NoteRoot(aRoot, mJSParticipant);
+ }
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::NoteNativeRoot(void* aRoot,
+ nsCycleCollectionParticipant* aParticipant)
+{
+ NoteRoot(aRoot, aParticipant);
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::DescribeRefCountedNode(nsrefcnt aRefCount, const char* aObjName)
+{
+ MOZ_RELEASE_ASSERT(aRefCount != 0, "CCed refcounted object has zero refcount");
+ MOZ_RELEASE_ASSERT(aRefCount != UINT32_MAX, "CCed refcounted object has overflowing refcount");
+
+ mResults.mVisitedRefCounted++;
+
+ if (mLogger) {
+ mLogger->NoteRefCountedObject((uint64_t)mCurrPi->mPointer, aRefCount,
+ aObjName);
+ }
+
+ mCurrPi->mRefCount = aRefCount;
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::DescribeGCedNode(bool aIsMarked, const char* aObjName,
+ uint64_t aCompartmentAddress)
+{
+ uint32_t refCount = aIsMarked ? UINT32_MAX : 0;
+ mResults.mVisitedGCed++;
+
+ if (mLogger) {
+ mLogger->NoteGCedObject((uint64_t)mCurrPi->mPointer, aIsMarked,
+ aObjName, aCompartmentAddress);
+ }
+
+ mCurrPi->mRefCount = refCount;
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::NoteXPCOMChild(nsISupports* aChild)
+{
+ nsCString edgeName;
+ if (WantDebugInfo()) {
+ edgeName.Assign(mNextEdgeName);
+ mNextEdgeName.Truncate();
+ }
+ if (!aChild || !(aChild = CanonicalizeXPCOMParticipant(aChild))) {
+ return;
+ }
+
+ nsXPCOMCycleCollectionParticipant* cp;
+ ToParticipant(aChild, &cp);
+ if (cp && (!cp->CanSkipThis(aChild) || WantAllTraces())) {
+ NoteChild(aChild, cp, edgeName);
+ }
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::NoteNativeChild(void* aChild,
+ nsCycleCollectionParticipant* aParticipant)
+{
+ nsCString edgeName;
+ if (WantDebugInfo()) {
+ edgeName.Assign(mNextEdgeName);
+ mNextEdgeName.Truncate();
+ }
+ if (!aChild) {
+ return;
+ }
+
+ MOZ_ASSERT(aParticipant, "Need a nsCycleCollectionParticipant!");
+ if (!aParticipant->CanSkipThis(aChild) || WantAllTraces()) {
+ NoteChild(aChild, aParticipant, edgeName);
+ }
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::NoteJSChild(const JS::GCCellPtr& aChild)
+{
+ if (!aChild) {
+ return;
+ }
+
+ nsCString edgeName;
+ if (MOZ_UNLIKELY(WantDebugInfo())) {
+ edgeName.Assign(mNextEdgeName);
+ mNextEdgeName.Truncate();
+ }
+
+ if (GCThingIsGrayCCThing(aChild) || MOZ_UNLIKELY(WantAllTraces())) {
+ if (JS::Zone* zone = MergeZone(aChild)) {
+ NoteChild(zone, mJSZoneParticipant, edgeName);
+ } else {
+ NoteChild(aChild.asCell(), mJSParticipant, edgeName);
+ }
+ }
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::NoteNextEdgeName(const char* aName)
+{
+ if (WantDebugInfo()) {
+ mNextEdgeName = aName;
+ }
+}
+
+PtrInfo*
+CCGraphBuilder::AddWeakMapNode(JS::GCCellPtr aNode)
+{
+ MOZ_ASSERT(aNode, "Weak map node should be non-null.");
+
+ if (!GCThingIsGrayCCThing(aNode) && !WantAllTraces()) {
+ return nullptr;
+ }
+
+ if (JS::Zone* zone = MergeZone(aNode)) {
+ return AddNode(zone, mJSZoneParticipant);
+ }
+ return AddNode(aNode.asCell(), mJSParticipant);
+}
+
+PtrInfo*
+CCGraphBuilder::AddWeakMapNode(JSObject* aObject)
+{
+ return AddWeakMapNode(JS::GCCellPtr(aObject));
+}
+
+NS_IMETHODIMP_(void)
+CCGraphBuilder::NoteWeakMapping(JSObject* aMap, JS::GCCellPtr aKey,
+ JSObject* aKdelegate, JS::GCCellPtr aVal)
+{
+ // Don't try to optimize away the entry here, as we've already attempted to
+ // do that in TraceWeakMapping in nsXPConnect.
+ WeakMapping* mapping = mGraph.mWeakMaps.AppendElement();
+ mapping->mMap = aMap ? AddWeakMapNode(aMap) : nullptr;
+ mapping->mKey = aKey ? AddWeakMapNode(aKey) : nullptr;
+ mapping->mKeyDelegate = aKdelegate ? AddWeakMapNode(aKdelegate) : mapping->mKey;
+ mapping->mVal = aVal ? AddWeakMapNode(aVal) : nullptr;
+
+ if (mLogger) {
+ mLogger->NoteWeakMapEntry((uint64_t)aMap, aKey ? aKey.unsafeAsInteger() : 0,
+ (uint64_t)aKdelegate,
+ aVal ? aVal.unsafeAsInteger() : 0);
+ }
+}
+
+static bool
+AddPurpleRoot(CCGraphBuilder& aBuilder, void* aRoot,
+ nsCycleCollectionParticipant* aParti)
+{
+ return aBuilder.AddPurpleRoot(aRoot, aParti);
+}
+
+// MayHaveChild() will be false after a Traverse if the object does
+// not have any children the CC will visit.
+class ChildFinder : public nsCycleCollectionTraversalCallback
+{
+public:
+ ChildFinder() : mMayHaveChild(false)
+ {
+ }
+
+ // The logic of the Note*Child functions must mirror that of their
+ // respective functions in CCGraphBuilder.
+ NS_IMETHOD_(void) NoteXPCOMChild(nsISupports* aChild);
+ NS_IMETHOD_(void) NoteNativeChild(void* aChild,
+ nsCycleCollectionParticipant* aHelper);
+ NS_IMETHOD_(void) NoteJSChild(const JS::GCCellPtr& aThing);
+
+ NS_IMETHOD_(void) DescribeRefCountedNode(nsrefcnt aRefcount,
+ const char* aObjname)
+ {
+ }
+ NS_IMETHOD_(void) DescribeGCedNode(bool aIsMarked,
+ const char* aObjname,
+ uint64_t aCompartmentAddress)
+ {
+ }
+ NS_IMETHOD_(void) NoteNextEdgeName(const char* aName)
+ {
+ }
+ bool MayHaveChild()
+ {
+ return mMayHaveChild;
+ }
+private:
+ bool mMayHaveChild;
+};
+
+NS_IMETHODIMP_(void)
+ChildFinder::NoteXPCOMChild(nsISupports* aChild)
+{
+ if (!aChild || !(aChild = CanonicalizeXPCOMParticipant(aChild))) {
+ return;
+ }
+ nsXPCOMCycleCollectionParticipant* cp;
+ ToParticipant(aChild, &cp);
+ if (cp && !cp->CanSkip(aChild, true)) {
+ mMayHaveChild = true;
+ }
+}
+
+NS_IMETHODIMP_(void)
+ChildFinder::NoteNativeChild(void* aChild,
+ nsCycleCollectionParticipant* aHelper)
+{
+ if (!aChild) {
+ return;
+ }
+ MOZ_ASSERT(aHelper, "Native child must have a participant");
+ if (!aHelper->CanSkip(aChild, true)) {
+ mMayHaveChild = true;
+ }
+}
+
+NS_IMETHODIMP_(void)
+ChildFinder::NoteJSChild(const JS::GCCellPtr& aChild)
+{
+ if (aChild && JS::GCThingIsMarkedGray(aChild)) {
+ mMayHaveChild = true;
+ }
+}
+
+static bool
+MayHaveChild(void* aObj, nsCycleCollectionParticipant* aCp)
+{
+ ChildFinder cf;
+ aCp->Traverse(aObj, cf);
+ return cf.MayHaveChild();
+}
+
+// JSPurpleBuffer keeps references to GCThings which might affect the
+// next cycle collection. It is owned only by itself and during unlink its
+// self reference is broken down and the object ends up killing itself.
+// If GC happens before CC, references to GCthings and the self reference are
+// removed.
+class JSPurpleBuffer
+{
+ ~JSPurpleBuffer()
+ {
+ MOZ_ASSERT(mValues.IsEmpty());
+ MOZ_ASSERT(mObjects.IsEmpty());
+ }
+
+public:
+ explicit JSPurpleBuffer(RefPtr<JSPurpleBuffer>& aReferenceToThis)
+ : mReferenceToThis(aReferenceToThis)
+ , mValues(kSegmentSize)
+ , mObjects(kSegmentSize)
+ {
+ mReferenceToThis = this;
+ mozilla::HoldJSObjects(this);
+ }
+
+ void Destroy()
+ {
+ mReferenceToThis = nullptr;
+ mValues.Clear();
+ mObjects.Clear();
+ mozilla::DropJSObjects(this);
+ }
+
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(JSPurpleBuffer)
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(JSPurpleBuffer)
+
+ RefPtr<JSPurpleBuffer>& mReferenceToThis;
+
+ // These are raw pointers instead of Heap<T> because we only need Heap<T> for
+ // pointers which may point into the nursery. The purple buffer never contains
+ // pointers to the nursery because nursery gcthings can never be gray and only
+ // gray things can be inserted into the purple buffer.
+ static const size_t kSegmentSize = 512;
+ SegmentedVector<JS::Value, kSegmentSize, InfallibleAllocPolicy> mValues;
+ SegmentedVector<JSObject*, kSegmentSize, InfallibleAllocPolicy> mObjects;
+};
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(JSPurpleBuffer)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(JSPurpleBuffer)
+ tmp->Destroy();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(JSPurpleBuffer)
+ CycleCollectionNoteChild(cb, tmp, "self");
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+#define NS_TRACE_SEGMENTED_ARRAY(_field, _type) \
+ { \
+ for (auto iter = tmp->_field.Iter(); !iter.Done(); iter.Next()) { \
+ js::gc::CallTraceCallbackOnNonHeap<_type, TraceCallbacks>( \
+ &iter.Get(), aCallbacks, #_field, aClosure); \
+ } \
+ }
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(JSPurpleBuffer)
+ NS_TRACE_SEGMENTED_ARRAY(mValues, JS::Value)
+ NS_TRACE_SEGMENTED_ARRAY(mObjects, JSObject*)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(JSPurpleBuffer, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(JSPurpleBuffer, Release)
+
+class SnowWhiteKiller : public TraceCallbacks
+{
+ struct SnowWhiteObject
+ {
+ void* mPointer;
+ nsCycleCollectionParticipant* mParticipant;
+ nsCycleCollectingAutoRefCnt* mRefCnt;
+ };
+
+ // Segments are 4 KiB on 32-bit and 8 KiB on 64-bit.
+ static const size_t kSegmentSize = sizeof(void*) * 1024;
+ typedef SegmentedVector<SnowWhiteObject, kSegmentSize, InfallibleAllocPolicy>
+ ObjectsVector;
+
+public:
+ explicit SnowWhiteKiller(nsCycleCollector* aCollector)
+ : mCollector(aCollector)
+ , mObjects(kSegmentSize)
+ {
+ MOZ_ASSERT(mCollector, "Calling SnowWhiteKiller after nsCC went away");
+ }
+
+ ~SnowWhiteKiller()
+ {
+ for (auto iter = mObjects.Iter(); !iter.Done(); iter.Next()) {
+ SnowWhiteObject& o = iter.Get();
+ if (!o.mRefCnt->get() && !o.mRefCnt->IsInPurpleBuffer()) {
+ mCollector->RemoveObjectFromGraph(o.mPointer);
+ o.mRefCnt->stabilizeForDeletion();
+ {
+ JS::AutoEnterCycleCollection autocc(mCollector->Context()->Context());
+ o.mParticipant->Trace(o.mPointer, *this, nullptr);
+ }
+ o.mParticipant->DeleteCycleCollectable(o.mPointer);
+ }
+ }
+ }
+
+ void
+ Visit(nsPurpleBuffer& aBuffer, nsPurpleBufferEntry* aEntry)
+ {
+ MOZ_ASSERT(aEntry->mObject, "Null object in purple buffer");
+ if (!aEntry->mRefCnt->get()) {
+ void* o = aEntry->mObject;
+ nsCycleCollectionParticipant* cp = aEntry->mParticipant;
+ CanonicalizeParticipant(&o, &cp);
+ SnowWhiteObject swo = { o, cp, aEntry->mRefCnt };
+ mObjects.InfallibleAppend(swo);
+ aBuffer.Remove(aEntry);
+ }
+ }
+
+ bool HasSnowWhiteObjects() const
+ {
+ return !mObjects.IsEmpty();
+ }
+
+ virtual void Trace(JS::Heap<JS::Value>* aValue, const char* aName,
+ void* aClosure) const override
+ {
+ const JS::Value& val = aValue->unbarrieredGet();
+ if (val.isMarkable() && ValueIsGrayCCThing(val)) {
+ MOZ_ASSERT(!js::gc::IsInsideNursery(val.toGCThing()));
+ mCollector->GetJSPurpleBuffer()->mValues.InfallibleAppend(val);
+ }
+ }
+
+ virtual void Trace(JS::Heap<jsid>* aId, const char* aName,
+ void* aClosure) const override
+ {
+ }
+
+ void AppendJSObjectToPurpleBuffer(JSObject* obj) const
+ {
+ if (obj && JS::ObjectIsMarkedGray(obj)) {
+ MOZ_ASSERT(JS::ObjectIsTenured(obj));
+ mCollector->GetJSPurpleBuffer()->mObjects.InfallibleAppend(obj);
+ }
+ }
+
+ virtual void Trace(JS::Heap<JSObject*>* aObject, const char* aName,
+ void* aClosure) const override
+ {
+ AppendJSObjectToPurpleBuffer(aObject->unbarrieredGet());
+ }
+
+ virtual void Trace(JSObject** aObject, const char* aName,
+ void* aClosure) const override
+ {
+ AppendJSObjectToPurpleBuffer(*aObject);
+ }
+
+ virtual void Trace(JS::TenuredHeap<JSObject*>* aObject, const char* aName,
+ void* aClosure) const override
+ {
+ AppendJSObjectToPurpleBuffer(aObject->unbarrieredGetPtr());
+ }
+
+ virtual void Trace(JS::Heap<JSString*>* aString, const char* aName,
+ void* aClosure) const override
+ {
+ }
+
+ virtual void Trace(JS::Heap<JSScript*>* aScript, const char* aName,
+ void* aClosure) const override
+ {
+ }
+
+ virtual void Trace(JS::Heap<JSFunction*>* aFunction, const char* aName,
+ void* aClosure) const override
+ {
+ }
+
+private:
+ RefPtr<nsCycleCollector> mCollector;
+ ObjectsVector mObjects;
+};
+
+class RemoveSkippableVisitor : public SnowWhiteKiller
+{
+public:
+ RemoveSkippableVisitor(nsCycleCollector* aCollector,
+ bool aRemoveChildlessNodes,
+ bool aAsyncSnowWhiteFreeing,
+ CC_ForgetSkippableCallback aCb)
+ : SnowWhiteKiller(aCollector)
+ , mRemoveChildlessNodes(aRemoveChildlessNodes)
+ , mAsyncSnowWhiteFreeing(aAsyncSnowWhiteFreeing)
+ , mDispatchedDeferredDeletion(false)
+ , mCallback(aCb)
+ {
+ }
+
+ ~RemoveSkippableVisitor()
+ {
+ // Note, we must call the callback before SnowWhiteKiller calls
+ // DeleteCycleCollectable!
+ if (mCallback) {
+ mCallback();
+ }
+ if (HasSnowWhiteObjects()) {
+ // Effectively a continuation.
+ nsCycleCollector_dispatchDeferredDeletion(true);
+ }
+ }
+
+ void
+ Visit(nsPurpleBuffer& aBuffer, nsPurpleBufferEntry* aEntry)
+ {
+ MOZ_ASSERT(aEntry->mObject, "null mObject in purple buffer");
+ if (!aEntry->mRefCnt->get()) {
+ if (!mAsyncSnowWhiteFreeing) {
+ SnowWhiteKiller::Visit(aBuffer, aEntry);
+ } else if (!mDispatchedDeferredDeletion) {
+ mDispatchedDeferredDeletion = true;
+ nsCycleCollector_dispatchDeferredDeletion(false);
+ }
+ return;
+ }
+ void* o = aEntry->mObject;
+ nsCycleCollectionParticipant* cp = aEntry->mParticipant;
+ CanonicalizeParticipant(&o, &cp);
+ if (aEntry->mRefCnt->IsPurple() && !cp->CanSkip(o, false) &&
+ (!mRemoveChildlessNodes || MayHaveChild(o, cp))) {
+ return;
+ }
+ aBuffer.Remove(aEntry);
+ }
+
+private:
+ bool mRemoveChildlessNodes;
+ bool mAsyncSnowWhiteFreeing;
+ bool mDispatchedDeferredDeletion;
+ CC_ForgetSkippableCallback mCallback;
+};
+
+void
+nsPurpleBuffer::RemoveSkippable(nsCycleCollector* aCollector,
+ bool aRemoveChildlessNodes,
+ bool aAsyncSnowWhiteFreeing,
+ CC_ForgetSkippableCallback aCb)
+{
+ RemoveSkippableVisitor visitor(aCollector, aRemoveChildlessNodes,
+ aAsyncSnowWhiteFreeing, aCb);
+ VisitEntries(visitor);
+}
+
+bool
+nsCycleCollector::FreeSnowWhite(bool aUntilNoSWInPurpleBuffer)
+{
+ CheckThreadSafety();
+
+ if (mFreeingSnowWhite) {
+ return false;
+ }
+
+ AutoRestore<bool> ar(mFreeingSnowWhite);
+ mFreeingSnowWhite = true;
+
+ bool hadSnowWhiteObjects = false;
+ do {
+ SnowWhiteKiller visitor(this);
+ mPurpleBuf.VisitEntries(visitor);
+ hadSnowWhiteObjects = hadSnowWhiteObjects ||
+ visitor.HasSnowWhiteObjects();
+ if (!visitor.HasSnowWhiteObjects()) {
+ break;
+ }
+ } while (aUntilNoSWInPurpleBuffer);
+ return hadSnowWhiteObjects;
+}
+
+void
+nsCycleCollector::ForgetSkippable(bool aRemoveChildlessNodes,
+ bool aAsyncSnowWhiteFreeing)
+{
+ CheckThreadSafety();
+
+ mozilla::Maybe<mozilla::AutoGlobalTimelineMarker> marker;
+ if (NS_IsMainThread()) {
+ marker.emplace("nsCycleCollector::ForgetSkippable", MarkerStackRequest::NO_STACK);
+ }
+
+ // If we remove things from the purple buffer during graph building, we may
+ // lose track of an object that was mutated during graph building.
+ MOZ_ASSERT(IsIdle());
+
+ if (mJSContext) {
+ mJSContext->PrepareForForgetSkippable();
+ }
+ MOZ_ASSERT(!mScanInProgress,
+ "Don't forget skippable or free snow-white while scan is in progress.");
+ mPurpleBuf.RemoveSkippable(this, aRemoveChildlessNodes,
+ aAsyncSnowWhiteFreeing, mForgetSkippableCB);
+}
+
+MOZ_NEVER_INLINE void
+nsCycleCollector::MarkRoots(SliceBudget& aBudget)
+{
+ JS::AutoAssertNoGC nogc;
+ TimeLog timeLog;
+ AutoRestore<bool> ar(mScanInProgress);
+ MOZ_RELEASE_ASSERT(!mScanInProgress);
+ mScanInProgress = true;
+ MOZ_ASSERT(mIncrementalPhase == GraphBuildingPhase);
+
+ JS::AutoEnterCycleCollection autocc(Context()->Context());
+ bool doneBuilding = mBuilder->BuildGraph(aBudget);
+
+ if (!doneBuilding) {
+ timeLog.Checkpoint("MarkRoots()");
+ return;
+ }
+
+ mBuilder = nullptr;
+ mIncrementalPhase = ScanAndCollectWhitePhase;
+ timeLog.Checkpoint("MarkRoots()");
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// Bacon & Rajan's |ScanRoots| routine.
+////////////////////////////////////////////////////////////////////////
+
+
+struct ScanBlackVisitor
+{
+ ScanBlackVisitor(uint32_t& aWhiteNodeCount, bool& aFailed)
+ : mWhiteNodeCount(aWhiteNodeCount), mFailed(aFailed)
+ {
+ }
+
+ bool ShouldVisitNode(PtrInfo const* aPi)
+ {
+ return aPi->mColor != black;
+ }
+
+ MOZ_NEVER_INLINE void VisitNode(PtrInfo* aPi)
+ {
+ if (aPi->mColor == white) {
+ --mWhiteNodeCount;
+ }
+ aPi->mColor = black;
+ }
+
+ void Failed()
+ {
+ mFailed = true;
+ }
+
+private:
+ uint32_t& mWhiteNodeCount;
+ bool& mFailed;
+};
+
+static void
+FloodBlackNode(uint32_t& aWhiteNodeCount, bool& aFailed, PtrInfo* aPi)
+{
+ GraphWalker<ScanBlackVisitor>(ScanBlackVisitor(aWhiteNodeCount,
+ aFailed)).Walk(aPi);
+ MOZ_ASSERT(aPi->mColor == black || !aPi->WasTraversed(),
+ "FloodBlackNode should make aPi black");
+}
+
+// Iterate over the WeakMaps. If we mark anything while iterating
+// over the WeakMaps, we must iterate over all of the WeakMaps again.
+void
+nsCycleCollector::ScanWeakMaps()
+{
+ bool anyChanged;
+ bool failed = false;
+ do {
+ anyChanged = false;
+ for (uint32_t i = 0; i < mGraph.mWeakMaps.Length(); i++) {
+ WeakMapping* wm = &mGraph.mWeakMaps[i];
+
+ // If any of these are null, the original object was marked black.
+ uint32_t mColor = wm->mMap ? wm->mMap->mColor : black;
+ uint32_t kColor = wm->mKey ? wm->mKey->mColor : black;
+ uint32_t kdColor = wm->mKeyDelegate ? wm->mKeyDelegate->mColor : black;
+ uint32_t vColor = wm->mVal ? wm->mVal->mColor : black;
+
+ MOZ_ASSERT(mColor != grey, "Uncolored weak map");
+ MOZ_ASSERT(kColor != grey, "Uncolored weak map key");
+ MOZ_ASSERT(kdColor != grey, "Uncolored weak map key delegate");
+ MOZ_ASSERT(vColor != grey, "Uncolored weak map value");
+
+ if (mColor == black && kColor != black && kdColor == black) {
+ FloodBlackNode(mWhiteNodeCount, failed, wm->mKey);
+ anyChanged = true;
+ }
+
+ if (mColor == black && kColor == black && vColor != black) {
+ FloodBlackNode(mWhiteNodeCount, failed, wm->mVal);
+ anyChanged = true;
+ }
+ }
+ } while (anyChanged);
+
+ if (failed) {
+ MOZ_ASSERT(false, "Ran out of memory in ScanWeakMaps");
+ CC_TELEMETRY(_OOM, true);
+ }
+}
+
+// Flood black from any objects in the purple buffer that are in the CC graph.
+class PurpleScanBlackVisitor
+{
+public:
+ PurpleScanBlackVisitor(CCGraph& aGraph, nsCycleCollectorLogger* aLogger,
+ uint32_t& aCount, bool& aFailed)
+ : mGraph(aGraph), mLogger(aLogger), mCount(aCount), mFailed(aFailed)
+ {
+ }
+
+ void
+ Visit(nsPurpleBuffer& aBuffer, nsPurpleBufferEntry* aEntry)
+ {
+ MOZ_ASSERT(aEntry->mObject,
+ "Entries with null mObject shouldn't be in the purple buffer.");
+ MOZ_ASSERT(aEntry->mRefCnt->get() != 0,
+ "Snow-white objects shouldn't be in the purple buffer.");
+
+ void* obj = aEntry->mObject;
+ if (!aEntry->mParticipant) {
+ obj = CanonicalizeXPCOMParticipant(static_cast<nsISupports*>(obj));
+ MOZ_ASSERT(obj, "Don't add objects that don't participate in collection!");
+ }
+
+ PtrInfo* pi = mGraph.FindNode(obj);
+ if (!pi) {
+ return;
+ }
+ MOZ_ASSERT(pi->mParticipant, "No dead objects should be in the purple buffer.");
+ if (MOZ_UNLIKELY(mLogger)) {
+ mLogger->NoteIncrementalRoot((uint64_t)pi->mPointer);
+ }
+ if (pi->mColor == black) {
+ return;
+ }
+ FloodBlackNode(mCount, mFailed, pi);
+ }
+
+private:
+ CCGraph& mGraph;
+ RefPtr<nsCycleCollectorLogger> mLogger;
+ uint32_t& mCount;
+ bool& mFailed;
+};
+
+// Objects that have been stored somewhere since the start of incremental graph building must
+// be treated as live for this cycle collection, because we may not have accurate information
+// about who holds references to them.
+void
+nsCycleCollector::ScanIncrementalRoots()
+{
+ TimeLog timeLog;
+
+ // Reference counted objects:
+ // We cleared the purple buffer at the start of the current ICC, so if a
+ // refcounted object is purple, it may have been AddRef'd during the current
+ // ICC. (It may also have only been released.) If that is the case, we cannot
+ // be sure that the set of things pointing to the object in the CC graph
+ // is accurate. Therefore, for safety, we treat any purple objects as being
+ // live during the current CC. We don't remove anything from the purple
+ // buffer here, so these objects will be suspected and freed in the next CC
+ // if they are garbage.
+ bool failed = false;
+ PurpleScanBlackVisitor purpleScanBlackVisitor(mGraph, mLogger,
+ mWhiteNodeCount, failed);
+ mPurpleBuf.VisitEntries(purpleScanBlackVisitor);
+ timeLog.Checkpoint("ScanIncrementalRoots::fix purple");
+
+ bool hasJSContext = !!mJSContext;
+ nsCycleCollectionParticipant* jsParticipant =
+ hasJSContext ? mJSContext->GCThingParticipant() : nullptr;
+ nsCycleCollectionParticipant* zoneParticipant =
+ hasJSContext ? mJSContext->ZoneParticipant() : nullptr;
+ bool hasLogger = !!mLogger;
+
+ NodePool::Enumerator etor(mGraph.mNodes);
+ while (!etor.IsDone()) {
+ PtrInfo* pi = etor.GetNext();
+
+ // As an optimization, if an object has already been determined to be live,
+ // don't consider it further. We can't do this if there is a listener,
+ // because the listener wants to know the complete set of incremental roots.
+ if (pi->mColor == black && MOZ_LIKELY(!hasLogger)) {
+ continue;
+ }
+
+ // Garbage collected objects:
+ // If a GCed object was added to the graph with a refcount of zero, and is
+ // now marked black by the GC, it was probably gray before and was exposed
+ // to active JS, so it may have been stored somewhere, so it needs to be
+ // treated as live.
+ if (pi->IsGrayJS() && MOZ_LIKELY(hasJSContext)) {
+ // If the object is still marked gray by the GC, nothing could have gotten
+ // hold of it, so it isn't an incremental root.
+ if (pi->mParticipant == jsParticipant) {
+ JS::GCCellPtr ptr(pi->mPointer, JS::GCThingTraceKind(pi->mPointer));
+ if (GCThingIsGrayCCThing(ptr)) {
+ continue;
+ }
+ } else if (pi->mParticipant == zoneParticipant) {
+ JS::Zone* zone = static_cast<JS::Zone*>(pi->mPointer);
+ if (js::ZoneGlobalsAreAllGray(zone)) {
+ continue;
+ }
+ } else {
+ MOZ_ASSERT(false, "Non-JS thing with 0 refcount? Treating as live.");
+ }
+ } else if (!pi->mParticipant && pi->WasTraversed()) {
+ // Dead traversed refcounted objects:
+ // If the object was traversed, it must have been alive at the start of
+ // the CC, and thus had a positive refcount. It is dead now, so its
+ // refcount must have decreased at some point during the CC. Therefore,
+ // it would be in the purple buffer if it wasn't dead, so treat it as an
+ // incremental root.
+ //
+ // This should not cause leaks because as the object died it should have
+ // released anything it held onto, which will add them to the purple
+ // buffer, which will cause them to be considered in the next CC.
+ } else {
+ continue;
+ }
+
+ // At this point, pi must be an incremental root.
+
+ // If there's a listener, tell it about this root. We don't bother with the
+ // optimization of skipping the Walk() if pi is black: it will just return
+ // without doing anything and there's no need to make this case faster.
+ if (MOZ_UNLIKELY(hasLogger) && pi->mPointer) {
+ // Dead objects aren't logged. See bug 1031370.
+ mLogger->NoteIncrementalRoot((uint64_t)pi->mPointer);
+ }
+
+ FloodBlackNode(mWhiteNodeCount, failed, pi);
+ }
+
+ timeLog.Checkpoint("ScanIncrementalRoots::fix nodes");
+
+ if (failed) {
+ NS_ASSERTION(false, "Ran out of memory in ScanIncrementalRoots");
+ CC_TELEMETRY(_OOM, true);
+ }
+}
+
+// Mark nodes white and make sure their refcounts are ok.
+// No nodes are marked black during this pass to ensure that refcount
+// checking is run on all nodes not marked black by ScanIncrementalRoots.
+void
+nsCycleCollector::ScanWhiteNodes(bool aFullySynchGraphBuild)
+{
+ NodePool::Enumerator nodeEnum(mGraph.mNodes);
+ while (!nodeEnum.IsDone()) {
+ PtrInfo* pi = nodeEnum.GetNext();
+ if (pi->mColor == black) {
+ // Incremental roots can be in a nonsensical state, so don't
+ // check them. This will miss checking nodes that are merely
+ // reachable from incremental roots.
+ MOZ_ASSERT(!aFullySynchGraphBuild,
+ "In a synch CC, no nodes should be marked black early on.");
+ continue;
+ }
+ MOZ_ASSERT(pi->mColor == grey);
+
+ if (!pi->WasTraversed()) {
+ // This node was deleted before it was traversed, so there's no reason
+ // to look at it.
+ MOZ_ASSERT(!pi->mParticipant, "Live nodes should all have been traversed");
+ continue;
+ }
+
+ if (pi->mInternalRefs == pi->mRefCount || pi->IsGrayJS()) {
+ pi->mColor = white;
+ ++mWhiteNodeCount;
+ continue;
+ }
+
+ if (pi->mInternalRefs > pi->mRefCount) {
+#ifdef MOZ_CRASHREPORTER
+ const char* piName = "Unknown";
+ if (pi->mParticipant) {
+ piName = pi->mParticipant->ClassName();
+ }
+ nsPrintfCString msg("More references to an object than its refcount, for class %s", piName);
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("CycleCollector"), msg);
+#endif
+ MOZ_CRASH();
+ }
+
+ // This node will get marked black in the next pass.
+ }
+}
+
+// Any remaining grey nodes that haven't already been deleted must be alive,
+// so mark them and their children black. Any nodes that are black must have
+// already had their children marked black, so there's no need to look at them
+// again. This pass may turn some white nodes to black.
+void
+nsCycleCollector::ScanBlackNodes()
+{
+ bool failed = false;
+ NodePool::Enumerator nodeEnum(mGraph.mNodes);
+ while (!nodeEnum.IsDone()) {
+ PtrInfo* pi = nodeEnum.GetNext();
+ if (pi->mColor == grey && pi->WasTraversed()) {
+ FloodBlackNode(mWhiteNodeCount, failed, pi);
+ }
+ }
+
+ if (failed) {
+ NS_ASSERTION(false, "Ran out of memory in ScanBlackNodes");
+ CC_TELEMETRY(_OOM, true);
+ }
+}
+
+void
+nsCycleCollector::ScanRoots(bool aFullySynchGraphBuild)
+{
+ JS::AutoAssertNoGC nogc;
+ AutoRestore<bool> ar(mScanInProgress);
+ MOZ_RELEASE_ASSERT(!mScanInProgress);
+ mScanInProgress = true;
+ mWhiteNodeCount = 0;
+ MOZ_ASSERT(mIncrementalPhase == ScanAndCollectWhitePhase);
+
+ JS::AutoEnterCycleCollection autocc(Context()->Context());
+
+ if (!aFullySynchGraphBuild) {
+ ScanIncrementalRoots();
+ }
+
+ TimeLog timeLog;
+ ScanWhiteNodes(aFullySynchGraphBuild);
+ timeLog.Checkpoint("ScanRoots::ScanWhiteNodes");
+
+ ScanBlackNodes();
+ timeLog.Checkpoint("ScanRoots::ScanBlackNodes");
+
+ // Scanning weak maps must be done last.
+ ScanWeakMaps();
+ timeLog.Checkpoint("ScanRoots::ScanWeakMaps");
+
+ if (mLogger) {
+ mLogger->BeginResults();
+
+ NodePool::Enumerator etor(mGraph.mNodes);
+ while (!etor.IsDone()) {
+ PtrInfo* pi = etor.GetNext();
+ if (!pi->WasTraversed()) {
+ continue;
+ }
+ switch (pi->mColor) {
+ case black:
+ if (!pi->IsGrayJS() && !pi->IsBlackJS() &&
+ pi->mInternalRefs != pi->mRefCount) {
+ mLogger->DescribeRoot((uint64_t)pi->mPointer,
+ pi->mInternalRefs);
+ }
+ break;
+ case white:
+ mLogger->DescribeGarbage((uint64_t)pi->mPointer);
+ break;
+ case grey:
+ MOZ_ASSERT(false, "All traversed objects should be black or white");
+ break;
+ }
+ }
+
+ mLogger->End();
+ mLogger = nullptr;
+ timeLog.Checkpoint("ScanRoots::listener");
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// Bacon & Rajan's |CollectWhite| routine, somewhat modified.
+////////////////////////////////////////////////////////////////////////
+
+bool
+nsCycleCollector::CollectWhite()
+{
+ // Explanation of "somewhat modified": we have no way to collect the
+ // set of whites "all at once", we have to ask each of them to drop
+ // their outgoing links and assume this will cause the garbage cycle
+ // to *mostly* self-destruct (except for the reference we continue
+ // to hold).
+ //
+ // To do this "safely" we must make sure that the white nodes we're
+ // operating on are stable for the duration of our operation. So we
+ // make 3 sets of calls to language runtimes:
+ //
+ // - Root(whites), which should pin the whites in memory.
+ // - Unlink(whites), which drops outgoing links on each white.
+ // - Unroot(whites), which returns the whites to normal GC.
+
+ // Segments are 4 KiB on 32-bit and 8 KiB on 64-bit.
+ static const size_t kSegmentSize = sizeof(void*) * 1024;
+ SegmentedVector<PtrInfo*, kSegmentSize, InfallibleAllocPolicy>
+ whiteNodes(kSegmentSize);
+ TimeLog timeLog;
+
+ MOZ_ASSERT(mIncrementalPhase == ScanAndCollectWhitePhase);
+
+ uint32_t numWhiteNodes = 0;
+ uint32_t numWhiteGCed = 0;
+ uint32_t numWhiteJSZones = 0;
+
+ {
+ JS::AutoAssertNoGC nogc;
+ bool hasJSContext = !!mJSContext;
+ nsCycleCollectionParticipant* zoneParticipant =
+ hasJSContext ? mJSContext->ZoneParticipant() : nullptr;
+
+ NodePool::Enumerator etor(mGraph.mNodes);
+ while (!etor.IsDone()) {
+ PtrInfo* pinfo = etor.GetNext();
+ if (pinfo->mColor == white && pinfo->mParticipant) {
+ if (pinfo->IsGrayJS()) {
+ MOZ_ASSERT(mJSContext);
+ ++numWhiteGCed;
+ JS::Zone* zone;
+ if (MOZ_UNLIKELY(pinfo->mParticipant == zoneParticipant)) {
+ ++numWhiteJSZones;
+ zone = static_cast<JS::Zone*>(pinfo->mPointer);
+ } else {
+ JS::GCCellPtr ptr(pinfo->mPointer, JS::GCThingTraceKind(pinfo->mPointer));
+ zone = JS::GetTenuredGCThingZone(ptr);
+ }
+ mJSContext->AddZoneWaitingForGC(zone);
+ } else {
+ whiteNodes.InfallibleAppend(pinfo);
+ pinfo->mParticipant->Root(pinfo->mPointer);
+ ++numWhiteNodes;
+ }
+ }
+ }
+ }
+
+ mResults.mFreedRefCounted += numWhiteNodes;
+ mResults.mFreedGCed += numWhiteGCed;
+ mResults.mFreedJSZones += numWhiteJSZones;
+
+ timeLog.Checkpoint("CollectWhite::Root");
+
+ if (mBeforeUnlinkCB) {
+ mBeforeUnlinkCB();
+ timeLog.Checkpoint("CollectWhite::BeforeUnlinkCB");
+ }
+
+ // Unlink() can trigger a GC, so do not touch any JS or anything
+ // else not in whiteNodes after here.
+
+ for (auto iter = whiteNodes.Iter(); !iter.Done(); iter.Next()) {
+ PtrInfo* pinfo = iter.Get();
+ MOZ_ASSERT(pinfo->mParticipant,
+ "Unlink shouldn't see objects removed from graph.");
+ pinfo->mParticipant->Unlink(pinfo->mPointer);
+#ifdef DEBUG
+ if (mJSContext) {
+ mJSContext->AssertNoObjectsToTrace(pinfo->mPointer);
+ }
+#endif
+ }
+ timeLog.Checkpoint("CollectWhite::Unlink");
+
+ JS::AutoAssertNoGC nogc;
+ for (auto iter = whiteNodes.Iter(); !iter.Done(); iter.Next()) {
+ PtrInfo* pinfo = iter.Get();
+ MOZ_ASSERT(pinfo->mParticipant,
+ "Unroot shouldn't see objects removed from graph.");
+ pinfo->mParticipant->Unroot(pinfo->mPointer);
+ }
+ timeLog.Checkpoint("CollectWhite::Unroot");
+
+ nsCycleCollector_dispatchDeferredDeletion(false, true);
+ timeLog.Checkpoint("CollectWhite::dispatchDeferredDeletion");
+
+ mIncrementalPhase = CleanupPhase;
+
+ return numWhiteNodes > 0 || numWhiteGCed > 0 || numWhiteJSZones > 0;
+}
+
+
+////////////////////////
+// Memory reporting
+////////////////////////
+
+MOZ_DEFINE_MALLOC_SIZE_OF(CycleCollectorMallocSizeOf)
+
+NS_IMETHODIMP
+nsCycleCollector::CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize)
+{
+ size_t objectSize, graphSize, purpleBufferSize;
+ SizeOfIncludingThis(CycleCollectorMallocSizeOf,
+ &objectSize, &graphSize,
+ &purpleBufferSize);
+
+ if (objectSize > 0) {
+ MOZ_COLLECT_REPORT(
+ "explicit/cycle-collector/collector-object", KIND_HEAP, UNITS_BYTES,
+ objectSize,
+ "Memory used for the cycle collector object itself.");
+ }
+
+ if (graphSize > 0) {
+ MOZ_COLLECT_REPORT(
+ "explicit/cycle-collector/graph", KIND_HEAP, UNITS_BYTES,
+ graphSize,
+ "Memory used for the cycle collector's graph. This should be zero when "
+ "the collector is idle.");
+ }
+
+ if (purpleBufferSize > 0) {
+ MOZ_COLLECT_REPORT(
+ "explicit/cycle-collector/purple-buffer", KIND_HEAP, UNITS_BYTES,
+ purpleBufferSize,
+ "Memory used for the cycle collector's purple buffer.");
+ }
+
+ return NS_OK;
+};
+
+
+////////////////////////////////////////////////////////////////////////
+// Collector implementation
+////////////////////////////////////////////////////////////////////////
+
+nsCycleCollector::nsCycleCollector() :
+ mActivelyCollecting(false),
+ mFreeingSnowWhite(false),
+ mScanInProgress(false),
+ mJSContext(nullptr),
+ mIncrementalPhase(IdlePhase),
+#ifdef DEBUG
+ mThread(NS_GetCurrentThread()),
+#endif
+ mWhiteNodeCount(0),
+ mBeforeUnlinkCB(nullptr),
+ mForgetSkippableCB(nullptr),
+ mUnmergedNeeded(0),
+ mMergedInARow(0)
+{
+}
+
+nsCycleCollector::~nsCycleCollector()
+{
+ UnregisterWeakMemoryReporter(this);
+}
+
+void
+nsCycleCollector::RegisterJSContext(CycleCollectedJSContext* aJSContext)
+{
+ MOZ_RELEASE_ASSERT(!mJSContext, "Multiple registrations of JS context in cycle collector");
+ mJSContext = aJSContext;
+
+ if (!NS_IsMainThread()) {
+ return;
+ }
+
+ // We can't register as a reporter in nsCycleCollector() because that runs
+ // before the memory reporter manager is initialized. So we do it here
+ // instead.
+ RegisterWeakMemoryReporter(this);
+}
+
+void
+nsCycleCollector::ForgetJSContext()
+{
+ MOZ_RELEASE_ASSERT(mJSContext, "Forgetting JS context in cycle collector before a JS context was registered");
+ mJSContext = nullptr;
+}
+
+#ifdef DEBUG
+static bool
+HasParticipant(void* aPtr, nsCycleCollectionParticipant* aParti)
+{
+ if (aParti) {
+ return true;
+ }
+
+ nsXPCOMCycleCollectionParticipant* xcp;
+ ToParticipant(static_cast<nsISupports*>(aPtr), &xcp);
+ return xcp != nullptr;
+}
+#endif
+
+MOZ_ALWAYS_INLINE void
+nsCycleCollector::Suspect(void* aPtr, nsCycleCollectionParticipant* aParti,
+ nsCycleCollectingAutoRefCnt* aRefCnt)
+{
+ CheckThreadSafety();
+
+ // Don't call AddRef or Release of a CCed object in a Traverse() method.
+ MOZ_ASSERT(!mScanInProgress, "Attempted to call Suspect() while a scan was in progress");
+
+ if (MOZ_UNLIKELY(mScanInProgress)) {
+ return;
+ }
+
+ MOZ_ASSERT(aPtr, "Don't suspect null pointers");
+
+ MOZ_ASSERT(HasParticipant(aPtr, aParti),
+ "Suspected nsISupports pointer must QI to nsXPCOMCycleCollectionParticipant");
+
+ mPurpleBuf.Put(aPtr, aParti, aRefCnt);
+}
+
+void
+nsCycleCollector::CheckThreadSafety()
+{
+#ifdef DEBUG
+ nsIThread* currentThread = NS_GetCurrentThread();
+ // XXXkhuey we can be called so late in shutdown that NS_GetCurrentThread
+ // returns null (after the thread manager has shut down)
+ MOZ_ASSERT(mThread == currentThread || !currentThread);
+#endif
+}
+
+// The cycle collector uses the mark bitmap to discover what JS objects
+// were reachable only from XPConnect roots that might participate in
+// cycles. We ask the JS context whether we need to force a GC before
+// this CC. It returns true on startup (before the mark bits have been set),
+// and also when UnmarkGray has run out of stack. We also force GCs on shut
+// down to collect cycles involving both DOM and JS.
+void
+nsCycleCollector::FixGrayBits(bool aForceGC, TimeLog& aTimeLog)
+{
+ CheckThreadSafety();
+
+ if (!mJSContext) {
+ return;
+ }
+
+ if (!aForceGC) {
+ mJSContext->FixWeakMappingGrayBits();
+ aTimeLog.Checkpoint("FixWeakMappingGrayBits");
+
+ bool needGC = !mJSContext->AreGCGrayBitsValid();
+ // Only do a telemetry ping for non-shutdown CCs.
+ CC_TELEMETRY(_NEED_GC, needGC);
+ if (!needGC) {
+ return;
+ }
+ mResults.mForcedGC = true;
+ }
+
+ mJSContext->GarbageCollect(aForceGC ? JS::gcreason::SHUTDOWN_CC :
+ JS::gcreason::CC_FORCED);
+ aTimeLog.Checkpoint("FixGrayBits GC");
+}
+
+bool
+nsCycleCollector::IsIncrementalGCInProgress()
+{
+ return mJSContext && JS::IsIncrementalGCInProgress(mJSContext->Context());
+}
+
+void
+nsCycleCollector::FinishAnyIncrementalGCInProgress()
+{
+ if (IsIncrementalGCInProgress()) {
+ NS_WARNING("Finishing incremental GC in progress during CC");
+ JS::PrepareForIncrementalGC(mJSContext->Context());
+ JS::FinishIncrementalGC(mJSContext->Context(), JS::gcreason::CC_FORCED);
+ }
+}
+
+void
+nsCycleCollector::CleanupAfterCollection()
+{
+ TimeLog timeLog;
+ MOZ_ASSERT(mIncrementalPhase == CleanupPhase);
+ mGraph.Clear();
+ timeLog.Checkpoint("CleanupAfterCollection::mGraph.Clear()");
+
+ uint32_t interval =
+ (uint32_t)((TimeStamp::Now() - mCollectionStart).ToMilliseconds());
+#ifdef COLLECT_TIME_DEBUG
+ printf("cc: total cycle collector time was %ums in %u slices\n", interval,
+ mResults.mNumSlices);
+ printf("cc: visited %u ref counted and %u GCed objects, freed %d ref counted and %d GCed objects",
+ mResults.mVisitedRefCounted, mResults.mVisitedGCed,
+ mResults.mFreedRefCounted, mResults.mFreedGCed);
+ uint32_t numVisited = mResults.mVisitedRefCounted + mResults.mVisitedGCed;
+ if (numVisited > 1000) {
+ uint32_t numFreed = mResults.mFreedRefCounted + mResults.mFreedGCed;
+ printf(" (%d%%)", 100 * numFreed / numVisited);
+ }
+ printf(".\ncc: \n");
+#endif
+
+ CC_TELEMETRY( , interval);
+ CC_TELEMETRY(_VISITED_REF_COUNTED, mResults.mVisitedRefCounted);
+ CC_TELEMETRY(_VISITED_GCED, mResults.mVisitedGCed);
+ CC_TELEMETRY(_COLLECTED, mWhiteNodeCount);
+ timeLog.Checkpoint("CleanupAfterCollection::telemetry");
+
+ if (mJSContext) {
+ mJSContext->FinalizeDeferredThings(mResults.mAnyManual
+ ? CycleCollectedJSContext::FinalizeNow
+ : CycleCollectedJSContext::FinalizeIncrementally);
+ mJSContext->EndCycleCollectionCallback(mResults);
+ timeLog.Checkpoint("CleanupAfterCollection::EndCycleCollectionCallback()");
+ }
+ mIncrementalPhase = IdlePhase;
+}
+
+void
+nsCycleCollector::ShutdownCollect()
+{
+ FinishAnyIncrementalGCInProgress();
+
+ SliceBudget unlimitedBudget = SliceBudget::unlimited();
+ uint32_t i;
+ for (i = 0; i < DEFAULT_SHUTDOWN_COLLECTIONS; ++i) {
+ if (!Collect(ShutdownCC, unlimitedBudget, nullptr)) {
+ break;
+ }
+ }
+ NS_WARNING_ASSERTION(i < NORMAL_SHUTDOWN_COLLECTIONS, "Extra shutdown CC");
+}
+
+static void
+PrintPhase(const char* aPhase)
+{
+#ifdef DEBUG_PHASES
+ printf("cc: begin %s on %s\n", aPhase,
+ NS_IsMainThread() ? "mainthread" : "worker");
+#endif
+}
+
+bool
+nsCycleCollector::Collect(ccType aCCType,
+ SliceBudget& aBudget,
+ nsICycleCollectorListener* aManualListener,
+ bool aPreferShorterSlices)
+{
+ CheckThreadSafety();
+
+ // This can legitimately happen in a few cases. See bug 383651.
+ if (mActivelyCollecting || mFreeingSnowWhite) {
+ return false;
+ }
+ mActivelyCollecting = true;
+
+ MOZ_ASSERT(!IsIncrementalGCInProgress());
+
+ mozilla::Maybe<mozilla::AutoGlobalTimelineMarker> marker;
+ if (NS_IsMainThread()) {
+ marker.emplace("nsCycleCollector::Collect", MarkerStackRequest::NO_STACK);
+ }
+
+ bool startedIdle = IsIdle();
+ bool collectedAny = false;
+
+ // If the CC started idle, it will call BeginCollection, which
+ // will do FreeSnowWhite, so it doesn't need to be done here.
+ if (!startedIdle) {
+ TimeLog timeLog;
+ FreeSnowWhite(true);
+ timeLog.Checkpoint("Collect::FreeSnowWhite");
+ }
+
+ if (aCCType != SliceCC) {
+ mResults.mAnyManual = true;
+ }
+
+ ++mResults.mNumSlices;
+
+ bool continueSlice = aBudget.isUnlimited() || !aPreferShorterSlices;
+ do {
+ switch (mIncrementalPhase) {
+ case IdlePhase:
+ PrintPhase("BeginCollection");
+ BeginCollection(aCCType, aManualListener);
+ break;
+ case GraphBuildingPhase:
+ PrintPhase("MarkRoots");
+ MarkRoots(aBudget);
+
+ // Only continue this slice if we're running synchronously or the
+ // next phase will probably be short, to reduce the max pause for this
+ // collection.
+ // (There's no need to check if we've finished graph building, because
+ // if we haven't, we've already exceeded our budget, and will finish
+ // this slice anyways.)
+ continueSlice = aBudget.isUnlimited() ||
+ (mResults.mNumSlices < 3 && !aPreferShorterSlices);
+ break;
+ case ScanAndCollectWhitePhase:
+ // We do ScanRoots and CollectWhite in a single slice to ensure
+ // that we won't unlink a live object if a weak reference is
+ // promoted to a strong reference after ScanRoots has finished.
+ // See bug 926533.
+ PrintPhase("ScanRoots");
+ ScanRoots(startedIdle);
+ PrintPhase("CollectWhite");
+ collectedAny = CollectWhite();
+ break;
+ case CleanupPhase:
+ PrintPhase("CleanupAfterCollection");
+ CleanupAfterCollection();
+ continueSlice = false;
+ break;
+ }
+ if (continueSlice) {
+ // Force SliceBudget::isOverBudget to check the time.
+ aBudget.step(SliceBudget::CounterReset);
+ continueSlice = !aBudget.isOverBudget();
+ }
+ } while (continueSlice);
+
+ // Clear mActivelyCollecting here to ensure that a recursive call to
+ // Collect() does something.
+ mActivelyCollecting = false;
+
+ if (aCCType != SliceCC && !startedIdle) {
+ // We were in the middle of an incremental CC (using its own listener).
+ // Somebody has forced a CC, so after having finished out the current CC,
+ // run the CC again using the new listener.
+ MOZ_ASSERT(IsIdle());
+ if (Collect(aCCType, aBudget, aManualListener)) {
+ collectedAny = true;
+ }
+ }
+
+ MOZ_ASSERT_IF(aCCType != SliceCC, IsIdle());
+
+ return collectedAny;
+}
+
+// Any JS objects we have in the graph could die when we GC, but we
+// don't want to abandon the current CC, because the graph contains
+// information about purple roots. So we synchronously finish off
+// the current CC.
+void
+nsCycleCollector::PrepareForGarbageCollection()
+{
+ if (IsIdle()) {
+ MOZ_ASSERT(mGraph.IsEmpty(), "Non-empty graph when idle");
+ MOZ_ASSERT(!mBuilder, "Non-null builder when idle");
+ if (mJSPurpleBuffer) {
+ mJSPurpleBuffer->Destroy();
+ }
+ return;
+ }
+
+ FinishAnyCurrentCollection();
+}
+
+void
+nsCycleCollector::FinishAnyCurrentCollection()
+{
+ if (IsIdle()) {
+ return;
+ }
+
+ SliceBudget unlimitedBudget = SliceBudget::unlimited();
+ PrintPhase("FinishAnyCurrentCollection");
+ // Use SliceCC because we only want to finish the CC in progress.
+ Collect(SliceCC, unlimitedBudget, nullptr);
+
+ // It is only okay for Collect() to have failed to finish the
+ // current CC if we're reentering the CC at some point past
+ // graph building. We need to be past the point where the CC will
+ // look at JS objects so that it is safe to GC.
+ MOZ_ASSERT(IsIdle() ||
+ (mActivelyCollecting && mIncrementalPhase != GraphBuildingPhase),
+ "Reentered CC during graph building");
+}
+
+// Don't merge too many times in a row, and do at least a minimum
+// number of unmerged CCs in a row.
+static const uint32_t kMinConsecutiveUnmerged = 3;
+static const uint32_t kMaxConsecutiveMerged = 3;
+
+bool
+nsCycleCollector::ShouldMergeZones(ccType aCCType)
+{
+ if (!mJSContext) {
+ return false;
+ }
+
+ MOZ_ASSERT(mUnmergedNeeded <= kMinConsecutiveUnmerged);
+ MOZ_ASSERT(mMergedInARow <= kMaxConsecutiveMerged);
+
+ if (mMergedInARow == kMaxConsecutiveMerged) {
+ MOZ_ASSERT(mUnmergedNeeded == 0);
+ mUnmergedNeeded = kMinConsecutiveUnmerged;
+ }
+
+ if (mUnmergedNeeded > 0) {
+ mUnmergedNeeded--;
+ mMergedInARow = 0;
+ return false;
+ }
+
+ if (aCCType == SliceCC && mJSContext->UsefulToMergeZones()) {
+ mMergedInARow++;
+ return true;
+ } else {
+ mMergedInARow = 0;
+ return false;
+ }
+}
+
+void
+nsCycleCollector::BeginCollection(ccType aCCType,
+ nsICycleCollectorListener* aManualListener)
+{
+ TimeLog timeLog;
+ MOZ_ASSERT(IsIdle());
+
+ mCollectionStart = TimeStamp::Now();
+
+ if (mJSContext) {
+ mJSContext->BeginCycleCollectionCallback();
+ timeLog.Checkpoint("BeginCycleCollectionCallback()");
+ }
+
+ bool isShutdown = (aCCType == ShutdownCC);
+
+ // Set up the listener for this CC.
+ MOZ_ASSERT_IF(isShutdown, !aManualListener);
+ MOZ_ASSERT(!mLogger, "Forgot to clear a previous listener?");
+
+ if (aManualListener) {
+ aManualListener->AsLogger(getter_AddRefs(mLogger));
+ }
+
+ aManualListener = nullptr;
+ if (!mLogger && mParams.LogThisCC(isShutdown)) {
+ mLogger = new nsCycleCollectorLogger();
+ if (mParams.AllTracesThisCC(isShutdown)) {
+ mLogger->SetAllTraces();
+ }
+ }
+
+ // On a WantAllTraces CC, force a synchronous global GC to prevent
+ // hijinks from ForgetSkippable and compartmental GCs.
+ bool forceGC = isShutdown || (mLogger && mLogger->IsAllTraces());
+
+ // BeginCycleCollectionCallback() might have started an IGC, and we need
+ // to finish it before we run FixGrayBits.
+ FinishAnyIncrementalGCInProgress();
+ timeLog.Checkpoint("Pre-FixGrayBits finish IGC");
+
+ FixGrayBits(forceGC, timeLog);
+
+ FreeSnowWhite(true);
+ timeLog.Checkpoint("BeginCollection FreeSnowWhite");
+
+ if (mLogger && NS_FAILED(mLogger->Begin())) {
+ mLogger = nullptr;
+ }
+
+ // FreeSnowWhite could potentially have started an IGC, which we need
+ // to finish before we look at any JS roots.
+ FinishAnyIncrementalGCInProgress();
+ timeLog.Checkpoint("Post-FreeSnowWhite finish IGC");
+
+ // Set up the data structures for building the graph.
+ JS::AutoAssertNoGC nogc;
+ JS::AutoEnterCycleCollection autocc(mJSContext->Context());
+ mGraph.Init();
+ mResults.Init();
+ mResults.mAnyManual = (aCCType != SliceCC);
+ bool mergeZones = ShouldMergeZones(aCCType);
+ mResults.mMergedZones = mergeZones;
+
+ MOZ_ASSERT(!mBuilder, "Forgot to clear mBuilder");
+ mBuilder = new CCGraphBuilder(mGraph, mResults, mJSContext, mLogger,
+ mergeZones);
+ timeLog.Checkpoint("BeginCollection prepare graph builder");
+
+ if (mJSContext) {
+ mJSContext->TraverseRoots(*mBuilder);
+ timeLog.Checkpoint("mJSContext->TraverseRoots()");
+ }
+
+ AutoRestore<bool> ar(mScanInProgress);
+ MOZ_RELEASE_ASSERT(!mScanInProgress);
+ mScanInProgress = true;
+ mPurpleBuf.SelectPointers(*mBuilder);
+ timeLog.Checkpoint("SelectPointers()");
+
+ mBuilder->DoneAddingRoots();
+ mIncrementalPhase = GraphBuildingPhase;
+}
+
+uint32_t
+nsCycleCollector::SuspectedCount()
+{
+ CheckThreadSafety();
+ return mPurpleBuf.Count();
+}
+
+void
+nsCycleCollector::Shutdown(bool aDoCollect)
+{
+ CheckThreadSafety();
+
+ // Always delete snow white objects.
+ FreeSnowWhite(true);
+
+ if (aDoCollect) {
+ ShutdownCollect();
+ }
+}
+
+void
+nsCycleCollector::RemoveObjectFromGraph(void* aObj)
+{
+ if (IsIdle()) {
+ return;
+ }
+
+ mGraph.RemoveObjectFromMap(aObj);
+}
+
+void
+nsCycleCollector::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
+ size_t* aObjectSize,
+ size_t* aGraphSize,
+ size_t* aPurpleBufferSize) const
+{
+ *aObjectSize = aMallocSizeOf(this);
+
+ *aGraphSize = mGraph.SizeOfExcludingThis(aMallocSizeOf);
+
+ *aPurpleBufferSize = mPurpleBuf.SizeOfExcludingThis(aMallocSizeOf);
+
+ // These fields are deliberately not measured:
+ // - mJSContext: because it's non-owning and measured by JS reporters.
+ // - mParams: because it only contains scalars.
+}
+
+JSPurpleBuffer*
+nsCycleCollector::GetJSPurpleBuffer()
+{
+ if (!mJSPurpleBuffer) {
+ // The Release call here confuses the GC analysis.
+ JS::AutoSuppressGCAnalysis nogc;
+ // JSPurpleBuffer keeps itself alive, but we need to create it in such way
+ // that it ends up in the normal purple buffer. That happens when
+ // nsRefPtr goes out of the scope and calls Release.
+ RefPtr<JSPurpleBuffer> pb = new JSPurpleBuffer(mJSPurpleBuffer);
+ }
+ return mJSPurpleBuffer;
+}
+
+////////////////////////////////////////////////////////////////////////
+// Module public API (exported in nsCycleCollector.h)
+// Just functions that redirect into the singleton, once it's built.
+////////////////////////////////////////////////////////////////////////
+
+void
+nsCycleCollector_registerJSContext(CycleCollectedJSContext* aCx)
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+ MOZ_ASSERT(data->mCollector);
+ // But we shouldn't already have a context.
+ MOZ_ASSERT(!data->mContext);
+
+ data->mContext = aCx;
+ data->mCollector->RegisterJSContext(aCx);
+}
+
+void
+nsCycleCollector_forgetJSContext()
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+ // And we shouldn't have already forgotten our context.
+ MOZ_ASSERT(data->mContext);
+
+ // But it may have shutdown already.
+ if (data->mCollector) {
+ data->mCollector->ForgetJSContext();
+ data->mContext = nullptr;
+ } else {
+ data->mContext = nullptr;
+ delete data;
+ sCollectorData.set(nullptr);
+ }
+}
+
+/* static */ CycleCollectedJSContext*
+CycleCollectedJSContext::Get()
+{
+ CollectorData* data = sCollectorData.get();
+ if (data) {
+ return data->mContext;
+ }
+ return nullptr;
+}
+
+MOZ_NEVER_INLINE static void
+SuspectAfterShutdown(void* aPtr, nsCycleCollectionParticipant* aCp,
+ nsCycleCollectingAutoRefCnt* aRefCnt,
+ bool* aShouldDelete)
+{
+ if (aRefCnt->get() == 0) {
+ if (!aShouldDelete) {
+ // The CC is shut down, so we can't be in the middle of an ICC.
+ CanonicalizeParticipant(&aPtr, &aCp);
+ aRefCnt->stabilizeForDeletion();
+ aCp->DeleteCycleCollectable(aPtr);
+ } else {
+ *aShouldDelete = true;
+ }
+ } else {
+ // Make sure we'll get called again.
+ aRefCnt->RemoveFromPurpleBuffer();
+ }
+}
+
+void
+NS_CycleCollectorSuspect3(void* aPtr, nsCycleCollectionParticipant* aCp,
+ nsCycleCollectingAutoRefCnt* aRefCnt,
+ bool* aShouldDelete)
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+
+ if (MOZ_LIKELY(data->mCollector)) {
+ data->mCollector->Suspect(aPtr, aCp, aRefCnt);
+ return;
+ }
+ SuspectAfterShutdown(aPtr, aCp, aRefCnt, aShouldDelete);
+}
+
+uint32_t
+nsCycleCollector_suspectedCount()
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+
+ if (!data->mCollector) {
+ return 0;
+ }
+
+ return data->mCollector->SuspectedCount();
+}
+
+bool
+nsCycleCollector_init()
+{
+#ifdef DEBUG
+ static bool sInitialized;
+
+ MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+ MOZ_ASSERT(!sInitialized, "Called twice!?");
+ sInitialized = true;
+#endif
+
+ return sCollectorData.init();
+}
+
+void
+nsCycleCollector_startup()
+{
+ if (sCollectorData.get()) {
+ MOZ_CRASH();
+ }
+
+ CollectorData* data = new CollectorData;
+ data->mCollector = new nsCycleCollector();
+ data->mContext = nullptr;
+
+ sCollectorData.set(data);
+}
+
+void
+nsCycleCollector_setBeforeUnlinkCallback(CC_BeforeUnlinkCallback aCB)
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+ MOZ_ASSERT(data->mCollector);
+
+ data->mCollector->SetBeforeUnlinkCallback(aCB);
+}
+
+void
+nsCycleCollector_setForgetSkippableCallback(CC_ForgetSkippableCallback aCB)
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+ MOZ_ASSERT(data->mCollector);
+
+ data->mCollector->SetForgetSkippableCallback(aCB);
+}
+
+void
+nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes,
+ bool aAsyncSnowWhiteFreeing)
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+ MOZ_ASSERT(data->mCollector);
+
+ PROFILER_LABEL("nsCycleCollector", "forgetSkippable",
+ js::ProfileEntry::Category::CC);
+
+ TimeLog timeLog;
+ data->mCollector->ForgetSkippable(aRemoveChildlessNodes,
+ aAsyncSnowWhiteFreeing);
+ timeLog.Checkpoint("ForgetSkippable()");
+}
+
+void
+nsCycleCollector_dispatchDeferredDeletion(bool aContinuation, bool aPurge)
+{
+ CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
+ if (cx) {
+ cx->DispatchDeferredDeletion(aContinuation, aPurge);
+ }
+}
+
+bool
+nsCycleCollector_doDeferredDeletion()
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+ MOZ_ASSERT(data->mCollector);
+ MOZ_ASSERT(data->mContext);
+
+ return data->mCollector->FreeSnowWhite(false);
+}
+
+already_AddRefed<nsICycleCollectorLogSink>
+nsCycleCollector_createLogSink()
+{
+ nsCOMPtr<nsICycleCollectorLogSink> sink = new nsCycleCollectorLogSinkToFile();
+ return sink.forget();
+}
+
+void
+nsCycleCollector_collect(nsICycleCollectorListener* aManualListener)
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+ MOZ_ASSERT(data->mCollector);
+
+ PROFILER_LABEL("nsCycleCollector", "collect",
+ js::ProfileEntry::Category::CC);
+
+ SliceBudget unlimitedBudget = SliceBudget::unlimited();
+ data->mCollector->Collect(ManualCC, unlimitedBudget, aManualListener);
+}
+
+void
+nsCycleCollector_collectSlice(SliceBudget& budget,
+ bool aPreferShorterSlices)
+{
+ CollectorData* data = sCollectorData.get();
+
+ // We should have started the cycle collector by now.
+ MOZ_ASSERT(data);
+ MOZ_ASSERT(data->mCollector);
+
+ PROFILER_LABEL("nsCycleCollector", "collectSlice",
+ js::ProfileEntry::Category::CC);
+
+ data->mCollector->Collect(SliceCC, budget, nullptr, aPreferShorterSlices);
+}
+
+void
+nsCycleCollector_prepareForGarbageCollection()
+{
+ CollectorData* data = sCollectorData.get();
+
+ MOZ_ASSERT(data);
+
+ if (!data->mCollector) {
+ return;
+ }
+
+ data->mCollector->PrepareForGarbageCollection();
+}
+
+void
+nsCycleCollector_finishAnyCurrentCollection()
+{
+ CollectorData* data = sCollectorData.get();
+
+ MOZ_ASSERT(data);
+
+ if (!data->mCollector) {
+ return;
+ }
+
+ data->mCollector->FinishAnyCurrentCollection();
+}
+
+void
+nsCycleCollector_shutdown(bool aDoCollect)
+{
+ CollectorData* data = sCollectorData.get();
+
+ if (data) {
+ MOZ_ASSERT(data->mCollector);
+ PROFILER_LABEL("nsCycleCollector", "shutdown",
+ js::ProfileEntry::Category::CC);
+
+ data->mCollector->Shutdown(aDoCollect);
+ data->mCollector = nullptr;
+ if (data->mContext) {
+ // Run any remaining tasks that may have been enqueued via
+ // RunInStableState during the final cycle collection.
+ data->mContext->ProcessStableStateQueue();
+ }
+ if (!data->mContext) {
+ delete data;
+ sCollectorData.set(nullptr);
+ }
+ }
+}
diff --git a/xpcom/base/nsCycleCollector.h b/xpcom/base/nsCycleCollector.h
new file mode 100644
index 0000000000..cd3fff406a
--- /dev/null
+++ b/xpcom/base/nsCycleCollector.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 nsCycleCollector_h__
+#define nsCycleCollector_h__
+
+class nsICycleCollectorListener;
+class nsICycleCollectorLogSink;
+class nsISupports;
+template<class T> struct already_AddRefed;
+
+#include "nsError.h"
+#include "nsID.h"
+
+#include "js/SliceBudget.h"
+
+namespace mozilla {
+class CycleCollectedJSContext;
+} // namespace mozilla
+
+bool nsCycleCollector_init();
+
+void nsCycleCollector_startup();
+
+typedef void (*CC_BeforeUnlinkCallback)(void);
+void nsCycleCollector_setBeforeUnlinkCallback(CC_BeforeUnlinkCallback aCB);
+
+typedef void (*CC_ForgetSkippableCallback)(void);
+void nsCycleCollector_setForgetSkippableCallback(CC_ForgetSkippableCallback aCB);
+
+void nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes = false,
+ bool aAsyncSnowWhiteFreeing = false);
+
+void nsCycleCollector_prepareForGarbageCollection();
+
+// If an incremental cycle collection is in progress, finish it.
+void nsCycleCollector_finishAnyCurrentCollection();
+
+void nsCycleCollector_dispatchDeferredDeletion(bool aContinuation = false,
+ bool aPurge = false);
+bool nsCycleCollector_doDeferredDeletion();
+
+already_AddRefed<nsICycleCollectorLogSink> nsCycleCollector_createLogSink();
+
+void nsCycleCollector_collect(nsICycleCollectorListener* aManualListener);
+
+void nsCycleCollector_collectSlice(js::SliceBudget& budget,
+ bool aPreferShorterSlices = false);
+
+uint32_t nsCycleCollector_suspectedCount();
+
+// If aDoCollect is true, then run the GC and CC a few times before
+// shutting down the CC completely.
+void nsCycleCollector_shutdown(bool aDoCollect = true);
+
+// Helpers for interacting with JS
+void nsCycleCollector_registerJSContext(mozilla::CycleCollectedJSContext* aCx);
+void nsCycleCollector_forgetJSContext();
+
+#define NS_CYCLE_COLLECTOR_LOGGER_CID \
+{ 0x58be81b4, 0x39d2, 0x437c, \
+{ 0x94, 0xea, 0xae, 0xde, 0x2c, 0x62, 0x08, 0xd3 } }
+
+extern nsresult
+nsCycleCollectorLoggerConstructor(nsISupports* aOuter,
+ const nsIID& aIID,
+ void** aInstancePtr);
+
+#endif // nsCycleCollector_h__
diff --git a/xpcom/base/nsCycleCollectorTraceJSHelpers.cpp b/xpcom/base/nsCycleCollectorTraceJSHelpers.cpp
new file mode 100644
index 0000000000..eb06a389c1
--- /dev/null
+++ b/xpcom/base/nsCycleCollectorTraceJSHelpers.cpp
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "jsapi.h"
+#include "jsfriendapi.h"
+
+void
+CycleCollectionNoteEdgeNameImpl(nsCycleCollectionTraversalCallback& aCallback,
+ const char* aName,
+ uint32_t aFlags)
+{
+ nsAutoCString arrayEdgeName(aName);
+ if (aFlags & CycleCollectionEdgeNameArrayFlag) {
+ arrayEdgeName.AppendLiteral("[i]");
+ }
+ aCallback.NoteNextEdgeName(arrayEdgeName.get());
+}
+
+void
+nsScriptObjectTracer::NoteJSChild(JS::GCCellPtr aGCThing, const char* aName,
+ void* aClosure)
+{
+ nsCycleCollectionTraversalCallback* cb =
+ static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, aName);
+ if (mozilla::AddToCCKind(aGCThing.kind())) {
+ cb->NoteJSChild(aGCThing);
+ }
+}
+
+void
+TraceCallbackFunc::Trace(JS::Heap<JS::Value>* aPtr, const char* aName,
+ void* aClosure) const
+{
+ if (aPtr->unbarrieredGet().isMarkable()) {
+ mCallback(JS::GCCellPtr(aPtr->unbarrieredGet()), aName, aClosure);
+ }
+}
+
+void
+TraceCallbackFunc::Trace(JS::Heap<jsid>* aPtr, const char* aName,
+ void* aClosure) const
+{
+ if (JSID_IS_GCTHING(aPtr->unbarrieredGet())) {
+ mCallback(JSID_TO_GCTHING(aPtr->unbarrieredGet()), aName, aClosure);
+ }
+}
+
+void
+TraceCallbackFunc::Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
+ void* aClosure) const
+{
+ if (*aPtr) {
+ mCallback(JS::GCCellPtr(aPtr->unbarrieredGet()), aName, aClosure);
+ }
+}
+
+void
+TraceCallbackFunc::Trace(JSObject** aPtr, const char* aName,
+ void* aClosure) const
+{
+ if (*aPtr) {
+ mCallback(JS::GCCellPtr(*aPtr), aName, aClosure);
+ }
+}
+
+void
+TraceCallbackFunc::Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
+ void* aClosure) const
+{
+ if (*aPtr) {
+ mCallback(JS::GCCellPtr(aPtr->unbarrieredGetPtr()), aName, aClosure);
+ }
+}
+
+void
+TraceCallbackFunc::Trace(JS::Heap<JSFunction*>* aPtr, const char* aName,
+ void* aClosure) const
+{
+ if (*aPtr) {
+ mCallback(JS::GCCellPtr(aPtr->unbarrieredGet()), aName, aClosure);
+ }
+}
+
+void
+TraceCallbackFunc::Trace(JS::Heap<JSString*>* aPtr, const char* aName,
+ void* aClosure) const
+{
+ if (*aPtr) {
+ mCallback(JS::GCCellPtr(aPtr->unbarrieredGet()), aName, aClosure);
+ }
+}
+
+void
+TraceCallbackFunc::Trace(JS::Heap<JSScript*>* aPtr, const char* aName,
+ void* aClosure) const
+{
+ if (*aPtr) {
+ mCallback(JS::GCCellPtr(aPtr->unbarrieredGet()), aName, aClosure);
+ }
+}
diff --git a/xpcom/base/nsDebugImpl.cpp b/xpcom/base/nsDebugImpl.cpp
new file mode 100644
index 0000000000..36288d2037
--- /dev/null
+++ b/xpcom/base/nsDebugImpl.cpp
@@ -0,0 +1,607 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Chromium headers must come before Mozilla headers.
+#include "base/process_util.h"
+
+#include "mozilla/Atomics.h"
+
+#include "nsDebugImpl.h"
+#include "nsDebug.h"
+#ifdef MOZ_CRASHREPORTER
+# include "nsExceptionHandler.h"
+#endif
+#include "nsString.h"
+#include "nsXULAppAPI.h"
+#include "prprf.h"
+#include "nsError.h"
+#include "prerror.h"
+#include "prerr.h"
+#include "prenv.h"
+
+#ifdef ANDROID
+#include <android/log.h>
+#endif
+
+#ifdef _WIN32
+/* for getenv() */
+#include <stdlib.h>
+#endif
+
+#include "nsTraceRefcnt.h"
+
+#if defined(XP_UNIX)
+#include <signal.h>
+#endif
+
+#if defined(XP_WIN)
+#include <tchar.h>
+#include "nsString.h"
+#endif
+
+#if defined(XP_MACOSX) || defined(__DragonFly__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__)
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#endif
+
+#if defined(__OpenBSD__)
+#include <sys/proc.h>
+#endif
+
+#if defined(__DragonFly__) || defined(__FreeBSD__)
+#include <sys/user.h>
+#endif
+
+#if defined(__NetBSD__)
+#undef KERN_PROC
+#define KERN_PROC KERN_PROC2
+#define KINFO_PROC struct kinfo_proc2
+#else
+#define KINFO_PROC struct kinfo_proc
+#endif
+
+#if defined(XP_MACOSX)
+#define KP_FLAGS kp_proc.p_flag
+#elif defined(__DragonFly__)
+#define KP_FLAGS kp_flags
+#elif defined(__FreeBSD__)
+#define KP_FLAGS ki_flag
+#elif defined(__OpenBSD__) && !defined(_P_TRACED)
+#define KP_FLAGS p_psflags
+#define P_TRACED PS_TRACED
+#else
+#define KP_FLAGS p_flag
+#endif
+
+#include "mozilla/mozalloc_abort.h"
+
+static void
+Abort(const char* aMsg);
+
+static void
+RealBreak();
+
+static void
+Break(const char* aMsg);
+
+#if defined(_WIN32)
+#include <windows.h>
+#include <signal.h>
+#include <malloc.h> // for _alloca
+#elif defined(XP_UNIX)
+#include <stdlib.h>
+#endif
+
+using namespace mozilla;
+
+static const char* sMultiprocessDescription = nullptr;
+
+static Atomic<int32_t> gAssertionCount;
+
+NS_IMPL_QUERY_INTERFACE(nsDebugImpl, nsIDebug2)
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsDebugImpl::AddRef()
+{
+ return 2;
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsDebugImpl::Release()
+{
+ return 1;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::Assertion(const char* aStr, const char* aExpr,
+ const char* aFile, int32_t aLine)
+{
+ NS_DebugBreak(NS_DEBUG_ASSERTION, aStr, aExpr, aFile, aLine);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::Warning(const char* aStr, const char* aFile, int32_t aLine)
+{
+ NS_DebugBreak(NS_DEBUG_WARNING, aStr, nullptr, aFile, aLine);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::Break(const char* aFile, int32_t aLine)
+{
+ NS_DebugBreak(NS_DEBUG_BREAK, nullptr, nullptr, aFile, aLine);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::Abort(const char* aFile, int32_t aLine)
+{
+ NS_DebugBreak(NS_DEBUG_ABORT, nullptr, nullptr, aFile, aLine);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::GetIsDebugBuild(bool* aResult)
+{
+#ifdef DEBUG
+ *aResult = true;
+#else
+ *aResult = false;
+#endif
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::GetAssertionCount(int32_t* aResult)
+{
+ *aResult = gAssertionCount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::GetIsDebuggerAttached(bool* aResult)
+{
+ *aResult = false;
+
+#if defined(XP_WIN)
+ *aResult = ::IsDebuggerPresent();
+#elif defined(XP_MACOSX) || defined(__DragonFly__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__)
+ // Specify the info we're looking for
+ int mib[] = {
+ CTL_KERN,
+ KERN_PROC,
+ KERN_PROC_PID,
+ getpid(),
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ sizeof(KINFO_PROC),
+ 1,
+#endif
+ };
+ u_int mibSize = sizeof(mib) / sizeof(int);
+
+ KINFO_PROC info;
+ size_t infoSize = sizeof(info);
+ memset(&info, 0, infoSize);
+
+ if (sysctl(mib, mibSize, &info, &infoSize, nullptr, 0)) {
+ // if the call fails, default to false
+ *aResult = false;
+ return NS_OK;
+ }
+
+ if (info.KP_FLAGS & P_TRACED) {
+ *aResult = true;
+ }
+#endif
+
+ return NS_OK;
+}
+
+/* static */ void
+nsDebugImpl::SetMultiprocessMode(const char* aDesc)
+{
+ sMultiprocessDescription = aDesc;
+}
+
+/**
+ * Implementation of the nsDebug methods. Note that this code is
+ * always compiled in, in case some other module that uses it is
+ * compiled with debugging even if this library is not.
+ */
+enum nsAssertBehavior
+{
+ NS_ASSERT_UNINITIALIZED,
+ NS_ASSERT_WARN,
+ NS_ASSERT_SUSPEND,
+ NS_ASSERT_STACK,
+ NS_ASSERT_TRAP,
+ NS_ASSERT_ABORT,
+ NS_ASSERT_STACK_AND_ABORT
+};
+
+static nsAssertBehavior
+GetAssertBehavior()
+{
+ static nsAssertBehavior gAssertBehavior = NS_ASSERT_UNINITIALIZED;
+ if (gAssertBehavior != NS_ASSERT_UNINITIALIZED) {
+ return gAssertBehavior;
+ }
+
+ gAssertBehavior = NS_ASSERT_WARN;
+
+ const char* assertString = PR_GetEnv("XPCOM_DEBUG_BREAK");
+ if (!assertString || !*assertString) {
+ return gAssertBehavior;
+ }
+ if (!strcmp(assertString, "warn")) {
+ return gAssertBehavior = NS_ASSERT_WARN;
+ }
+ if (!strcmp(assertString, "suspend")) {
+ return gAssertBehavior = NS_ASSERT_SUSPEND;
+ }
+ if (!strcmp(assertString, "stack")) {
+ return gAssertBehavior = NS_ASSERT_STACK;
+ }
+ if (!strcmp(assertString, "abort")) {
+ return gAssertBehavior = NS_ASSERT_ABORT;
+ }
+ if (!strcmp(assertString, "trap") || !strcmp(assertString, "break")) {
+ return gAssertBehavior = NS_ASSERT_TRAP;
+ }
+ if (!strcmp(assertString, "stack-and-abort")) {
+ return gAssertBehavior = NS_ASSERT_STACK_AND_ABORT;
+ }
+
+ fprintf(stderr, "Unrecognized value of XPCOM_DEBUG_BREAK\n");
+ return gAssertBehavior;
+}
+
+struct FixedBuffer
+{
+ FixedBuffer() : curlen(0)
+ {
+ buffer[0] = '\0';
+ }
+
+ char buffer[500];
+ uint32_t curlen;
+};
+
+static int
+StuffFixedBuffer(void* aClosure, const char* aBuf, uint32_t aLen)
+{
+ if (!aLen) {
+ return 0;
+ }
+
+ FixedBuffer* fb = (FixedBuffer*)aClosure;
+
+ // strip the trailing null, we add it again later
+ if (aBuf[aLen - 1] == '\0') {
+ --aLen;
+ }
+
+ if (fb->curlen + aLen >= sizeof(fb->buffer)) {
+ aLen = sizeof(fb->buffer) - fb->curlen - 1;
+ }
+
+ if (aLen) {
+ memcpy(fb->buffer + fb->curlen, aBuf, aLen);
+ fb->curlen += aLen;
+ fb->buffer[fb->curlen] = '\0';
+ }
+
+ return aLen;
+}
+
+EXPORT_XPCOM_API(void)
+NS_DebugBreak(uint32_t aSeverity, const char* aStr, const char* aExpr,
+ const char* aFile, int32_t aLine)
+{
+ FixedBuffer nonPIDBuf;
+ FixedBuffer buf;
+ const char* sevString = "WARNING";
+
+ switch (aSeverity) {
+ case NS_DEBUG_ASSERTION:
+ sevString = "###!!! ASSERTION";
+ break;
+
+ case NS_DEBUG_BREAK:
+ sevString = "###!!! BREAK";
+ break;
+
+ case NS_DEBUG_ABORT:
+ sevString = "###!!! ABORT";
+ break;
+
+ default:
+ aSeverity = NS_DEBUG_WARNING;
+ }
+
+#define PRINT_TO_NONPID_BUFFER(...) PR_sxprintf(StuffFixedBuffer, &nonPIDBuf, __VA_ARGS__)
+ PRINT_TO_NONPID_BUFFER("%s: ", sevString);
+ if (aStr) {
+ PRINT_TO_NONPID_BUFFER("%s: ", aStr);
+ }
+ if (aExpr) {
+ PRINT_TO_NONPID_BUFFER("'%s', ", aExpr);
+ }
+ if (aFile) {
+ PRINT_TO_NONPID_BUFFER("file %s, ", aFile);
+ }
+ if (aLine != -1) {
+ PRINT_TO_NONPID_BUFFER("line %d", aLine);
+ }
+#undef PRINT_TO_NONPID_BUFFER
+
+ // Print "[PID]" or "[Desc PID]" at the beginning of the message.
+#define PRINT_TO_BUFFER(...) PR_sxprintf(StuffFixedBuffer, &buf, __VA_ARGS__)
+ PRINT_TO_BUFFER("[");
+ if (sMultiprocessDescription) {
+ PRINT_TO_BUFFER("%s ", sMultiprocessDescription);
+ }
+ PRINT_TO_BUFFER("%d] %s", base::GetCurrentProcId(), nonPIDBuf.buffer);
+#undef PRINT_TO_BUFFER
+
+
+ // errors on platforms without a debugdlg ring a bell on stderr
+#if !defined(XP_WIN)
+ if (aSeverity != NS_DEBUG_WARNING) {
+ fprintf(stderr, "\07");
+ }
+#endif
+
+#ifdef ANDROID
+ __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", buf.buffer);
+#endif
+
+ // Write the message to stderr unless it's a warning and MOZ_IGNORE_WARNINGS
+ // is set.
+ if (!(PR_GetEnv("MOZ_IGNORE_WARNINGS") && aSeverity == NS_DEBUG_WARNING)) {
+ fprintf(stderr, "%s\n", buf.buffer);
+ fflush(stderr);
+ }
+
+ switch (aSeverity) {
+ case NS_DEBUG_WARNING:
+ return;
+
+ case NS_DEBUG_BREAK:
+ Break(buf.buffer);
+ return;
+
+ case NS_DEBUG_ABORT: {
+#if defined(MOZ_CRASHREPORTER)
+ // Updating crash annotations in the child causes us to do IPC. This can
+ // really cause trouble if we're asserting from within IPC code. So we
+ // have to do without the annotations in that case.
+ if (XRE_IsParentProcess()) {
+ // Don't include the PID in the crash report annotation to
+ // allow faceting on crash-stats.mozilla.org.
+ nsCString note("xpcom_runtime_abort(");
+ note += nonPIDBuf.buffer;
+ note += ")";
+ CrashReporter::AppendAppNotesToCrashReport(note);
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AbortMessage"),
+ nsDependentCString(nonPIDBuf.buffer));
+ }
+#endif // MOZ_CRASHREPORTER
+
+#if defined(DEBUG) && defined(_WIN32)
+ RealBreak();
+#endif
+#if defined(DEBUG)
+ nsTraceRefcnt::WalkTheStack(stderr);
+#endif
+ Abort(buf.buffer);
+ return;
+ }
+ }
+
+ // Now we deal with assertions
+ gAssertionCount++;
+
+ switch (GetAssertBehavior()) {
+ case NS_ASSERT_WARN:
+ return;
+
+ case NS_ASSERT_SUSPEND:
+#ifdef XP_UNIX
+ fprintf(stderr, "Suspending process; attach with the debugger.\n");
+ kill(0, SIGSTOP);
+#else
+ Break(buf.buffer);
+#endif
+ return;
+
+ case NS_ASSERT_STACK:
+ nsTraceRefcnt::WalkTheStack(stderr);
+ return;
+
+ case NS_ASSERT_STACK_AND_ABORT:
+ nsTraceRefcnt::WalkTheStack(stderr);
+ // Fall through to abort
+ MOZ_FALLTHROUGH;
+
+ case NS_ASSERT_ABORT:
+ Abort(buf.buffer);
+ return;
+
+ case NS_ASSERT_TRAP:
+ case NS_ASSERT_UNINITIALIZED: // Default to "trap" behavior
+ Break(buf.buffer);
+ return;
+ }
+}
+
+static void
+Abort(const char* aMsg)
+{
+ mozalloc_abort(aMsg);
+}
+
+static void
+RealBreak()
+{
+#if defined(_WIN32)
+ ::DebugBreak();
+#elif defined(XP_MACOSX)
+ raise(SIGTRAP);
+#elif defined(__GNUC__) && (defined(__i386__) || defined(__i386) || defined(__x86_64__))
+ asm("int $3");
+#elif defined(__arm__)
+ asm(
+#ifdef __ARM_ARCH_4T__
+ /* ARMv4T doesn't support the BKPT instruction, so if the compiler target
+ * is ARMv4T, we want to ensure the assembler will understand that ARMv5T
+ * instruction, while keeping the resulting object tagged as ARMv4T.
+ */
+ ".arch armv5t\n"
+ ".object_arch armv4t\n"
+#endif
+ "BKPT #0");
+#elif defined(SOLARIS)
+#if defined(__i386__) || defined(__i386) || defined(__x86_64__)
+ asm("int $3");
+#else
+ raise(SIGTRAP);
+#endif
+#else
+#warning do not know how to break on this platform
+#endif
+}
+
+// Abort() calls this function, don't call it!
+static void
+Break(const char* aMsg)
+{
+#if defined(_WIN32)
+ static int ignoreDebugger;
+ if (!ignoreDebugger) {
+ const char* shouldIgnoreDebugger = getenv("XPCOM_DEBUG_DLG");
+ ignoreDebugger =
+ 1 + (shouldIgnoreDebugger && !strcmp(shouldIgnoreDebugger, "1"));
+ }
+ if ((ignoreDebugger == 2) || !::IsDebuggerPresent()) {
+ DWORD code = IDRETRY;
+
+ /* Create the debug dialog out of process to avoid the crashes caused by
+ * Windows events leaking into our event loop from an in process dialog.
+ * We do this by launching windbgdlg.exe (built in xpcom/windbgdlg).
+ * See http://bugzilla.mozilla.org/show_bug.cgi?id=54792
+ */
+ PROCESS_INFORMATION pi;
+ STARTUPINFOW si;
+ wchar_t executable[MAX_PATH];
+ wchar_t* pName;
+
+ memset(&pi, 0, sizeof(pi));
+
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ si.wShowWindow = SW_SHOW;
+
+ // 2nd arg of CreateProcess is in/out
+ wchar_t* msgCopy = (wchar_t*)_alloca((strlen(aMsg) + 1) * sizeof(wchar_t));
+ wcscpy(msgCopy, NS_ConvertUTF8toUTF16(aMsg).get());
+
+ if (GetModuleFileNameW(GetModuleHandleW(L"xpcom.dll"), executable, MAX_PATH) &&
+ (pName = wcsrchr(executable, '\\')) != nullptr &&
+ wcscpy(pName + 1, L"windbgdlg.exe") &&
+ CreateProcessW(executable, msgCopy, nullptr, nullptr,
+ false, DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
+ nullptr, nullptr, &si, &pi)) {
+ WaitForSingleObject(pi.hProcess, INFINITE);
+ GetExitCodeProcess(pi.hProcess, &code);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+
+ switch (code) {
+ case IDABORT:
+ //This should exit us
+ raise(SIGABRT);
+ //If we are ignored exit this way..
+ _exit(3);
+
+ case IDIGNORE:
+ return;
+ }
+ }
+
+ RealBreak();
+#elif defined(XP_MACOSX)
+ /* Note that we put this Mac OS X test above the GNUC/x86 test because the
+ * GNUC/x86 test is also true on Intel Mac OS X and we want the PPC/x86
+ * impls to be the same.
+ */
+ RealBreak();
+#elif defined(__GNUC__) && (defined(__i386__) || defined(__i386) || defined(__x86_64__))
+ RealBreak();
+#elif defined(__arm__)
+ RealBreak();
+#elif defined(SOLARIS)
+ RealBreak();
+#else
+#warning do not know how to break on this platform
+#endif
+}
+
+nsresult
+nsDebugImpl::Create(nsISupports* aOuter, const nsIID& aIID, void** aInstancePtr)
+{
+ static const nsDebugImpl* sImpl;
+
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ if (!sImpl) {
+ sImpl = new nsDebugImpl();
+ }
+
+ return const_cast<nsDebugImpl*>(sImpl)->QueryInterface(aIID, aInstancePtr);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+nsresult
+NS_ErrorAccordingToNSPR()
+{
+ PRErrorCode err = PR_GetError();
+ switch (err) {
+ case PR_OUT_OF_MEMORY_ERROR: return NS_ERROR_OUT_OF_MEMORY;
+ case PR_WOULD_BLOCK_ERROR: return NS_BASE_STREAM_WOULD_BLOCK;
+ case PR_FILE_NOT_FOUND_ERROR: return NS_ERROR_FILE_NOT_FOUND;
+ case PR_READ_ONLY_FILESYSTEM_ERROR: return NS_ERROR_FILE_READ_ONLY;
+ case PR_NOT_DIRECTORY_ERROR: return NS_ERROR_FILE_NOT_DIRECTORY;
+ case PR_IS_DIRECTORY_ERROR: return NS_ERROR_FILE_IS_DIRECTORY;
+ case PR_LOOP_ERROR: return NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
+ case PR_FILE_EXISTS_ERROR: return NS_ERROR_FILE_ALREADY_EXISTS;
+ case PR_FILE_IS_LOCKED_ERROR: return NS_ERROR_FILE_IS_LOCKED;
+ case PR_FILE_TOO_BIG_ERROR: return NS_ERROR_FILE_TOO_BIG;
+ case PR_NO_DEVICE_SPACE_ERROR: return NS_ERROR_FILE_NO_DEVICE_SPACE;
+ case PR_NAME_TOO_LONG_ERROR: return NS_ERROR_FILE_NAME_TOO_LONG;
+ case PR_DIRECTORY_NOT_EMPTY_ERROR: return NS_ERROR_FILE_DIR_NOT_EMPTY;
+ case PR_NO_ACCESS_RIGHTS_ERROR: return NS_ERROR_FILE_ACCESS_DENIED;
+ default: return NS_ERROR_FAILURE;
+ }
+}
+
+void
+NS_ABORT_OOM(size_t aSize)
+{
+#if defined(MOZ_CRASHREPORTER)
+ CrashReporter::AnnotateOOMAllocationSize(aSize);
+#endif
+ MOZ_CRASH("OOM");
+}
diff --git a/xpcom/base/nsDebugImpl.h b/xpcom/base/nsDebugImpl.h
new file mode 100644
index 0000000000..23680238a4
--- /dev/null
+++ b/xpcom/base/nsDebugImpl.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/. */
+
+#ifndef nsDebugImpl_h
+#define nsDebugImpl_h
+
+#include "nsIDebug2.h"
+
+class nsDebugImpl : public nsIDebug2
+{
+public:
+ nsDebugImpl() = default;
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDEBUG2
+
+ static nsresult Create(nsISupports* aOuter, const nsIID& aIID,
+ void** aInstancePtr);
+
+ /*
+ * Inform nsDebugImpl that we're in multiprocess mode.
+ *
+ * If aDesc is not nullptr, the string it points to must be
+ * statically-allocated (i.e., it must be a string literal).
+ */
+ static void SetMultiprocessMode(const char* aDesc);
+};
+
+
+#define NS_DEBUG_CONTRACTID "@mozilla.org/xpcom/debug;1"
+#define NS_DEBUG_CID \
+{ /* cb6cdb94-e417-4601-b4a5-f991bf41453d */ \
+ 0xcb6cdb94, \
+ 0xe417, \
+ 0x4601, \
+ {0xb4, 0xa5, 0xf9, 0x91, 0xbf, 0x41, 0x45, 0x3d} \
+}
+
+#endif // nsDebugImpl_h
diff --git a/xpcom/base/nsDumpUtils.cpp b/xpcom/base/nsDumpUtils.cpp
new file mode 100644
index 0000000000..c68862d08a
--- /dev/null
+++ b/xpcom/base/nsDumpUtils.cpp
@@ -0,0 +1,513 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "nsDumpUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "prenv.h"
+#include <errno.h>
+#include "mozilla/Services.h"
+#include "nsIObserverService.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Unused.h"
+
+#ifdef XP_UNIX // {
+#include "mozilla/Preferences.h"
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+using namespace mozilla;
+
+/*
+ * The following code supports triggering a registered callback upon
+ * receiving a specific signal.
+ *
+ * Take about:memory for example, we register
+ * 1. doGCCCDump for doMemoryReport
+ * 2. doMemoryReport for sDumpAboutMemorySignum(SIGRTMIN)
+ * and sDumpAboutMemoryAfterMMUSignum(SIGRTMIN+1).
+ *
+ * When we receive one of these signals, we write the signal number to a pipe.
+ * The IO thread then notices that the pipe has been written to, and kicks off
+ * the appropriate task on the main thread.
+ *
+ * This scheme is similar to using signalfd(), except it's portable and it
+ * doesn't require the use of sigprocmask, which is problematic because it
+ * masks signals received by child processes.
+ *
+ * In theory, we could use Chromium's MessageLoopForIO::CatchSignal() for this.
+ * But that uses libevent, which does not handle the realtime signals (bug
+ * 794074).
+ */
+
+// This is the write-end of a pipe that we use to notice when a
+// specific signal occurs.
+static Atomic<int> sDumpPipeWriteFd(-1);
+
+const char* const FifoWatcher::kPrefName =
+ "memory_info_dumper.watch_fifo.enabled";
+
+static void
+DumpSignalHandler(int aSignum)
+{
+ // This is a signal handler, so everything in here needs to be
+ // async-signal-safe. Be careful!
+
+ if (sDumpPipeWriteFd != -1) {
+ uint8_t signum = static_cast<int>(aSignum);
+ Unused << write(sDumpPipeWriteFd, &signum, sizeof(signum));
+ }
+}
+
+NS_IMPL_ISUPPORTS(FdWatcher, nsIObserver);
+
+void
+FdWatcher::Init()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ os->AddObserver(this, "xpcom-shutdown", /* ownsWeak = */ false);
+
+ XRE_GetIOMessageLoop()->PostTask(NewRunnableMethod(this, &FdWatcher::StartWatching));
+}
+
+// Implementations may call this function multiple times if they ensure that
+// it's safe to call OpenFd() multiple times and they call StopWatching()
+// first.
+void
+FdWatcher::StartWatching()
+{
+ MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+ MOZ_ASSERT(mFd == -1);
+
+ mFd = OpenFd();
+ if (mFd == -1) {
+ LOG("FdWatcher: OpenFd failed.");
+ return;
+ }
+
+ MessageLoopForIO::current()->WatchFileDescriptor(
+ mFd, /* persistent = */ true,
+ MessageLoopForIO::WATCH_READ,
+ &mReadWatcher, this);
+}
+
+// Since implementations can call StartWatching() multiple times, they can of
+// course call StopWatching() multiple times.
+void
+FdWatcher::StopWatching()
+{
+ MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+
+ mReadWatcher.StopWatchingFileDescriptor();
+ if (mFd != -1) {
+ close(mFd);
+ mFd = -1;
+ }
+}
+
+StaticRefPtr<SignalPipeWatcher> SignalPipeWatcher::sSingleton;
+
+/* static */ SignalPipeWatcher*
+SignalPipeWatcher::GetSingleton()
+{
+ if (!sSingleton) {
+ sSingleton = new SignalPipeWatcher();
+ sSingleton->Init();
+ ClearOnShutdown(&sSingleton);
+ }
+ return sSingleton;
+}
+
+void
+SignalPipeWatcher::RegisterCallback(uint8_t aSignal,
+ PipeCallback aCallback)
+{
+ MutexAutoLock lock(mSignalInfoLock);
+
+ for (SignalInfoArray::index_type i = 0; i < mSignalInfo.Length(); ++i) {
+ if (mSignalInfo[i].mSignal == aSignal) {
+ LOG("Register Signal(%d) callback failed! (DUPLICATE)", aSignal);
+ return;
+ }
+ }
+ SignalInfo signalInfo = { aSignal, aCallback };
+ mSignalInfo.AppendElement(signalInfo);
+ RegisterSignalHandler(signalInfo.mSignal);
+}
+
+void
+SignalPipeWatcher::RegisterSignalHandler(uint8_t aSignal)
+{
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = DumpSignalHandler;
+
+ if (aSignal) {
+ if (sigaction(aSignal, &action, nullptr)) {
+ LOG("SignalPipeWatcher failed to register sig %d.", aSignal);
+ }
+ } else {
+ MutexAutoLock lock(mSignalInfoLock);
+ for (SignalInfoArray::index_type i = 0; i < mSignalInfo.Length(); i++) {
+ if (sigaction(mSignalInfo[i].mSignal, &action, nullptr)) {
+ LOG("SignalPipeWatcher failed to register signal(%d) "
+ "dump signal handler.", mSignalInfo[i].mSignal);
+ }
+ }
+ }
+}
+
+SignalPipeWatcher::~SignalPipeWatcher()
+{
+ if (sDumpPipeWriteFd != -1) {
+ StopWatching();
+ }
+}
+
+int
+SignalPipeWatcher::OpenFd()
+{
+ MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+
+ // Create a pipe. When we receive a signal in our signal handler, we'll
+ // write the signum to the write-end of this pipe.
+ int pipeFds[2];
+ if (pipe(pipeFds)) {
+ LOG("SignalPipeWatcher failed to create pipe.");
+ return -1;
+ }
+
+ // Close this pipe on calls to exec().
+ fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC);
+ fcntl(pipeFds[1], F_SETFD, FD_CLOEXEC);
+
+ int readFd = pipeFds[0];
+ sDumpPipeWriteFd = pipeFds[1];
+
+ RegisterSignalHandler();
+ return readFd;
+}
+
+void
+SignalPipeWatcher::StopWatching()
+{
+ MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+
+ // Close sDumpPipeWriteFd /after/ setting the fd to -1.
+ // Otherwise we have the (admittedly far-fetched) race where we
+ //
+ // 1) close sDumpPipeWriteFd
+ // 2) open a new fd with the same number as sDumpPipeWriteFd
+ // had.
+ // 3) receive a signal, then write to the fd.
+ int pipeWriteFd = sDumpPipeWriteFd.exchange(-1);
+ close(pipeWriteFd);
+
+ FdWatcher::StopWatching();
+}
+
+void
+SignalPipeWatcher::OnFileCanReadWithoutBlocking(int aFd)
+{
+ MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+
+ uint8_t signum;
+ ssize_t numReceived = read(aFd, &signum, sizeof(signum));
+ if (numReceived != sizeof(signum)) {
+ LOG("Error reading from buffer in "
+ "SignalPipeWatcher::OnFileCanReadWithoutBlocking.");
+ return;
+ }
+
+ {
+ MutexAutoLock lock(mSignalInfoLock);
+ for (SignalInfoArray::index_type i = 0; i < mSignalInfo.Length(); i++) {
+ if (signum == mSignalInfo[i].mSignal) {
+ mSignalInfo[i].mCallback(signum);
+ return;
+ }
+ }
+ }
+ LOG("SignalPipeWatcher got unexpected signum.");
+}
+
+StaticRefPtr<FifoWatcher> FifoWatcher::sSingleton;
+
+/* static */ FifoWatcher*
+FifoWatcher::GetSingleton()
+{
+ if (!sSingleton) {
+ nsAutoCString dirPath;
+ Preferences::GetCString(
+ "memory_info_dumper.watch_fifo.directory", &dirPath);
+ sSingleton = new FifoWatcher(dirPath);
+ sSingleton->Init();
+ ClearOnShutdown(&sSingleton);
+ }
+ return sSingleton;
+}
+
+/* static */ bool
+FifoWatcher::MaybeCreate()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!XRE_IsParentProcess()) {
+ // We want this to be main-process only, since two processes can't listen
+ // to the same fifo.
+ return false;
+ }
+
+ if (!Preferences::GetBool(kPrefName, false)) {
+ LOG("Fifo watcher disabled via pref.");
+ return false;
+ }
+
+ // The FifoWatcher is held alive by the observer service.
+ if (!sSingleton) {
+ GetSingleton();
+ }
+ return true;
+}
+
+void
+FifoWatcher::RegisterCallback(const nsCString& aCommand, FifoCallback aCallback)
+{
+ MutexAutoLock lock(mFifoInfoLock);
+
+ for (FifoInfoArray::index_type i = 0; i < mFifoInfo.Length(); ++i) {
+ if (mFifoInfo[i].mCommand.Equals(aCommand)) {
+ LOG("Register command(%s) callback failed! (DUPLICATE)", aCommand.get());
+ return;
+ }
+ }
+ FifoInfo aFifoInfo = { aCommand, aCallback };
+ mFifoInfo.AppendElement(aFifoInfo);
+}
+
+FifoWatcher::~FifoWatcher()
+{
+}
+
+int
+FifoWatcher::OpenFd()
+{
+ // If the memory_info_dumper.directory pref is specified, put the fifo
+ // there. Otherwise, put it into the system's tmp directory.
+
+ nsCOMPtr<nsIFile> file;
+
+ nsresult rv;
+ if (mDirPath.Length() > 0) {
+ rv = XRE_GetFileFromPath(mDirPath.get(), getter_AddRefs(file));
+ if (NS_FAILED(rv)) {
+ LOG("FifoWatcher failed to open file \"%s\"", mDirPath.get());
+ return -1;
+ }
+ } else {
+ rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(file));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return -1;
+ }
+ }
+
+ rv = file->AppendNative(NS_LITERAL_CSTRING("debug_info_trigger"));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return -1;
+ }
+
+ nsAutoCString path;
+ rv = file->GetNativePath(path);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return -1;
+ }
+
+ // unlink might fail because the file doesn't exist, or for other reasons.
+ // But we don't care it fails; any problems will be detected later, when we
+ // try to mkfifo or open the file.
+ if (unlink(path.get())) {
+ LOG("FifoWatcher::OpenFifo unlink failed; errno=%d. "
+ "Continuing despite error.", errno);
+ }
+
+ if (mkfifo(path.get(), 0766)) {
+ LOG("FifoWatcher::OpenFifo mkfifo failed; errno=%d", errno);
+ return -1;
+ }
+
+#ifdef ANDROID
+ // Android runs with a umask, so we need to chmod our fifo to make it
+ // world-writable.
+ chmod(path.get(), 0666);
+#endif
+
+ int fd;
+ do {
+ // The fifo will block until someone else has written to it. In
+ // particular, open() will block until someone else has opened it for
+ // writing! We want open() to succeed and read() to block, so we open
+ // with NONBLOCK and then fcntl that away.
+ fd = open(path.get(), O_RDONLY | O_NONBLOCK);
+ } while (fd == -1 && errno == EINTR);
+
+ if (fd == -1) {
+ LOG("FifoWatcher::OpenFifo open failed; errno=%d", errno);
+ return -1;
+ }
+
+ // Make fd blocking now that we've opened it.
+ if (fcntl(fd, F_SETFL, 0)) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+void
+FifoWatcher::OnFileCanReadWithoutBlocking(int aFd)
+{
+ MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
+
+ char buf[1024];
+ int nread;
+ do {
+ // sizeof(buf) - 1 to leave space for the null-terminator.
+ nread = read(aFd, buf, sizeof(buf));
+ } while (nread == -1 && errno == EINTR);
+
+ if (nread == -1) {
+ // We want to avoid getting into a situation where
+ // OnFileCanReadWithoutBlocking is called in an infinite loop, so when
+ // something goes wrong, stop watching the fifo altogether.
+ LOG("FifoWatcher hit an error (%d) and is quitting.", errno);
+ StopWatching();
+ return;
+ }
+
+ if (nread == 0) {
+ // If we get EOF, that means that the other side closed the fifo. We need
+ // to close and re-open the fifo; if we don't,
+ // OnFileCanWriteWithoutBlocking will be called in an infinite loop.
+
+ LOG("FifoWatcher closing and re-opening fifo.");
+ StopWatching();
+ StartWatching();
+ return;
+ }
+
+ nsAutoCString inputStr;
+ inputStr.Append(buf, nread);
+
+ // Trimming whitespace is important because if you do
+ // |echo "foo" >> debug_info_trigger|,
+ // it'll actually write "foo\n" to the fifo.
+ inputStr.Trim("\b\t\r\n");
+
+ {
+ MutexAutoLock lock(mFifoInfoLock);
+
+ for (FifoInfoArray::index_type i = 0; i < mFifoInfo.Length(); i++) {
+ const nsCString commandStr = mFifoInfo[i].mCommand;
+ if (inputStr == commandStr.get()) {
+ mFifoInfo[i].mCallback(inputStr);
+ return;
+ }
+ }
+ }
+ LOG("Got unexpected value from fifo; ignoring it.");
+}
+
+#endif // XP_UNIX }
+
+// In Android case, this function will open a file named aFilename under
+// /data/local/tmp/"aFoldername".
+// Otherwise, it will open a file named aFilename under "NS_OS_TEMP_DIR".
+/* static */ nsresult
+nsDumpUtils::OpenTempFile(const nsACString& aFilename, nsIFile** aFile,
+ const nsACString& aFoldername, Mode aMode)
+{
+#ifdef ANDROID
+ // For Android, first try the downloads directory which is world-readable
+ // rather than the temp directory which is not.
+ if (!*aFile) {
+ char* env = PR_GetEnv("DOWNLOADS_DIRECTORY");
+ if (env) {
+ NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true, aFile);
+ }
+ }
+#endif
+ nsresult rv;
+ if (!*aFile) {
+ rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, aFile);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+#ifdef ANDROID
+ // /data/local/tmp is a true tmp directory; anyone can create a file there,
+ // but only the user which created the file can remove it. We want non-root
+ // users to be able to remove these files, so we write them into a
+ // subdirectory of the temp directory and chmod 777 that directory.
+ if (aFoldername != EmptyCString()) {
+ rv = (*aFile)->AppendNative(aFoldername);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // It's OK if this fails; that probably just means that the directory
+ // already exists.
+ Unused << (*aFile)->Create(nsIFile::DIRECTORY_TYPE, 0777);
+
+ nsAutoCString dirPath;
+ rv = (*aFile)->GetNativePath(dirPath);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ while (chmod(dirPath.get(), 0777) == -1 && errno == EINTR) {
+ }
+ }
+#endif
+
+ nsCOMPtr<nsIFile> file(*aFile);
+
+ rv = file->AppendNative(aFilename);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (aMode == CREATE_UNIQUE) {
+ rv = file->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0666);
+ } else {
+ rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0666);
+ }
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+#ifdef ANDROID
+ // Make this file world-read/writable; the permissions passed to the
+ // CreateUnique call above are not sufficient on Android, which runs with a
+ // umask.
+ nsAutoCString path;
+ rv = file->GetNativePath(path);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ while (chmod(path.get(), 0666) == -1 && errno == EINTR) {
+ }
+#endif
+
+ return NS_OK;
+}
diff --git a/xpcom/base/nsDumpUtils.h b/xpcom/base/nsDumpUtils.h
new file mode 100644
index 0000000000..12a99da18d
--- /dev/null
+++ b/xpcom/base/nsDumpUtils.h
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_nsDumpUtils_h
+#define mozilla_nsDumpUtils_h
+
+#include "nsIObserver.h"
+#include "base/message_loop.h"
+#include "nsXULAppAPI.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/StaticPtr.h"
+#include "nsTArray.h"
+
+#ifdef LOG
+#undef LOG
+#endif
+
+#ifdef ANDROID
+#include "android/log.h"
+#define LOG(...) __android_log_print(ANDROID_LOG_INFO, "Gecko:DumpUtils", ## __VA_ARGS__)
+#else
+#define LOG(...)
+#endif
+
+#ifdef XP_UNIX // {
+
+/**
+ * Abstract base class for something which watches an fd and takes action when
+ * we can read from it without blocking.
+ */
+class FdWatcher
+ : public MessageLoopForIO::Watcher
+ , public nsIObserver
+{
+protected:
+ MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
+ int mFd;
+
+ virtual ~FdWatcher()
+ {
+ // StopWatching should have run.
+ MOZ_ASSERT(mFd == -1);
+ }
+
+public:
+ FdWatcher()
+ : mFd(-1)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ /**
+ * Open the fd to watch. If we encounter an error, return -1.
+ */
+ virtual int OpenFd() = 0;
+
+ /**
+ * Called when you can read() from the fd without blocking. Note that this
+ * function is also called when you're at eof (read() returns 0 in this case).
+ */
+ virtual void OnFileCanReadWithoutBlocking(int aFd) override = 0;
+ virtual void OnFileCanWriteWithoutBlocking(int aFd) override {};
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ /**
+ * Initialize this object. This should be called right after the object is
+ * constructed. (This would go in the constructor, except we interact with
+ * XPCOM, which we can't do from a constructor because our refcount is 0 at
+ * that point.)
+ */
+ void Init();
+
+ // Implementations may call this function multiple times if they ensure that
+
+ virtual void StartWatching();
+
+ // Since implementations can call StartWatching() multiple times, they can of
+ // course call StopWatching() multiple times.
+ virtual void StopWatching();
+
+ NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
+
+ XRE_GetIOMessageLoop()->PostTask(mozilla::NewRunnableMethod(this, &FdWatcher::StopWatching));
+
+ return NS_OK;
+ }
+};
+
+typedef void (*FifoCallback)(const nsCString& aInputStr);
+struct FifoInfo
+{
+ nsCString mCommand;
+ FifoCallback mCallback;
+};
+typedef nsTArray<FifoInfo> FifoInfoArray;
+
+class FifoWatcher : public FdWatcher
+{
+public:
+ /**
+ * The name of the preference used to enable/disable the FifoWatcher.
+ */
+ static const char* const kPrefName;
+
+ static FifoWatcher* GetSingleton();
+
+ static bool MaybeCreate();
+
+ void RegisterCallback(const nsCString& aCommand, FifoCallback aCallback);
+
+ virtual ~FifoWatcher();
+
+ virtual int OpenFd();
+
+ virtual void OnFileCanReadWithoutBlocking(int aFd);
+
+private:
+ nsAutoCString mDirPath;
+
+ static mozilla::StaticRefPtr<FifoWatcher> sSingleton;
+
+ explicit FifoWatcher(nsCString aPath)
+ : mDirPath(aPath)
+ , mFifoInfoLock("FifoWatcher.mFifoInfoLock")
+ {
+ }
+
+ mozilla::Mutex mFifoInfoLock; // protects mFifoInfo
+ FifoInfoArray mFifoInfo;
+};
+
+typedef void (*PipeCallback)(const uint8_t aRecvSig);
+struct SignalInfo
+{
+ uint8_t mSignal;
+ PipeCallback mCallback;
+};
+typedef nsTArray<SignalInfo> SignalInfoArray;
+
+class SignalPipeWatcher : public FdWatcher
+{
+public:
+ static SignalPipeWatcher* GetSingleton();
+
+ void RegisterCallback(uint8_t aSignal, PipeCallback aCallback);
+
+ void RegisterSignalHandler(uint8_t aSignal = 0);
+
+ virtual ~SignalPipeWatcher();
+
+ virtual int OpenFd();
+
+ virtual void StopWatching();
+
+ virtual void OnFileCanReadWithoutBlocking(int aFd);
+
+private:
+ static mozilla::StaticRefPtr<SignalPipeWatcher> sSingleton;
+
+ SignalPipeWatcher()
+ : mSignalInfoLock("SignalPipeWatcher.mSignalInfoLock")
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ mozilla::Mutex mSignalInfoLock; // protects mSignalInfo
+ SignalInfoArray mSignalInfo;
+};
+
+#endif // XP_UNIX }
+
+class nsDumpUtils
+{
+public:
+
+ enum Mode {
+ CREATE,
+ CREATE_UNIQUE
+ };
+
+ /**
+ * This function creates a new unique file based on |aFilename| in a
+ * world-readable temp directory. This is the system temp directory
+ * or, in the case of Android, the downloads directory. If |aFile| is
+ * non-null, it is assumed to point to a folder, and that folder is used
+ * instead.
+ */
+ static nsresult OpenTempFile(const nsACString& aFilename,
+ nsIFile** aFile,
+ const nsACString& aFoldername = EmptyCString(),
+ Mode aMode = CREATE_UNIQUE);
+};
+
+#endif
diff --git a/xpcom/base/nsError.h b/xpcom/base/nsError.h
new file mode 100644
index 0000000000..b9e5d23f6a
--- /dev/null
+++ b/xpcom/base/nsError.h
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 nsError_h__
+#define nsError_h__
+
+#ifndef __cplusplus
+#error nsError.h no longer supports C sources
+#endif
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Likely.h"
+
+#include <stdint.h>
+
+/*
+ * To add error code to your module, you need to do the following:
+ *
+ * 1) Add a module offset code. Add yours to the bottom of the list
+ * right below this comment, adding 1.
+ *
+ * 2) In your module, define a header file which uses one of the
+ * NE_ERROR_GENERATExxxxxx macros. Some examples below:
+ *
+ * #define NS_ERROR_MYMODULE_MYERROR1 NS_ERROR_GENERATE(NS_ERROR_SEVERITY_ERROR,NS_ERROR_MODULE_MYMODULE,1)
+ * #define NS_ERROR_MYMODULE_MYERROR2 NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_MYMODULE,2)
+ * #define NS_ERROR_MYMODULE_MYERROR3 NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_MYMODULE,3)
+ *
+ */
+
+
+/**
+ * @name Standard Module Offset Code. Each Module should identify a unique number
+ * and then all errors associated with that module become offsets from the
+ * base associated with that module id. There are 16 bits of code bits for
+ * each module.
+ */
+
+#define NS_ERROR_MODULE_XPCOM 1
+#define NS_ERROR_MODULE_BASE 2
+#define NS_ERROR_MODULE_GFX 3
+#define NS_ERROR_MODULE_WIDGET 4
+#define NS_ERROR_MODULE_CALENDAR 5
+#define NS_ERROR_MODULE_NETWORK 6
+#define NS_ERROR_MODULE_PLUGINS 7
+#define NS_ERROR_MODULE_LAYOUT 8
+#define NS_ERROR_MODULE_HTMLPARSER 9
+#define NS_ERROR_MODULE_RDF 10
+#define NS_ERROR_MODULE_UCONV 11
+#define NS_ERROR_MODULE_REG 12
+#define NS_ERROR_MODULE_FILES 13
+#define NS_ERROR_MODULE_DOM 14
+#define NS_ERROR_MODULE_IMGLIB 15
+#define NS_ERROR_MODULE_MAILNEWS 16
+#define NS_ERROR_MODULE_EDITOR 17
+#define NS_ERROR_MODULE_XPCONNECT 18
+#define NS_ERROR_MODULE_PROFILE 19
+#define NS_ERROR_MODULE_LDAP 20
+#define NS_ERROR_MODULE_SECURITY 21
+#define NS_ERROR_MODULE_DOM_XPATH 22
+/* 23 used to be NS_ERROR_MODULE_DOM_RANGE (see bug 711047) */
+#define NS_ERROR_MODULE_URILOADER 24
+#define NS_ERROR_MODULE_CONTENT 25
+#define NS_ERROR_MODULE_PYXPCOM 26
+#define NS_ERROR_MODULE_XSLT 27
+#define NS_ERROR_MODULE_IPC 28
+#define NS_ERROR_MODULE_SVG 29
+#define NS_ERROR_MODULE_STORAGE 30
+#define NS_ERROR_MODULE_SCHEMA 31
+#define NS_ERROR_MODULE_DOM_FILE 32
+#define NS_ERROR_MODULE_DOM_INDEXEDDB 33
+#define NS_ERROR_MODULE_DOM_FILEHANDLE 34
+#define NS_ERROR_MODULE_SIGNED_JAR 35
+#define NS_ERROR_MODULE_DOM_FILESYSTEM 36
+#define NS_ERROR_MODULE_DOM_BLUETOOTH 37
+#define NS_ERROR_MODULE_SIGNED_APP 38
+#define NS_ERROR_MODULE_DOM_ANIM 39
+#define NS_ERROR_MODULE_DOM_PUSH 40
+#define NS_ERROR_MODULE_DOM_MEDIA 41
+
+/* NS_ERROR_MODULE_GENERAL should be used by modules that do not
+ * care if return code values overlap. Callers of methods that
+ * return such codes should be aware that they are not
+ * globally unique. Implementors should be careful about blindly
+ * returning codes from other modules that might also use
+ * the generic base.
+ */
+#define NS_ERROR_MODULE_GENERAL 51
+
+/**
+ * @name Severity Code. This flag identifies the level of warning
+ */
+
+#define NS_ERROR_SEVERITY_SUCCESS 0
+#define NS_ERROR_SEVERITY_ERROR 1
+
+/**
+ * @name Mozilla Code. This flag separates consumers of mozilla code
+ * from the native platform
+ */
+
+#define NS_ERROR_MODULE_BASE_OFFSET 0x45
+
+/* Helpers for defining our enum, to be undef'd later */
+#define SUCCESS_OR_FAILURE(sev, module, code) \
+ ((uint32_t)(sev) << 31) | \
+ ((uint32_t)(module + NS_ERROR_MODULE_BASE_OFFSET) << 16) | \
+ (uint32_t)(code)
+#define SUCCESS(code) \
+ SUCCESS_OR_FAILURE(NS_ERROR_SEVERITY_SUCCESS, MODULE, code)
+#define FAILURE(code) \
+ SUCCESS_OR_FAILURE(NS_ERROR_SEVERITY_ERROR, MODULE, code)
+
+/**
+ * @name Standard return values
+ */
+
+/*@{*/
+
+enum class nsresult : uint32_t
+{
+ #undef ERROR
+ #define ERROR(key, val) key = val
+ #include "ErrorList.h"
+ #undef ERROR
+};
+
+/*
+ * enum classes don't place their initializers in the global scope, so we need
+ * constants for compatibility with old code.
+ */
+const nsresult
+ #define ERROR(key, val) key = nsresult::key
+ #include "ErrorList.h"
+ #undef ERROR
+;
+
+#undef SUCCESS_OR_FAILURE
+#undef SUCCESS
+#undef FAILURE
+
+/**
+ * @name Standard Error Handling Macros
+ * @return 0 or 1 (false/true with bool type for C++)
+ */
+
+inline uint32_t
+NS_FAILED_impl(nsresult aErr)
+{
+ return static_cast<uint32_t>(aErr) & 0x80000000;
+}
+#define NS_FAILED(_nsresult) ((bool)MOZ_UNLIKELY(NS_FAILED_impl(_nsresult)))
+#define NS_SUCCEEDED(_nsresult) ((bool)MOZ_LIKELY(!NS_FAILED_impl(_nsresult)))
+
+/* Check that our enum type is actually uint32_t as expected */
+static_assert(((nsresult)0) < ((nsresult)-1),
+ "nsresult must be an unsigned type");
+static_assert(sizeof(nsresult) == sizeof(uint32_t),
+ "nsresult must be 32 bits");
+
+#define MOZ_ALWAYS_SUCCEEDS(expr) MOZ_ALWAYS_TRUE(NS_SUCCEEDED(expr))
+
+/**
+ * @name Standard Error Generating Macros
+ */
+
+#define NS_ERROR_GENERATE(sev, module, code) \
+ (nsresult)(((uint32_t)(sev) << 31) | \
+ ((uint32_t)(module + NS_ERROR_MODULE_BASE_OFFSET) << 16) | \
+ ((uint32_t)(code)))
+
+#define NS_ERROR_GENERATE_SUCCESS(module, code) \
+ NS_ERROR_GENERATE(NS_ERROR_SEVERITY_SUCCESS, module, code)
+
+#define NS_ERROR_GENERATE_FAILURE(module, code) \
+ NS_ERROR_GENERATE(NS_ERROR_SEVERITY_ERROR, module, code)
+
+ /*
+ * This will return the nsresult corresponding to the most recent NSPR failure
+ * returned by PR_GetError.
+ *
+ ***********************************************************************
+ * Do not depend on this function. It will be going away!
+ ***********************************************************************
+ */
+extern nsresult
+NS_ErrorAccordingToNSPR();
+
+
+/**
+ * @name Standard Macros for retrieving error bits
+ */
+
+inline constexpr uint16_t
+NS_ERROR_GET_CODE(nsresult aErr)
+{
+ return uint32_t(aErr) & 0xffff;
+}
+inline constexpr uint16_t
+NS_ERROR_GET_MODULE(nsresult aErr)
+{
+ return ((uint32_t(aErr) >> 16) - NS_ERROR_MODULE_BASE_OFFSET) & 0x1fff;
+}
+inline bool
+NS_ERROR_GET_SEVERITY(nsresult aErr)
+{
+ return uint32_t(aErr) >> 31;
+}
+
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4251) /* 'nsCOMPtr<class nsIInputStream>' needs to have dll-interface to be used by clients of class 'nsInputStream' */
+#pragma warning(disable: 4275) /* non dll-interface class 'nsISupports' used as base for dll-interface class 'nsIRDFNode' */
+#endif
+
+#endif
diff --git a/xpcom/base/nsErrorService.cpp b/xpcom/base/nsErrorService.cpp
new file mode 100644
index 0000000000..d39b4f31ed
--- /dev/null
+++ b/xpcom/base/nsErrorService.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "nsErrorService.h"
+#include "nsCRTGlue.h"
+#include "nsAutoPtr.h"
+
+NS_IMPL_ISUPPORTS(nsErrorService, nsIErrorService)
+
+nsresult
+nsErrorService::Create(nsISupports* aOuter, const nsIID& aIID,
+ void** aInstancePtr)
+{
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+ RefPtr<nsErrorService> serv = new nsErrorService();
+ return serv->QueryInterface(aIID, aInstancePtr);
+}
+
+NS_IMETHODIMP
+nsErrorService::RegisterErrorStringBundle(int16_t aErrorModule,
+ const char* aStringBundleURL)
+{
+ mErrorStringBundleURLMap.Put(aErrorModule, new nsCString(aStringBundleURL));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsErrorService::UnregisterErrorStringBundle(int16_t aErrorModule)
+{
+ mErrorStringBundleURLMap.Remove(aErrorModule);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsErrorService::GetErrorStringBundle(int16_t aErrorModule, char** aResult)
+{
+ nsCString* bundleURL = mErrorStringBundleURLMap.Get(aErrorModule);
+ if (!bundleURL) {
+ return NS_ERROR_FAILURE;
+ }
+ *aResult = ToNewCString(*bundleURL);
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/xpcom/base/nsErrorService.h b/xpcom/base/nsErrorService.h
new file mode 100644
index 0000000000..783c99ef95
--- /dev/null
+++ b/xpcom/base/nsErrorService.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 nsErrorService_h__
+#define nsErrorService_h__
+
+#include "mozilla/Attributes.h"
+
+#include "nsIErrorService.h"
+#include "nsClassHashtable.h"
+#include "nsHashKeys.h"
+
+class nsErrorService final : public nsIErrorService
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIERRORSERVICE
+
+ nsErrorService()
+ {
+ }
+
+ static nsresult
+ Create(nsISupports* aOuter, const nsIID& aIID, void** aInstancePtr);
+
+private:
+ ~nsErrorService()
+ {
+ }
+
+ nsClassHashtable<nsUint32HashKey, nsCString> mErrorStringBundleURLMap;
+};
+
+#endif // nsErrorService_h__
diff --git a/xpcom/base/nsGZFileWriter.cpp b/xpcom/base/nsGZFileWriter.cpp
new file mode 100644
index 0000000000..a5bc5be39d
--- /dev/null
+++ b/xpcom/base/nsGZFileWriter.cpp
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "nsGZFileWriter.h"
+#include "nsIFile.h"
+#include "nsString.h"
+#include "zlib.h"
+
+#ifdef XP_WIN
+#include <io.h>
+#define _dup dup
+#else
+#include <unistd.h>
+#endif
+
+NS_IMPL_ISUPPORTS(nsGZFileWriter, nsIGZFileWriter)
+
+nsGZFileWriter::nsGZFileWriter(Operation aMode)
+ : mMode(aMode)
+ , mInitialized(false)
+ , mFinished(false)
+{
+}
+
+nsGZFileWriter::~nsGZFileWriter()
+{
+ if (mInitialized && !mFinished) {
+ Finish();
+ }
+}
+
+NS_IMETHODIMP
+nsGZFileWriter::Init(nsIFile* aFile)
+{
+ if (NS_WARN_IF(mInitialized) ||
+ NS_WARN_IF(mFinished)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Get a FILE out of our nsIFile. Convert that into a file descriptor which
+ // gzip can own. Then close our FILE, leaving only gzip's fd open.
+
+ FILE* file;
+ nsresult rv = aFile->OpenANSIFileDesc(mMode == Create ? "wb" : "ab", &file);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return InitANSIFileDesc(file);
+}
+
+NS_IMETHODIMP
+nsGZFileWriter::InitANSIFileDesc(FILE* aFile)
+{
+ mGZFile = gzdopen(dup(fileno(aFile)), mMode == Create ? "wb" : "ab");
+ fclose(aFile);
+
+ // gzdopen returns nullptr on error.
+ if (NS_WARN_IF(!mGZFile)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mInitialized = true;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGZFileWriter::Write(const nsACString& aStr)
+{
+ if (NS_WARN_IF(!mInitialized) ||
+ NS_WARN_IF(mFinished)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // gzwrite uses a return value of 0 to indicate failure. Otherwise, it
+ // returns the number of uncompressed bytes written. To ensure we can
+ // distinguish between success and failure, don't call gzwrite when we have 0
+ // bytes to write.
+ if (aStr.IsEmpty()) {
+ return NS_OK;
+ }
+
+ // gzwrite never does a short write -- that is, the return value should
+ // always be either 0 or aStr.Length(), and we shouldn't have to call it
+ // multiple times in order to get it to read the whole buffer.
+ int rv = gzwrite(mGZFile, aStr.BeginReading(), aStr.Length());
+ if (NS_WARN_IF(rv != static_cast<int>(aStr.Length()))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGZFileWriter::Finish()
+{
+ if (NS_WARN_IF(!mInitialized) ||
+ NS_WARN_IF(mFinished)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mFinished = true;
+ gzclose(mGZFile);
+
+ // Ignore errors from gzclose; it's not like there's anything we can do about
+ // it, at this point!
+ return NS_OK;
+}
diff --git a/xpcom/base/nsGZFileWriter.h b/xpcom/base/nsGZFileWriter.h
new file mode 100644
index 0000000000..4bb91173c3
--- /dev/null
+++ b/xpcom/base/nsGZFileWriter.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsGZFileWriter_h
+#define nsGZFileWriter_h
+
+#include "nsIGZFileWriter.h"
+#include "zlib.h"
+
+/**
+ * A simple class for writing .gz files.
+ */
+class nsGZFileWriter final : public nsIGZFileWriter
+{
+ virtual ~nsGZFileWriter();
+
+public:
+
+ enum Operation {
+ Append,
+ Create
+ };
+
+
+ explicit nsGZFileWriter(Operation aMode = Create);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIGZFILEWRITER
+
+ /**
+ * nsIGZFileWriter exposes two non-virtual overloads of Write(). We
+ * duplicate them here so that you can call these overloads on a pointer to
+ * the concrete nsGZFileWriter class.
+ */
+ MOZ_MUST_USE nsresult Write(const char* aStr)
+ {
+ return nsIGZFileWriter::Write(aStr);
+ }
+
+ MOZ_MUST_USE nsresult Write(const char* aStr, uint32_t aLen)
+ {
+ return nsIGZFileWriter::Write(aStr, aLen);
+ }
+
+private:
+ Operation mMode;
+ bool mInitialized;
+ bool mFinished;
+ gzFile mGZFile;
+};
+
+#endif
diff --git a/xpcom/base/nsIConsoleListener.idl b/xpcom/base/nsIConsoleListener.idl
new file mode 100644
index 0000000000..45e6fcb1fc
--- /dev/null
+++ b/xpcom/base/nsIConsoleListener.idl
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Used by the console service to notify listeners of new console messages.
+ */
+
+#include "nsISupports.idl"
+
+interface nsIConsoleMessage;
+
+[scriptable, function, uuid(35c400a4-5792-438c-b915-65e30d58d557)]
+interface nsIConsoleListener : nsISupports
+{
+ void observe(in nsIConsoleMessage aMessage);
+};
diff --git a/xpcom/base/nsIConsoleMessage.idl b/xpcom/base/nsIConsoleMessage.idl
new file mode 100644
index 0000000000..bf233b28bb
--- /dev/null
+++ b/xpcom/base/nsIConsoleMessage.idl
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * This is intended as a base interface; implementations may want to
+ * provide an object that can be qi'ed to provide more specific
+ * message information.
+ */
+[scriptable, uuid(3aba9617-10e2-4839-83ae-2e6fc4df428b)]
+interface nsIConsoleMessage : nsISupports
+{
+ /** Log level constants. */
+ const uint32_t debug = 0;
+ const uint32_t info = 1;
+ const uint32_t warn = 2;
+ const uint32_t error = 3;
+
+ /**
+ * The log level of this message.
+ */
+ readonly attribute uint32_t logLevel;
+
+ /**
+ * The time (in milliseconds from the Epoch) that the message instance
+ * was initialised.
+ * The timestamp is initialized as JS_now/1000 so that it can be
+ * compared to Date.now in Javascript.
+ */
+ readonly attribute long long timeStamp;
+
+ [binaryname(MessageMoz)] readonly attribute wstring message;
+
+ AUTF8String toString();
+};
+
+%{ C++
+#define NS_CONSOLEMESSAGE_CID \
+{ 0x024efc9e, 0x54dc, 0x4844, { 0x80, 0x4b, 0x41, 0xd3, 0xf3, 0x69, 0x90, 0x73 }}
+%}
diff --git a/xpcom/base/nsIConsoleService.idl b/xpcom/base/nsIConsoleService.idl
new file mode 100644
index 0000000000..fb9e906fb7
--- /dev/null
+++ b/xpcom/base/nsIConsoleService.idl
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIConsoleListener;
+interface nsIConsoleMessage;
+
+[scriptable, uuid(0eb81d20-c37e-42d4-82a8-ca9ae96bdf52)]
+interface nsIConsoleService : nsISupports
+{
+ void logMessage(in nsIConsoleMessage message);
+
+ /**
+ * Convenience method for logging simple messages.
+ */
+ void logStringMessage(in wstring message);
+
+ /**
+ * Get an array of all the messages logged so far. If no messages
+ * are logged, this function will return a count of 0, but still
+ * will allocate one word for messages, so as to show up as a
+ * 0-length array when called from script.
+ */
+ void getMessageArray([optional] out uint32_t count,
+ [retval, array, size_is(count)] out nsIConsoleMessage messages);
+
+ /**
+ * To guard against stack overflows from listeners that could log
+ * messages (it's easy to do this inadvertently from listeners
+ * implemented in JavaScript), we don't call any listeners when
+ * another error is already being logged.
+ */
+ void registerListener(in nsIConsoleListener listener);
+
+ /**
+ * Each registered listener should also be unregistered.
+ */
+ void unregisterListener(in nsIConsoleListener listener);
+
+ /**
+ * Clear the message buffer (e.g. for privacy reasons).
+ */
+ void reset();
+};
+
+
+%{ C++
+#define NS_CONSOLESERVICE_CID \
+{ 0x7e3ff85c, 0x1dd2, 0x11b2, { 0x8d, 0x4b, 0xeb, 0x45, 0x2c, 0xb0, 0xff, 0x40 }}
+
+#define NS_CONSOLESERVICE_CONTRACTID "@mozilla.org/consoleservice;1"
+%}
+
diff --git a/xpcom/base/nsICycleCollectorListener.idl b/xpcom/base/nsICycleCollectorListener.idl
new file mode 100644
index 0000000000..cfdf9abe90
--- /dev/null
+++ b/xpcom/base/nsICycleCollectorListener.idl
@@ -0,0 +1,164 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+%{C++
+#include <stdio.h>
+
+class nsCycleCollectorLogger;
+%}
+
+[ptr] native FILE(FILE);
+[ptr] native nsCycleCollectorLoggerPtr (nsCycleCollectorLogger);
+interface nsIFile;
+
+/**
+ * A set of interfaces for recording the cycle collector's work. An instance
+ * of @mozilla.org/cycle-collector-logger;1 can be configured to enable various
+ * options, then passed to the cycle collector when it runs.
+ * Note that additional logging options are available by setting environment
+ * variables, as described at the top of nsCycleCollector.cpp.
+ */
+
+/**
+ * nsICycleCollectorHandler is the interface JS code should implement to
+ * receive the results logged by a @mozilla.org/cycle-collector-logger;1
+ * instance. Pass an instance of this to the logger's 'processNext' method
+ * after the collection has run. This will describe the objects the cycle
+ * collector visited, the edges it found, and the conclusions it reached
+ * about the liveness of objects.
+ *
+ * In more detail:
+ * - For each node in the graph:
+ * - a call is made to either |noteRefCountedObject| or |noteGCedObject|, to
+ * describe the node itself; and
+ * - for each edge starting at that node, a call is made to |noteEdge|.
+ *
+ * - Then, a series of calls are made to:
+ * - |describeRoot|, for reference-counted nodes that the CC has identified as
+ * being alive because there are unknown references to those nodes.
+ * - |describeGarbage|, for nodes the cycle collector has identified as garbage.
+ *
+ * Any node not mentioned in a call to |describeRoot| or |describeGarbage| is
+ * neither a root nor garbage. The cycle collector was able to find all of the
+ * edges implied by the node's reference count.
+ */
+[scriptable, uuid(7f093367-1492-4b89-87af-c01dbc831246)]
+interface nsICycleCollectorHandler : nsISupports
+{
+ void noteRefCountedObject(in ACString aAddress,
+ in unsigned long aRefCount,
+ in ACString aObjectDescription);
+ void noteGCedObject(in ACString aAddress,
+ in boolean aMarked,
+ in ACString aObjectDescription,
+ in ACString aCompartmentAddress);
+ void noteEdge(in ACString aFromAddress,
+ in ACString aToAddress,
+ in ACString aEdgeName);
+ void describeRoot(in ACString aAddress,
+ in unsigned long aKnownEdges);
+ void describeGarbage(in ACString aAddress);
+};
+
+
+/**
+ * This interface allows replacing the log-writing backend for an
+ * nsICycleCollectorListener. As this interface is also called while
+ * the cycle collector is running, it cannot be implemented in JS.
+ */
+[scriptable, builtinclass, uuid(3ad9875f-d0e4-4ac2-87e3-f127f6c02ce1)]
+interface nsICycleCollectorLogSink : nsISupports
+{
+ [noscript] void open(out FILE aGCLog, out FILE aCCLog);
+ void closeGCLog();
+ void closeCCLog();
+
+ // This string will appear somewhere in the log's filename.
+ attribute AString filenameIdentifier;
+
+ // This is the process ID; it can be changed if logging is on behalf
+ // of another process.
+ attribute int32_t processIdentifier;
+
+ // The GC log file, if logging to files.
+ readonly attribute nsIFile gcLog;
+
+ // The CC log file, if logging to files.
+ readonly attribute nsIFile ccLog;
+};
+
+
+/**
+ * This interface is used to configure some reporting options for the cycle
+ * collector. This interface cannot be implemented by JavaScript code, as it
+ * is called while the cycle collector is running.
+ *
+ * To analyze cycle collection data in JS:
+ *
+ * - Create an instance of @mozilla.org/cycle-collector-logger;1, which
+ * implements this interface.
+ *
+ * - Set its |disableLog| property to true. This prevents the logger from
+ * printing messages about each method call to a temporary log file.
+ *
+ * - Set its |wantAfterProcessing| property to true. This tells the logger
+ * to record calls to its methods in memory. The |processNext| method
+ * returns events from this record.
+ *
+ * - Perform a collection using the logger. For example, call
+ * |nsIDOMWindowUtils|'s |garbageCollect| method, passing the logger as
+ * the |aListener| argument.
+ *
+ * - When the collection is complete, loop calling the logger's
+ * |processNext| method, passing a JavaScript object that implements
+ * nsICycleCollectorHandler. This JS code is free to allocate and operate
+ * on objects however it pleases: the cycle collector has finished its
+ * work, and the JS code is simply consuming recorded data.
+ */
+[scriptable, builtinclass, uuid(703b53b6-24f6-40c6-9ea9-aeb2dc53d170)]
+interface nsICycleCollectorListener : nsISupports
+{
+ // Return a listener that directs the cycle collector to traverse
+ // objects that it knows won't be collectable.
+ //
+ // Note that even this listener will not visit every node in the heap;
+ // the cycle collector can't see the entire heap. But while this
+ // listener is in use, the collector disables some optimizations it
+ // normally uses to avoid certain classes of objects that are certainly
+ // alive. So, if your purpose is to get a view of the portion of the
+ // heap that is of interest to the cycle collector, and not simply find
+ // garbage, then you should use the listener this returns.
+ //
+ // Note that this does not necessarily return a new listener; rather, it may
+ // simply set a flag on this listener (a side effect!) and return it.
+ nsICycleCollectorListener allTraces();
+
+ // True if this listener will behave like one returned by allTraces().
+ readonly attribute boolean wantAllTraces;
+
+ // If true, do not log each method call to a temporary file.
+ // Initially false.
+ attribute boolean disableLog;
+
+ // If |disableLog| is false, this object will be sent the log text.
+ attribute nsICycleCollectorLogSink logSink;
+
+ // If true, record all method calls in memory, to be retrieved later
+ // using |processNext|. Initially false.
+ attribute boolean wantAfterProcessing;
+
+ // Report the next recorded event to |aHandler|, and remove it from the
+ // record. Return false if there isn't anything more to process.
+ //
+ // Note that we only record events to report here if our
+ // |wantAfterProcessing| property is true.
+ boolean processNext(in nsICycleCollectorHandler aHandler);
+
+ // Return the current object as an nsCycleCollectorLogger*, which is the
+ // only class that should be implementing this interface. We need the
+ // concrete implementation type to help the GC rooting analysis.
+ [noscript] nsCycleCollectorLoggerPtr asLogger();
+};
diff --git a/xpcom/base/nsIDebug2.idl b/xpcom/base/nsIDebug2.idl
new file mode 100644
index 0000000000..4401f8a917
--- /dev/null
+++ b/xpcom/base/nsIDebug2.idl
@@ -0,0 +1,82 @@
+/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* interface to expose information about calls to NS_DebugBreak */
+
+#include "nsISupports.idl"
+
+/**
+ * For use by consumers in scripted languages (JavaScript, Java, Python,
+ * Perl, ...).
+ *
+ * @note C/C++ consumers who are planning to use the nsIDebug2 interface with
+ * the "@mozilla.org/xpcom;1" contract should use NS_DebugBreak from xpcom
+ * glue instead.
+ *
+ */
+
+[scriptable, uuid(9641dc15-10fb-42e3-a285-18be90a5c10b)]
+interface nsIDebug2 : nsISupports
+{
+ /**
+ * Whether XPCOM was compiled with DEBUG defined. This often
+ * correlates to whether other code (e.g., Firefox, XULRunner) was
+ * compiled with DEBUG defined.
+ */
+ readonly attribute boolean isDebugBuild;
+
+ /**
+ * The number of assertions since process start.
+ */
+ readonly attribute long assertionCount;
+
+ /**
+ * Whether a debugger is currently attached.
+ * Supports Windows + Mac
+ */
+ readonly attribute bool isDebuggerAttached;
+
+ /**
+ * Show an assertion and trigger nsIDebug2.break().
+ *
+ * @param aStr assertion message
+ * @param aExpr expression that failed
+ * @param aFile file containing assertion
+ * @param aLine line number of assertion
+ */
+ void assertion(in string aStr,
+ in string aExpr,
+ in string aFile,
+ in long aLine);
+
+ /**
+ * Show a warning.
+ *
+ * @param aStr warning message
+ * @param aFile file containing assertion
+ * @param aLine line number of assertion
+ */
+ void warning(in string aStr,
+ in string aFile,
+ in long aLine);
+
+ /**
+ * Request to break into a debugger.
+ *
+ * @param aFile file containing break request
+ * @param aLine line number of break request
+ */
+ void break(in string aFile,
+ in long aLine);
+
+ /**
+ * Request the process to trigger a fatal abort.
+ *
+ * @param aFile file containing abort request
+ * @param aLine line number of abort request
+ */
+ void abort(in string aFile,
+ in long aLine);
+};
diff --git a/xpcom/base/nsIErrorService.idl b/xpcom/base/nsIErrorService.idl
new file mode 100644
index 0000000000..9eeea2382f
--- /dev/null
+++ b/xpcom/base/nsIErrorService.idl
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * nsIErrorService: This is an interim service that allows nsresult codes to be mapped to
+ * string bundles that can be used to look up error messages. String bundle keys can also
+ * be mapped.
+ *
+ * This service will eventually get replaced by extending xpidl to allow errors to be defined.
+ * (http://bugzilla.mozilla.org/show_bug.cgi?id=13423).
+ */
+[scriptable, uuid(afe1f190-a3c2-11e3-a5e2-0800200c9a66)]
+interface nsIErrorService : nsISupports
+{
+ /**
+ * Registers a string bundle URL for an error module. Error modules are obtained from
+ * nsresult code with NS_ERROR_GET_MODULE.
+ */
+ void registerErrorStringBundle(in short errorModule, in string stringBundleURL);
+
+ /**
+ * Unregisters a string bundle URL for an error module.
+ */
+ void unregisterErrorStringBundle(in short errorModule);
+
+ /**
+ * Retrieves a string bundle URL for an error module.
+ */
+ string getErrorStringBundle(in short errorModule);
+};
+
+%{C++
+
+// The global nsIErrorService:
+#define NS_ERRORSERVICE_NAME "Error Service"
+#define NS_ERRORSERVICE_CONTRACTID "@mozilla.org/xpcom/error-service;1"
+#define NS_ERRORSERVICE_CID \
+{ /* 744afd5e-5f8c-11d4-9877-00c04fa0cf4a */ \
+ 0x744afd5e, \
+ 0x5f8c, \
+ 0x11d4, \
+ {0x98, 0x77, 0x00, 0xc0, 0x4f, 0xa0, 0xcf, 0x4a} \
+}
+
+%}
diff --git a/xpcom/base/nsIException.idl b/xpcom/base/nsIException.idl
new file mode 100644
index 0000000000..ff5a402ed7
--- /dev/null
+++ b/xpcom/base/nsIException.idl
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Interfaces for representing cross-language exceptions and stack traces.
+ */
+
+
+#include "nsISupports.idl"
+
+[scriptable, builtinclass, uuid(28bfb2a2-5ea6-4738-918b-049dc4d51f0b)]
+interface nsIStackFrame : nsISupports
+{
+ // see nsIProgrammingLanguage for list of language consts
+ readonly attribute uint32_t language;
+ readonly attribute AUTF8String languageName;
+ [implicit_jscontext]
+ readonly attribute AString filename;
+ [implicit_jscontext]
+ readonly attribute AString name;
+ // Valid line numbers begin at '1'. '0' indicates unknown.
+ [implicit_jscontext]
+ readonly attribute int32_t lineNumber;
+ [implicit_jscontext]
+ readonly attribute int32_t columnNumber;
+ readonly attribute AUTF8String sourceLine;
+ [implicit_jscontext]
+ readonly attribute AString asyncCause;
+ [implicit_jscontext]
+ readonly attribute nsIStackFrame asyncCaller;
+ [implicit_jscontext]
+ readonly attribute nsIStackFrame caller;
+
+ // Returns a formatted stack string that looks like the sort of
+ // string that would be returned by .stack on JS Error objects.
+ // Only works on JS-language stack frames.
+ [implicit_jscontext]
+ readonly attribute AString formattedStack;
+
+ // Returns the underlying SavedFrame object for native JavaScript stacks,
+ // or null if this is not a native JavaScript stack frame.
+ readonly attribute jsval nativeSavedFrame;
+
+ [implicit_jscontext]
+ AUTF8String toString();
+};
+
+[scriptable, builtinclass, uuid(4371b5bf-6845-487f-8d9d-3f1e4a9badd2)]
+interface nsIException : nsISupports
+{
+ // A custom message set by the thrower.
+ [binaryname(MessageMoz)] readonly attribute AUTF8String message;
+ // The nsresult associated with this exception.
+ readonly attribute nsresult result;
+ // The name of the error code (ie, a string repr of |result|)
+ readonly attribute AUTF8String name;
+
+ // Filename location. This is the location that caused the
+ // error, which may or may not be a source file location.
+ // For example, standard language errors would generally have
+ // the same location as their top stack entry. File
+ // parsers may put the location of the file they were parsing,
+ // etc.
+
+ // null indicates "no data"
+ [implicit_jscontext]
+ readonly attribute AString filename;
+ // Valid line numbers begin at '1'. '0' indicates unknown.
+ [implicit_jscontext]
+ readonly attribute uint32_t lineNumber;
+ // Valid column numbers begin at 0.
+ // We don't have an unambiguous indicator for unknown.
+ readonly attribute uint32_t columnNumber;
+
+ // A stack trace, if available.
+ readonly attribute nsIStackFrame location;
+
+ // Arbitary data for the implementation.
+ readonly attribute nsISupports data;
+
+ // A generic formatter - make it suitable to print, etc.
+ [implicit_jscontext]
+ AUTF8String toString();
+};
diff --git a/xpcom/base/nsIGZFileWriter.idl b/xpcom/base/nsIGZFileWriter.idl
new file mode 100644
index 0000000000..11e27f5c48
--- /dev/null
+++ b/xpcom/base/nsIGZFileWriter.idl
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+%{C++
+#include "nsDependentString.h"
+#include <stdio.h>
+%}
+
+interface nsIFile;
+[ptr] native FILE(FILE);
+
+/**
+ * A simple interface for writing to a .gz file.
+ *
+ * Note that the file that this interface produces has a different format than
+ * what you'd get if you compressed your data as a gzip stream and dumped the
+ * result to a file.
+ *
+ * The standard gunzip tool cannot decompress a raw gzip stream, but can handle
+ * the files produced by this interface.
+ */
+[scriptable, uuid(6bd5642c-1b90-4499-ba4b-199f27efaba5)]
+interface nsIGZFileWriter : nsISupports
+{
+ /**
+ * Initialize this object. We'll write our gzip'ed data to the given file,
+ * overwriting its contents if the file exists.
+ *
+ * init() will return an error if called twice. It's an error to call any
+ * other method on this interface without first calling init().
+ */
+ [must_use] void init(in nsIFile file);
+
+ /**
+ * Alternate version of init() for use when the file is already opened;
+ * e.g., with a FileDescriptor passed over IPC.
+ */
+ [noscript, must_use] void initANSIFileDesc(in FILE file);
+
+ /**
+ * Write the given string to the file.
+ */
+ [must_use] void write(in AUTF8String str);
+
+ /*
+ * The following two overloads of Write() are C++ because we can't overload
+ * methods in XPIDL. Anyway, they don't add much functionality for JS
+ * callers.
+ */
+ %{C++
+ /**
+ * Write the given char* to the file (not including the null-terminator).
+ */
+ MOZ_MUST_USE nsresult Write(const char* str)
+ {
+ return Write(str, strlen(str));
+ }
+
+ /**
+ * Write |length| bytes of |str| to the file.
+ */
+ MOZ_MUST_USE nsresult Write(const char* str, uint32_t len)
+ {
+ return Write(nsDependentCString(str, len));
+ }
+ %}
+
+ /**
+ * Close this nsIGZFileWriter. Classes implementing nsIGZFileWriter will run
+ * this method when the underlying object is destroyed, so it's not strictly
+ * necessary to explicitly call it from your code.
+ *
+ * It's an error to call this method twice, and it's an error to call write()
+ * after finish() has been called.
+ */
+ void finish();
+};
diff --git a/xpcom/base/nsIID.h b/xpcom/base/nsIID.h
new file mode 100644
index 0000000000..f2d788576a
--- /dev/null
+++ b/xpcom/base/nsIID.h
@@ -0,0 +1,10 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 __nsIID_h
+#define __nsIID_h
+#include "nsID.h"
+#endif /* __nsIID_h */
diff --git a/xpcom/base/nsIInterfaceRequestor.idl b/xpcom/base/nsIInterfaceRequestor.idl
new file mode 100644
index 0000000000..9727c53cb3
--- /dev/null
+++ b/xpcom/base/nsIInterfaceRequestor.idl
@@ -0,0 +1,36 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * The nsIInterfaceRequestor interface defines a generic interface for
+ * requesting interfaces that a given object might provide access to.
+ * This is very similar to QueryInterface found in nsISupports.
+ * The main difference is that interfaces returned from GetInterface()
+ * are not required to provide a way back to the object implementing this
+ * interface. The semantics of QI() dictate that given an interface A that
+ * you QI() on to get to interface B, you must be able to QI on B to get back
+ * to A. This interface however allows you to obtain an interface C from A
+ * that may or most likely will not have the ability to get back to A.
+ */
+
+[scriptable, uuid(033A1470-8B2A-11d3-AF88-00A024FFC08C)]
+interface nsIInterfaceRequestor : nsISupports
+{
+ /**
+ * Retrieves the specified interface pointer.
+ *
+ * @param uuid The IID of the interface being requested.
+ * @param result [out] The interface pointer to be filled in if
+ * the interface is accessible.
+ * @throws NS_NOINTERFACE - interface not accessible.
+ * @throws NS_ERROR* - method failure.
+ */
+ void getInterface(in nsIIDRef uuid,
+ [iid_is(uuid),retval] out nsQIResult result);
+};
+
diff --git a/xpcom/base/nsIMacUtils.idl b/xpcom/base/nsIMacUtils.idl
new file mode 100644
index 0000000000..9a60df47cd
--- /dev/null
+++ b/xpcom/base/nsIMacUtils.idl
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * nsIMacUtils: Generic globally-available Mac-specific utilities.
+ */
+
+[scriptable, uuid(5E9072D7-FF95-455E-9466-8AF9841A72EC)]
+interface nsIMacUtils : nsISupports
+{
+ /**
+ * True when the main executable is a fat file supporting at least
+ * ppc and x86 (universal binary).
+ */
+ readonly attribute boolean isUniversalBinary;
+
+ /**
+ * Returns a string containing a list of architectures delimited
+ * by "-". Architecture sets are always in the same order:
+ * ppc > i386 > ppc64 > x86_64 > (future additions)
+ */
+ readonly attribute AString architecturesInBinary;
+
+ /**
+ * True when running under binary translation (Rosetta).
+ */
+ readonly attribute boolean isTranslated;
+};
diff --git a/xpcom/base/nsIMemory.idl b/xpcom/base/nsIMemory.idl
new file mode 100644
index 0000000000..0c1a050b76
--- /dev/null
+++ b/xpcom/base/nsIMemory.idl
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ *
+ * nsIMemory: interface to allocate and deallocate memory. Also provides
+ * for notifications in low-memory situations.
+ *
+ * The frozen exported symbols moz_xmalloc, moz_xrealloc, and free
+ * provide a more efficient way to access XPCOM memory allocation. Using
+ * those symbols is preferred to using the methods on this interface.
+ *
+ * A client that wishes to be notified of low memory situations (for
+ * example, because the client maintains a large memory cache that
+ * could be released when memory is tight) should register with the
+ * observer service (see nsIObserverService) using the topic
+ * "memory-pressure". There are specific types of notications
+ * that can occur. These types will be passed as the |aData|
+ * parameter of the of the "memory-pressure" notification:
+ *
+ * "low-memory"
+ * This will be passed as the extra data when the pressure
+ * observer is being asked to flush for low-memory conditions.
+ *
+ * "low-memory-ongoing"
+ * This will be passed when we continue to be in a low-memory
+ * condition and we want to flush caches and do other cheap
+ * forms of memory minimization, but heavy handed approaches like
+ * a GC are unlikely to succeed.
+ *
+ * "-no-forward"
+ * This is appended to the above two parameters when the resulting
+ * notification should not be forwarded to the child processes.
+ *
+ * "heap-minimize"
+ * This will be passed as the extra data when the pressure
+ * observer is being asked to flush because of a heap minimize
+ * call.
+ *
+ * "alloc-failure"
+ * This will be passed as the extra data when the pressure
+ * observer has been asked to flush because a malloc() or
+ * realloc() has failed.
+ *
+ * "lowering-priority"
+ * This will be passed as the extra data when the priority of a child
+ * process is lowered. The pressure observers could take the chance to
+ * clear caches that could be easily regenerated. This type of
+ * notification only appears in child processes.
+ */
+
+[scriptable, uuid(1e004834-6d8f-425a-bc9c-a2812ed43bb7)]
+interface nsIMemory : nsISupports
+{
+ /**
+ * Attempts to shrink the heap.
+ * @param immediate - if true, heap minimization will occur
+ * immediately if the call was made on the main thread. If
+ * false, the flush will be scheduled to happen when the app is
+ * idle.
+ * @throws NS_ERROR_FAILURE if 'immediate' is set an the call
+ * was not on the application's main thread.
+ */
+ void heapMinimize(in boolean immediate);
+
+ /**
+ * This predicate can be used to determine if the platform is a "low-memory"
+ * platform. Callers may use this to dynamically tune their behaviour
+ * to favour reduced memory usage at the expense of performance. The value
+ * returned by this function will not change over the lifetime of the process.
+ */
+ boolean isLowMemoryPlatform();
+};
+
diff --git a/xpcom/base/nsIMemoryInfoDumper.idl b/xpcom/base/nsIMemoryInfoDumper.idl
new file mode 100644
index 0000000000..7e08503a44
--- /dev/null
+++ b/xpcom/base/nsIMemoryInfoDumper.idl
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIFile;
+interface nsICycleCollectorLogSink;
+
+[scriptable, function, uuid(2dea18fc-fbfa-4bf7-ad45-0efaf5495f5e)]
+interface nsIFinishDumpingCallback : nsISupports
+{
+ void callback(in nsISupports data);
+};
+
+/**
+ * Callback interface for |dumpGCAndCCLogsToFile|, below. Note that
+ * these method calls can occur before |dumpGCAndCCLogsToFile|
+ * returns.
+ */
+[scriptable, uuid(dc1b2b24-65bd-441b-b6bd-cb5825a7ed14)]
+interface nsIDumpGCAndCCLogsCallback : nsISupports
+{
+ /**
+ * Called whenever a process has successfully finished dumping its GC/CC logs.
+ * Incomplete dumps (e.g., if the child crashes or is killed due to memory
+ * exhaustion) are not reported.
+ *
+ * @param aGCLog The file that the GC log was written to.
+ *
+ * @param aCCLog The file that the CC log was written to.
+ *
+ * @param aIsParent indicates whether this log file pair is from the
+ * parent process.
+ */
+ void onDump(in nsIFile aGCLog,
+ in nsIFile aCCLog,
+ in bool aIsParent);
+
+ /**
+ * Called when GC/CC logging has finished, after all calls to |onDump|.
+ */
+ void onFinish();
+};
+
+[scriptable, builtinclass, uuid(48541b74-47ee-4a62-9557-7f4b809bda5c)]
+interface nsIMemoryInfoDumper : nsISupports
+{
+ /**
+ * This dumps gzipped memory reports for this process and its child
+ * processes. If a file of the given name exists, it will be overwritten.
+ *
+ * @param aFilename The output file.
+ *
+ * @param aFinishDumping The callback called on completion.
+ *
+ * @param aFinishDumpingData The environment for the callback.
+ *
+ * @param aAnonymize Should the reports be anonymized?
+ *
+ * Sample output, annotated with comments for explanatory purposes.
+ *
+ * {
+ * // The version number of the format, which will be incremented each time
+ * // backwards-incompatible changes are made. A mandatory integer.
+ * "version": 1
+ *
+ * // Equal to nsIMemoryReporterManager::hasMozMallocUsableSize. A
+ * // mandatory boolean.
+ * "hasMozMallocUsableSize": true,
+ *
+ * // The memory reports. A mandatory array.
+ * "reports": [
+ * // The properties correspond to the arguments of
+ * // nsIHandleReportCallback::callback. Every one is mandatory.
+ * {"process":"Main Process (pid 12345)", "path":"explicit/foo/bar",
+ * "kind":1, "units":0, "amount":2000000, "description":"Foo bar."},
+ * {"process":"Main Process (pid 12345)", "path":"heap-allocated",
+ * "kind":1, "units":0, "amount":3000000, "description":"Heap allocated."},
+ * {"process":"Main Process (pid 12345)", "path":"vsize",
+ * "kind":1, "units":0, "amount":10000000, "description":"Vsize."}
+ * ]
+ * }
+ */
+ void dumpMemoryReportsToNamedFile(in AString aFilename,
+ in nsIFinishDumpingCallback aFinishDumping,
+ in nsISupports aFinishDumpingData,
+ in boolean aAnonymize);
+
+ /**
+ * Similar to dumpMemoryReportsToNamedFile, this method dumps gzipped memory
+ * reports for this process and its child processes to files in the tmp
+ * directory called memory-reports-<identifier>-<pid>.json.gz (or something
+ * similar, such as memory-reports-<identifier>-<pid>-1.json.gz; no existing
+ * file will be overwritten).
+ *
+ * If DMD is enabled, this method also dumps gzipped DMD output for this
+ * process and its child processes to files in the tmp directory called
+ * dmd-<identifier>-<pid>.txt.gz (or something similar; again, no existing
+ * file will be overwritten).
+ *
+ * @param aIdentifier this identifier will appear in the filename of our
+ * about:memory dump and those of our children.
+ *
+ * If the identifier is empty, the implementation may set it arbitrarily
+ * and use that new value for its own dump and the dumps of its child
+ * processes. For example, the implementation may set |aIdentifier| to the
+ * number of seconds since the epoch.
+ *
+ * @param aAnonymize Should the reports be anonymized?
+ *
+ * @param aMinimizeMemoryUsage indicates whether we should run a series of
+ * gc/cc's in an attempt to reduce our memory usage before collecting our
+ * memory report.
+ */
+ void dumpMemoryInfoToTempDir(
+ in AString aIdentifier,
+ in boolean aAnonymize,
+ in boolean aMinimizeMemoryUsage);
+
+ /**
+ * Dump GC and CC logs to files in the OS's temp directory (or in
+ * $MOZ_CC_LOG_DIRECTORY, if that environment variable is specified).
+ *
+ * @param aIdentifier If aIdentifier is non-empty, this string will appear in
+ * the filenames of the logs we create (both for this process and, if
+ * aDumpChildProcesses is true, for our child processes).
+ *
+ * If aIdentifier is empty, the implementation may set it to an
+ * arbitrary value; for example, it may set aIdentifier to the number
+ * of seconds since the epoch.
+ *
+ * @param aDumpAllTraces indicates whether we should run an all-traces CC
+ * log. An all-traces log visits all objects currently eligible for cycle
+ * collection, while a non-all-traces log avoids visiting some objects
+ * which we know are reachable.
+ *
+ * All-traces logs are much bigger than the alternative, but they may be
+ * helpful when trying to understand why a particular object is alive. For
+ * example, a non-traces-log will skip references held by an active
+ * document; if your object is being held alive by such a document, you
+ * probably want to see those references.
+ *
+ * @param aDumpChildProcesses indicates whether we should call
+ * DumpGCAndCCLogsToFile in our child processes. If so, the child processes
+ * will dump their children, and so on.
+ *
+ */
+ void dumpGCAndCCLogsToFile(in AString aIdentifier,
+ in bool aDumpAllTraces,
+ in bool aDumpChildProcesses,
+ in nsIDumpGCAndCCLogsCallback aCallback);
+
+ /**
+ * Like |dumpGCAndCCLogsToFile|, but sends the logs to the given log
+ * sink object instead of accessing the filesystem directly, and
+ * dumps the current process only.
+ */
+ void dumpGCAndCCLogsToSink(in bool aDumpAllTraces,
+ in nsICycleCollectorLogSink aSink);
+};
diff --git a/xpcom/base/nsIMemoryReporter.idl b/xpcom/base/nsIMemoryReporter.idl
new file mode 100644
index 0000000000..9617877df6
--- /dev/null
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -0,0 +1,581 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+%{C++
+#include <stdio.h>
+%}
+
+interface mozIDOMWindowProxy;
+interface nsIRunnable;
+interface nsISimpleEnumerator;
+[ptr] native FILE(FILE);
+
+/*
+ * Memory reporters measure Firefox's memory usage. They are primarily used to
+ * generate the about:memory page. You should read
+ * https://developer.mozilla.org/en-US/docs/Mozilla/Performance/Memory_reporting
+ * before writing a memory reporter.
+ */
+
+[scriptable, function, uuid(62ef0e1c-dbd6-11e3-aa75-3c970e9f4238)]
+interface nsIMemoryReporterCallback : nsISupports
+{
+ /*
+ * The arguments to the callback are as follows.
+ *
+ *
+ * |process| The name of the process containing this reporter. Each
+ * reporter initially has "" in this field, indicating that it applies to the
+ * current process. (This is true even for reporters in a child process.)
+ * When a reporter from a child process is copied into the main process, the
+ * copy has its 'process' field set appropriately.
+ *
+ *
+ * |path| The path that this memory usage should be reported under. Paths
+ * are '/'-delimited, eg. "a/b/c".
+ *
+ * Each reporter can be viewed as representing a leaf node in a tree.
+ * Internal nodes of the tree don't have reporters. So, for example, the
+ * reporters "explicit/a/b", "explicit/a/c", "explicit/d/e", and
+ * "explicit/d/f" define this tree:
+ *
+ * explicit
+ * |--a
+ * | |--b [*]
+ * | \--c [*]
+ * \--d
+ * |--e [*]
+ * \--f [*]
+ *
+ * Nodes marked with a [*] have a reporter. Notice that the internal
+ * nodes are implicitly defined by the paths.
+ *
+ * Nodes within a tree should not overlap measurements, otherwise the
+ * parent node measurements will be double-counted. So in the example
+ * above, |b| should not count any allocations counted by |c|, and vice
+ * versa.
+ *
+ * All nodes within each tree must have the same units.
+ *
+ * If you want to include a '/' not as a path separator, e.g. because the
+ * path contains a URL, you need to convert each '/' in the URL to a '\'.
+ * Consumers of the path will undo this change. Any other '\' character
+ * in a path will also be changed. This is clumsy but hasn't caused any
+ * problems so far.
+ *
+ * The paths of all reporters form a set of trees. Trees can be
+ * "degenerate", i.e. contain a single entry with no '/'.
+ *
+ *
+ * |kind| There are three kinds of memory reporters.
+ *
+ * - HEAP: reporters measuring memory allocated by the heap allocator,
+ * e.g. by calling malloc, calloc, realloc, memalign, operator new, or
+ * operator new[]. Reporters in this category must have units
+ * UNITS_BYTES.
+ *
+ * - NONHEAP: reporters measuring memory which the program explicitly
+ * allocated, but does not live on the heap. Such memory is commonly
+ * allocated by calling one of the OS's memory-mapping functions (e.g.
+ * mmap, VirtualAlloc, or vm_allocate). Reporters in this category
+ * must have units UNITS_BYTES.
+ *
+ * - OTHER: reporters which don't fit into either of these categories.
+ * They can have any units.
+ *
+ * The kind only matters for reporters in the "explicit" tree;
+ * aboutMemory.js uses it to calculate "heap-unclassified".
+ *
+ *
+ * |units| The units on the reporter's amount. One of the following.
+ *
+ * - BYTES: The amount contains a number of bytes.
+ *
+ * - COUNT: The amount is an instantaneous count of things currently in
+ * existence. For instance, the number of tabs currently open would have
+ * units COUNT.
+ *
+ * - COUNT_CUMULATIVE: The amount contains the number of times some event
+ * has occurred since the application started up. For instance, the
+ * number of times the user has opened a new tab would have units
+ * COUNT_CUMULATIVE.
+ *
+ * The amount returned by a reporter with units COUNT_CUMULATIVE must
+ * never decrease over the lifetime of the application.
+ *
+ * - PERCENTAGE: The amount contains a fraction that should be expressed as
+ * a percentage. NOTE! The |amount| field should be given a value 100x
+ * the actual percentage; this number will be divided by 100 when shown.
+ * This allows a fractional percentage to be shown even though |amount| is
+ * an integer. E.g. if the actual percentage is 12.34%, |amount| should
+ * be 1234.
+ *
+ * Values greater than 100% are allowed.
+ *
+ *
+ * |amount| The numeric value reported by this memory reporter. Accesses
+ * can fail if something goes wrong when getting the amount.
+ *
+ *
+ * |description| A human-readable description of this memory usage report.
+ */
+ void callback(in ACString process, in AUTF8String path, in int32_t kind,
+ in int32_t units, in int64_t amount,
+ in AUTF8String description, in nsISupports data);
+};
+
+/*
+ * An nsIMemoryReporter reports one or more memory measurements via a
+ * callback function which is called once for each measurement.
+ *
+ * An nsIMemoryReporter that reports a single measurement is sometimes called a
+ * "uni-reporter". One that reports multiple measurements is sometimes called
+ * a "multi-reporter".
+ *
+ * aboutMemory.js is the most important consumer of memory reports. It
+ * places the following constraints on reports.
+ *
+ * - All reports within a single sub-tree must have the same units.
+ *
+ * - There may be an "explicit" tree. If present, it represents
+ * non-overlapping regions of memory that have been explicitly allocated with
+ * an OS-level allocation (e.g. mmap/VirtualAlloc/vm_allocate) or a
+ * heap-level allocation (e.g. malloc/calloc/operator new). Reporters in
+ * this tree must have kind HEAP or NONHEAP, units BYTES.
+ *
+ * It is preferred, but not required, that report descriptions use complete
+ * sentences (i.e. start with a capital letter and end with a period, or
+ * similar).
+ */
+[scriptable, uuid(92a36db1-46bd-4fe6-988e-47db47236d8b)]
+interface nsIMemoryReporter : nsISupports
+{
+ /*
+ * Run the reporter.
+ *
+ * If |anonymize| is true, the memory reporter should anonymize any
+ * privacy-sensitive details in memory report paths, by replacing them with a
+ * string such as "<anonymized>". Anonymized memory reports may be sent
+ * automatically via crash reports or telemetry.
+ *
+ * The following things are considered privacy-sensitive.
+ *
+ * - Content domains and URLs, and information derived from them.
+ * - Content data, such as strings.
+ * - Details about content code, such as filenames, function names or stack
+ * traces.
+ * - Details about or data from the user's system, such as filenames.
+ * - Running apps.
+ *
+ * In short, anything that could identify parts of the user's browsing
+ * history is considered privacy-sensitive.
+ *
+ * The following thing are not considered privacy-sensitive.
+ *
+ * - Chrome domains and URLs.
+ * - Information about installed extensions.
+ */
+ void collectReports(in nsIMemoryReporterCallback callback,
+ in nsISupports data,
+ in boolean anonymize);
+
+ /*
+ * Kinds. See the |kind| comment in nsIMemoryReporterCallback.
+ */
+ const int32_t KIND_NONHEAP = 0;
+ const int32_t KIND_HEAP = 1;
+ const int32_t KIND_OTHER = 2;
+
+ /*
+ * Units. See the |units| comment in nsIMemoryReporterCallback.
+ */
+ const int32_t UNITS_BYTES = 0;
+ const int32_t UNITS_COUNT = 1;
+ const int32_t UNITS_COUNT_CUMULATIVE = 2;
+ const int32_t UNITS_PERCENTAGE = 3;
+};
+
+[scriptable, function, uuid(548b3909-c04d-4ca6-8466-b8bee3837457)]
+interface nsIFinishReportingCallback : nsISupports
+{
+ void callback(in nsISupports data);
+};
+
+[scriptable, builtinclass, uuid(2998574d-8993-407a-b1a5-8ad7417653e1)]
+interface nsIMemoryReporterManager : nsISupports
+{
+ /*
+ * Initialize.
+ */
+ [must_use] void init();
+
+ /*
+ * Register the given nsIMemoryReporter. The Manager service will hold a
+ * strong reference to the given reporter, and will be responsible for freeing
+ * the reporter at shutdown. You may manually unregister the reporter with
+ * unregisterStrongReporter() at any point.
+ */
+ void registerStrongReporter(in nsIMemoryReporter reporter);
+ void registerStrongAsyncReporter(in nsIMemoryReporter reporter);
+
+ /*
+ * Like registerReporter, but the Manager service will hold a weak reference
+ * via a raw pointer to the given reporter. The reporter should be
+ * unregistered before shutdown.
+ * You cannot register JavaScript components with this function! Always
+ * register your JavaScript components with registerStrongReporter().
+ */
+ void registerWeakReporter(in nsIMemoryReporter reporter);
+ void registerWeakAsyncReporter(in nsIMemoryReporter reporter);
+
+ /*
+ * Unregister the given memory reporter, which must have been registered with
+ * registerStrongReporter(). You normally don't need to unregister your
+ * strong reporters, as nsIMemoryReporterManager will take care of that at
+ * shutdown.
+ */
+ void unregisterStrongReporter(in nsIMemoryReporter reporter);
+
+ /*
+ * Unregister the given memory reporter, which must have been registered with
+ * registerWeakReporter().
+ */
+ void unregisterWeakReporter(in nsIMemoryReporter reporter);
+
+ /*
+ * These functions should only be used for testing purposes.
+ */
+ void blockRegistrationAndHideExistingReporters();
+ void unblockRegistrationAndRestoreOriginalReporters();
+ void registerStrongReporterEvenIfBlocked(in nsIMemoryReporter aReporter);
+
+ /*
+ * Get memory reports for the current process and all child processes.
+ * |handleReport| is called for each report, and |finishReporting| is called
+ * once all reports have been handled.
+ *
+ * |finishReporting| is called even if, for example, some child processes
+ * fail to report back. However, calls to this method will silently and
+ * immediately abort -- and |finishReporting| will not be called -- if a
+ * previous getReports() call is still in flight, i.e. if it has not yet
+ * finished invoking |finishReporting|. The silent abort is because the
+ * in-flight request will finish soon, and the caller would very likely just
+ * catch and ignore any error anyway.
+ *
+ * If |anonymize| is true, it indicates that the memory reporters should
+ * anonymize any privacy-sensitive data (see above).
+ */
+ void getReports(in nsIMemoryReporterCallback handleReport,
+ in nsISupports handleReportData,
+ in nsIFinishReportingCallback finishReporting,
+ in nsISupports finishReportingData,
+ in boolean anonymize);
+
+ /*
+ * As above, but: If |minimizeMemoryUsage| is true, then each process will
+ * minimize its memory usage (see the |minimizeMemoryUsage| method) before
+ * gathering its report. If DMD is enabled and |DMDDumpIdent| is non-empty
+ * then write a DMD report to a file in the usual temporary directory (see
+ * |dumpMemoryInfoToTempDir| in |nsIMemoryInfoDumper|.)
+ */
+ [noscript] void
+ getReportsExtended(in nsIMemoryReporterCallback handleReport,
+ in nsISupports handleReportData,
+ in nsIFinishReportingCallback finishReporting,
+ in nsISupports finishReportingData,
+ in boolean anonymize,
+ in boolean minimizeMemoryUsage,
+ in AString DMDDumpIdent);
+
+ /*
+ * As above, but if DMD is enabled and |DMDFile| is non-null then
+ * write a DMD report to that file and close it.
+ */
+ [noscript] void
+ getReportsForThisProcessExtended(in nsIMemoryReporterCallback handleReport,
+ in nsISupports handleReportData,
+ in boolean anonymize,
+ in FILE DMDFile,
+ in nsIFinishReportingCallback finishReporting,
+ in nsISupports finishReportingData);
+
+ /*
+ * Called by an asynchronous memory reporter upon completion.
+ */
+ [noscript] void endReport();
+
+ /*
+ * The memory reporter manager, for the most part, treats reporters
+ * registered with it as a black box. However, there are some
+ * "distinguished" amounts (as could be reported by a memory reporter) that
+ * the manager provides as attributes, because they are sufficiently
+ * interesting that we want external code (e.g. telemetry) to be able to rely
+ * on them.
+ *
+ * Note that these are not reporters and so getReports() does not look at
+ * them. However, distinguished amounts can be embedded in a reporter.
+ *
+ * Access to these attributes can fail. In particular, some of them are not
+ * available on all platforms.
+ *
+ * If you add a new distinguished amount, please update
+ * toolkit/components/aboutmemory/tests/test_memoryReporters.xul.
+ *
+ * |vsize| (UNITS_BYTES) The virtual size, i.e. the amount of address space
+ * taken up.
+ *
+ * |vsizeMaxContiguous| (UNITS_BYTES) The size of the largest contiguous
+ * block of virtual memory.
+ *
+ * |resident| (UNITS_BYTES) The resident size (a.k.a. RSS or physical memory
+ * used).
+ *
+ * |residentFast| (UNITS_BYTES) This is like |resident|, but on Mac OS
+ * |resident| can purge pages, which is slow. It also affects the result of
+ * |residentFast|, and so |resident| and |residentFast| should not be used
+ * together.
+ *
+ * |residentPeak| (UNITS_BYTES) The peak resident size.
+ *
+ * |residentUnique| (UNITS_BYTES) The unique set size (a.k.a. USS).
+ *
+ * |heapAllocated| (UNITS_BYTES) Memory mapped by the heap allocator.
+ *
+ * |heapOverheadFraction| (UNITS_PERCENTAGE) In the heap allocator, this is
+ * the fraction of committed heap bytes that are overhead. Like all
+ * UNITS_PERCENTAGE measurements, its amount is multiplied by 100x so it can
+ * be represented by an int64_t.
+ *
+ * |JSMainRuntimeGCHeap| (UNITS_BYTES) Size of the main JS runtime's GC
+ * heap.
+ *
+ * |JSMainRuntimeTemporaryPeak| (UNITS_BYTES) Peak size of the transient
+ * storage in the main JSRuntime.
+ *
+ * |JSMainRuntimeCompartments{System,User}| (UNITS_COUNT) The number of
+ * {system,user} compartments in the main JS runtime.
+ *
+ * |imagesContentUsedUncompressed| (UNITS_BYTES) Memory used for decoded
+ * raster images in content.
+ *
+ * |storageSQLite| (UNITS_BYTES) Memory used by SQLite.
+ *
+ * |lowMemoryEvents{Virtual,Physical}| (UNITS_COUNT_CUMULATIVE) The number
+ * of low-{virtual,physical}-memory events that have occurred since the
+ * process started.
+ *
+ * |ghostWindows| (UNITS_COUNT) The number of ghost windows.
+ *
+ * |pageFaultsHard| (UNITS_COUNT_CUMULATIVE) The number of hard (a.k.a.
+ * major) page faults that have occurred since the process started.
+ */
+ [must_use] readonly attribute int64_t vsize;
+ [must_use] readonly attribute int64_t vsizeMaxContiguous;
+ [must_use] readonly attribute int64_t resident;
+ [must_use] readonly attribute int64_t residentFast;
+ [must_use] readonly attribute int64_t residentPeak;
+ [must_use] readonly attribute int64_t residentUnique;
+
+ [must_use] readonly attribute int64_t heapAllocated;
+ [must_use] readonly attribute int64_t heapOverheadFraction;
+
+ [must_use] readonly attribute int64_t JSMainRuntimeGCHeap;
+ [must_use] readonly attribute int64_t JSMainRuntimeTemporaryPeak;
+ [must_use] readonly attribute int64_t JSMainRuntimeCompartmentsSystem;
+ [must_use] readonly attribute int64_t JSMainRuntimeCompartmentsUser;
+
+ [must_use] readonly attribute int64_t imagesContentUsedUncompressed;
+
+ [must_use] readonly attribute int64_t storageSQLite;
+
+ [must_use] readonly attribute int64_t lowMemoryEventsVirtual;
+ [must_use] readonly attribute int64_t lowMemoryEventsPhysical;
+
+ [must_use] readonly attribute int64_t ghostWindows;
+
+ [must_use] readonly attribute int64_t pageFaultsHard;
+
+ /*
+ * This attribute indicates if moz_malloc_usable_size() works.
+ */
+ [infallible] readonly attribute boolean hasMozMallocUsableSize;
+
+ /*
+ * These attributes indicate DMD's status. "Enabled" means enabled at
+ * build-time.
+ */
+ [infallible] readonly attribute boolean isDMDEnabled;
+ [infallible] readonly attribute boolean isDMDRunning;
+
+ /*
+ * Run a series of GC/CC's in an attempt to minimize the application's memory
+ * usage. When we're finished, we invoke the given runnable if it's not
+ * null.
+ */
+ [must_use] void minimizeMemoryUsage(in nsIRunnable callback);
+
+ /*
+ * Measure the memory that is known to be owned by this tab, split up into
+ * several broad categories. Note that this will be an underestimate of the
+ * true number, due to imperfect memory reporter coverage (corresponding to
+ * about:memory's "heap-unclassified"), and due to some memory shared between
+ * tabs not being counted.
+ *
+ * The time taken for the measurement (split into JS and non-JS parts) is
+ * also returned.
+ */
+ [must_use]
+ void sizeOfTab(in mozIDOMWindowProxy window,
+ out int64_t jsObjectsSize, out int64_t jsStringsSize,
+ out int64_t jsOtherSize, out int64_t domSize,
+ out int64_t styleSize, out int64_t otherSize,
+ out int64_t totalSize,
+ out double jsMilliseconds, out double nonJSMilliseconds);
+};
+
+%{C++
+
+#include "js/TypeDecls.h"
+#include "nsStringGlue.h"
+#include "nsTArray.h"
+
+class nsPIDOMWindowOuter;
+
+// nsIHandleReportCallback is a better name, but keep nsIMemoryReporterCallback
+// around for backwards compatibility.
+typedef nsIMemoryReporterCallback nsIHandleReportCallback;
+
+namespace mozilla {
+
+// All the following registration/unregistration functions don't use
+// MOZ_MUST_USE because ignoring failures is common and reasonable.
+
+// Register a memory reporter. The manager service will hold a strong
+// reference to this reporter.
+XPCOM_API(nsresult) RegisterStrongMemoryReporter(nsIMemoryReporter* aReporter);
+XPCOM_API(nsresult) RegisterStrongAsyncMemoryReporter(nsIMemoryReporter* aReporter);
+
+// Register a memory reporter. The manager service will hold a weak reference
+// to this reporter.
+XPCOM_API(nsresult) RegisterWeakMemoryReporter(nsIMemoryReporter* aReporter);
+XPCOM_API(nsresult) RegisterWeakAsyncMemoryReporter(nsIMemoryReporter* aReporter);
+
+// Unregister a strong memory reporter.
+XPCOM_API(nsresult) UnregisterStrongMemoryReporter(nsIMemoryReporter* aReporter);
+
+// Unregister a weak memory reporter.
+XPCOM_API(nsresult) UnregisterWeakMemoryReporter(nsIMemoryReporter* aReporter);
+
+// The memory reporter manager provides access to several distinguished
+// amounts via attributes. Some of these amounts are provided by Gecko
+// components that cannot be accessed directly from XPCOM code. So we provide
+// the following functions for those components to be registered with the
+// manager.
+
+typedef int64_t (*InfallibleAmountFn)();
+
+#define DECL_REGISTER_DISTINGUISHED_AMOUNT(kind, name) \
+ nsresult Register##name##DistinguishedAmount(kind##AmountFn aAmountFn);
+#define DECL_UNREGISTER_DISTINGUISHED_AMOUNT(name) \
+ nsresult Unregister##name##DistinguishedAmount();
+
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeGCHeap)
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeTemporaryPeak)
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsSystem)
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsUser)
+
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, ImagesContentUsedUncompressed)
+DECL_UNREGISTER_DISTINGUISHED_AMOUNT(ImagesContentUsedUncompressed)
+
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, StorageSQLite)
+DECL_UNREGISTER_DISTINGUISHED_AMOUNT(StorageSQLite)
+
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsVirtual)
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsPhysical)
+
+DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, GhostWindows)
+
+#undef DECL_REGISTER_DISTINGUISHED_AMOUNT
+#undef DECL_UNREGISTER_DISTINGUISHED_AMOUNT
+
+// Likewise for per-tab measurement.
+
+typedef MOZ_MUST_USE nsresult (*JSSizeOfTabFn)(JSObject* aObj,
+ size_t* aJsObjectsSize,
+ size_t* aJsStringSize,
+ size_t* aJsPrivateSize,
+ size_t* aJsOtherSize);
+typedef MOZ_MUST_USE nsresult (*NonJSSizeOfTabFn)(nsPIDOMWindowOuter* aWindow,
+ size_t* aDomSize,
+ size_t* aStyleSize,
+ size_t* aOtherSize);
+
+nsresult RegisterJSSizeOfTab(JSSizeOfTabFn aSizeOfTabFn);
+nsresult RegisterNonJSSizeOfTab(NonJSSizeOfTabFn aSizeOfTabFn);
+
+}
+
+#if defined(MOZ_DMD)
+#if !defined(MOZ_MEMORY)
+#error "MOZ_DMD requires MOZ_MEMORY"
+#endif
+
+#include "DMD.h"
+
+#define MOZ_REPORT(ptr) mozilla::dmd::Report(ptr)
+#define MOZ_REPORT_ON_ALLOC(ptr) mozilla::dmd::ReportOnAlloc(ptr)
+
+#else
+
+#define MOZ_REPORT(ptr)
+#define MOZ_REPORT_ON_ALLOC(ptr)
+
+#endif // defined(MOZ_DMD)
+
+// Functions generated via this macro should be used by all traversal-based
+// memory reporters. Such functions return |moz_malloc_size_of(ptr)|; this
+// will always be zero on some obscure platforms.
+//
+// You might be wondering why we have a macro that creates multiple functions
+// that differ only in their name, instead of a single MallocSizeOf function.
+// It's mostly to help with DMD integration, though it sometimes also helps
+// with debugging and temporary ad hoc profiling. The function name chosen
+// doesn't matter greatly, but it's best to make it similar to the path used by
+// the relevant memory reporter(s).
+#define MOZ_DEFINE_MALLOC_SIZE_OF(fn) \
+ static size_t fn(const void* aPtr) \
+ { \
+ MOZ_REPORT(aPtr); \
+ return moz_malloc_size_of(aPtr); \
+ }
+
+// Functions generated by the next two macros should be used by wrapping
+// allocators that report heap blocks as soon as they are allocated and
+// unreport them as soon as they are freed. Such allocators are used in cases
+// where we have third-party code that we cannot modify. The two functions
+// must always be used in tandem.
+#define MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(fn) \
+ static size_t fn(const void* aPtr) \
+ { \
+ MOZ_REPORT_ON_ALLOC(aPtr); \
+ return moz_malloc_size_of(aPtr); \
+ }
+#define MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(fn) \
+ static size_t fn(const void* aPtr) \
+ { \
+ return moz_malloc_size_of(aPtr); \
+ }
+
+// This macro assumes the presence of appropriate |aHandleReport| and |aData|
+// variables. The (void) is there because we should always ignore the return
+// value of the callback, because callback failures aren't fatal.
+#define MOZ_COLLECT_REPORT(path, kind, units, amount, description) \
+ (void)aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(path), \
+ kind, units, amount, \
+ NS_LITERAL_CSTRING(description), aData)
+
+%}
diff --git a/xpcom/base/nsIMessageLoop.idl b/xpcom/base/nsIMessageLoop.idl
new file mode 100644
index 0000000000..b10d1c5761
--- /dev/null
+++ b/xpcom/base/nsIMessageLoop.idl
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIRunnable;
+
+/**
+ * This service allows access to the current thread's Chromium MessageLoop
+ * instance, with some extra sugar added. If you're calling from C++, it may
+ * or may not make sense for you to use this interface. If you're calling from
+ * JS, you don't have a choice!
+ *
+ * Right now, you can only call PostIdleTask(), and the wrath of khuey is
+ * stopping you from adding other methods.
+ *
+ * nsIMessageLoop's contractid is "@mozilla.org/message-loop;1".
+ */
+[scriptable, uuid(3E8C58E8-E52C-43E0-8E66-669CA788FF5F)]
+interface nsIMessageLoop : nsISupports
+{
+ /**
+ * Posts a task to be run when this thread's message loop is idle, or after
+ * ensureRunsAfterMS milliseconds have elapsed. (That is, the task is
+ * guaranteed to run /eventually/.)
+ *
+ * Note that if the event loop is busy, we will hold a reference to the task
+ * until ensureRunsAfterMS milliseconds have elapsed. Be careful when
+ * specifying long timeouts and tasks which hold references to windows or
+ * other large objects, because you can leak memory in a difficult-to-detect
+ * way!
+ */
+ void postIdleTask(in nsIRunnable task, in uint32_t ensureRunsAfterMS);
+};
diff --git a/xpcom/base/nsIMutable.idl b/xpcom/base/nsIMutable.idl
new file mode 100644
index 0000000000..c5c7baab0f
--- /dev/null
+++ b/xpcom/base/nsIMutable.idl
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * nsIMutable defines an interface to be implemented by objects which
+ * can be made immutable.
+ */
+[scriptable, uuid(321578d0-03c1-4d95-8821-021ac612d18d)]
+interface nsIMutable : nsISupports
+{
+ /**
+ * Control whether or not this object can be modified. If the flag is
+ * false, no modification is allowed. Once the flag has been set to false,
+ * it cannot be reset back to true -- attempts to do so throw
+ * NS_ERROR_INVALID_ARG.
+ */
+ attribute boolean mutable;
+};
diff --git a/xpcom/base/nsIProgrammingLanguage.idl b/xpcom/base/nsIProgrammingLanguage.idl
new file mode 100644
index 0000000000..57621168b8
--- /dev/null
+++ b/xpcom/base/nsIProgrammingLanguage.idl
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * Legacy constants for specifying programming languages.
+ *
+ * JAVASCRIPT is needed to avoid breaking addons that use it in nsIClassInfo
+ * to define fields that are no longer needed.
+ *
+ * UNKNOWN and JAVASCRIPT are also used in implementations of
+ * nsIStackFrame::language.
+ */
+
+[scriptable, uuid(02ad9f22-3c98-46f3-be4e-2f5c9299e29a)]
+interface nsIProgrammingLanguage : nsISupports
+{
+ const uint32_t UNKNOWN = 0;
+ // 1 is unused.
+ const uint32_t JAVASCRIPT = 2;
+};
diff --git a/xpcom/base/nsISecurityConsoleMessage.idl b/xpcom/base/nsISecurityConsoleMessage.idl
new file mode 100644
index 0000000000..917968d83b
--- /dev/null
+++ b/xpcom/base/nsISecurityConsoleMessage.idl
@@ -0,0 +1,20 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/*
+ * Holds localization message tag and message category
+ * for security related console messages.
+ */
+[uuid(FE9FC9B6-DDE2-11E2-A8F1-0A326188709B)]
+interface nsISecurityConsoleMessage : nsISupports
+{
+ attribute AString tag;
+ attribute AString category;
+};
+
+%{ C++
+#define NS_SECURITY_CONSOLE_MESSAGE_CONTRACTID "@mozilla.org/securityconsole/message;1"
+%}
diff --git a/xpcom/base/nsISizeOf.h b/xpcom/base/nsISizeOf.h
new file mode 100644
index 0000000000..35f5de44c7
--- /dev/null
+++ b/xpcom/base/nsISizeOf.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsISizeOf_h___
+#define nsISizeOf_h___
+
+#include "mozilla/MemoryReporting.h"
+#include "nsISupports.h"
+
+#define NS_ISIZEOF_IID \
+ {0x61d05579, 0xd7ec, 0x485c, \
+ { 0xa4, 0x0c, 0x31, 0xc7, 0x9a, 0x5c, 0xf9, 0xf3 }}
+
+class nsISizeOf : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISIZEOF_IID)
+
+ /**
+ * Measures the size of the things pointed to by the object.
+ */
+ virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
+
+ /**
+ * Like SizeOfExcludingThis, but also includes the size of the object itself.
+ */
+ virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsISizeOf, NS_ISIZEOF_IID)
+
+#endif /* nsISizeOf_h___ */
diff --git a/xpcom/base/nsIStatusReporter.idl b/xpcom/base/nsIStatusReporter.idl
new file mode 100644
index 0000000000..9f9245f498
--- /dev/null
+++ b/xpcom/base/nsIStatusReporter.idl
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsISimpleEnumerator;
+
+/*
+ * Status reporters show Firefox's service status.
+ */
+
+[scriptable, uuid(ffcb716c-deeb-44ea-9d9d-ab25dc6980a8)]
+interface nsIStatusReporter : nsISupports
+{
+ readonly attribute ACString name;
+ /*
+ * The name of the process containing this reporter. Each reporter initially
+ * has "" in this field, indicating that it applies to the current process.
+ */
+ readonly attribute ACString process;
+ /*
+ * A human-readable status description.
+ */
+ readonly attribute AUTF8String description;
+};
+
+[scriptable, uuid(fd531273-3319-4fcd-90f2-9f53876c3828)]
+interface nsIStatusReporterManager : nsISupports
+{
+
+ /*
+ * Return an enumerator of nsIStatusReporters that are currently registered.
+ */
+ nsISimpleEnumerator enumerateReporters();
+
+ /*
+ * Register the given nsIStatusReporter. After a reporter is registered,
+ * it will be available via enumerateReporters(). The Manager service
+ * will hold a strong reference to the given reporter.
+ */
+ void registerReporter(in nsIStatusReporter reporter);
+
+ /*
+ * Unregister the given status reporter.
+ */
+ void unregisterReporter(in nsIStatusReporter reporter);
+
+ /*
+ * Initialize.
+ */
+ void init();
+
+ /*
+ * Dump service status as a json file
+ */
+ void dumpReports();
+};
+
+%{C++
+
+/*
+ * Note that this defaults 'process' to "", which is usually what's desired.
+ */
+#define NS_STATUS_REPORTER_IMPLEMENT(_classname, _name, _desc_Function) \
+ class StatusReporter_##_classname final : public nsIStatusReporter { \
+ ~StatusReporter_##_classname() {} \
+ public: \
+ NS_DECL_ISUPPORTS \
+ NS_IMETHOD GetName(nsACString &name) override \
+ { name.AssignLiteral(_name); return NS_OK; } \
+ NS_IMETHOD GetProcess(nsACString &process) override \
+ { process.Truncate(); return NS_OK; } \
+ NS_IMETHOD GetDescription(nsACString &desc) override \
+ { _desc_Function(desc); return NS_OK; } \
+ }; \
+ NS_IMPL_ISUPPORTS(StatusReporter_##_classname, nsIStatusReporter)
+
+#define NS_STATUS_REPORTER_NAME(_classname) StatusReporter_##_classname
+
+nsresult NS_RegisterStatusReporter(nsIStatusReporter *reporter);
+nsresult NS_UnregisterStatusReporter(nsIStatusReporter *reporter);
+nsresult NS_DumpStatusReporter();
+
+#define NS_STATUS_REPORTER_MANAGER_CID \
+{ 0xe8eb4e7e, 0xf2cf, 0x45e5, \
+{ 0xb8, 0xa4, 0x6a, 0x0f, 0x50, 0x18, 0x84, 0x63 } }
+%}
diff --git a/xpcom/base/nsISupports.idl b/xpcom/base/nsISupports.idl
new file mode 100644
index 0000000000..45ea44ecb4
--- /dev/null
+++ b/xpcom/base/nsISupports.idl
@@ -0,0 +1,44 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * The mother of all xpcom interfaces.
+ */
+
+/* In order to get both the right typelib and the right header we force
+* the 'real' output from xpidl to be commented out in the generated header
+* and includes a copy of the original nsISupports.h. This is all just to deal
+* with the Mac specific ": public __comobject" thing.
+*/
+
+#include "nsrootidl.idl"
+
+%{C++
+/*
+ * Start commenting out the C++ versions of the below in the output header
+ */
+#if 0
+%}
+
+[scriptable, uuid(00000000-0000-0000-c000-000000000046)]
+interface nsISupports {
+ void QueryInterface(in nsIIDRef uuid,
+ [iid_is(uuid),retval] out nsQIResult result);
+ [noscript, notxpcom] nsrefcnt AddRef();
+ [noscript, notxpcom] nsrefcnt Release();
+};
+
+%{C++
+/*
+ * End commenting out the C++ versions of the above in the output header
+ */
+#endif
+%}
+
+
+%{C++
+#include "nsISupportsBase.h"
+#include "nsISupportsUtils.h"
+%}
diff --git a/xpcom/base/nsISupportsBase.h b/xpcom/base/nsISupportsBase.h
new file mode 100644
index 0000000000..bf5518b810
--- /dev/null
+++ b/xpcom/base/nsISupportsBase.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// IWYU pragma: private, include "nsISupports.h"
+
+#ifndef nsISupportsBase_h__
+#define nsISupportsBase_h__
+
+#ifndef nscore_h___
+#include "nscore.h"
+#endif
+
+#ifndef nsID_h__
+#include "nsID.h"
+#endif
+
+
+/*@{*/
+/**
+ * IID for the nsISupports interface
+ * {00000000-0000-0000-c000-000000000046}
+ *
+ * To maintain binary compatibility with COM's IUnknown, we define the IID
+ * of nsISupports to be the same as that of COM's IUnknown.
+ */
+#define NS_ISUPPORTS_IID \
+ { 0x00000000, 0x0000, 0x0000, \
+ {0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46} }
+
+/**
+ * Basic component object model interface. Objects which implement
+ * this interface support runtime interface discovery (QueryInterface)
+ * and a reference counted memory model (AddRef/Release). This is
+ * modelled after the win32 IUnknown API.
+ */
+class NS_NO_VTABLE nsISupports
+{
+public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISUPPORTS_IID)
+
+ /**
+ * @name Methods
+ */
+
+ //@{
+ /**
+ * A run time mechanism for interface discovery.
+ * @param aIID [in] A requested interface IID
+ * @param aInstancePtr [out] A pointer to an interface pointer to
+ * receive the result.
+ * @return <b>NS_OK</b> if the interface is supported by the associated
+ * instance, <b>NS_NOINTERFACE</b> if it is not.
+ *
+ * aInstancePtr must not be null.
+ */
+ NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) = 0;
+ /**
+ * Increases the reference count for this interface.
+ * The associated instance will not be deleted unless
+ * the reference count is returned to zero.
+ *
+ * @return The resulting reference count.
+ */
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
+
+ /**
+ * Decreases the reference count for this interface.
+ * Generally, if the reference count returns to zero,
+ * the associated instance is deleted.
+ *
+ * @return The resulting reference count.
+ */
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
+
+ //@}
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsISupports, NS_ISUPPORTS_IID)
+
+/*@}*/
+
+#endif
diff --git a/xpcom/base/nsIUUIDGenerator.idl b/xpcom/base/nsIUUIDGenerator.idl
new file mode 100644
index 0000000000..8715ed622b
--- /dev/null
+++ b/xpcom/base/nsIUUIDGenerator.idl
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+[ptr] native nsNonConstIDPtr(nsID);
+
+/**
+ * nsIUUIDGenerator is implemented by a service that can generate
+ * universally unique identifiers, ideally using any platform-native
+ * method for generating UUIDs.
+ */
+[scriptable, uuid(138ad1b2-c694-41cc-b201-333ce936d8b8)]
+interface nsIUUIDGenerator : nsISupports
+{
+ /**
+ * Obtains a new UUID using appropriate platform-specific methods to
+ * obtain a nsID that can be considered to be globally unique.
+ *
+ * @returns an nsID filled in with a new UUID.
+ *
+ * @throws NS_ERROR_FAILURE if a UUID cannot be generated (e.g. if
+ * an underlying source of randomness is not available)
+ */
+ nsIDPtr generateUUID();
+
+ /**
+ * Obtain a new UUID like the generateUUID method, but place it in
+ * the provided nsID pointer instead of allocating a new nsID.
+ *
+ * @param id an existing nsID pointer where the UUID will be stored.
+ *
+ * @throws NS_ERROR_FAILURE if a UUID cannot be generated (e.g. if
+ * an underlying source of randomness is not available)
+ */
+ [noscript] void generateUUIDInPlace(in nsNonConstIDPtr id);
+};
diff --git a/xpcom/base/nsIVersionComparator.idl b/xpcom/base/nsIVersionComparator.idl
new file mode 100644
index 0000000000..1a33cb74e7
--- /dev/null
+++ b/xpcom/base/nsIVersionComparator.idl
@@ -0,0 +1,49 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * Version strings are dot-separated sequences of version-parts.
+ *
+ * A version-part consists of up to four parts, all of which are optional:
+ *
+ * <number-a><string-b><number-c><string-d (everything else)>
+ *
+ * A version-part may also consist of a single asterisk "*" which indicates
+ * "infinity".
+ *
+ * Numbers are base-10, and are zero if left out.
+ * Strings are compared bytewise.
+ *
+ * For additional backwards compatibility, if "string-b" is "+" then
+ * "number-a" is incremented by 1 and "string-b" becomes "pre".
+ *
+ * 1.0pre1
+ * < 1.0pre2
+ * < 1.0 == 1.0.0 == 1.0.0.0
+ * < 1.1pre == 1.1pre0 == 1.0+
+ * < 1.1pre1a
+ * < 1.1pre1
+ * < 1.1pre10a
+ * < 1.1pre10
+ *
+ * Although not required by this interface, it is recommended that
+ * numbers remain within the limits of a signed char, i.e. -127 to 128.
+ */
+[scriptable, uuid(e6cd620a-edbb-41d2-9e42-9a2ffc8107f3)]
+interface nsIVersionComparator : nsISupports
+{
+ /**
+ * Compare two version strings
+ * @param A The first version
+ * @param B The second version
+ * @returns < 0 if A < B
+ * = 0 if A == B
+ * > 0 if A > B
+ */
+ long compare(in ACString A, in ACString B);
+};
+
diff --git a/xpcom/base/nsIWeakReference.idl b/xpcom/base/nsIWeakReference.idl
new file mode 100644
index 0000000000..73390b15f7
--- /dev/null
+++ b/xpcom/base/nsIWeakReference.idl
@@ -0,0 +1,74 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+%{C++
+#include "mozilla/MemoryReporting.h"
+%}
+
+/**
+ * An instance of |nsIWeakReference| is a proxy object that cooperates with
+ * its referent to give clients a non-owning, non-dangling reference. Clients
+ * own the proxy, and should generally manage it with an |nsCOMPtr| (see the
+ * type |nsWeakPtr| for a |typedef| name that stands out) as they would any
+ * other XPCOM object. The |QueryReferent| member function provides a
+ * (hopefully short-lived) owning reference on demand, through which clients
+ * can get useful access to the referent, while it still exists.
+ *
+ * @version 1.0
+ * @see nsISupportsWeakReference
+ * @see nsWeakReference
+ * @see nsWeakPtr
+ */
+[scriptable, uuid(9188bc85-f92e-11d2-81ef-0060083a0bcf)]
+interface nsIWeakReference : nsISupports
+ {
+ /**
+ * |QueryReferent| queries the referent, if it exists, and like |QueryInterface|, produces
+ * an owning reference to the desired interface. It is designed to look and act exactly
+ * like (a proxied) |QueryInterface|. Don't hold on to the produced interface permanently;
+ * that would defeat the purpose of using a non-owning |nsIWeakReference| in the first place.
+ */
+ void QueryReferent( in nsIIDRef uuid, [iid_is(uuid), retval] out nsQIResult result );
+
+%{C++
+ virtual size_t SizeOfOnlyThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
+%}
+ };
+
+
+/**
+ * |nsISupportsWeakReference| is a factory interface which produces appropriate
+ * instances of |nsIWeakReference|. Weak references in this scheme can only be
+ * produced for objects that implement this interface.
+ *
+ * @version 1.0
+ * @see nsIWeakReference
+ * @see nsSupportsWeakReference
+ */
+[scriptable, uuid(9188bc86-f92e-11d2-81ef-0060083a0bcf)]
+interface nsISupportsWeakReference : nsISupports
+ {
+ /**
+ * |GetWeakReference| produces an appropriate instance of |nsIWeakReference|.
+ * As with all good XPCOM `getters', you own the resulting interface and should
+ * manage it with an |nsCOMPtr|.
+ *
+ * @see nsIWeakReference
+ * @see nsWeakPtr
+ * @see nsCOMPtr
+ */
+ nsIWeakReference GetWeakReference();
+ };
+
+
+%{C++
+#ifdef MOZILLA_INTERNAL_API
+#include "nsIWeakReferenceUtils.h"
+#endif
+%}
+
diff --git a/xpcom/base/nsInterfaceRequestorAgg.cpp b/xpcom/base/nsInterfaceRequestorAgg.cpp
new file mode 100644
index 0000000000..7e5cd83da8
--- /dev/null
+++ b/xpcom/base/nsInterfaceRequestorAgg.cpp
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "nsInterfaceRequestorAgg.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsCOMPtr.h"
+#include "mozilla/Attributes.h"
+#include "nsThreadUtils.h"
+#include "nsProxyRelease.h"
+
+class nsInterfaceRequestorAgg final : public nsIInterfaceRequestor
+{
+public:
+ // XXX This needs to support threadsafe refcounting until we fix bug 243591.
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIINTERFACEREQUESTOR
+
+ nsInterfaceRequestorAgg(nsIInterfaceRequestor* aFirst,
+ nsIInterfaceRequestor* aSecond,
+ nsIEventTarget* aConsumerTarget = nullptr)
+ : mFirst(aFirst)
+ , mSecond(aSecond)
+ , mConsumerTarget(aConsumerTarget)
+ {
+ if (!mConsumerTarget) {
+ mConsumerTarget = NS_GetCurrentThread();
+ }
+ }
+
+private:
+ ~nsInterfaceRequestorAgg();
+
+ nsCOMPtr<nsIInterfaceRequestor> mFirst, mSecond;
+ nsCOMPtr<nsIEventTarget> mConsumerTarget;
+};
+
+NS_IMPL_ISUPPORTS(nsInterfaceRequestorAgg, nsIInterfaceRequestor)
+
+NS_IMETHODIMP
+nsInterfaceRequestorAgg::GetInterface(const nsIID& aIID, void** aResult)
+{
+ nsresult rv = NS_ERROR_NO_INTERFACE;
+ if (mFirst) {
+ rv = mFirst->GetInterface(aIID, aResult);
+ }
+ if (mSecond && NS_FAILED(rv)) {
+ rv = mSecond->GetInterface(aIID, aResult);
+ }
+ return rv;
+}
+
+nsInterfaceRequestorAgg::~nsInterfaceRequestorAgg()
+{
+ NS_ProxyRelease(mConsumerTarget, mFirst.forget());
+ NS_ProxyRelease(mConsumerTarget, mSecond.forget());
+}
+
+nsresult
+NS_NewInterfaceRequestorAggregation(nsIInterfaceRequestor* aFirst,
+ nsIInterfaceRequestor* aSecond,
+ nsIInterfaceRequestor** aResult)
+{
+ *aResult = new nsInterfaceRequestorAgg(aFirst, aSecond);
+ if (!*aResult) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+nsresult
+NS_NewInterfaceRequestorAggregation(nsIInterfaceRequestor* aFirst,
+ nsIInterfaceRequestor* aSecond,
+ nsIEventTarget* aTarget,
+ nsIInterfaceRequestor** aResult)
+{
+ *aResult = new nsInterfaceRequestorAgg(aFirst, aSecond, aTarget);
+ if (!*aResult) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
diff --git a/xpcom/base/nsInterfaceRequestorAgg.h b/xpcom/base/nsInterfaceRequestorAgg.h
new file mode 100644
index 0000000000..62b4f61f93
--- /dev/null
+++ b/xpcom/base/nsInterfaceRequestorAgg.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 nsInterfaceRequestorAgg_h__
+#define nsInterfaceRequestorAgg_h__
+
+#include "nsError.h"
+
+class nsIEventTarget;
+class nsIInterfaceRequestor;
+
+/**
+ * This function returns an instance of nsIInterfaceRequestor that aggregates
+ * two nsIInterfaceRequestor instances. Its GetInterface method queries
+ * aFirst for the requested interface and will query aSecond only if aFirst
+ * failed to supply the requested interface. Both aFirst and aSecond may
+ * be null, and will be released on the main thread when the aggregator is
+ * destroyed.
+ */
+extern nsresult
+NS_NewInterfaceRequestorAggregation(nsIInterfaceRequestor* aFirst,
+ nsIInterfaceRequestor* aSecond,
+ nsIInterfaceRequestor** aResult);
+
+/**
+ * Like the previous method, but aFirst and aSecond will be released on the
+ * provided target thread.
+ */
+extern nsresult
+NS_NewInterfaceRequestorAggregation(nsIInterfaceRequestor* aFirst,
+ nsIInterfaceRequestor* aSecond,
+ nsIEventTarget* aTarget,
+ nsIInterfaceRequestor** aResult);
+
+#endif // !defined( nsInterfaceRequestorAgg_h__ )
diff --git a/xpcom/base/nsMacUtilsImpl.cpp b/xpcom/base/nsMacUtilsImpl.cpp
new file mode 100644
index 0000000000..e2706047a6
--- /dev/null
+++ b/xpcom/base/nsMacUtilsImpl.cpp
@@ -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/. */
+
+#include "nsMacUtilsImpl.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+
+NS_IMPL_ISUPPORTS(nsMacUtilsImpl, nsIMacUtils)
+
+nsresult
+nsMacUtilsImpl::GetArchString(nsAString& aArchString)
+{
+ if (!mBinaryArchs.IsEmpty()) {
+ aArchString.Assign(mBinaryArchs);
+ return NS_OK;
+ }
+
+ aArchString.Truncate();
+
+ bool foundPPC = false,
+ foundX86 = false,
+ foundPPC64 = false,
+ foundX86_64 = false;
+
+ CFBundleRef mainBundle = ::CFBundleGetMainBundle();
+ if (!mainBundle) {
+ return NS_ERROR_FAILURE;
+ }
+
+ CFArrayRef archList = ::CFBundleCopyExecutableArchitectures(mainBundle);
+ if (!archList) {
+ return NS_ERROR_FAILURE;
+ }
+
+ CFIndex archCount = ::CFArrayGetCount(archList);
+ for (CFIndex i = 0; i < archCount; i++) {
+ CFNumberRef arch =
+ static_cast<CFNumberRef>(::CFArrayGetValueAtIndex(archList, i));
+
+ int archInt = 0;
+ if (!::CFNumberGetValue(arch, kCFNumberIntType, &archInt)) {
+ ::CFRelease(archList);
+ return NS_ERROR_FAILURE;
+ }
+
+ if (archInt == kCFBundleExecutableArchitecturePPC) {
+ foundPPC = true;
+ } else if (archInt == kCFBundleExecutableArchitectureI386) {
+ foundX86 = true;
+ } else if (archInt == kCFBundleExecutableArchitecturePPC64) {
+ foundPPC64 = true;
+ } else if (archInt == kCFBundleExecutableArchitectureX86_64) {
+ foundX86_64 = true;
+ }
+ }
+
+ ::CFRelease(archList);
+
+ // The order in the string must always be the same so
+ // don't do this in the loop.
+ if (foundPPC) {
+ mBinaryArchs.AppendLiteral("ppc");
+ }
+
+ if (foundX86) {
+ if (!mBinaryArchs.IsEmpty()) {
+ mBinaryArchs.Append('-');
+ }
+ mBinaryArchs.AppendLiteral("i386");
+ }
+
+ if (foundPPC64) {
+ if (!mBinaryArchs.IsEmpty()) {
+ mBinaryArchs.Append('-');
+ }
+ mBinaryArchs.AppendLiteral("ppc64");
+ }
+
+ if (foundX86_64) {
+ if (!mBinaryArchs.IsEmpty()) {
+ mBinaryArchs.Append('-');
+ }
+ mBinaryArchs.AppendLiteral("x86_64");
+ }
+
+ aArchString.Assign(mBinaryArchs);
+
+ return (aArchString.IsEmpty() ? NS_ERROR_FAILURE : NS_OK);
+}
+
+NS_IMETHODIMP
+nsMacUtilsImpl::GetIsUniversalBinary(bool* aIsUniversalBinary)
+{
+ if (NS_WARN_IF(!aIsUniversalBinary)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ *aIsUniversalBinary = false;
+
+ nsAutoString archString;
+ nsresult rv = GetArchString(archString);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // The delimiter char in the arch string is '-', so if that character
+ // is in the string we know we have multiple architectures.
+ *aIsUniversalBinary = (archString.Find("-") > -1);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMacUtilsImpl::GetArchitecturesInBinary(nsAString& aArchString)
+{
+ return GetArchString(aArchString);
+}
+
+// True when running under binary translation (Rosetta).
+NS_IMETHODIMP
+nsMacUtilsImpl::GetIsTranslated(bool* aIsTranslated)
+{
+#ifdef __ppc__
+ static bool sInitialized = false;
+
+ // Initialize sIsNative to 1. If the sysctl fails because it doesn't
+ // exist, then translation is not possible, so the process must not be
+ // running translated.
+ static int32_t sIsNative = 1;
+
+ if (!sInitialized) {
+ size_t sz = sizeof(sIsNative);
+ sysctlbyname("sysctl.proc_native", &sIsNative, &sz, nullptr, 0);
+ sInitialized = true;
+ }
+
+ *aIsTranslated = !sIsNative;
+#else
+ // Translation only exists for ppc code. Other architectures aren't
+ // translated.
+ *aIsTranslated = false;
+#endif
+
+ return NS_OK;
+}
diff --git a/xpcom/base/nsMacUtilsImpl.h b/xpcom/base/nsMacUtilsImpl.h
new file mode 100644
index 0000000000..4a480ffe54
--- /dev/null
+++ b/xpcom/base/nsMacUtilsImpl.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 nsMacUtilsImpl_h___
+#define nsMacUtilsImpl_h___
+
+#include "nsIMacUtils.h"
+#include "nsString.h"
+#include "mozilla/Attributes.h"
+
+class nsMacUtilsImpl final : public nsIMacUtils
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMACUTILS
+
+ nsMacUtilsImpl()
+ {
+ }
+
+private:
+ ~nsMacUtilsImpl()
+ {
+ }
+
+ nsresult GetArchString(nsAString& aArchString);
+
+ // A string containing a "-" delimited list of architectures
+ // in our binary.
+ nsString mBinaryArchs;
+};
+
+// Global singleton service
+// 697BD3FD-43E5-41CE-AD5E-C339175C0818
+#define NS_MACUTILSIMPL_CID \
+ {0x697BD3FD, 0x43E5, 0x41CE, {0xAD, 0x5E, 0xC3, 0x39, 0x17, 0x5C, 0x08, 0x18}}
+#define NS_MACUTILSIMPL_CONTRACTID "@mozilla.org/xpcom/mac-utils;1"
+
+#endif /* nsMacUtilsImpl_h___ */
diff --git a/xpcom/base/nsMemoryImpl.cpp b/xpcom/base/nsMemoryImpl.cpp
new file mode 100644
index 0000000000..1d1576fbd4
--- /dev/null
+++ b/xpcom/base/nsMemoryImpl.cpp
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "nsMemoryImpl.h"
+#include "nsThreadUtils.h"
+
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsISimpleEnumerator.h"
+
+#include "nsCOMPtr.h"
+#include "mozilla/Services.h"
+
+#ifdef ANDROID
+#include <stdio.h>
+
+// Minimum memory threshold for a device to be considered
+// a low memory platform. This value has be in sync with
+// Java's equivalent threshold, defined in
+// mobile/android/base/util/HardwareUtils.java
+#define LOW_MEMORY_THRESHOLD_KB (384 * 1024)
+#endif
+
+static nsMemoryImpl sGlobalMemory;
+
+NS_IMPL_QUERY_INTERFACE(nsMemoryImpl, nsIMemory)
+
+NS_IMETHODIMP
+nsMemoryImpl::HeapMinimize(bool aImmediate)
+{
+ return FlushMemory(u"heap-minimize", aImmediate);
+}
+
+NS_IMETHODIMP
+nsMemoryImpl::IsLowMemoryPlatform(bool* aResult)
+{
+#ifdef ANDROID
+ static int sLowMemory = -1; // initialize to unknown, lazily evaluate to 0 or 1
+ if (sLowMemory == -1) {
+ sLowMemory = 0; // assume "not low memory" in case file operations fail
+ *aResult = false;
+
+ // check if MemTotal from /proc/meminfo is less than LOW_MEMORY_THRESHOLD_KB
+ FILE* fd = fopen("/proc/meminfo", "r");
+ if (!fd) {
+ return NS_OK;
+ }
+ uint64_t mem = 0;
+ int rv = fscanf(fd, "MemTotal: %llu kB", &mem);
+ if (fclose(fd)) {
+ return NS_OK;
+ }
+ if (rv != 1) {
+ return NS_OK;
+ }
+ sLowMemory = (mem < LOW_MEMORY_THRESHOLD_KB) ? 1 : 0;
+ }
+ *aResult = (sLowMemory == 1);
+#else
+ *aResult = false;
+#endif
+ return NS_OK;
+}
+
+/*static*/ nsresult
+nsMemoryImpl::Create(nsISupports* aOuter, const nsIID& aIID, void** aResult)
+{
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+ return sGlobalMemory.QueryInterface(aIID, aResult);
+}
+
+nsresult
+nsMemoryImpl::FlushMemory(const char16_t* aReason, bool aImmediate)
+{
+ nsresult rv = NS_OK;
+
+ if (aImmediate) {
+ // They've asked us to run the flusher *immediately*. We've
+ // got to be on the UI main thread for us to be able to do
+ // that...are we?
+ if (!NS_IsMainThread()) {
+ NS_ERROR("can't synchronously flush memory: not on UI thread");
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ bool lastVal = sIsFlushing.exchange(true);
+ if (lastVal) {
+ return NS_OK;
+ }
+
+ PRIntervalTime now = PR_IntervalNow();
+
+ // Run the flushers immediately if we can; otherwise, proxy to the
+ // UI thread an run 'em asynchronously.
+ if (aImmediate) {
+ rv = RunFlushers(aReason);
+ } else {
+ // Don't broadcast more than once every 1000ms to avoid being noisy
+ if (PR_IntervalToMicroseconds(now - sLastFlushTime) > 1000) {
+ sFlushEvent.mReason = aReason;
+ rv = NS_DispatchToMainThread(&sFlushEvent);
+ }
+ }
+
+ sLastFlushTime = now;
+ return rv;
+}
+
+nsresult
+nsMemoryImpl::RunFlushers(const char16_t* aReason)
+{
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (os) {
+
+ // Instead of:
+ // os->NotifyObservers(this, "memory-pressure", aReason);
+ // we are going to do this manually to see who/what is
+ // deallocating.
+
+ nsCOMPtr<nsISimpleEnumerator> e;
+ os->EnumerateObservers("memory-pressure", getter_AddRefs(e));
+
+ if (e) {
+ nsCOMPtr<nsIObserver> observer;
+ bool loop = true;
+
+ while (NS_SUCCEEDED(e->HasMoreElements(&loop)) && loop) {
+ nsCOMPtr<nsISupports> supports;
+ e->GetNext(getter_AddRefs(supports));
+
+ if (!supports) {
+ continue;
+ }
+
+ observer = do_QueryInterface(supports);
+ observer->Observe(observer, "memory-pressure", aReason);
+ }
+ }
+ }
+
+ sIsFlushing = false;
+ return NS_OK;
+}
+
+// XXX need NS_IMPL_STATIC_ADDREF/RELEASE
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsMemoryImpl::FlushEvent::AddRef()
+{
+ return 2;
+}
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsMemoryImpl::FlushEvent::Release()
+{
+ return 1;
+}
+NS_IMPL_QUERY_INTERFACE(nsMemoryImpl::FlushEvent, nsIRunnable)
+
+NS_IMETHODIMP
+nsMemoryImpl::FlushEvent::Run()
+{
+ sGlobalMemory.RunFlushers(mReason);
+ return NS_OK;
+}
+
+mozilla::Atomic<bool>
+nsMemoryImpl::sIsFlushing;
+
+PRIntervalTime
+nsMemoryImpl::sLastFlushTime = 0;
+
+nsMemoryImpl::FlushEvent
+nsMemoryImpl::sFlushEvent;
+
+nsresult
+NS_GetMemoryManager(nsIMemory** aResult)
+{
+ return sGlobalMemory.QueryInterface(NS_GET_IID(nsIMemory), (void**)aResult);
+}
diff --git a/xpcom/base/nsMemoryImpl.h b/xpcom/base/nsMemoryImpl.h
new file mode 100644
index 0000000000..9e2d46d388
--- /dev/null
+++ b/xpcom/base/nsMemoryImpl.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsMemoryImpl_h__
+#define nsMemoryImpl_h__
+
+#include "mozilla/Atomics.h"
+
+#include "nsIMemory.h"
+#include "nsIRunnable.h"
+
+// nsMemoryImpl is a static object. We can do this because it doesn't have
+// a constructor/destructor or any instance members. Please don't add
+// instance member variables, only static member variables.
+
+class nsMemoryImpl : public nsIMemory
+{
+public:
+ // We don't use the generic macros because we are a special static object
+ NS_IMETHOD QueryInterface(REFNSIID aIID, void** aResult) override;
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override
+ {
+ return 1;
+ }
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) override
+ {
+ return 1;
+ }
+
+ NS_DECL_NSIMEMORY
+
+ static nsresult Create(nsISupports* aOuter,
+ const nsIID& aIID, void** aResult);
+
+ nsresult FlushMemory(const char16_t* aReason, bool aImmediate);
+ nsresult RunFlushers(const char16_t* aReason);
+
+protected:
+ struct FlushEvent : public nsIRunnable
+ {
+ constexpr FlushEvent() : mReason(nullptr) {}
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIRUNNABLE
+ const char16_t* mReason;
+ };
+
+ static mozilla::Atomic<bool> sIsFlushing;
+ static FlushEvent sFlushEvent;
+ static PRIntervalTime sLastFlushTime;
+};
+
+#endif // nsMemoryImpl_h__
diff --git a/xpcom/base/nsMemoryInfoDumper.cpp b/xpcom/base/nsMemoryInfoDumper.cpp
new file mode 100644
index 0000000000..06453b126f
--- /dev/null
+++ b/xpcom/base/nsMemoryInfoDumper.cpp
@@ -0,0 +1,830 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/JSONWriter.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/nsMemoryInfoDumper.h"
+#include "mozilla/DebugOnly.h"
+#include "nsDumpUtils.h"
+
+#include "mozilla/Unused.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ContentChild.h"
+#include "nsIConsoleService.h"
+#include "nsCycleCollector.h"
+#include "nsICycleCollectorListener.h"
+#include "nsIMemoryReporter.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsGZFileWriter.h"
+#include "nsJSEnvironment.h"
+#include "nsPrintfCString.h"
+#include "nsISimpleEnumerator.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIFile.h"
+
+#ifdef XP_WIN
+#include <process.h>
+#ifndef getpid
+#define getpid _getpid
+#endif
+#else
+#include <unistd.h>
+#endif
+
+#ifdef XP_UNIX
+#define MOZ_SUPPORTS_FIFO 1
+#endif
+
+#if defined(XP_LINUX) || defined(__FreeBSD__)
+#define MOZ_SUPPORTS_RT_SIGNALS 1
+#endif
+
+#if defined(MOZ_SUPPORTS_RT_SIGNALS)
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+
+#if defined(MOZ_SUPPORTS_FIFO)
+#include "mozilla/Preferences.h"
+#endif
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+namespace {
+
+class DumpMemoryInfoToTempDirRunnable : public Runnable
+{
+public:
+ DumpMemoryInfoToTempDirRunnable(const nsAString& aIdentifier,
+ bool aAnonymize, bool aMinimizeMemoryUsage)
+ : mIdentifier(aIdentifier)
+ , mAnonymize(aAnonymize)
+ , mMinimizeMemoryUsage(aMinimizeMemoryUsage)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ nsCOMPtr<nsIMemoryInfoDumper> dumper =
+ do_GetService("@mozilla.org/memory-info-dumper;1");
+ dumper->DumpMemoryInfoToTempDir(mIdentifier, mAnonymize,
+ mMinimizeMemoryUsage);
+ return NS_OK;
+ }
+
+private:
+ const nsString mIdentifier;
+ const bool mAnonymize;
+ const bool mMinimizeMemoryUsage;
+};
+
+class GCAndCCLogDumpRunnable final
+ : public Runnable
+ , public nsIDumpGCAndCCLogsCallback
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+
+ GCAndCCLogDumpRunnable(const nsAString& aIdentifier,
+ bool aDumpAllTraces,
+ bool aDumpChildProcesses)
+ : mIdentifier(aIdentifier)
+ , mDumpAllTraces(aDumpAllTraces)
+ , mDumpChildProcesses(aDumpChildProcesses)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ nsCOMPtr<nsIMemoryInfoDumper> dumper =
+ do_GetService("@mozilla.org/memory-info-dumper;1");
+
+ dumper->DumpGCAndCCLogsToFile(mIdentifier, mDumpAllTraces,
+ mDumpChildProcesses, this);
+ return NS_OK;
+ }
+
+ NS_IMETHOD OnDump(nsIFile* aGCLog, nsIFile* aCCLog, bool aIsParent) override
+ {
+ return NS_OK;
+ }
+
+ NS_IMETHOD OnFinish() override
+ {
+ return NS_OK;
+ }
+
+private:
+ ~GCAndCCLogDumpRunnable() {}
+
+ const nsString mIdentifier;
+ const bool mDumpAllTraces;
+ const bool mDumpChildProcesses;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED(GCAndCCLogDumpRunnable, Runnable,
+ nsIDumpGCAndCCLogsCallback)
+
+} // namespace
+
+#if defined(MOZ_SUPPORTS_RT_SIGNALS) // {
+namespace {
+
+/*
+ * The following code supports dumping about:memory upon receiving a signal.
+ *
+ * We listen for the following signals:
+ *
+ * - SIGRTMIN: Dump our memory reporters (and those of our child
+ * processes),
+ * - SIGRTMIN + 1: Dump our memory reporters (and those of our child
+ * processes) after minimizing memory usage, and
+ * - SIGRTMIN + 2: Dump the GC and CC logs in this and our child processes.
+ *
+ * When we receive one of these signals, we write the signal number to a pipe.
+ * The IO thread then notices that the pipe has been written to, and kicks off
+ * the appropriate task on the main thread.
+ *
+ * This scheme is similar to using signalfd(), except it's portable and it
+ * doesn't require the use of sigprocmask, which is problematic because it
+ * masks signals received by child processes.
+ *
+ * In theory, we could use Chromium's MessageLoopForIO::CatchSignal() for this.
+ * But that uses libevent, which does not handle the realtime signals (bug
+ * 794074).
+ */
+
+// It turns out that at least on some systems, SIGRTMIN is not a compile-time
+// constant, so these have to be set at runtime.
+static uint8_t sDumpAboutMemorySignum; // SIGRTMIN
+static uint8_t sDumpAboutMemoryAfterMMUSignum; // SIGRTMIN + 1
+static uint8_t sGCAndCCDumpSignum; // SIGRTMIN + 2
+
+void doMemoryReport(const uint8_t aRecvSig)
+{
+ // Dump our memory reports (but run this on the main thread!).
+ bool minimize = aRecvSig == sDumpAboutMemoryAfterMMUSignum;
+ LOG("SignalWatcher(sig %d) dispatching memory report runnable.", aRecvSig);
+ RefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
+ new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
+ /* anonymize = */ false,
+ minimize);
+ NS_DispatchToMainThread(runnable);
+}
+
+void doGCCCDump(const uint8_t aRecvSig)
+{
+ LOG("SignalWatcher(sig %d) dispatching GC/CC log runnable.", aRecvSig);
+ // Dump GC and CC logs (from the main thread).
+ RefPtr<GCAndCCLogDumpRunnable> runnable =
+ new GCAndCCLogDumpRunnable(/* identifier = */ EmptyString(),
+ /* allTraces = */ true,
+ /* dumpChildProcesses = */ true);
+ NS_DispatchToMainThread(runnable);
+}
+
+} // namespace
+#endif // MOZ_SUPPORTS_RT_SIGNALS }
+
+#if defined(MOZ_SUPPORTS_FIFO) // {
+namespace {
+
+void
+doMemoryReport(const nsCString& aInputStr)
+{
+ bool minimize = aInputStr.EqualsLiteral("minimize memory report");
+ LOG("FifoWatcher(command:%s) dispatching memory report runnable.",
+ aInputStr.get());
+ RefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
+ new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
+ /* anonymize = */ false,
+ minimize);
+ NS_DispatchToMainThread(runnable);
+}
+
+void
+doGCCCDump(const nsCString& aInputStr)
+{
+ bool doAllTracesGCCCDump = aInputStr.EqualsLiteral("gc log");
+ LOG("FifoWatcher(command:%s) dispatching GC/CC log runnable.", aInputStr.get());
+ RefPtr<GCAndCCLogDumpRunnable> runnable =
+ new GCAndCCLogDumpRunnable(/* identifier = */ EmptyString(),
+ doAllTracesGCCCDump,
+ /* dumpChildProcesses = */ true);
+ NS_DispatchToMainThread(runnable);
+}
+
+bool
+SetupFifo()
+{
+#ifdef DEBUG
+ static bool fifoCallbacksRegistered = false;
+#endif
+
+ if (!FifoWatcher::MaybeCreate()) {
+ return false;
+ }
+
+ MOZ_ASSERT(!fifoCallbacksRegistered,
+ "FifoWatcher callbacks should be registered only once");
+
+ FifoWatcher* fw = FifoWatcher::GetSingleton();
+ // Dump our memory reports (but run this on the main thread!).
+ fw->RegisterCallback(NS_LITERAL_CSTRING("memory report"),
+ doMemoryReport);
+ fw->RegisterCallback(NS_LITERAL_CSTRING("minimize memory report"),
+ doMemoryReport);
+ // Dump GC and CC logs (from the main thread).
+ fw->RegisterCallback(NS_LITERAL_CSTRING("gc log"),
+ doGCCCDump);
+ fw->RegisterCallback(NS_LITERAL_CSTRING("abbreviated gc log"),
+ doGCCCDump);
+
+#ifdef DEBUG
+ fifoCallbacksRegistered = true;
+#endif
+ return true;
+}
+
+void
+OnFifoEnabledChange(const char* /*unused*/, void* /*unused*/)
+{
+ LOG("%s changed", FifoWatcher::kPrefName);
+ if (SetupFifo()) {
+ Preferences::UnregisterCallback(OnFifoEnabledChange,
+ FifoWatcher::kPrefName,
+ nullptr);
+ }
+}
+
+} // namespace
+#endif // MOZ_SUPPORTS_FIFO }
+
+NS_IMPL_ISUPPORTS(nsMemoryInfoDumper, nsIMemoryInfoDumper)
+
+nsMemoryInfoDumper::nsMemoryInfoDumper()
+{
+}
+
+nsMemoryInfoDumper::~nsMemoryInfoDumper()
+{
+}
+
+/* static */ void
+nsMemoryInfoDumper::Initialize()
+{
+#if defined(MOZ_SUPPORTS_RT_SIGNALS)
+ SignalPipeWatcher* sw = SignalPipeWatcher::GetSingleton();
+
+ // Dump memory reporters (and those of our child processes)
+ sDumpAboutMemorySignum = SIGRTMIN;
+ sw->RegisterCallback(sDumpAboutMemorySignum, doMemoryReport);
+ // Dump our memory reporters after minimizing memory usage
+ sDumpAboutMemoryAfterMMUSignum = SIGRTMIN + 1;
+ sw->RegisterCallback(sDumpAboutMemoryAfterMMUSignum, doMemoryReport);
+ // Dump the GC and CC logs in this and our child processes.
+ sGCAndCCDumpSignum = SIGRTMIN + 2;
+ sw->RegisterCallback(sGCAndCCDumpSignum, doGCCCDump);
+#endif
+
+#if defined(MOZ_SUPPORTS_FIFO)
+ if (!SetupFifo()) {
+ // NB: This gets loaded early enough that it's possible there is a user pref
+ // set to enable the fifo watcher that has not been loaded yet. Register
+ // to attempt to initialize if the fifo watcher becomes enabled by
+ // a user pref.
+ Preferences::RegisterCallback(OnFifoEnabledChange,
+ FifoWatcher::kPrefName,
+ nullptr);
+ }
+#endif
+}
+
+static void
+EnsureNonEmptyIdentifier(nsAString& aIdentifier)
+{
+ if (!aIdentifier.IsEmpty()) {
+ return;
+ }
+
+ // If the identifier is empty, set it to the number of whole seconds since the
+ // epoch. This identifier will appear in the files that this process
+ // generates and also the files generated by this process's children, allowing
+ // us to identify which files are from the same memory report request.
+ aIdentifier.AppendInt(static_cast<int64_t>(PR_Now()) / 1000000);
+}
+
+// Use XPCOM refcounting to fire |onFinish| when all reference-holders
+// (remote dump actors or the |DumpGCAndCCLogsToFile| activation itself)
+// have gone away.
+class nsDumpGCAndCCLogsCallbackHolder final
+ : public nsIDumpGCAndCCLogsCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ explicit nsDumpGCAndCCLogsCallbackHolder(nsIDumpGCAndCCLogsCallback* aCallback)
+ : mCallback(aCallback)
+ {
+ }
+
+ NS_IMETHOD OnFinish() override
+ {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ NS_IMETHOD OnDump(nsIFile* aGCLog, nsIFile* aCCLog, bool aIsParent) override
+ {
+ return mCallback->OnDump(aGCLog, aCCLog, aIsParent);
+ }
+
+private:
+ ~nsDumpGCAndCCLogsCallbackHolder()
+ {
+ Unused << mCallback->OnFinish();
+ }
+
+ nsCOMPtr<nsIDumpGCAndCCLogsCallback> mCallback;
+};
+
+NS_IMPL_ISUPPORTS(nsDumpGCAndCCLogsCallbackHolder, nsIDumpGCAndCCLogsCallback)
+
+NS_IMETHODIMP
+nsMemoryInfoDumper::DumpGCAndCCLogsToFile(const nsAString& aIdentifier,
+ bool aDumpAllTraces,
+ bool aDumpChildProcesses,
+ nsIDumpGCAndCCLogsCallback* aCallback)
+{
+ nsString identifier(aIdentifier);
+ EnsureNonEmptyIdentifier(identifier);
+ nsCOMPtr<nsIDumpGCAndCCLogsCallback> callbackHolder =
+ new nsDumpGCAndCCLogsCallbackHolder(aCallback);
+
+ if (aDumpChildProcesses) {
+ nsTArray<ContentParent*> children;
+ ContentParent::GetAll(children);
+ for (uint32_t i = 0; i < children.Length(); i++) {
+ ContentParent* cp = children[i];
+ nsCOMPtr<nsICycleCollectorLogSink> logSink =
+ nsCycleCollector_createLogSink();
+
+ logSink->SetFilenameIdentifier(identifier);
+ logSink->SetProcessIdentifier(cp->Pid());
+
+ Unused << cp->CycleCollectWithLogs(aDumpAllTraces, logSink,
+ callbackHolder);
+ }
+ }
+
+ nsCOMPtr<nsICycleCollectorListener> logger =
+ do_CreateInstance("@mozilla.org/cycle-collector-logger;1");
+
+ if (aDumpAllTraces) {
+ nsCOMPtr<nsICycleCollectorListener> allTracesLogger;
+ logger->AllTraces(getter_AddRefs(allTracesLogger));
+ logger = allTracesLogger;
+ }
+
+ nsCOMPtr<nsICycleCollectorLogSink> logSink;
+ logger->GetLogSink(getter_AddRefs(logSink));
+
+ logSink->SetFilenameIdentifier(identifier);
+
+ nsJSContext::CycleCollectNow(logger);
+
+ nsCOMPtr<nsIFile> gcLog, ccLog;
+ logSink->GetGcLog(getter_AddRefs(gcLog));
+ logSink->GetCcLog(getter_AddRefs(ccLog));
+ callbackHolder->OnDump(gcLog, ccLog, /* parent = */ true);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMemoryInfoDumper::DumpGCAndCCLogsToSink(bool aDumpAllTraces,
+ nsICycleCollectorLogSink* aSink)
+{
+ nsCOMPtr<nsICycleCollectorListener> logger =
+ do_CreateInstance("@mozilla.org/cycle-collector-logger;1");
+
+ if (aDumpAllTraces) {
+ nsCOMPtr<nsICycleCollectorListener> allTracesLogger;
+ logger->AllTraces(getter_AddRefs(allTracesLogger));
+ logger = allTracesLogger;
+ }
+
+ logger->SetLogSink(aSink);
+
+ nsJSContext::CycleCollectNow(logger);
+
+ return NS_OK;
+}
+
+static void
+MakeFilename(const char* aPrefix, const nsAString& aIdentifier,
+ int aPid, const char* aSuffix, nsACString& aResult)
+{
+ aResult = nsPrintfCString("%s-%s-%d.%s",
+ aPrefix,
+ NS_ConvertUTF16toUTF8(aIdentifier).get(),
+ aPid, aSuffix);
+}
+
+// This class wraps GZFileWriter so it can be used with JSONWriter, overcoming
+// the following two problems:
+// - It provides a JSONWriterFunc::Write() that calls nsGZFileWriter::Write().
+// - It can be stored as a UniquePtr, whereas nsGZFileWriter is refcounted.
+class GZWriterWrapper : public JSONWriteFunc
+{
+public:
+ explicit GZWriterWrapper(nsGZFileWriter* aGZWriter)
+ : mGZWriter(aGZWriter)
+ {}
+
+ void Write(const char* aStr)
+ {
+ // Ignore any failure because JSONWriteFunc doesn't have a mechanism for
+ // handling errors.
+ Unused << mGZWriter->Write(aStr);
+ }
+
+ nsresult Finish() { return mGZWriter->Finish(); }
+
+private:
+ RefPtr<nsGZFileWriter> mGZWriter;
+};
+
+// We need two callbacks: one that handles reports, and one that is called at
+// the end of reporting. Both the callbacks need access to the same JSONWriter,
+// so we implement both of them in this one class.
+class HandleReportAndFinishReportingCallbacks final
+ : public nsIHandleReportCallback, public nsIFinishReportingCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ HandleReportAndFinishReportingCallbacks(UniquePtr<JSONWriter> aWriter,
+ nsIFinishDumpingCallback* aFinishDumping,
+ nsISupports* aFinishDumpingData)
+ : mWriter(Move(aWriter))
+ , mFinishDumping(aFinishDumping)
+ , mFinishDumpingData(aFinishDumpingData)
+ {
+ }
+
+ // This is the callback for nsIHandleReportCallback.
+ NS_IMETHOD Callback(const nsACString& aProcess, const nsACString& aPath,
+ int32_t aKind, int32_t aUnits, int64_t aAmount,
+ const nsACString& aDescription,
+ nsISupports* aData) override
+ {
+ nsAutoCString process;
+ if (aProcess.IsEmpty()) {
+ // If the process is empty, the report originated with the process doing
+ // the dumping. In that case, generate the process identifier, which is
+ // of the form "$PROCESS_NAME (pid $PID)", or just "(pid $PID)" if we
+ // don't have a process name. If we're the main process, we let
+ // $PROCESS_NAME be "Main Process".
+ if (XRE_IsParentProcess()) {
+ // We're the main process.
+ process.AssignLiteral("Main Process");
+ } else if (ContentChild* cc = ContentChild::GetSingleton()) {
+ // Try to get the process name from ContentChild.
+ cc->GetProcessName(process);
+ }
+ ContentChild::AppendProcessId(process);
+
+ } else {
+ // Otherwise, the report originated with another process and already has a
+ // process name. Just use that.
+ process = aProcess;
+ }
+
+ mWriter->StartObjectElement();
+ {
+ mWriter->StringProperty("process", process.get());
+ mWriter->StringProperty("path", PromiseFlatCString(aPath).get());
+ mWriter->IntProperty("kind", aKind);
+ mWriter->IntProperty("units", aUnits);
+ mWriter->IntProperty("amount", aAmount);
+ mWriter->StringProperty("description",
+ PromiseFlatCString(aDescription).get());
+ }
+ mWriter->EndObject();
+
+ return NS_OK;
+ }
+
+ // This is the callback for nsIFinishReportingCallback.
+ NS_IMETHOD Callback(nsISupports* aData) override
+ {
+ mWriter->EndArray(); // end of "reports" array
+ mWriter->End();
+
+ // The call to Finish() deallocates the memory allocated by the first Write
+ // call. Because that memory was live while the memory reporters ran and
+ // was measured by them -- by "heap-allocated" if nothing else -- we want
+ // DMD to see it as well. So we deliberately don't call Finish() until
+ // after DMD finishes.
+ nsresult rv = static_cast<GZWriterWrapper*>(mWriter->WriteFunc())->Finish();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!mFinishDumping) {
+ return NS_OK;
+ }
+
+ return mFinishDumping->Callback(mFinishDumpingData);
+ }
+
+private:
+ ~HandleReportAndFinishReportingCallbacks() {}
+
+ UniquePtr<JSONWriter> mWriter;
+ nsCOMPtr<nsIFinishDumpingCallback> mFinishDumping;
+ nsCOMPtr<nsISupports> mFinishDumpingData;
+};
+
+NS_IMPL_ISUPPORTS(HandleReportAndFinishReportingCallbacks,
+ nsIHandleReportCallback, nsIFinishReportingCallback)
+
+class TempDirFinishCallback final : public nsIFinishDumpingCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ TempDirFinishCallback(nsIFile* aReportsTmpFile,
+ const nsCString& aReportsFinalFilename)
+ : mReportsTmpFile(aReportsTmpFile)
+ , mReportsFilename(aReportsFinalFilename)
+ {
+ }
+
+ NS_IMETHOD Callback(nsISupports* aData) override
+ {
+ // Rename the memory reports file, now that we're done writing all the
+ // files. Its final name is "memory-report<-identifier>-<pid>.json.gz".
+
+ nsCOMPtr<nsIFile> reportsFinalFile;
+ nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
+ getter_AddRefs(reportsFinalFile));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ #ifdef ANDROID
+ rv = reportsFinalFile->AppendNative(NS_LITERAL_CSTRING("memory-reports"));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ #endif
+
+ rv = reportsFinalFile->AppendNative(mReportsFilename);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = reportsFinalFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsAutoString reportsFinalFilename;
+ rv = reportsFinalFile->GetLeafName(reportsFinalFilename);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = mReportsTmpFile->MoveTo(/* directory */ nullptr,
+ reportsFinalFilename);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Write a message to the console.
+
+ nsCOMPtr<nsIConsoleService> cs =
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsString path;
+ mReportsTmpFile->GetPath(path);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsString msg = NS_LITERAL_STRING("nsIMemoryInfoDumper dumped reports to ");
+ msg.Append(path);
+ return cs->LogStringMessage(msg.get());
+ }
+
+private:
+ ~TempDirFinishCallback() {}
+
+ nsCOMPtr<nsIFile> mReportsTmpFile;
+ nsCString mReportsFilename;
+};
+
+NS_IMPL_ISUPPORTS(TempDirFinishCallback, nsIFinishDumpingCallback)
+
+static nsresult
+DumpMemoryInfoToFile(
+ nsIFile* aReportsFile,
+ nsIFinishDumpingCallback* aFinishDumping,
+ nsISupports* aFinishDumpingData,
+ bool aAnonymize,
+ bool aMinimizeMemoryUsage,
+ nsAString& aDMDIdentifier)
+{
+ RefPtr<nsGZFileWriter> gzWriter = new nsGZFileWriter();
+ nsresult rv = gzWriter->Init(aReportsFile);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ auto jsonWriter =
+ MakeUnique<JSONWriter>(MakeUnique<GZWriterWrapper>(gzWriter));
+
+ nsCOMPtr<nsIMemoryReporterManager> mgr =
+ do_GetService("@mozilla.org/memory-reporter-manager;1");
+
+ // This is the first write to the file, and it causes |aWriter| to allocate
+ // over 200 KiB of memory.
+ jsonWriter->Start();
+ {
+ // Increment this number if the format changes.
+ jsonWriter->IntProperty("version", 1);
+ jsonWriter->BoolProperty("hasMozMallocUsableSize",
+ mgr->GetHasMozMallocUsableSize());
+ jsonWriter->StartArrayProperty("reports");
+ }
+
+ RefPtr<HandleReportAndFinishReportingCallbacks>
+ handleReportAndFinishReporting =
+ new HandleReportAndFinishReportingCallbacks(Move(jsonWriter),
+ aFinishDumping,
+ aFinishDumpingData);
+ rv = mgr->GetReportsExtended(handleReportAndFinishReporting, nullptr,
+ handleReportAndFinishReporting, nullptr,
+ aAnonymize,
+ aMinimizeMemoryUsage,
+ aDMDIdentifier);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMemoryInfoDumper::DumpMemoryReportsToNamedFile(
+ const nsAString& aFilename,
+ nsIFinishDumpingCallback* aFinishDumping,
+ nsISupports* aFinishDumpingData,
+ bool aAnonymize)
+{
+ MOZ_ASSERT(!aFilename.IsEmpty());
+
+ // Create the file.
+
+ nsCOMPtr<nsIFile> reportsFile;
+ nsresult rv = NS_NewLocalFile(aFilename, false, getter_AddRefs(reportsFile));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ reportsFile->InitWithPath(aFilename);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ bool exists;
+ rv = reportsFile->Exists(&exists);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!exists) {
+ rv = reportsFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ nsString dmdIdent = EmptyString();
+ return DumpMemoryInfoToFile(reportsFile, aFinishDumping, aFinishDumpingData,
+ aAnonymize, /* minimizeMemoryUsage = */ false,
+ dmdIdent);
+}
+
+NS_IMETHODIMP
+nsMemoryInfoDumper::DumpMemoryInfoToTempDir(const nsAString& aIdentifier,
+ bool aAnonymize,
+ bool aMinimizeMemoryUsage)
+{
+ nsString identifier(aIdentifier);
+ EnsureNonEmptyIdentifier(identifier);
+
+ // Open a new file named something like
+ //
+ // incomplete-memory-report-<identifier>-<pid>.json.gz
+ //
+ // in NS_OS_TEMP_DIR for writing. When we're finished writing the report,
+ // we'll rename this file and get rid of the "incomplete-" prefix.
+ //
+ // We do this because we don't want scripts which poll the filesystem
+ // looking for memory report dumps to grab a file before we're finished
+ // writing to it.
+
+ // The "unified" indicates that we merge the memory reports from all
+ // processes and write out one file, rather than a separate file for
+ // each process as was the case before bug 946407. This is so that
+ // the get_about_memory.py script in the B2G repository can
+ // determine when it's done waiting for files to appear.
+ nsCString reportsFinalFilename;
+ MakeFilename("unified-memory-report", identifier, getpid(), "json.gz",
+ reportsFinalFilename);
+
+ nsCOMPtr<nsIFile> reportsTmpFile;
+ nsresult rv;
+ // In Android case, this function will open a file named aFilename under
+ // specific folder (/data/local/tmp/memory-reports). Otherwise, it will
+ // open a file named aFilename under "NS_OS_TEMP_DIR".
+ rv = nsDumpUtils::OpenTempFile(NS_LITERAL_CSTRING("incomplete-") +
+ reportsFinalFilename,
+ getter_AddRefs(reportsTmpFile),
+ NS_LITERAL_CSTRING("memory-reports"));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ RefPtr<TempDirFinishCallback> finishDumping =
+ new TempDirFinishCallback(reportsTmpFile, reportsFinalFilename);
+
+ return DumpMemoryInfoToFile(reportsTmpFile, finishDumping, nullptr,
+ aAnonymize, aMinimizeMemoryUsage, identifier);
+}
+
+#ifdef MOZ_DMD
+dmd::DMDFuncs::Singleton dmd::DMDFuncs::sSingleton;
+
+nsresult
+nsMemoryInfoDumper::OpenDMDFile(const nsAString& aIdentifier, int aPid,
+ FILE** aOutFile)
+{
+ if (!dmd::IsRunning()) {
+ *aOutFile = nullptr;
+ return NS_OK;
+ }
+
+ // Create a filename like dmd-<identifier>-<pid>.json.gz, which will be used
+ // if DMD is enabled.
+ nsCString dmdFilename;
+ MakeFilename("dmd", aIdentifier, aPid, "json.gz", dmdFilename);
+
+ // Open a new DMD file named |dmdFilename| in NS_OS_TEMP_DIR for writing,
+ // and dump DMD output to it. This must occur after the memory reporters
+ // have been run (above), but before the memory-reports file has been
+ // renamed (so scripts can detect the DMD file, if present).
+
+ nsresult rv;
+ nsCOMPtr<nsIFile> dmdFile;
+ rv = nsDumpUtils::OpenTempFile(dmdFilename,
+ getter_AddRefs(dmdFile),
+ NS_LITERAL_CSTRING("memory-reports"));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ rv = dmdFile->OpenANSIFileDesc("wb", aOutFile);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "OpenANSIFileDesc failed");
+
+ // Print the path, because on some platforms (e.g. Mac) it's not obvious.
+ nsCString path;
+ rv = dmdFile->GetNativePath(path);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ dmd::StatusMsg("opened %s for writing\n", path.get());
+
+ return rv;
+}
+
+nsresult
+nsMemoryInfoDumper::DumpDMDToFile(FILE* aFile)
+{
+ RefPtr<nsGZFileWriter> gzWriter = new nsGZFileWriter();
+ nsresult rv = gzWriter->InitANSIFileDesc(aFile);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Dump DMD's memory reports analysis to the file.
+ dmd::Analyze(MakeUnique<GZWriterWrapper>(gzWriter));
+
+ rv = gzWriter->Finish();
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Finish failed");
+ return rv;
+}
+#endif // MOZ_DMD
+
diff --git a/xpcom/base/nsMemoryInfoDumper.h b/xpcom/base/nsMemoryInfoDumper.h
new file mode 100644
index 0000000000..6bba176f2d
--- /dev/null
+++ b/xpcom/base/nsMemoryInfoDumper.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_nsMemoryInfoDumper_h
+#define mozilla_nsMemoryInfoDumper_h
+
+#include "nsIMemoryInfoDumper.h"
+#include <stdio.h>
+
+/**
+ * This class facilitates dumping information about our memory usage to disk.
+ *
+ * Its cpp file also has Linux-only code which watches various OS signals and
+ * dumps memory info upon receiving a signal. You can activate these listeners
+ * by calling Initialize().
+ */
+class nsMemoryInfoDumper : public nsIMemoryInfoDumper
+{
+ virtual ~nsMemoryInfoDumper();
+
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMEMORYINFODUMPER
+
+ nsMemoryInfoDumper();
+
+ static void Initialize();
+
+#ifdef MOZ_DMD
+ // Open an appropriately named file for a DMD report. If DMD is
+ // disabled, return a null FILE* instead.
+ static nsresult OpenDMDFile(const nsAString& aIdentifier, int aPid,
+ FILE** aOutFile);
+ // Write a DMD report to the given file and close it.
+ static nsresult DumpDMDToFile(FILE* aFile);
+#endif
+};
+
+#define NS_MEMORY_INFO_DUMPER_CID \
+{ 0x00bd71fb, 0x7f09, 0x4ec3, \
+{ 0x96, 0xaf, 0xa0, 0xb5, 0x22, 0xb7, 0x79, 0x69 } }
+
+#endif
diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp
new file mode 100644
index 0000000000..aa3d74dfd5
--- /dev/null
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -0,0 +1,2717 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "nsAtomTable.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+#include "nsPrintfCString.h"
+#include "nsServiceManagerUtils.h"
+#include "nsMemoryReporterManager.h"
+#include "nsITimer.h"
+#include "nsThreadUtils.h"
+#include "nsPIDOMWindow.h"
+#include "nsIObserverService.h"
+#include "nsIGlobalObject.h"
+#include "nsIXPConnect.h"
+#if defined(XP_UNIX) || defined(MOZ_DMD)
+#include "nsMemoryInfoDumper.h"
+#endif
+#include "mozilla/Attributes.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "mozilla/dom/PMemoryReportRequestParent.h" // for dom::MemoryReport
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/ipc/FileDescriptorUtils.h"
+
+#ifdef XP_WIN
+#include <process.h>
+#ifndef getpid
+#define getpid _getpid
+#endif
+#else
+#include <unistd.h>
+#endif
+
+using namespace mozilla;
+
+#if defined(MOZ_MEMORY)
+# define HAVE_JEMALLOC_STATS 1
+# include "mozmemory.h"
+#endif // MOZ_MEMORY
+
+#if defined(XP_LINUX)
+
+#include <malloc.h>
+#include <string.h>
+#include <stdlib.h>
+
+static MOZ_MUST_USE nsresult
+GetProcSelfStatmField(int aField, int64_t* aN)
+{
+ // There are more than two fields, but we're only interested in the first
+ // two.
+ static const int MAX_FIELD = 2;
+ size_t fields[MAX_FIELD];
+ MOZ_ASSERT(aField < MAX_FIELD, "bad field number");
+ FILE* f = fopen("/proc/self/statm", "r");
+ if (f) {
+ int nread = fscanf(f, "%zu %zu", &fields[0], &fields[1]);
+ fclose(f);
+ if (nread == MAX_FIELD) {
+ *aN = fields[aField] * getpagesize();
+ return NS_OK;
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+static MOZ_MUST_USE nsresult
+GetProcSelfSmapsPrivate(int64_t* aN)
+{
+ // You might be tempted to calculate USS by subtracting the "shared" value
+ // from the "resident" value in /proc/<pid>/statm. But at least on Linux,
+ // statm's "shared" value actually counts pages backed by files, which has
+ // little to do with whether the pages are actually shared. /proc/self/smaps
+ // on the other hand appears to give us the correct information.
+
+ FILE* f = fopen("/proc/self/smaps", "r");
+ if (NS_WARN_IF(!f)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // We carry over the end of the buffer to the beginning to make sure we only
+ // interpret complete lines.
+ static const uint32_t carryOver = 32;
+ static const uint32_t readSize = 4096;
+
+ int64_t amount = 0;
+ char buffer[carryOver + readSize + 1];
+
+ // Fill the beginning of the buffer with spaces, as a sentinel for the first
+ // iteration.
+ memset(buffer, ' ', carryOver);
+
+ for (;;) {
+ size_t bytes = fread(buffer + carryOver, sizeof(*buffer), readSize, f);
+ char* end = buffer + bytes;
+ char* ptr = buffer;
+ end[carryOver] = '\0';
+ // We are looking for lines like "Private_{Clean,Dirty}: 4 kB".
+ while ((ptr = strstr(ptr, "Private"))) {
+ if (ptr >= end) {
+ break;
+ }
+ ptr += sizeof("Private_Xxxxx:");
+ amount += strtol(ptr, nullptr, 10);
+ }
+ if (bytes < readSize) {
+ // We do not expect any match within the end of the buffer.
+ MOZ_ASSERT(!strstr(end, "Private"));
+ break;
+ }
+ // Carry the end of the buffer over to the beginning.
+ memcpy(buffer, end, carryOver);
+ }
+
+ fclose(f);
+ // Convert from kB to bytes.
+ *aN = amount * 1024;
+ return NS_OK;
+}
+
+#define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
+static MOZ_MUST_USE nsresult
+VsizeDistinguishedAmount(int64_t* aN)
+{
+ return GetProcSelfStatmField(0, aN);
+}
+
+static MOZ_MUST_USE nsresult
+ResidentDistinguishedAmount(int64_t* aN)
+{
+ return GetProcSelfStatmField(1, aN);
+}
+
+static MOZ_MUST_USE nsresult
+ResidentFastDistinguishedAmount(int64_t* aN)
+{
+ return ResidentDistinguishedAmount(aN);
+}
+
+#define HAVE_RESIDENT_UNIQUE_REPORTER 1
+static MOZ_MUST_USE nsresult
+ResidentUniqueDistinguishedAmount(int64_t* aN)
+{
+ return GetProcSelfSmapsPrivate(aN);
+}
+
+#ifdef HAVE_MALLINFO
+#define HAVE_SYSTEM_HEAP_REPORTER 1
+static MOZ_MUST_USE nsresult
+SystemHeapSize(int64_t* aSizeOut)
+{
+ struct mallinfo info = mallinfo();
+
+ // The documentation in the glibc man page makes it sound like |uordblks|
+ // would suffice, but that only gets the small allocations that are put in
+ // the brk heap. We need |hblkhd| as well to get the larger allocations
+ // that are mmapped.
+ //
+ // The fields in |struct mallinfo| are all |int|, <sigh>, so it is
+ // unreliable if memory usage gets high. However, the system heap size on
+ // Linux should usually be zero (so long as jemalloc is enabled) so that
+ // shouldn't be a problem. Nonetheless, cast the |int|s to |size_t| before
+ // adding them to provide a small amount of extra overflow protection.
+ *aSizeOut = size_t(info.hblkhd) + size_t(info.uordblks);
+ return NS_OK;
+}
+#endif
+
+#elif defined(__DragonFly__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__) \
+ || defined(__FreeBSD_kernel__)
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <sys/user.h>
+#endif
+
+#include <unistd.h>
+
+#if defined(__NetBSD__)
+#undef KERN_PROC
+#define KERN_PROC KERN_PROC2
+#define KINFO_PROC struct kinfo_proc2
+#else
+#define KINFO_PROC struct kinfo_proc
+#endif
+
+#if defined(__DragonFly__)
+#define KP_SIZE(kp) (kp.kp_vm_map_size)
+#define KP_RSS(kp) (kp.kp_vm_rssize * getpagesize())
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#define KP_SIZE(kp) (kp.ki_size)
+#define KP_RSS(kp) (kp.ki_rssize * getpagesize())
+#elif defined(__NetBSD__)
+#define KP_SIZE(kp) (kp.p_vm_msize * getpagesize())
+#define KP_RSS(kp) (kp.p_vm_rssize * getpagesize())
+#elif defined(__OpenBSD__)
+#define KP_SIZE(kp) ((kp.p_vm_dsize + kp.p_vm_ssize \
+ + kp.p_vm_tsize) * getpagesize())
+#define KP_RSS(kp) (kp.p_vm_rssize * getpagesize())
+#endif
+
+static MOZ_MUST_USE nsresult
+GetKinfoProcSelf(KINFO_PROC* aProc)
+{
+ int mib[] = {
+ CTL_KERN,
+ KERN_PROC,
+ KERN_PROC_PID,
+ getpid(),
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ sizeof(KINFO_PROC),
+ 1,
+#endif
+ };
+ u_int miblen = sizeof(mib) / sizeof(mib[0]);
+ size_t size = sizeof(KINFO_PROC);
+ if (sysctl(mib, miblen, aProc, &size, nullptr, 0)) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+#define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
+static MOZ_MUST_USE nsresult
+VsizeDistinguishedAmount(int64_t* aN)
+{
+ KINFO_PROC proc;
+ nsresult rv = GetKinfoProcSelf(&proc);
+ if (NS_SUCCEEDED(rv)) {
+ *aN = KP_SIZE(proc);
+ }
+ return rv;
+}
+
+static MOZ_MUST_USE nsresult
+ResidentDistinguishedAmount(int64_t* aN)
+{
+ KINFO_PROC proc;
+ nsresult rv = GetKinfoProcSelf(&proc);
+ if (NS_SUCCEEDED(rv)) {
+ *aN = KP_RSS(proc);
+ }
+ return rv;
+}
+
+static MOZ_MUST_USE nsresult
+ResidentFastDistinguishedAmount(int64_t* aN)
+{
+ return ResidentDistinguishedAmount(aN);
+}
+
+#ifdef __FreeBSD__
+#include <libutil.h>
+#include <algorithm>
+
+static MOZ_MUST_USE nsresult
+GetKinfoVmentrySelf(int64_t* aPrss, uint64_t* aMaxreg)
+{
+ int cnt;
+ struct kinfo_vmentry* vmmap;
+ struct kinfo_vmentry* kve;
+ if (!(vmmap = kinfo_getvmmap(getpid(), &cnt))) {
+ return NS_ERROR_FAILURE;
+ }
+ if (aPrss) {
+ *aPrss = 0;
+ }
+ if (aMaxreg) {
+ *aMaxreg = 0;
+ }
+
+ for (int i = 0; i < cnt; i++) {
+ kve = &vmmap[i];
+ if (aPrss) {
+ *aPrss += kve->kve_private_resident;
+ }
+ if (aMaxreg) {
+ *aMaxreg = std::max(*aMaxreg, kve->kve_end - kve->kve_start);
+ }
+ }
+
+ free(vmmap);
+ return NS_OK;
+}
+
+#define HAVE_PRIVATE_REPORTER 1
+static MOZ_MUST_USE nsresult
+PrivateDistinguishedAmount(int64_t* aN)
+{
+ int64_t priv;
+ nsresult rv = GetKinfoVmentrySelf(&priv, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ *aN = priv * getpagesize();
+ return NS_OK;
+}
+
+#define HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER 1
+static MOZ_MUST_USE nsresult
+VsizeMaxContiguousDistinguishedAmount(int64_t* aN)
+{
+ uint64_t biggestRegion;
+ nsresult rv = GetKinfoVmentrySelf(nullptr, &biggestRegion);
+ if (NS_SUCCEEDED(rv)) {
+ *aN = biggestRegion;
+ }
+ return NS_OK;
+}
+#endif // FreeBSD
+
+#elif defined(SOLARIS)
+
+#include <procfs.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+static void
+XMappingIter(int64_t& aVsize, int64_t& aResident)
+{
+ aVsize = -1;
+ aResident = -1;
+ int mapfd = open("/proc/self/xmap", O_RDONLY);
+ struct stat st;
+ prxmap_t* prmapp = nullptr;
+ if (mapfd >= 0) {
+ if (!fstat(mapfd, &st)) {
+ int nmap = st.st_size / sizeof(prxmap_t);
+ while (1) {
+ // stat(2) on /proc/<pid>/xmap returns an incorrect value,
+ // prior to the release of Solaris 11.
+ // Here is a workaround for it.
+ nmap *= 2;
+ prmapp = (prxmap_t*)malloc((nmap + 1) * sizeof(prxmap_t));
+ if (!prmapp) {
+ // out of memory
+ break;
+ }
+ int n = pread(mapfd, prmapp, (nmap + 1) * sizeof(prxmap_t), 0);
+ if (n < 0) {
+ break;
+ }
+ if (nmap >= n / sizeof(prxmap_t)) {
+ aVsize = 0;
+ aResident = 0;
+ for (int i = 0; i < n / sizeof(prxmap_t); i++) {
+ aVsize += prmapp[i].pr_size;
+ aResident += prmapp[i].pr_rss * prmapp[i].pr_pagesize;
+ }
+ break;
+ }
+ free(prmapp);
+ }
+ free(prmapp);
+ }
+ close(mapfd);
+ }
+}
+
+#define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
+static MOZ_MUST_USE nsresult
+VsizeDistinguishedAmount(int64_t* aN)
+{
+ int64_t vsize, resident;
+ XMappingIter(vsize, resident);
+ if (vsize == -1) {
+ return NS_ERROR_FAILURE;
+ }
+ *aN = vsize;
+ return NS_OK;
+}
+
+static MOZ_MUST_USE nsresult
+ResidentDistinguishedAmount(int64_t* aN)
+{
+ int64_t vsize, resident;
+ XMappingIter(vsize, resident);
+ if (resident == -1) {
+ return NS_ERROR_FAILURE;
+ }
+ *aN = resident;
+ return NS_OK;
+}
+
+static MOZ_MUST_USE nsresult
+ResidentFastDistinguishedAmount(int64_t* aN)
+{
+ return ResidentDistinguishedAmount(aN);
+}
+
+#elif defined(XP_MACOSX)
+
+#include <mach/mach_init.h>
+#include <mach/mach_vm.h>
+#include <mach/shared_region.h>
+#include <mach/task.h>
+#include <sys/sysctl.h>
+
+static MOZ_MUST_USE bool
+GetTaskBasicInfo(struct task_basic_info* aTi)
+{
+ mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
+ kern_return_t kr = task_info(mach_task_self(), TASK_BASIC_INFO,
+ (task_info_t)aTi, &count);
+ return kr == KERN_SUCCESS;
+}
+
+// The VSIZE figure on Mac includes huge amounts of shared memory and is always
+// absurdly high, eg. 2GB+ even at start-up. But both 'top' and 'ps' report
+// it, so we might as well too.
+#define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
+static MOZ_MUST_USE nsresult
+VsizeDistinguishedAmount(int64_t* aN)
+{
+ task_basic_info ti;
+ if (!GetTaskBasicInfo(&ti)) {
+ return NS_ERROR_FAILURE;
+ }
+ *aN = ti.virtual_size;
+ return NS_OK;
+}
+
+// If we're using jemalloc on Mac, we need to instruct jemalloc to purge the
+// pages it has madvise(MADV_FREE)'d before we read our RSS in order to get
+// an accurate result. The OS will take away MADV_FREE'd pages when there's
+// memory pressure, so ideally, they shouldn't count against our RSS.
+//
+// Purging these pages can take a long time for some users (see bug 789975),
+// so we provide the option to get the RSS without purging first.
+static MOZ_MUST_USE nsresult
+ResidentDistinguishedAmountHelper(int64_t* aN, bool aDoPurge)
+{
+#ifdef HAVE_JEMALLOC_STATS
+#ifndef MOZ_JEMALLOC4
+ if (aDoPurge) {
+ Telemetry::AutoTimer<Telemetry::MEMORY_FREE_PURGED_PAGES_MS> timer;
+ jemalloc_purge_freed_pages();
+ }
+#endif
+#endif
+
+ task_basic_info ti;
+ if (!GetTaskBasicInfo(&ti)) {
+ return NS_ERROR_FAILURE;
+ }
+ *aN = ti.resident_size;
+ return NS_OK;
+}
+
+static MOZ_MUST_USE nsresult
+ResidentFastDistinguishedAmount(int64_t* aN)
+{
+ return ResidentDistinguishedAmountHelper(aN, /* doPurge = */ false);
+}
+
+static MOZ_MUST_USE nsresult
+ResidentDistinguishedAmount(int64_t* aN)
+{
+ return ResidentDistinguishedAmountHelper(aN, /* doPurge = */ true);
+}
+
+#define HAVE_RESIDENT_UNIQUE_REPORTER 1
+
+static bool
+InSharedRegion(mach_vm_address_t aAddr, cpu_type_t aType)
+{
+ mach_vm_address_t base;
+ mach_vm_address_t size;
+
+ switch (aType) {
+ case CPU_TYPE_ARM:
+ base = SHARED_REGION_BASE_ARM;
+ size = SHARED_REGION_SIZE_ARM;
+ break;
+ case CPU_TYPE_I386:
+ base = SHARED_REGION_BASE_I386;
+ size = SHARED_REGION_SIZE_I386;
+ break;
+ case CPU_TYPE_X86_64:
+ base = SHARED_REGION_BASE_X86_64;
+ size = SHARED_REGION_SIZE_X86_64;
+ break;
+ default:
+ return false;
+ }
+
+ return base <= aAddr && aAddr < (base + size);
+}
+
+static MOZ_MUST_USE nsresult
+ResidentUniqueDistinguishedAmount(int64_t* aN)
+{
+ if (!aN) {
+ return NS_ERROR_FAILURE;
+ }
+
+ cpu_type_t cpu_type;
+ size_t len = sizeof(cpu_type);
+ if (sysctlbyname("sysctl.proc_cputype", &cpu_type, &len, NULL, 0) != 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Roughly based on libtop_update_vm_regions in
+ // http://www.opensource.apple.com/source/top/top-100.1.2/libtop.c
+ size_t privatePages = 0;
+ mach_vm_size_t size = 0;
+ for (mach_vm_address_t addr = MACH_VM_MIN_ADDRESS; ; addr += size) {
+ vm_region_top_info_data_t info;
+ mach_msg_type_number_t infoCount = VM_REGION_TOP_INFO_COUNT;
+ mach_port_t objectName;
+
+ kern_return_t kr =
+ mach_vm_region(mach_task_self(), &addr, &size, VM_REGION_TOP_INFO,
+ reinterpret_cast<vm_region_info_t>(&info),
+ &infoCount, &objectName);
+ if (kr == KERN_INVALID_ADDRESS) {
+ // Done iterating VM regions.
+ break;
+ } else if (kr != KERN_SUCCESS) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (InSharedRegion(addr, cpu_type) && info.share_mode != SM_PRIVATE) {
+ continue;
+ }
+
+ switch (info.share_mode) {
+ case SM_LARGE_PAGE:
+ // NB: Large pages are not shareable and always resident.
+ case SM_PRIVATE:
+ privatePages += info.private_pages_resident;
+ privatePages += info.shared_pages_resident;
+ break;
+ case SM_COW:
+ privatePages += info.private_pages_resident;
+ if (info.ref_count == 1) {
+ // Treat copy-on-write pages as private if they only have one reference.
+ privatePages += info.shared_pages_resident;
+ }
+ break;
+ case SM_SHARED:
+ default:
+ break;
+ }
+ }
+
+ vm_size_t pageSize;
+ if (host_page_size(mach_host_self(), &pageSize) != KERN_SUCCESS) {
+ pageSize = PAGE_SIZE;
+ }
+
+ *aN = privatePages * pageSize;
+ return NS_OK;
+}
+
+#elif defined(XP_WIN)
+
+#include <windows.h>
+#include <psapi.h>
+#include <algorithm>
+
+#define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
+static MOZ_MUST_USE nsresult
+VsizeDistinguishedAmount(int64_t* aN)
+{
+ MEMORYSTATUSEX s;
+ s.dwLength = sizeof(s);
+
+ if (!GlobalMemoryStatusEx(&s)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aN = s.ullTotalVirtual - s.ullAvailVirtual;
+ return NS_OK;
+}
+
+static MOZ_MUST_USE nsresult
+ResidentDistinguishedAmount(int64_t* aN)
+{
+ PROCESS_MEMORY_COUNTERS pmc;
+ pmc.cb = sizeof(PROCESS_MEMORY_COUNTERS);
+
+ if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aN = pmc.WorkingSetSize;
+ return NS_OK;
+}
+
+static MOZ_MUST_USE nsresult
+ResidentFastDistinguishedAmount(int64_t* aN)
+{
+ return ResidentDistinguishedAmount(aN);
+}
+
+#define HAVE_RESIDENT_UNIQUE_REPORTER 1
+
+static MOZ_MUST_USE nsresult
+ResidentUniqueDistinguishedAmount(int64_t* aN)
+{
+ // Determine how many entries we need.
+ PSAPI_WORKING_SET_INFORMATION tmp;
+ DWORD tmpSize = sizeof(tmp);
+ memset(&tmp, 0, tmpSize);
+
+ HANDLE proc = GetCurrentProcess();
+ QueryWorkingSet(proc, &tmp, tmpSize);
+
+ // Fudge the size in case new entries are added between calls.
+ size_t entries = tmp.NumberOfEntries * 2;
+
+ if (!entries) {
+ return NS_ERROR_FAILURE;
+ }
+
+ DWORD infoArraySize = tmpSize + (entries * sizeof(PSAPI_WORKING_SET_BLOCK));
+ UniqueFreePtr<PSAPI_WORKING_SET_INFORMATION> infoArray(
+ static_cast<PSAPI_WORKING_SET_INFORMATION*>(malloc(infoArraySize)));
+
+ if (!infoArray) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!QueryWorkingSet(proc, infoArray.get(), infoArraySize)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ entries = static_cast<size_t>(infoArray->NumberOfEntries);
+ size_t privatePages = 0;
+ for (size_t i = 0; i < entries; i++) {
+ // Count shared pages that only one process is using as private.
+ if (!infoArray->WorkingSetInfo[i].Shared ||
+ infoArray->WorkingSetInfo[i].ShareCount <= 1) {
+ privatePages++;
+ }
+ }
+
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+
+ *aN = privatePages * si.dwPageSize;
+ return NS_OK;
+}
+
+#define HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER 1
+static MOZ_MUST_USE nsresult
+VsizeMaxContiguousDistinguishedAmount(int64_t* aN)
+{
+ SIZE_T biggestRegion = 0;
+ MEMORY_BASIC_INFORMATION vmemInfo = { 0 };
+ for (size_t currentAddress = 0; ; ) {
+ if (!VirtualQuery((LPCVOID)currentAddress, &vmemInfo, sizeof(vmemInfo))) {
+ // Something went wrong, just return whatever we've got already.
+ break;
+ }
+
+ if (vmemInfo.State == MEM_FREE) {
+ biggestRegion = std::max(biggestRegion, vmemInfo.RegionSize);
+ }
+
+ SIZE_T lastAddress = currentAddress;
+ currentAddress += vmemInfo.RegionSize;
+
+ // If we overflow, we've examined all of the address space.
+ if (currentAddress < lastAddress) {
+ break;
+ }
+ }
+
+ *aN = biggestRegion;
+ return NS_OK;
+}
+
+#define HAVE_PRIVATE_REPORTER 1
+static MOZ_MUST_USE nsresult
+PrivateDistinguishedAmount(int64_t* aN)
+{
+ PROCESS_MEMORY_COUNTERS_EX pmcex;
+ pmcex.cb = sizeof(PROCESS_MEMORY_COUNTERS_EX);
+
+ if (!GetProcessMemoryInfo(GetCurrentProcess(),
+ (PPROCESS_MEMORY_COUNTERS) &pmcex, sizeof(pmcex))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aN = pmcex.PrivateUsage;
+ return NS_OK;
+}
+
+#define HAVE_SYSTEM_HEAP_REPORTER 1
+// Windows can have multiple separate heaps. During testing there were multiple
+// heaps present but the non-default ones had sizes no more than a few 10s of
+// KiBs. So we combine their sizes into a single measurement.
+static MOZ_MUST_USE nsresult
+SystemHeapSize(int64_t* aSizeOut)
+{
+ // Get the number of heaps.
+ DWORD nHeaps = GetProcessHeaps(0, nullptr);
+ NS_ENSURE_TRUE(nHeaps != 0, NS_ERROR_FAILURE);
+
+ // Get handles to all heaps, checking that the number of heaps hasn't
+ // changed in the meantime.
+ UniquePtr<HANDLE[]> heaps(new HANDLE[nHeaps]);
+ DWORD nHeaps2 = GetProcessHeaps(nHeaps, heaps.get());
+ NS_ENSURE_TRUE(nHeaps2 != 0 && nHeaps2 == nHeaps, NS_ERROR_FAILURE);
+
+ // Lock and iterate over each heap to get its size.
+ int64_t heapsSize = 0;
+ for (DWORD i = 0; i < nHeaps; i++) {
+ HANDLE heap = heaps[i];
+
+ NS_ENSURE_TRUE(HeapLock(heap), NS_ERROR_FAILURE);
+
+ int64_t heapSize = 0;
+ PROCESS_HEAP_ENTRY entry;
+ entry.lpData = nullptr;
+ while (HeapWalk(heap, &entry)) {
+ // We don't count entry.cbOverhead, because we just want to measure the
+ // space available to the program.
+ if (entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) {
+ heapSize += entry.cbData;
+ }
+ }
+
+ // Check this result only after unlocking the heap, so that we don't leave
+ // the heap locked if there was an error.
+ DWORD lastError = GetLastError();
+
+ // I have no idea how things would proceed if unlocking this heap failed...
+ NS_ENSURE_TRUE(HeapUnlock(heap), NS_ERROR_FAILURE);
+
+ NS_ENSURE_TRUE(lastError == ERROR_NO_MORE_ITEMS, NS_ERROR_FAILURE);
+
+ heapsSize += heapSize;
+ }
+
+ *aSizeOut = heapsSize;
+ return NS_OK;
+}
+
+struct SegmentKind
+{
+ DWORD mState;
+ DWORD mType;
+ DWORD mProtect;
+ int mIsStack;
+};
+
+struct SegmentEntry : public PLDHashEntryHdr
+{
+ static PLDHashNumber HashKey(const void* aKey)
+ {
+ auto kind = static_cast<const SegmentKind*>(aKey);
+ return mozilla::HashGeneric(kind->mState, kind->mType, kind->mProtect,
+ kind->mIsStack);
+ }
+
+ static bool MatchEntry(const PLDHashEntryHdr* aEntry, const void* aKey)
+ {
+ auto kind = static_cast<const SegmentKind*>(aKey);
+ auto entry = static_cast<const SegmentEntry*>(aEntry);
+ return kind->mState == entry->mKind.mState &&
+ kind->mType == entry->mKind.mType &&
+ kind->mProtect == entry->mKind.mProtect &&
+ kind->mIsStack == entry->mKind.mIsStack;
+ }
+
+ static void InitEntry(PLDHashEntryHdr* aEntry, const void* aKey)
+ {
+ auto kind = static_cast<const SegmentKind*>(aKey);
+ auto entry = static_cast<SegmentEntry*>(aEntry);
+ entry->mKind = *kind;
+ entry->mCount = 0;
+ entry->mSize = 0;
+ }
+
+ static const PLDHashTableOps Ops;
+
+ SegmentKind mKind; // The segment kind.
+ uint32_t mCount; // The number of segments of this kind.
+ size_t mSize; // The combined size of segments of this kind.
+};
+
+/* static */ const PLDHashTableOps SegmentEntry::Ops = {
+ SegmentEntry::HashKey,
+ SegmentEntry::MatchEntry,
+ PLDHashTable::MoveEntryStub,
+ PLDHashTable::ClearEntryStub,
+ SegmentEntry::InitEntry
+};
+
+class WindowsAddressSpaceReporter final : public nsIMemoryReporter
+{
+ ~WindowsAddressSpaceReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ // First iterate over all the segments and record how many of each kind
+ // there were and their aggregate sizes. We use a hash table for this
+ // because there are a couple of dozen different kinds possible.
+
+ PLDHashTable table(&SegmentEntry::Ops, sizeof(SegmentEntry));
+ MEMORY_BASIC_INFORMATION info = { 0 };
+ bool isPrevSegStackGuard = false;
+ for (size_t currentAddress = 0; ; ) {
+ if (!VirtualQuery((LPCVOID)currentAddress, &info, sizeof(info))) {
+ // Something went wrong, just return whatever we've got already.
+ break;
+ }
+
+ size_t size = info.RegionSize;
+
+ // Note that |type| and |protect| are ignored in some cases.
+ DWORD state = info.State;
+ DWORD type =
+ (state == MEM_RESERVE || state == MEM_COMMIT) ? info.Type : 0;
+ DWORD protect = (state == MEM_COMMIT) ? info.Protect : 0;
+ bool isStack = isPrevSegStackGuard &&
+ state == MEM_COMMIT &&
+ type == MEM_PRIVATE &&
+ protect == PAGE_READWRITE;
+
+ SegmentKind kind = { state, type, protect, isStack ? 1 : 0 };
+ auto entry =
+ static_cast<SegmentEntry*>(table.Add(&kind, mozilla::fallible));
+ if (entry) {
+ entry->mCount += 1;
+ entry->mSize += size;
+ }
+
+ isPrevSegStackGuard = info.State == MEM_COMMIT &&
+ info.Type == MEM_PRIVATE &&
+ info.Protect == (PAGE_READWRITE|PAGE_GUARD);
+
+ size_t lastAddress = currentAddress;
+ currentAddress += size;
+
+ // If we overflow, we've examined all of the address space.
+ if (currentAddress < lastAddress) {
+ break;
+ }
+ }
+
+ // Then iterate over the hash table and report the details for each segment
+ // kind.
+
+ for (auto iter = table.Iter(); !iter.Done(); iter.Next()) {
+ // For each range of pages, we consider one or more of its State, Type
+ // and Protect values. These are documented at
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366775%28v=vs.85%29.aspx
+ // (for State and Type) and
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366786%28v=vs.85%29.aspx
+ // (for Protect).
+ //
+ // Not all State values have accompanying Type and Protection values.
+ bool doType = false;
+ bool doProtect = false;
+
+ auto entry = static_cast<const SegmentEntry*>(iter.Get());
+
+ nsCString path("address-space");
+
+ switch (entry->mKind.mState) {
+ case MEM_FREE:
+ path.AppendLiteral("/free");
+ break;
+
+ case MEM_RESERVE:
+ path.AppendLiteral("/reserved");
+ doType = true;
+ break;
+
+ case MEM_COMMIT:
+ path.AppendLiteral("/commit");
+ doType = true;
+ doProtect = true;
+ break;
+
+ default:
+ // Should be impossible, but handle it just in case.
+ path.AppendLiteral("/???");
+ break;
+ }
+
+ if (doType) {
+ switch (entry->mKind.mType) {
+ case MEM_IMAGE:
+ path.AppendLiteral("/image");
+ break;
+
+ case MEM_MAPPED:
+ path.AppendLiteral("/mapped");
+ break;
+
+ case MEM_PRIVATE:
+ path.AppendLiteral("/private");
+ break;
+
+ default:
+ // Should be impossible, but handle it just in case.
+ path.AppendLiteral("/???");
+ break;
+ }
+ }
+
+ if (doProtect) {
+ DWORD protect = entry->mKind.mProtect;
+ // Basic attributes. Exactly one of these should be set.
+ if (protect & PAGE_EXECUTE) {
+ path.AppendLiteral("/execute");
+ }
+ if (protect & PAGE_EXECUTE_READ) {
+ path.AppendLiteral("/execute-read");
+ }
+ if (protect & PAGE_EXECUTE_READWRITE) {
+ path.AppendLiteral("/execute-readwrite");
+ }
+ if (protect & PAGE_EXECUTE_WRITECOPY) {
+ path.AppendLiteral("/execute-writecopy");
+ }
+ if (protect & PAGE_NOACCESS) {
+ path.AppendLiteral("/noaccess");
+ }
+ if (protect & PAGE_READONLY) {
+ path.AppendLiteral("/readonly");
+ }
+ if (protect & PAGE_READWRITE) {
+ path.AppendLiteral("/readwrite");
+ }
+ if (protect & PAGE_WRITECOPY) {
+ path.AppendLiteral("/writecopy");
+ }
+
+ // Modifiers. At most one of these should be set.
+ if (protect & PAGE_GUARD) {
+ path.AppendLiteral("+guard");
+ }
+ if (protect & PAGE_NOCACHE) {
+ path.AppendLiteral("+nocache");
+ }
+ if (protect & PAGE_WRITECOMBINE) {
+ path.AppendLiteral("+writecombine");
+ }
+
+ // Annotate likely stack segments, too.
+ if (entry->mKind.mIsStack) {
+ path.AppendLiteral("+stack");
+ }
+ }
+
+ // Append the segment count.
+ path.AppendPrintf("(segments=%u)", entry->mCount);
+
+ aHandleReport->Callback(
+ EmptyCString(), path, KIND_OTHER, UNITS_BYTES, entry->mSize,
+ NS_LITERAL_CSTRING("From MEMORY_BASIC_INFORMATION."), aData);
+ }
+
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(WindowsAddressSpaceReporter, nsIMemoryReporter)
+
+#endif // XP_<PLATFORM>
+
+#ifdef HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER
+class VsizeMaxContiguousReporter final : public nsIMemoryReporter
+{
+ ~VsizeMaxContiguousReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ int64_t amount;
+ if (NS_SUCCEEDED(VsizeMaxContiguousDistinguishedAmount(&amount))) {
+ MOZ_COLLECT_REPORT(
+ "vsize-max-contiguous", KIND_OTHER, UNITS_BYTES, amount,
+ "Size of the maximum contiguous block of available virtual memory.");
+ }
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(VsizeMaxContiguousReporter, nsIMemoryReporter)
+#endif
+
+#ifdef HAVE_PRIVATE_REPORTER
+class PrivateReporter final : public nsIMemoryReporter
+{
+ ~PrivateReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ int64_t amount;
+ if (NS_SUCCEEDED(PrivateDistinguishedAmount(&amount))) {
+ MOZ_COLLECT_REPORT(
+ "private", KIND_OTHER, UNITS_BYTES, amount,
+"Memory that cannot be shared with other processes, including memory that is "
+"committed and marked MEM_PRIVATE, data that is not mapped, and executable "
+"pages that have been written to.");
+ }
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(PrivateReporter, nsIMemoryReporter)
+#endif
+
+#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
+class VsizeReporter final : public nsIMemoryReporter
+{
+ ~VsizeReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ int64_t amount;
+ if (NS_SUCCEEDED(VsizeDistinguishedAmount(&amount))) {
+ MOZ_COLLECT_REPORT(
+ "vsize", KIND_OTHER, UNITS_BYTES, amount,
+"Memory mapped by the process, including code and data segments, the heap, "
+"thread stacks, memory explicitly mapped by the process via mmap and similar "
+"operations, and memory shared with other processes. This is the vsize figure "
+"as reported by 'top' and 'ps'. This figure is of limited use on Mac, where "
+"processes share huge amounts of memory with one another. But even on other "
+"operating systems, 'resident' is a much better measure of the memory "
+"resources used by the process.");
+ }
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(VsizeReporter, nsIMemoryReporter)
+
+class ResidentReporter final : public nsIMemoryReporter
+{
+ ~ResidentReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ int64_t amount;
+ if (NS_SUCCEEDED(ResidentDistinguishedAmount(&amount))) {
+ MOZ_COLLECT_REPORT(
+ "resident", KIND_OTHER, UNITS_BYTES, amount,
+"Memory mapped by the process that is present in physical memory, also known "
+"as the resident set size (RSS). This is the best single figure to use when "
+"considering the memory resources used by the process, but it depends both on "
+"other processes being run and details of the OS kernel and so is best used "
+"for comparing the memory usage of a single process at different points in "
+"time.");
+ }
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(ResidentReporter, nsIMemoryReporter)
+
+#endif // HAVE_VSIZE_AND_RESIDENT_REPORTERS
+
+#ifdef HAVE_RESIDENT_UNIQUE_REPORTER
+class ResidentUniqueReporter final : public nsIMemoryReporter
+{
+ ~ResidentUniqueReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ int64_t amount = 0;
+ if (NS_SUCCEEDED(ResidentUniqueDistinguishedAmount(&amount))) {
+ MOZ_COLLECT_REPORT(
+ "resident-unique", KIND_OTHER, UNITS_BYTES, amount,
+"Memory mapped by the process that is present in physical memory and not "
+"shared with any other processes. This is also known as the process's unique "
+"set size (USS). This is the amount of RAM we'd expect to be freed if we "
+"closed this process.");
+ }
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(ResidentUniqueReporter, nsIMemoryReporter)
+
+#endif // HAVE_RESIDENT_UNIQUE_REPORTER
+
+#ifdef HAVE_SYSTEM_HEAP_REPORTER
+
+class SystemHeapReporter final : public nsIMemoryReporter
+{
+ ~SystemHeapReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ int64_t amount;
+ if (NS_SUCCEEDED(SystemHeapSize(&amount))) {
+ MOZ_COLLECT_REPORT(
+ "system-heap-allocated", KIND_OTHER, UNITS_BYTES, amount,
+"Memory used by the system allocator that is currently allocated to the "
+"application. This is distinct from the jemalloc heap that Firefox uses for "
+"most or all of its heap allocations. Ideally this number is zero, but "
+"on some platforms we cannot force every heap allocation through jemalloc.");
+ }
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(SystemHeapReporter, nsIMemoryReporter)
+
+#endif // HAVE_SYSTEM_HEAP_REPORTER
+
+#ifdef XP_UNIX
+
+#include <sys/resource.h>
+
+#define HAVE_RESIDENT_PEAK_REPORTER 1
+
+static MOZ_MUST_USE nsresult
+ResidentPeakDistinguishedAmount(int64_t* aN)
+{
+ struct rusage usage;
+ if (0 == getrusage(RUSAGE_SELF, &usage)) {
+ // The units for ru_maxrrs:
+ // - Mac: bytes
+ // - Solaris: pages? But some sources it actually always returns 0, so
+ // check for that
+ // - Linux, {Net/Open/Free}BSD, DragonFly: KiB
+#ifdef XP_MACOSX
+ *aN = usage.ru_maxrss;
+#elif defined(SOLARIS)
+ *aN = usage.ru_maxrss * getpagesize();
+#else
+ *aN = usage.ru_maxrss * 1024;
+#endif
+ if (*aN > 0) {
+ return NS_OK;
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+class ResidentPeakReporter final : public nsIMemoryReporter
+{
+ ~ResidentPeakReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ int64_t amount = 0;
+ if (NS_SUCCEEDED(ResidentPeakDistinguishedAmount(&amount))) {
+ MOZ_COLLECT_REPORT(
+ "resident-peak", KIND_OTHER, UNITS_BYTES, amount,
+"The peak 'resident' value for the lifetime of the process.");
+ }
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(ResidentPeakReporter, nsIMemoryReporter)
+
+#define HAVE_PAGE_FAULT_REPORTERS 1
+
+class PageFaultsSoftReporter final : public nsIMemoryReporter
+{
+ ~PageFaultsSoftReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ struct rusage usage;
+ int err = getrusage(RUSAGE_SELF, &usage);
+ if (err == 0) {
+ int64_t amount = usage.ru_minflt;
+ MOZ_COLLECT_REPORT(
+ "page-faults-soft", KIND_OTHER, UNITS_COUNT_CUMULATIVE, amount,
+"The number of soft page faults (also known as 'minor page faults') that "
+"have occurred since the process started. A soft page fault occurs when the "
+"process tries to access a page which is present in physical memory but is "
+"not mapped into the process's address space. For instance, a process might "
+"observe soft page faults when it loads a shared library which is already "
+"present in physical memory. A process may experience many thousands of soft "
+"page faults even when the machine has plenty of available physical memory, "
+"and because the OS services a soft page fault without accessing the disk, "
+"they impact performance much less than hard page faults.");
+ }
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(PageFaultsSoftReporter, nsIMemoryReporter)
+
+static MOZ_MUST_USE nsresult
+PageFaultsHardDistinguishedAmount(int64_t* aAmount)
+{
+ struct rusage usage;
+ int err = getrusage(RUSAGE_SELF, &usage);
+ if (err != 0) {
+ return NS_ERROR_FAILURE;
+ }
+ *aAmount = usage.ru_majflt;
+ return NS_OK;
+}
+
+class PageFaultsHardReporter final : public nsIMemoryReporter
+{
+ ~PageFaultsHardReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ int64_t amount = 0;
+ if (NS_SUCCEEDED(PageFaultsHardDistinguishedAmount(&amount))) {
+ MOZ_COLLECT_REPORT(
+ "page-faults-hard", KIND_OTHER, UNITS_COUNT_CUMULATIVE, amount,
+"The number of hard page faults (also known as 'major page faults') that have "
+"occurred since the process started. A hard page fault occurs when a process "
+"tries to access a page which is not present in physical memory. The "
+"operating system must access the disk in order to fulfill a hard page fault. "
+"When memory is plentiful, you should see very few hard page faults. But if "
+"the process tries to use more memory than your machine has available, you "
+"may see many thousands of hard page faults. Because accessing the disk is up "
+"to a million times slower than accessing RAM, the program may run very "
+"slowly when it is experiencing more than 100 or so hard page faults a "
+"second.");
+ }
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(PageFaultsHardReporter, nsIMemoryReporter)
+
+#endif // XP_UNIX
+
+/**
+ ** memory reporter implementation for jemalloc and OSX malloc,
+ ** to obtain info on total memory in use (that we know about,
+ ** at least -- on OSX, there are sometimes other zones in use).
+ **/
+
+#ifdef HAVE_JEMALLOC_STATS
+
+static size_t
+HeapOverhead(jemalloc_stats_t* aStats)
+{
+ return aStats->waste + aStats->bookkeeping +
+ aStats->page_cache + aStats->bin_unused;
+}
+
+// This has UNITS_PERCENTAGE, so it is multiplied by 100x *again* on top of the
+// 100x for the percentage.
+static int64_t
+HeapOverheadFraction(jemalloc_stats_t* aStats)
+{
+ size_t heapOverhead = HeapOverhead(aStats);
+ size_t heapCommitted = aStats->allocated + heapOverhead;
+ return int64_t(10000 * (heapOverhead / (double)heapCommitted));
+}
+
+class JemallocHeapReporter final : public nsIMemoryReporter
+{
+ ~JemallocHeapReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ jemalloc_stats_t stats;
+ jemalloc_stats(&stats);
+
+ MOZ_COLLECT_REPORT(
+ "heap-committed/allocated", KIND_OTHER, UNITS_BYTES, stats.allocated,
+"Memory mapped by the heap allocator that is currently allocated to the "
+"application. This may exceed the amount of memory requested by the "
+"application because the allocator regularly rounds up request sizes. (The "
+"exact amount requested is not recorded.)");
+
+ MOZ_COLLECT_REPORT(
+ "heap-allocated", KIND_OTHER, UNITS_BYTES, stats.allocated,
+"The same as 'heap-committed/allocated'.");
+
+ // We mark this and the other heap-overhead reporters as KIND_NONHEAP
+ // because KIND_HEAP memory means "counted in heap-allocated", which
+ // this is not.
+ MOZ_COLLECT_REPORT(
+ "explicit/heap-overhead/bin-unused", KIND_NONHEAP, UNITS_BYTES,
+ stats.bin_unused,
+"Unused bytes due to fragmentation in the bins used for 'small' (<= 2 KiB) "
+"allocations. These bytes will be used if additional allocations occur.");
+
+ if (stats.waste > 0) {
+ MOZ_COLLECT_REPORT(
+ "explicit/heap-overhead/waste", KIND_NONHEAP, UNITS_BYTES,
+ stats.waste,
+"Committed bytes which do not correspond to an active allocation and which the "
+"allocator is not intentionally keeping alive (i.e., not "
+"'explicit/heap-overhead/{bookkeeping,page-cache,bin-unused}').");
+ }
+
+ MOZ_COLLECT_REPORT(
+ "explicit/heap-overhead/bookkeeping", KIND_NONHEAP, UNITS_BYTES,
+ stats.bookkeeping,
+"Committed bytes which the heap allocator uses for internal data structures.");
+
+ MOZ_COLLECT_REPORT(
+ "explicit/heap-overhead/page-cache", KIND_NONHEAP, UNITS_BYTES,
+ stats.page_cache,
+"Memory which the allocator could return to the operating system, but hasn't. "
+"The allocator keeps this memory around as an optimization, so it doesn't "
+"have to ask the OS the next time it needs to fulfill a request. This value "
+"is typically not larger than a few megabytes.");
+
+ MOZ_COLLECT_REPORT(
+ "heap-committed/overhead", KIND_OTHER, UNITS_BYTES,
+ HeapOverhead(&stats),
+"The sum of 'explicit/heap-overhead/*'.");
+
+ MOZ_COLLECT_REPORT(
+ "heap-mapped", KIND_OTHER, UNITS_BYTES, stats.mapped,
+"Amount of memory currently mapped. Includes memory that is uncommitted, i.e. "
+"neither in physical memory nor paged to disk.");
+
+ MOZ_COLLECT_REPORT(
+ "heap-chunksize", KIND_OTHER, UNITS_BYTES, stats.chunksize,
+ "Size of chunks.");
+
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(JemallocHeapReporter, nsIMemoryReporter)
+
+#endif // HAVE_JEMALLOC_STATS
+
+// Why is this here? At first glance, you'd think it could be defined and
+// registered with nsMemoryReporterManager entirely within nsAtomTable.cpp.
+// However, the obvious time to register it is when the table is initialized,
+// and that happens before XPCOM components are initialized, which means the
+// RegisterStrongMemoryReporter call fails. So instead we do it here.
+class AtomTablesReporter final : public nsIMemoryReporter
+{
+ MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
+
+ ~AtomTablesReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ size_t Main, Static;
+ NS_SizeOfAtomTablesIncludingThis(MallocSizeOf, &Main, &Static);
+
+ MOZ_COLLECT_REPORT(
+ "explicit/atom-tables/main", KIND_HEAP, UNITS_BYTES, Main,
+ "Memory used by the main atoms table.");
+
+ MOZ_COLLECT_REPORT(
+ "explicit/atom-tables/static", KIND_HEAP, UNITS_BYTES, Static,
+ "Memory used by the static atoms table.");
+
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(AtomTablesReporter, nsIMemoryReporter)
+
+#ifdef DEBUG
+
+// Ideally, this would be implemented in BlockingResourceBase.cpp.
+// However, this ends up breaking the linking step of various unit tests due
+// to adding a new dependency to libdmd for a commonly used feature (mutexes)
+// in DMD builds. So instead we do it here.
+class DeadlockDetectorReporter final : public nsIMemoryReporter
+{
+ MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
+
+ ~DeadlockDetectorReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ MOZ_COLLECT_REPORT(
+ "explicit/deadlock-detector", KIND_HEAP, UNITS_BYTES,
+ BlockingResourceBase::SizeOfDeadlockDetector(MallocSizeOf),
+ "Memory used by the deadlock detector.");
+
+ return NS_OK;
+ }
+};
+NS_IMPL_ISUPPORTS(DeadlockDetectorReporter, nsIMemoryReporter)
+
+#endif
+
+#ifdef MOZ_DMD
+
+namespace mozilla {
+namespace dmd {
+
+class DMDReporter final : public nsIMemoryReporter
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ dmd::Sizes sizes;
+ dmd::SizeOf(&sizes);
+
+ MOZ_COLLECT_REPORT(
+ "explicit/dmd/stack-traces/used", KIND_HEAP, UNITS_BYTES,
+ sizes.mStackTracesUsed,
+ "Memory used by stack traces which correspond to at least "
+ "one heap block DMD is tracking.");
+
+ MOZ_COLLECT_REPORT(
+ "explicit/dmd/stack-traces/unused", KIND_HEAP, UNITS_BYTES,
+ sizes.mStackTracesUnused,
+ "Memory used by stack traces which don't correspond to any heap "
+ "blocks DMD is currently tracking.");
+
+ MOZ_COLLECT_REPORT(
+ "explicit/dmd/stack-traces/table", KIND_HEAP, UNITS_BYTES,
+ sizes.mStackTraceTable,
+ "Memory used by DMD's stack trace table.");
+
+ MOZ_COLLECT_REPORT(
+ "explicit/dmd/live-block-table", KIND_HEAP, UNITS_BYTES,
+ sizes.mLiveBlockTable,
+ "Memory used by DMD's live block table.");
+
+ MOZ_COLLECT_REPORT(
+ "explicit/dmd/dead-block-list", KIND_HEAP, UNITS_BYTES,
+ sizes.mDeadBlockTable,
+ "Memory used by DMD's dead block list.");
+
+ return NS_OK;
+ }
+
+private:
+ ~DMDReporter() {}
+};
+NS_IMPL_ISUPPORTS(DMDReporter, nsIMemoryReporter)
+
+} // namespace dmd
+} // namespace mozilla
+
+#endif // MOZ_DMD
+
+/**
+ ** nsMemoryReporterManager implementation
+ **/
+
+NS_IMPL_ISUPPORTS(nsMemoryReporterManager, nsIMemoryReporterManager)
+
+NS_IMETHODIMP
+nsMemoryReporterManager::Init()
+{
+ if (!NS_IsMainThread()) {
+ MOZ_CRASH();
+ }
+
+ // Under normal circumstances this function is only called once. However,
+ // we've (infrequently) seen memory report dumps in crash reports that
+ // suggest that this function is sometimes called multiple times. That in
+ // turn means that multiple reporters of each kind are registered, which
+ // leads to duplicated reports of individual measurements such as "resident",
+ // "vsize", etc.
+ //
+ // It's unclear how these multiple calls can occur. The only plausible theory
+ // so far is badly-written extensions, because this function is callable from
+ // JS code via nsIMemoryReporter.idl.
+ //
+ // Whatever the cause, it's a bad thing. So we protect against it with the
+ // following check.
+ static bool isInited = false;
+ if (isInited) {
+ NS_WARNING("nsMemoryReporterManager::Init() has already been called!");
+ return NS_OK;
+ }
+ isInited = true;
+
+#if defined(HAVE_JEMALLOC_STATS) && defined(MOZ_GLUE_IN_PROGRAM)
+ if (!jemalloc_stats) {
+ return NS_ERROR_FAILURE;
+ }
+#endif
+
+#ifdef HAVE_JEMALLOC_STATS
+ RegisterStrongReporter(new JemallocHeapReporter());
+#endif
+
+#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
+ RegisterStrongReporter(new VsizeReporter());
+ RegisterStrongReporter(new ResidentReporter());
+#endif
+
+#ifdef HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER
+ RegisterStrongReporter(new VsizeMaxContiguousReporter());
+#endif
+
+#ifdef HAVE_RESIDENT_PEAK_REPORTER
+ RegisterStrongReporter(new ResidentPeakReporter());
+#endif
+
+#ifdef HAVE_RESIDENT_UNIQUE_REPORTER
+ RegisterStrongReporter(new ResidentUniqueReporter());
+#endif
+
+#ifdef HAVE_PAGE_FAULT_REPORTERS
+ RegisterStrongReporter(new PageFaultsSoftReporter());
+ RegisterStrongReporter(new PageFaultsHardReporter());
+#endif
+
+#ifdef HAVE_PRIVATE_REPORTER
+ RegisterStrongReporter(new PrivateReporter());
+#endif
+
+#ifdef HAVE_SYSTEM_HEAP_REPORTER
+ RegisterStrongReporter(new SystemHeapReporter());
+#endif
+
+ RegisterStrongReporter(new AtomTablesReporter());
+
+#ifdef DEBUG
+ RegisterStrongReporter(new DeadlockDetectorReporter());
+#endif
+
+#ifdef MOZ_DMD
+ RegisterStrongReporter(new mozilla::dmd::DMDReporter());
+#endif
+
+#ifdef XP_WIN
+ RegisterStrongReporter(new WindowsAddressSpaceReporter());
+#endif
+
+#ifdef XP_UNIX
+ nsMemoryInfoDumper::Initialize();
+#endif
+
+ return NS_OK;
+}
+
+nsMemoryReporterManager::nsMemoryReporterManager()
+ : mMutex("nsMemoryReporterManager::mMutex")
+ , mIsRegistrationBlocked(false)
+ , mStrongReporters(new StrongReportersTable())
+ , mWeakReporters(new WeakReportersTable())
+ , mSavedStrongReporters(nullptr)
+ , mSavedWeakReporters(nullptr)
+ , mNextGeneration(1)
+ , mPendingProcessesState(nullptr)
+ , mPendingReportersState(nullptr)
+{
+}
+
+nsMemoryReporterManager::~nsMemoryReporterManager()
+{
+ delete mStrongReporters;
+ delete mWeakReporters;
+ NS_ASSERTION(!mSavedStrongReporters, "failed to restore strong reporters");
+ NS_ASSERTION(!mSavedWeakReporters, "failed to restore weak reporters");
+}
+
+#ifdef MOZ_WIDGET_GONK
+#define DEBUG_CHILD_PROCESS_MEMORY_REPORTING 1
+#endif
+
+#ifdef DEBUG_CHILD_PROCESS_MEMORY_REPORTING
+#define MEMORY_REPORTING_LOG(format, ...) \
+ printf_stderr("++++ MEMORY REPORTING: " format, ##__VA_ARGS__);
+#else
+#define MEMORY_REPORTING_LOG(...)
+#endif
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetReports(
+ nsIHandleReportCallback* aHandleReport,
+ nsISupports* aHandleReportData,
+ nsIFinishReportingCallback* aFinishReporting,
+ nsISupports* aFinishReportingData,
+ bool aAnonymize)
+{
+ return GetReportsExtended(aHandleReport, aHandleReportData,
+ aFinishReporting, aFinishReportingData,
+ aAnonymize,
+ /* minimize = */ false,
+ /* DMDident = */ EmptyString());
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetReportsExtended(
+ nsIHandleReportCallback* aHandleReport,
+ nsISupports* aHandleReportData,
+ nsIFinishReportingCallback* aFinishReporting,
+ nsISupports* aFinishReportingData,
+ bool aAnonymize,
+ bool aMinimize,
+ const nsAString& aDMDDumpIdent)
+{
+ nsresult rv;
+
+ // Memory reporters are not necessarily threadsafe, so this function must
+ // be called from the main thread.
+ if (!NS_IsMainThread()) {
+ MOZ_CRASH();
+ }
+
+ uint32_t generation = mNextGeneration++;
+
+ if (mPendingProcessesState) {
+ // A request is in flight. Don't start another one. And don't report
+ // an error; just ignore it, and let the in-flight request finish.
+ MEMORY_REPORTING_LOG("GetReports (gen=%u, s->gen=%u): abort\n",
+ generation, mPendingProcessesState->mGeneration);
+ return NS_OK;
+ }
+
+ MEMORY_REPORTING_LOG("GetReports (gen=%u)\n", generation);
+
+ uint32_t concurrency = Preferences::GetUint("memory.report_concurrency", 1);
+ MOZ_ASSERT(concurrency >= 1);
+ if (concurrency < 1) {
+ concurrency = 1;
+ }
+ mPendingProcessesState = new PendingProcessesState(generation,
+ aAnonymize,
+ aMinimize,
+ concurrency,
+ aHandleReport,
+ aHandleReportData,
+ aFinishReporting,
+ aFinishReportingData,
+ aDMDDumpIdent);
+
+ if (aMinimize) {
+ nsCOMPtr<nsIRunnable> callback =
+ NewRunnableMethod(this, &nsMemoryReporterManager::StartGettingReports);
+ rv = MinimizeMemoryUsage(callback);
+ } else {
+ rv = StartGettingReports();
+ }
+ return rv;
+}
+
+nsresult
+nsMemoryReporterManager::StartGettingReports()
+{
+ PendingProcessesState* s = mPendingProcessesState;
+ nsresult rv;
+
+ // Get reports for this process.
+ FILE* parentDMDFile = nullptr;
+#ifdef MOZ_DMD
+ if (!s->mDMDDumpIdent.IsEmpty()) {
+ rv = nsMemoryInfoDumper::OpenDMDFile(s->mDMDDumpIdent, getpid(),
+ &parentDMDFile);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ // Proceed with the memory report as if DMD were disabled.
+ parentDMDFile = nullptr;
+ }
+ }
+#endif
+
+ // This is async.
+ GetReportsForThisProcessExtended(s->mHandleReport, s->mHandleReportData,
+ s->mAnonymize, parentDMDFile,
+ s->mFinishReporting, s->mFinishReportingData);
+
+ nsTArray<ContentParent*> childWeakRefs;
+ ContentParent::GetAll(childWeakRefs);
+ if (!childWeakRefs.IsEmpty()) {
+ // Request memory reports from child processes. This happens
+ // after the parent report so that the parent's main thread will
+ // be free to process the child reports, instead of causing them
+ // to be buffered and consume (possibly scarce) memory.
+
+ for (size_t i = 0; i < childWeakRefs.Length(); ++i) {
+ s->mChildrenPending.AppendElement(childWeakRefs[i]);
+ }
+
+ nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
+ // Don't use NS_ENSURE_* here; can't return until the report is finished.
+ if (NS_WARN_IF(!timer)) {
+ FinishReporting();
+ return NS_ERROR_FAILURE;
+ }
+ rv = timer->InitWithFuncCallback(TimeoutCallback,
+ this, kTimeoutLengthMS,
+ nsITimer::TYPE_ONE_SHOT);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ FinishReporting();
+ return rv;
+ }
+
+ MOZ_ASSERT(!s->mTimer);
+ s->mTimer.swap(timer);
+ }
+
+ return NS_OK;
+}
+
+void
+nsMemoryReporterManager::DispatchReporter(
+ nsIMemoryReporter* aReporter, bool aIsAsync,
+ nsIHandleReportCallback* aHandleReport,
+ nsISupports* aHandleReportData,
+ bool aAnonymize)
+{
+ MOZ_ASSERT(mPendingReportersState);
+
+ // Grab refs to everything used in the lambda function.
+ RefPtr<nsMemoryReporterManager> self = this;
+ nsCOMPtr<nsIMemoryReporter> reporter = aReporter;
+ nsCOMPtr<nsIHandleReportCallback> handleReport = aHandleReport;
+ nsCOMPtr<nsISupports> handleReportData = aHandleReportData;
+
+ nsCOMPtr<nsIRunnable> event = NS_NewRunnableFunction(
+ [self, reporter, aIsAsync, handleReport, handleReportData, aAnonymize] () {
+ reporter->CollectReports(handleReport, handleReportData, aAnonymize);
+ if (!aIsAsync) {
+ self->EndReport();
+ }
+ });
+
+ NS_DispatchToMainThread(event);
+ mPendingReportersState->mReportsPending++;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetReportsForThisProcessExtended(
+ nsIHandleReportCallback* aHandleReport, nsISupports* aHandleReportData,
+ bool aAnonymize, FILE* aDMDFile,
+ nsIFinishReportingCallback* aFinishReporting,
+ nsISupports* aFinishReportingData)
+{
+ // Memory reporters are not necessarily threadsafe, so this function must
+ // be called from the main thread.
+ if (!NS_IsMainThread()) {
+ MOZ_CRASH();
+ }
+
+ if (NS_WARN_IF(mPendingReportersState)) {
+ // Report is already in progress.
+ return NS_ERROR_IN_PROGRESS;
+ }
+
+#ifdef MOZ_DMD
+ if (aDMDFile) {
+ // Clear DMD's reportedness state before running the memory
+ // reporters, to avoid spurious twice-reported warnings.
+ dmd::ClearReports();
+ }
+#else
+ MOZ_ASSERT(!aDMDFile);
+#endif
+
+ mPendingReportersState = new PendingReportersState(
+ aFinishReporting, aFinishReportingData, aDMDFile);
+
+ {
+ mozilla::MutexAutoLock autoLock(mMutex);
+
+ for (auto iter = mStrongReporters->Iter(); !iter.Done(); iter.Next()) {
+ DispatchReporter(iter.Key(), iter.Data(),
+ aHandleReport, aHandleReportData, aAnonymize);
+ }
+
+ for (auto iter = mWeakReporters->Iter(); !iter.Done(); iter.Next()) {
+ nsCOMPtr<nsIMemoryReporter> reporter = iter.Key();
+ DispatchReporter(reporter, iter.Data(),
+ aHandleReport, aHandleReportData, aAnonymize);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::EndReport()
+{
+ if (--mPendingReportersState->mReportsPending == 0) {
+#ifdef MOZ_DMD
+ if (mPendingReportersState->mDMDFile) {
+ nsMemoryInfoDumper::DumpDMDToFile(mPendingReportersState->mDMDFile);
+ }
+#endif
+ if (mPendingProcessesState) {
+ // This is the parent process.
+ EndProcessReport(mPendingProcessesState->mGeneration, true);
+ } else {
+ mPendingReportersState->mFinishReporting->Callback(
+ mPendingReportersState->mFinishReportingData);
+ }
+
+ delete mPendingReportersState;
+ mPendingReportersState = nullptr;
+ }
+
+ return NS_OK;
+}
+
+nsMemoryReporterManager::PendingProcessesState*
+nsMemoryReporterManager::GetStateForGeneration(uint32_t aGeneration)
+{
+ // Memory reporting only happens on the main thread.
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ PendingProcessesState* s = mPendingProcessesState;
+
+ if (!s) {
+ // If we reach here, then:
+ //
+ // - A child process reported back too late, and no subsequent request
+ // is in flight.
+ //
+ // So there's nothing to be done. Just ignore it.
+ MEMORY_REPORTING_LOG(
+ "HandleChildReports: no request in flight (aGen=%u)\n",
+ aGeneration);
+ return nullptr;
+ }
+
+ if (aGeneration != s->mGeneration) {
+ // If we reach here, a child process must have reported back, too late,
+ // while a subsequent (higher-numbered) request is in flight. Again,
+ // ignore it.
+ MOZ_ASSERT(aGeneration < s->mGeneration);
+ MEMORY_REPORTING_LOG(
+ "HandleChildReports: gen mismatch (aGen=%u, s->gen=%u)\n",
+ aGeneration, s->mGeneration);
+ return nullptr;
+ }
+
+ return s;
+}
+
+// This function has no return value. If something goes wrong, there's no
+// clear place to report the problem to, but that's ok -- we will end up
+// hitting the timeout and executing TimeoutCallback().
+void
+nsMemoryReporterManager::HandleChildReport(
+ uint32_t aGeneration,
+ const dom::MemoryReport& aChildReport)
+{
+ PendingProcessesState* s = GetStateForGeneration(aGeneration);
+ if (!s) {
+ return;
+ }
+
+ // Child reports should have a non-empty process.
+ MOZ_ASSERT(!aChildReport.process().IsEmpty());
+
+ // If the call fails, ignore and continue.
+ s->mHandleReport->Callback(aChildReport.process(),
+ aChildReport.path(),
+ aChildReport.kind(),
+ aChildReport.units(),
+ aChildReport.amount(),
+ aChildReport.desc(),
+ s->mHandleReportData);
+}
+
+/* static */ bool
+nsMemoryReporterManager::StartChildReport(mozilla::dom::ContentParent* aChild,
+ const PendingProcessesState* aState)
+{
+ if (!aChild->IsAlive()) {
+ MEMORY_REPORTING_LOG("StartChildReports (gen=%u): child exited before"
+ " its report was started\n",
+ aState->mGeneration);
+ return false;
+ }
+
+ mozilla::dom::MaybeFileDesc dmdFileDesc = void_t();
+#ifdef MOZ_DMD
+ if (!aState->mDMDDumpIdent.IsEmpty()) {
+ FILE *dmdFile = nullptr;
+ nsresult rv = nsMemoryInfoDumper::OpenDMDFile(aState->mDMDDumpIdent,
+ aChild->Pid(), &dmdFile);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ // Proceed with the memory report as if DMD were disabled.
+ dmdFile = nullptr;
+ }
+ if (dmdFile) {
+ dmdFileDesc = mozilla::ipc::FILEToFileDescriptor(dmdFile);
+ fclose(dmdFile);
+ }
+ }
+#endif
+ return aChild->SendPMemoryReportRequestConstructor(
+ aState->mGeneration, aState->mAnonymize, aState->mMinimize, dmdFileDesc);
+}
+
+void
+nsMemoryReporterManager::EndProcessReport(uint32_t aGeneration, bool aSuccess)
+{
+ PendingProcessesState* s = GetStateForGeneration(aGeneration);
+ if (!s) {
+ return;
+ }
+
+ MOZ_ASSERT(s->mNumProcessesRunning > 0);
+ s->mNumProcessesRunning--;
+ s->mNumProcessesCompleted++;
+ MEMORY_REPORTING_LOG("HandleChildReports (aGen=%u): process %u %s"
+ " (%u running, %u pending)\n",
+ aGeneration, s->mNumProcessesCompleted,
+ aSuccess ? "completed" : "exited during report",
+ s->mNumProcessesRunning,
+ static_cast<unsigned>(s->mChildrenPending.Length()));
+
+ // Start pending children up to the concurrency limit.
+ while (s->mNumProcessesRunning < s->mConcurrencyLimit &&
+ !s->mChildrenPending.IsEmpty()) {
+ // Pop last element from s->mChildrenPending
+ RefPtr<ContentParent> nextChild;
+ nextChild.swap(s->mChildrenPending.LastElement());
+ s->mChildrenPending.TruncateLength(s->mChildrenPending.Length() - 1);
+ // Start report (if the child is still alive).
+ if (StartChildReport(nextChild, s)) {
+ ++s->mNumProcessesRunning;
+ MEMORY_REPORTING_LOG("HandleChildReports (aGen=%u): started child report"
+ " (%u running, %u pending)\n",
+ aGeneration, s->mNumProcessesRunning,
+ static_cast<unsigned>(s->mChildrenPending.Length()));
+ }
+ }
+
+ // If all the child processes (if any) have reported, we can cancel
+ // the timer (if started) and finish up. Otherwise, just return.
+ if (s->mNumProcessesRunning == 0) {
+ MOZ_ASSERT(s->mChildrenPending.IsEmpty());
+ if (s->mTimer) {
+ s->mTimer->Cancel();
+ }
+ FinishReporting();
+ }
+}
+
+/* static */ void
+nsMemoryReporterManager::TimeoutCallback(nsITimer* aTimer, void* aData)
+{
+ nsMemoryReporterManager* mgr = static_cast<nsMemoryReporterManager*>(aData);
+ PendingProcessesState* s = mgr->mPendingProcessesState;
+
+ // Release assert because: if the pointer is null we're about to
+ // crash regardless of DEBUG, and this way the compiler doesn't
+ // complain about unused variables.
+ MOZ_RELEASE_ASSERT(s, "mgr->mPendingProcessesState");
+ MEMORY_REPORTING_LOG("TimeoutCallback (s->gen=%u; %u running, %u pending)\n",
+ s->mGeneration, s->mNumProcessesRunning,
+ static_cast<unsigned>(s->mChildrenPending.Length()));
+
+ // We don't bother sending any kind of cancellation message to the child
+ // processes that haven't reported back.
+ mgr->FinishReporting();
+}
+
+nsresult
+nsMemoryReporterManager::FinishReporting()
+{
+ // Memory reporting only happens on the main thread.
+ if (!NS_IsMainThread()) {
+ MOZ_CRASH();
+ }
+
+ MOZ_ASSERT(mPendingProcessesState);
+ MEMORY_REPORTING_LOG("FinishReporting (s->gen=%u; %u processes reported)\n",
+ mPendingProcessesState->mGeneration,
+ mPendingProcessesState->mNumProcessesCompleted);
+
+ // Call this before deleting |mPendingProcessesState|. That way, if
+ // |mFinishReportData| calls GetReports(), it will silently abort, as
+ // required.
+ nsresult rv = mPendingProcessesState->mFinishReporting->Callback(
+ mPendingProcessesState->mFinishReportingData);
+
+ delete mPendingProcessesState;
+ mPendingProcessesState = nullptr;
+ return rv;
+}
+
+nsMemoryReporterManager::PendingProcessesState::PendingProcessesState(
+ uint32_t aGeneration, bool aAnonymize, bool aMinimize,
+ uint32_t aConcurrencyLimit,
+ nsIHandleReportCallback* aHandleReport,
+ nsISupports* aHandleReportData,
+ nsIFinishReportingCallback* aFinishReporting,
+ nsISupports* aFinishReportingData,
+ const nsAString& aDMDDumpIdent)
+ : mGeneration(aGeneration)
+ , mAnonymize(aAnonymize)
+ , mMinimize(aMinimize)
+ , mChildrenPending()
+ , mNumProcessesRunning(1) // reporting starts with the parent
+ , mNumProcessesCompleted(0)
+ , mConcurrencyLimit(aConcurrencyLimit)
+ , mHandleReport(aHandleReport)
+ , mHandleReportData(aHandleReportData)
+ , mFinishReporting(aFinishReporting)
+ , mFinishReportingData(aFinishReportingData)
+ , mDMDDumpIdent(aDMDDumpIdent)
+{
+}
+
+static void
+CrashIfRefcountIsZero(nsISupports* aObj)
+{
+ // This will probably crash if the object's refcount is 0.
+ uint32_t refcnt = NS_ADDREF(aObj);
+ if (refcnt <= 1) {
+ MOZ_CRASH("CrashIfRefcountIsZero: refcount is zero");
+ }
+ NS_RELEASE(aObj);
+}
+
+nsresult
+nsMemoryReporterManager::RegisterReporterHelper(
+ nsIMemoryReporter* aReporter, bool aForce, bool aStrong, bool aIsAsync)
+{
+ // This method is thread-safe.
+ mozilla::MutexAutoLock autoLock(mMutex);
+
+ if (mIsRegistrationBlocked && !aForce) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (mStrongReporters->Contains(aReporter) ||
+ mWeakReporters->Contains(aReporter)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // If |aStrong| is true, |aReporter| may have a refcnt of 0, so we take
+ // a kung fu death grip before calling PutEntry. Otherwise, if PutEntry
+ // addref'ed and released |aReporter| before finally addref'ing it for
+ // good, it would free aReporter! The kung fu death grip could itself be
+ // problematic if PutEntry didn't addref |aReporter| (because then when the
+ // death grip goes out of scope, we would delete the reporter). In debug
+ // mode, we check that this doesn't happen.
+ //
+ // If |aStrong| is false, we require that |aReporter| have a non-zero
+ // refcnt.
+ //
+ if (aStrong) {
+ nsCOMPtr<nsIMemoryReporter> kungFuDeathGrip = aReporter;
+ mStrongReporters->Put(aReporter, aIsAsync);
+ CrashIfRefcountIsZero(aReporter);
+ } else {
+ CrashIfRefcountIsZero(aReporter);
+ nsCOMPtr<nsIXPConnectWrappedJS> jsComponent = do_QueryInterface(aReporter);
+ if (jsComponent) {
+ // We cannot allow non-native reporters (WrappedJS), since we'll be
+ // holding onto a raw pointer, which would point to the wrapper,
+ // and that wrapper is likely to go away as soon as this register
+ // call finishes. This would then lead to subsequent crashes in
+ // CollectReports().
+ return NS_ERROR_XPC_BAD_CONVERT_JS;
+ }
+ mWeakReporters->Put(aReporter, aIsAsync);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::RegisterStrongReporter(nsIMemoryReporter* aReporter)
+{
+ return RegisterReporterHelper(aReporter, /* force = */ false,
+ /* strong = */ true,
+ /* async = */ false);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::RegisterStrongAsyncReporter(nsIMemoryReporter* aReporter)
+{
+ return RegisterReporterHelper(aReporter, /* force = */ false,
+ /* strong = */ true,
+ /* async = */ true);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::RegisterWeakReporter(nsIMemoryReporter* aReporter)
+{
+ return RegisterReporterHelper(aReporter, /* force = */ false,
+ /* strong = */ false,
+ /* async = */ false);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::RegisterWeakAsyncReporter(nsIMemoryReporter* aReporter)
+{
+ return RegisterReporterHelper(aReporter, /* force = */ false,
+ /* strong = */ false,
+ /* async = */ true);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::RegisterStrongReporterEvenIfBlocked(
+ nsIMemoryReporter* aReporter)
+{
+ return RegisterReporterHelper(aReporter, /* force = */ true,
+ /* strong = */ true,
+ /* async = */ false);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::UnregisterStrongReporter(nsIMemoryReporter* aReporter)
+{
+ // This method is thread-safe.
+ mozilla::MutexAutoLock autoLock(mMutex);
+
+ MOZ_ASSERT(!mWeakReporters->Contains(aReporter));
+
+ if (mStrongReporters->Contains(aReporter)) {
+ mStrongReporters->Remove(aReporter);
+ return NS_OK;
+ }
+
+ // We don't register new reporters when the block is in place, but we do
+ // unregister existing reporters. This is so we don't keep holding strong
+ // references that these reporters aren't expecting (which can keep them
+ // alive longer than intended).
+ if (mSavedStrongReporters && mSavedStrongReporters->Contains(aReporter)) {
+ mSavedStrongReporters->Remove(aReporter);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::UnregisterWeakReporter(nsIMemoryReporter* aReporter)
+{
+ // This method is thread-safe.
+ mozilla::MutexAutoLock autoLock(mMutex);
+
+ MOZ_ASSERT(!mStrongReporters->Contains(aReporter));
+
+ if (mWeakReporters->Contains(aReporter)) {
+ mWeakReporters->Remove(aReporter);
+ return NS_OK;
+ }
+
+ // We don't register new reporters when the block is in place, but we do
+ // unregister existing reporters. This is so we don't keep holding weak
+ // references that the old reporters aren't expecting (which can end up as
+ // dangling pointers that lead to use-after-frees).
+ if (mSavedWeakReporters && mSavedWeakReporters->Contains(aReporter)) {
+ mSavedWeakReporters->Remove(aReporter);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::BlockRegistrationAndHideExistingReporters()
+{
+ // This method is thread-safe.
+ mozilla::MutexAutoLock autoLock(mMutex);
+ if (mIsRegistrationBlocked) {
+ return NS_ERROR_FAILURE;
+ }
+ mIsRegistrationBlocked = true;
+
+ // Hide the existing reporters, saving them for later restoration.
+ MOZ_ASSERT(!mSavedStrongReporters);
+ MOZ_ASSERT(!mSavedWeakReporters);
+ mSavedStrongReporters = mStrongReporters;
+ mSavedWeakReporters = mWeakReporters;
+ mStrongReporters = new StrongReportersTable();
+ mWeakReporters = new WeakReportersTable();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::UnblockRegistrationAndRestoreOriginalReporters()
+{
+ // This method is thread-safe.
+ mozilla::MutexAutoLock autoLock(mMutex);
+ if (!mIsRegistrationBlocked) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Banish the current reporters, and restore the hidden ones.
+ delete mStrongReporters;
+ delete mWeakReporters;
+ mStrongReporters = mSavedStrongReporters;
+ mWeakReporters = mSavedWeakReporters;
+ mSavedStrongReporters = nullptr;
+ mSavedWeakReporters = nullptr;
+
+ mIsRegistrationBlocked = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetVsize(int64_t* aVsize)
+{
+#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
+ return VsizeDistinguishedAmount(aVsize);
+#else
+ *aVsize = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+#endif
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetVsizeMaxContiguous(int64_t* aAmount)
+{
+#ifdef HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER
+ return VsizeMaxContiguousDistinguishedAmount(aAmount);
+#else
+ *aAmount = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+#endif
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetResident(int64_t* aAmount)
+{
+#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
+ return ResidentDistinguishedAmount(aAmount);
+#else
+ *aAmount = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+#endif
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetResidentFast(int64_t* aAmount)
+{
+#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
+ return ResidentFastDistinguishedAmount(aAmount);
+#else
+ *aAmount = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+#endif
+}
+
+/*static*/ int64_t
+nsMemoryReporterManager::ResidentFast()
+{
+#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
+ int64_t amount;
+ nsresult rv = ResidentFastDistinguishedAmount(&amount);
+ NS_ENSURE_SUCCESS(rv, 0);
+ return amount;
+#else
+ return 0;
+#endif
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetResidentPeak(int64_t* aAmount)
+{
+#ifdef HAVE_RESIDENT_PEAK_REPORTER
+ return ResidentPeakDistinguishedAmount(aAmount);
+#else
+ *aAmount = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+#endif
+}
+
+/*static*/ int64_t
+nsMemoryReporterManager::ResidentPeak()
+{
+#ifdef HAVE_RESIDENT_PEAK_REPORTER
+ int64_t amount = 0;
+ nsresult rv = ResidentPeakDistinguishedAmount(&amount);
+ NS_ENSURE_SUCCESS(rv, 0);
+ return amount;
+#else
+ return 0;
+#endif
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetResidentUnique(int64_t* aAmount)
+{
+#ifdef HAVE_RESIDENT_UNIQUE_REPORTER
+ return ResidentUniqueDistinguishedAmount(aAmount);
+#else
+ *aAmount = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+#endif
+}
+
+/*static*/ int64_t
+nsMemoryReporterManager::ResidentUnique()
+{
+#ifdef HAVE_RESIDENT_UNIQUE_REPORTER
+ int64_t amount = 0;
+ nsresult rv = ResidentUniqueDistinguishedAmount(&amount);
+ NS_ENSURE_SUCCESS(rv, 0);
+ return amount;
+#else
+ return 0;
+#endif
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetHeapAllocated(int64_t* aAmount)
+{
+#ifdef HAVE_JEMALLOC_STATS
+ jemalloc_stats_t stats;
+ jemalloc_stats(&stats);
+ *aAmount = stats.allocated;
+ return NS_OK;
+#else
+ *aAmount = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+#endif
+}
+
+// This has UNITS_PERCENTAGE, so it is multiplied by 100x.
+NS_IMETHODIMP
+nsMemoryReporterManager::GetHeapOverheadFraction(int64_t* aAmount)
+{
+#ifdef HAVE_JEMALLOC_STATS
+ jemalloc_stats_t stats;
+ jemalloc_stats(&stats);
+ *aAmount = HeapOverheadFraction(&stats);
+ return NS_OK;
+#else
+ *aAmount = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+#endif
+}
+
+static MOZ_MUST_USE nsresult
+GetInfallibleAmount(InfallibleAmountFn aAmountFn, int64_t* aAmount)
+{
+ if (aAmountFn) {
+ *aAmount = aAmountFn();
+ return NS_OK;
+ }
+ *aAmount = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetJSMainRuntimeGCHeap(int64_t* aAmount)
+{
+ return GetInfallibleAmount(mAmountFns.mJSMainRuntimeGCHeap, aAmount);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetJSMainRuntimeTemporaryPeak(int64_t* aAmount)
+{
+ return GetInfallibleAmount(mAmountFns.mJSMainRuntimeTemporaryPeak, aAmount);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetJSMainRuntimeCompartmentsSystem(int64_t* aAmount)
+{
+ return GetInfallibleAmount(mAmountFns.mJSMainRuntimeCompartmentsSystem,
+ aAmount);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetJSMainRuntimeCompartmentsUser(int64_t* aAmount)
+{
+ return GetInfallibleAmount(mAmountFns.mJSMainRuntimeCompartmentsUser,
+ aAmount);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetImagesContentUsedUncompressed(int64_t* aAmount)
+{
+ return GetInfallibleAmount(mAmountFns.mImagesContentUsedUncompressed,
+ aAmount);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetStorageSQLite(int64_t* aAmount)
+{
+ return GetInfallibleAmount(mAmountFns.mStorageSQLite, aAmount);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetLowMemoryEventsVirtual(int64_t* aAmount)
+{
+ return GetInfallibleAmount(mAmountFns.mLowMemoryEventsVirtual, aAmount);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetLowMemoryEventsPhysical(int64_t* aAmount)
+{
+ return GetInfallibleAmount(mAmountFns.mLowMemoryEventsPhysical, aAmount);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetGhostWindows(int64_t* aAmount)
+{
+ return GetInfallibleAmount(mAmountFns.mGhostWindows, aAmount);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetPageFaultsHard(int64_t* aAmount)
+{
+#ifdef HAVE_PAGE_FAULT_REPORTERS
+ return PageFaultsHardDistinguishedAmount(aAmount);
+#else
+ *aAmount = 0;
+ return NS_ERROR_NOT_AVAILABLE;
+#endif
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetHasMozMallocUsableSize(bool* aHas)
+{
+ void* p = malloc(16);
+ if (!p) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ size_t usable = moz_malloc_usable_size(p);
+ free(p);
+ *aHas = !!(usable > 0);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetIsDMDEnabled(bool* aIsEnabled)
+{
+#ifdef MOZ_DMD
+ *aIsEnabled = true;
+#else
+ *aIsEnabled = false;
+#endif
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::GetIsDMDRunning(bool* aIsRunning)
+{
+#ifdef MOZ_DMD
+ *aIsRunning = dmd::IsRunning();
+#else
+ *aIsRunning = false;
+#endif
+ return NS_OK;
+}
+
+namespace {
+
+/**
+ * This runnable lets us implement
+ * nsIMemoryReporterManager::MinimizeMemoryUsage(). We fire a heap-minimize
+ * notification, spin the event loop, and repeat this process a few times.
+ *
+ * When this sequence finishes, we invoke the callback function passed to the
+ * runnable's constructor.
+ */
+class MinimizeMemoryUsageRunnable : public Runnable
+{
+public:
+ explicit MinimizeMemoryUsageRunnable(nsIRunnable* aCallback)
+ : mCallback(aCallback)
+ , mRemainingIters(sNumIters)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (!os) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (mRemainingIters == 0) {
+ os->NotifyObservers(nullptr, "after-minimize-memory-usage",
+ u"MinimizeMemoryUsageRunnable");
+ if (mCallback) {
+ mCallback->Run();
+ }
+ return NS_OK;
+ }
+
+ os->NotifyObservers(nullptr, "memory-pressure", u"heap-minimize");
+ mRemainingIters--;
+ NS_DispatchToMainThread(this);
+
+ return NS_OK;
+ }
+
+private:
+ // Send sNumIters heap-minimize notifications, spinning the event
+ // loop after each notification (see bug 610166 comment 12 for an
+ // explanation), because one notification doesn't cut it.
+ static const uint32_t sNumIters = 3;
+
+ nsCOMPtr<nsIRunnable> mCallback;
+ uint32_t mRemainingIters;
+};
+
+} // namespace
+
+NS_IMETHODIMP
+nsMemoryReporterManager::MinimizeMemoryUsage(nsIRunnable* aCallback)
+{
+ RefPtr<MinimizeMemoryUsageRunnable> runnable =
+ new MinimizeMemoryUsageRunnable(aCallback);
+
+ return NS_DispatchToMainThread(runnable);
+}
+
+NS_IMETHODIMP
+nsMemoryReporterManager::SizeOfTab(mozIDOMWindowProxy* aTopWindow,
+ int64_t* aJSObjectsSize,
+ int64_t* aJSStringsSize,
+ int64_t* aJSOtherSize,
+ int64_t* aDomSize,
+ int64_t* aStyleSize,
+ int64_t* aOtherSize,
+ int64_t* aTotalSize,
+ double* aJSMilliseconds,
+ double* aNonJSMilliseconds)
+{
+ nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aTopWindow);
+ auto* piWindow = nsPIDOMWindowOuter::From(aTopWindow);
+ if (NS_WARN_IF(!global) || NS_WARN_IF(!piWindow)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ TimeStamp t1 = TimeStamp::Now();
+
+ // Measure JS memory consumption (and possibly some non-JS consumption, via
+ // |jsPrivateSize|).
+ size_t jsObjectsSize, jsStringsSize, jsPrivateSize, jsOtherSize;
+ nsresult rv = mSizeOfTabFns.mJS(global->GetGlobalJSObject(),
+ &jsObjectsSize, &jsStringsSize,
+ &jsPrivateSize, &jsOtherSize);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ TimeStamp t2 = TimeStamp::Now();
+
+ // Measure non-JS memory consumption.
+ size_t domSize, styleSize, otherSize;
+ rv = mSizeOfTabFns.mNonJS(piWindow, &domSize, &styleSize, &otherSize);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ TimeStamp t3 = TimeStamp::Now();
+
+ *aTotalSize = 0;
+#define DO(aN, n) { *aN = (n); *aTotalSize += (n); }
+ DO(aJSObjectsSize, jsObjectsSize);
+ DO(aJSStringsSize, jsStringsSize);
+ DO(aJSOtherSize, jsOtherSize);
+ DO(aDomSize, jsPrivateSize + domSize);
+ DO(aStyleSize, styleSize);
+ DO(aOtherSize, otherSize);
+#undef DO
+
+ *aJSMilliseconds = (t2 - t1).ToMilliseconds();
+ *aNonJSMilliseconds = (t3 - t2).ToMilliseconds();
+
+ return NS_OK;
+}
+
+namespace mozilla {
+
+#define GET_MEMORY_REPORTER_MANAGER(mgr) \
+ RefPtr<nsMemoryReporterManager> mgr = \
+ nsMemoryReporterManager::GetOrCreate(); \
+ if (!mgr) { \
+ return NS_ERROR_FAILURE; \
+ }
+
+nsresult
+RegisterStrongMemoryReporter(nsIMemoryReporter* aReporter)
+{
+ // Hold a strong reference to the argument to make sure it gets released if
+ // we return early below.
+ nsCOMPtr<nsIMemoryReporter> reporter = aReporter;
+ GET_MEMORY_REPORTER_MANAGER(mgr)
+ return mgr->RegisterStrongReporter(reporter);
+}
+
+nsresult
+RegisterStrongAsyncMemoryReporter(nsIMemoryReporter* aReporter)
+{
+ // Hold a strong reference to the argument to make sure it gets released if
+ // we return early below.
+ nsCOMPtr<nsIMemoryReporter> reporter = aReporter;
+ GET_MEMORY_REPORTER_MANAGER(mgr)
+ return mgr->RegisterStrongAsyncReporter(reporter);
+}
+
+nsresult
+RegisterWeakMemoryReporter(nsIMemoryReporter* aReporter)
+{
+ GET_MEMORY_REPORTER_MANAGER(mgr)
+ return mgr->RegisterWeakReporter(aReporter);
+}
+
+nsresult
+RegisterWeakAsyncMemoryReporter(nsIMemoryReporter* aReporter)
+{
+ GET_MEMORY_REPORTER_MANAGER(mgr)
+ return mgr->RegisterWeakAsyncReporter(aReporter);
+}
+
+nsresult
+UnregisterStrongMemoryReporter(nsIMemoryReporter* aReporter)
+{
+ GET_MEMORY_REPORTER_MANAGER(mgr)
+ return mgr->UnregisterStrongReporter(aReporter);
+}
+
+nsresult
+UnregisterWeakMemoryReporter(nsIMemoryReporter* aReporter)
+{
+ GET_MEMORY_REPORTER_MANAGER(mgr)
+ return mgr->UnregisterWeakReporter(aReporter);
+}
+
+// Macro for generating functions that register distinguished amount functions
+// with the memory reporter manager.
+#define DEFINE_REGISTER_DISTINGUISHED_AMOUNT(kind, name) \
+ nsresult \
+ Register##name##DistinguishedAmount(kind##AmountFn aAmountFn) \
+ { \
+ GET_MEMORY_REPORTER_MANAGER(mgr) \
+ mgr->mAmountFns.m##name = aAmountFn; \
+ return NS_OK; \
+ }
+
+// Macro for generating functions that unregister distinguished amount
+// functions with the memory reporter manager.
+#define DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(name) \
+ nsresult \
+ Unregister##name##DistinguishedAmount() \
+ { \
+ GET_MEMORY_REPORTER_MANAGER(mgr) \
+ mgr->mAmountFns.m##name = nullptr; \
+ return NS_OK; \
+ }
+
+DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeGCHeap)
+DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeTemporaryPeak)
+DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsSystem)
+DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsUser)
+
+DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, ImagesContentUsedUncompressed)
+DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(ImagesContentUsedUncompressed)
+
+DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, StorageSQLite)
+DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(StorageSQLite)
+
+DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsVirtual)
+DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsPhysical)
+
+DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, GhostWindows)
+
+#undef DEFINE_REGISTER_DISTINGUISHED_AMOUNT
+#undef DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT
+
+#define DEFINE_REGISTER_SIZE_OF_TAB(name) \
+ nsresult \
+ Register##name##SizeOfTab(name##SizeOfTabFn aSizeOfTabFn) \
+ { \
+ GET_MEMORY_REPORTER_MANAGER(mgr) \
+ mgr->mSizeOfTabFns.m##name = aSizeOfTabFn; \
+ return NS_OK; \
+ }
+
+DEFINE_REGISTER_SIZE_OF_TAB(JS);
+DEFINE_REGISTER_SIZE_OF_TAB(NonJS);
+
+#undef DEFINE_REGISTER_SIZE_OF_TAB
+
+#undef GET_MEMORY_REPORTER_MANAGER
+
+} // namespace mozilla
diff --git a/xpcom/base/nsMemoryReporterManager.h b/xpcom/base/nsMemoryReporterManager.h
new file mode 100644
index 0000000000..8240cbe34f
--- /dev/null
+++ b/xpcom/base/nsMemoryReporterManager.h
@@ -0,0 +1,288 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 nsMemoryReporterManager_h__
+#define nsMemoryReporterManager_h__
+
+#include "mozilla/Mutex.h"
+#include "nsHashKeys.h"
+#include "nsIMemoryReporter.h"
+#include "nsITimer.h"
+#include "nsServiceManagerUtils.h"
+#include "nsTHashtable.h"
+
+namespace mozilla {
+namespace dom {
+class ContentParent;
+class MemoryReport;
+} // namespace dom
+} // namespace mozilla
+
+class nsITimer;
+
+class nsMemoryReporterManager final : public nsIMemoryReporterManager
+{
+ virtual ~nsMemoryReporterManager();
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIMEMORYREPORTERMANAGER
+
+ nsMemoryReporterManager();
+
+ // Gets the memory reporter manager service.
+ static nsMemoryReporterManager* GetOrCreate()
+ {
+ nsCOMPtr<nsIMemoryReporterManager> imgr =
+ do_GetService("@mozilla.org/memory-reporter-manager;1");
+ return static_cast<nsMemoryReporterManager*>(imgr.get());
+ }
+
+ typedef nsDataHashtable<nsRefPtrHashKey<nsIMemoryReporter>, bool> StrongReportersTable;
+ typedef nsDataHashtable<nsPtrHashKey<nsIMemoryReporter>, bool> WeakReportersTable;
+
+ // Inter-process memory reporting proceeds as follows.
+ //
+ // - GetReports() (declared within NS_DECL_NSIMEMORYREPORTERMANAGER)
+ // synchronously gets memory reports for the current process, sets up some
+ // state (mPendingProcessesState) for when child processes report back --
+ // including a timer -- and starts telling child processes to get memory
+ // reports. Control then returns to the main event loop.
+ //
+ // The number of concurrent child process reports is limited by the pref
+ // "memory.report_concurrency" in order to prevent the memory overhead of
+ // memory reporting from causing problems, especially on B2G when swapping
+ // to compressed RAM; see bug 1154053.
+ //
+ // - HandleChildReport() is called (asynchronously) once per child process
+ // reporter callback.
+ //
+ // - EndProcessReport() is called (asynchronously) once per process that
+ // finishes reporting back, including the parent. If all processes do so
+ // before time-out, the timer is cancelled. If there are child processes
+ // whose requests have not yet been sent, they will be started until the
+ // concurrency limit is (again) reached.
+ //
+ // - TimeoutCallback() is called (asynchronously) if all the child processes
+ // don't respond within the time threshold.
+ //
+ // - FinishReporting() finishes things off. It is *always* called -- either
+ // from EndChildReport() (if all child processes have reported back) or
+ // from TimeoutCallback() (if time-out occurs).
+ //
+ // All operations occur on the main thread.
+ //
+ // The above sequence of steps is a "request". A partially-completed request
+ // is described as "in flight".
+ //
+ // Each request has a "generation", a unique number that identifies it. This
+ // is used to ensure that each reports from a child process corresponds to
+ // the appropriate request from the parent process. (It's easier to
+ // implement a generation system than to implement a child report request
+ // cancellation mechanism.)
+ //
+ // Failures are mostly ignored, because it's (a) typically the most sensible
+ // thing to do, and (b) often hard to do anything else. The following are
+ // the failure cases of note.
+ //
+ // - If a request is made while the previous request is in flight, the new
+ // request is ignored, as per getReports()'s specification. No error is
+ // reported, because the previous request will complete soon enough.
+ //
+ // - If one or more child processes fail to respond within the time limit,
+ // things will proceed as if they don't exist. No error is reported,
+ // because partial information is better than nothing.
+ //
+ // - If a child process reports after the time-out occurs, it is ignored.
+ // (Generation checking will ensure it is ignored even if a subsequent
+ // request is in flight; this is the main use of generations.) No error
+ // is reported, because there's nothing sensible to be done about it at
+ // this late stage.
+ //
+ // - If the time-out occurs after a child process has sent some reports but
+ // before it has signaled completion (see bug 1151597), then what it
+ // successfully sent will be included, with no explicit indication that it
+ // is incomplete.
+ //
+ // Now, what what happens if a child process is created/destroyed in the
+ // middle of a request? Well, PendingProcessesState is initialized with an array
+ // of child process actors as of when the report started. So...
+ //
+ // - If a process is created after reporting starts, it won't be sent a
+ // request for reports. So the reported data will reflect how things were
+ // when the request began.
+ //
+ // - If a process is destroyed before it starts reporting back, the reported
+ // data will reflect how things are when the request ends.
+ //
+ // - If a process is destroyed after it starts reporting back but before it
+ // finishes, the reported data will contain a partial report for it.
+ //
+ // - If a process is destroyed after reporting back, but before all other
+ // child processes have reported back, it will be included in the reported
+ // data. So the reported data will reflect how things were when the
+ // request began.
+ //
+ // The inconsistencies between these cases are unfortunate but difficult to
+ // avoid. It's enough of an edge case to not be worth doing more.
+ //
+ void HandleChildReport(uint32_t aGeneration,
+ const mozilla::dom::MemoryReport& aChildReport);
+ void EndProcessReport(uint32_t aGeneration, bool aSuccess);
+
+ // Functions that (a) implement distinguished amounts, and (b) are outside of
+ // this module.
+ struct AmountFns
+ {
+ mozilla::InfallibleAmountFn mJSMainRuntimeGCHeap;
+ mozilla::InfallibleAmountFn mJSMainRuntimeTemporaryPeak;
+ mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsSystem;
+ mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsUser;
+
+ mozilla::InfallibleAmountFn mImagesContentUsedUncompressed;
+
+ mozilla::InfallibleAmountFn mStorageSQLite;
+
+ mozilla::InfallibleAmountFn mLowMemoryEventsVirtual;
+ mozilla::InfallibleAmountFn mLowMemoryEventsPhysical;
+
+ mozilla::InfallibleAmountFn mGhostWindows;
+
+ AmountFns()
+ {
+ mozilla::PodZero(this);
+ }
+ };
+ AmountFns mAmountFns;
+
+ // Convenience function to get RSS easily from other code. This is useful
+ // when debugging transient memory spikes with printf instrumentation.
+ static int64_t ResidentFast();
+
+ // Convenience function to get peak RSS easily from other code.
+ static int64_t ResidentPeak();
+
+ // Convenience function to get USS easily from other code. This is useful
+ // when debugging unshared memory pages for forked processes.
+ static int64_t ResidentUnique();
+
+ // Functions that measure per-tab memory consumption.
+ struct SizeOfTabFns
+ {
+ mozilla::JSSizeOfTabFn mJS;
+ mozilla::NonJSSizeOfTabFn mNonJS;
+
+ SizeOfTabFns()
+ {
+ mozilla::PodZero(this);
+ }
+ };
+ SizeOfTabFns mSizeOfTabFns;
+
+private:
+ MOZ_MUST_USE nsresult
+ RegisterReporterHelper(nsIMemoryReporter* aReporter,
+ bool aForce, bool aStrongRef, bool aIsAsync);
+
+ MOZ_MUST_USE nsresult StartGettingReports();
+ // No MOZ_MUST_USE here because ignoring the result is common and reasonable.
+ nsresult FinishReporting();
+
+ void DispatchReporter(nsIMemoryReporter* aReporter, bool aIsAsync,
+ nsIHandleReportCallback* aHandleReport,
+ nsISupports* aHandleReportData,
+ bool aAnonymize);
+
+ static void TimeoutCallback(nsITimer* aTimer, void* aData);
+ // Note: this timeout needs to be long enough to allow for the
+ // possibility of DMD reports and/or running on a low-end phone.
+ static const uint32_t kTimeoutLengthMS = 50000;
+
+ mozilla::Mutex mMutex;
+ bool mIsRegistrationBlocked;
+
+ StrongReportersTable* mStrongReporters;
+ WeakReportersTable* mWeakReporters;
+
+ // These two are only used for testing purposes.
+ StrongReportersTable* mSavedStrongReporters;
+ WeakReportersTable* mSavedWeakReporters;
+
+ uint32_t mNextGeneration;
+
+ // Used to keep track of state of which processes are currently running and
+ // waiting to run memory reports. Holds references to parameters needed when
+ // requesting a memory report and finishing reporting.
+ struct PendingProcessesState
+ {
+ uint32_t mGeneration;
+ bool mAnonymize;
+ bool mMinimize;
+ nsCOMPtr<nsITimer> mTimer;
+ nsTArray<RefPtr<mozilla::dom::ContentParent>> mChildrenPending;
+ uint32_t mNumProcessesRunning;
+ uint32_t mNumProcessesCompleted;
+ uint32_t mConcurrencyLimit;
+ nsCOMPtr<nsIHandleReportCallback> mHandleReport;
+ nsCOMPtr<nsISupports> mHandleReportData;
+ nsCOMPtr<nsIFinishReportingCallback> mFinishReporting;
+ nsCOMPtr<nsISupports> mFinishReportingData;
+ nsString mDMDDumpIdent;
+
+ PendingProcessesState(uint32_t aGeneration, bool aAnonymize, bool aMinimize,
+ uint32_t aConcurrencyLimit,
+ nsIHandleReportCallback* aHandleReport,
+ nsISupports* aHandleReportData,
+ nsIFinishReportingCallback* aFinishReporting,
+ nsISupports* aFinishReportingData,
+ const nsAString& aDMDDumpIdent);
+ };
+
+ // Used to keep track of the state of the asynchronously run memory
+ // reporters. The callback and file handle used when all memory reporters
+ // have finished are also stored here.
+ struct PendingReportersState
+ {
+ // Number of memory reporters currently running.
+ uint32_t mReportsPending;
+
+ // Callback for when all memory reporters have completed.
+ nsCOMPtr<nsIFinishReportingCallback> mFinishReporting;
+ nsCOMPtr<nsISupports> mFinishReportingData;
+
+ // File handle to write a DMD report to if requested.
+ FILE* mDMDFile;
+
+ PendingReportersState(nsIFinishReportingCallback* aFinishReporting,
+ nsISupports* aFinishReportingData,
+ FILE* aDMDFile)
+ : mReportsPending(0)
+ , mFinishReporting(aFinishReporting)
+ , mFinishReportingData(aFinishReportingData)
+ , mDMDFile(aDMDFile)
+ {
+ }
+ };
+
+ // When this is non-null, a request is in flight. Note: We use manual
+ // new/delete for this because its lifetime doesn't match block scope or
+ // anything like that.
+ PendingProcessesState* mPendingProcessesState;
+
+ // This is reinitialized each time a call to GetReports is initiated.
+ PendingReportersState* mPendingReportersState;
+
+ PendingProcessesState* GetStateForGeneration(uint32_t aGeneration);
+ static MOZ_MUST_USE bool
+ StartChildReport(mozilla::dom::ContentParent* aChild,
+ const PendingProcessesState* aState);
+};
+
+#define NS_MEMORY_REPORTER_MANAGER_CID \
+{ 0xfb97e4f5, 0x32dd, 0x497a, \
+{ 0xba, 0xa2, 0x7d, 0x1e, 0x55, 0x7, 0x99, 0x10 } }
+
+#endif // nsMemoryReporterManager_h__
diff --git a/xpcom/base/nsMessageLoop.cpp b/xpcom/base/nsMessageLoop.cpp
new file mode 100644
index 0000000000..fabf6b9f52
--- /dev/null
+++ b/xpcom/base/nsMessageLoop.cpp
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "nsMessageLoop.h"
+#include "mozilla/WeakPtr.h"
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "nsIRunnable.h"
+#include "nsITimer.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla;
+
+namespace {
+
+/**
+ * This Task runs its nsIRunnable when Run() is called, or after
+ * aEnsureRunsAfterMS milliseconds have elapsed since the object was
+ * constructed.
+ *
+ * Note that the MessageLoop owns this object and will delete it after it calls
+ * Run(). Tread lightly.
+ */
+class MessageLoopIdleTask
+ : public Runnable
+ , public SupportsWeakPtr<MessageLoopIdleTask>
+{
+public:
+ MOZ_DECLARE_WEAKREFERENCE_TYPENAME(MessageLoopIdleTask)
+ MessageLoopIdleTask(nsIRunnable* aTask, uint32_t aEnsureRunsAfterMS);
+ NS_IMETHOD Run() override;
+
+private:
+ nsresult Init(uint32_t aEnsureRunsAfterMS);
+
+ nsCOMPtr<nsIRunnable> mTask;
+ nsCOMPtr<nsITimer> mTimer;
+
+ virtual ~MessageLoopIdleTask() {}
+};
+
+/**
+ * This timer callback calls MessageLoopIdleTask::Run() when its timer fires.
+ * (The timer can't call back into MessageLoopIdleTask directly since that's
+ * not a refcounted object; it's owned by the MessageLoop.)
+ *
+ * We keep a weak reference to the MessageLoopIdleTask, although a raw pointer
+ * should in theory suffice: When the MessageLoopIdleTask runs (right before
+ * the MessageLoop deletes it), it cancels its timer. But the weak pointer
+ * saves us from worrying about an edge case somehow messing us up here.
+ */
+class MessageLoopTimerCallback
+ : public nsITimerCallback
+{
+public:
+ explicit MessageLoopTimerCallback(MessageLoopIdleTask* aTask);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSITIMERCALLBACK
+
+private:
+ WeakPtr<MessageLoopIdleTask> mTask;
+
+ virtual ~MessageLoopTimerCallback() {}
+};
+
+MessageLoopIdleTask::MessageLoopIdleTask(nsIRunnable* aTask,
+ uint32_t aEnsureRunsAfterMS)
+ : mTask(aTask)
+{
+ // Init() really shouldn't fail, but if it does, we schedule our runnable
+ // immediately, because it's more important to guarantee that we run the task
+ // eventually than it is to run the task when we're idle.
+ nsresult rv = Init(aEnsureRunsAfterMS);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Running idle task early because we couldn't initialize our timer.");
+ NS_DispatchToCurrentThread(mTask);
+
+ mTask = nullptr;
+ mTimer = nullptr;
+ }
+}
+
+nsresult
+MessageLoopIdleTask::Init(uint32_t aEnsureRunsAfterMS)
+{
+ mTimer = do_CreateInstance("@mozilla.org/timer;1");
+ if (NS_WARN_IF(!mTimer)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ RefPtr<MessageLoopTimerCallback> callback =
+ new MessageLoopTimerCallback(this);
+
+ return mTimer->InitWithCallback(callback, aEnsureRunsAfterMS,
+ nsITimer::TYPE_ONE_SHOT);
+}
+
+NS_IMETHODIMP
+MessageLoopIdleTask::Run()
+{
+ // Null out our pointers because if Run() was called by the timer, this
+ // object will be kept alive by the MessageLoop until the MessageLoop calls
+ // Run().
+
+ if (mTimer) {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+
+ if (mTask) {
+ mTask->Run();
+ mTask = nullptr;
+ }
+
+ return NS_OK;
+}
+
+MessageLoopTimerCallback::MessageLoopTimerCallback(MessageLoopIdleTask* aTask)
+ : mTask(aTask)
+{
+}
+
+NS_IMETHODIMP
+MessageLoopTimerCallback::Notify(nsITimer* aTimer)
+{
+ // We don't expect to hit the case when the timer fires but mTask has been
+ // deleted, because mTask should cancel the timer before the mTask is
+ // deleted. But you never know...
+ NS_WARNING_ASSERTION(mTask, "This timer shouldn't have fired.");
+
+ if (mTask) {
+ mTask->Run();
+ }
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(MessageLoopTimerCallback, nsITimerCallback)
+
+} // namespace
+
+NS_IMPL_ISUPPORTS(nsMessageLoop, nsIMessageLoop)
+
+NS_IMETHODIMP
+nsMessageLoop::PostIdleTask(nsIRunnable* aTask, uint32_t aEnsureRunsAfterMS)
+{
+ // The message loop owns MessageLoopIdleTask and deletes it after calling
+ // Run(). Be careful...
+ RefPtr<MessageLoopIdleTask> idle =
+ new MessageLoopIdleTask(aTask, aEnsureRunsAfterMS);
+ MessageLoop::current()->PostIdleTask(idle.forget());
+
+ return NS_OK;
+}
+
+nsresult
+nsMessageLoopConstructor(nsISupports* aOuter,
+ const nsIID& aIID,
+ void** aInstancePtr)
+{
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+ nsISupports* messageLoop = new nsMessageLoop();
+ return messageLoop->QueryInterface(aIID, aInstancePtr);
+}
diff --git a/xpcom/base/nsMessageLoop.h b/xpcom/base/nsMessageLoop.h
new file mode 100644
index 0000000000..e7b1462216
--- /dev/null
+++ b/xpcom/base/nsMessageLoop.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/. */
+
+#include "nsIMessageLoop.h"
+
+/*
+ * nsMessageLoop implements nsIMessageLoop, which wraps Chromium's MessageLoop
+ * class and adds a bit of sugar.
+ */
+class nsMessageLoop : public nsIMessageLoop
+{
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMESSAGELOOP
+
+private:
+ virtual ~nsMessageLoop()
+ {
+ }
+};
+
+#define NS_MESSAGE_LOOP_CID \
+{0x67b3ac0c, 0xd806, 0x4d48, \
+{0x93, 0x9e, 0x6a, 0x81, 0x9e, 0x6c, 0x24, 0x8f}}
+
+extern nsresult
+nsMessageLoopConstructor(nsISupports* aOuter,
+ const nsIID& aIID,
+ void** aInstancePtr);
diff --git a/xpcom/base/nsObjCExceptions.h b/xpcom/base/nsObjCExceptions.h
new file mode 100644
index 0000000000..e63c92af57
--- /dev/null
+++ b/xpcom/base/nsObjCExceptions.h
@@ -0,0 +1,230 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Undo the damage that exception_defines.h does.
+#undef try
+#undef catch
+
+#ifndef nsObjCExceptions_h_
+#define nsObjCExceptions_h_
+
+#import <Foundation/Foundation.h>
+
+#ifdef DEBUG
+#import <ExceptionHandling/NSExceptionHandler.h>
+#endif
+
+#if defined(MOZ_CRASHREPORTER) && defined(__cplusplus)
+#include "nsICrashReporter.h"
+#include "nsCOMPtr.h"
+#include "nsServiceManagerUtils.h"
+#endif
+
+#include <unistd.h>
+#include <signal.h>
+#include "nsError.h"
+
+// Undo the damage that exception_defines.h does.
+#undef try
+#undef catch
+
+/* NOTE: Macros that claim to abort no longer abort, see bug 486574.
+ * If you actually want to log and abort, call "nsObjCExceptionLogAbort"
+ * from an exception handler. At some point we will fix this by replacing
+ * all macros in the tree with appropriately-named macros.
+ */
+
+// See Mozilla bug 163260.
+// This file can only be included in an Objective-C context.
+
+__attribute__((unused))
+static void
+nsObjCExceptionLog(NSException* aException)
+{
+ NSLog(@"Mozilla has caught an Obj-C exception [%@: %@]",
+ [aException name], [aException reason]);
+
+#if defined(MOZ_CRASHREPORTER) && defined(__cplusplus)
+ // Attach exception info to the crash report.
+ nsCOMPtr<nsICrashReporter> crashReporter =
+ do_GetService("@mozilla.org/toolkit/crash-reporter;1");
+ if (crashReporter) {
+ crashReporter->AppendObjCExceptionInfoToAppNotes(static_cast<void*>(aException));
+ }
+#endif
+
+#ifdef DEBUG
+ @try {
+ // Try to get stack information out of the exception. 10.5 returns the stack
+ // info with the callStackReturnAddresses selector.
+ NSArray* stackTrace = nil;
+ if ([aException respondsToSelector:@selector(callStackReturnAddresses)]) {
+ NSArray* addresses = (NSArray*)
+ [aException performSelector:@selector(callStackReturnAddresses)];
+ if ([addresses count]) {
+ stackTrace = addresses;
+ }
+ }
+
+ // 10.4 doesn't respond to callStackReturnAddresses so we'll try to pull the
+ // stack info out of the userInfo. It might not be there, sadly :(
+ if (!stackTrace) {
+ stackTrace = [[aException userInfo] objectForKey:NSStackTraceKey];
+ }
+
+ if (stackTrace) {
+ // The command line should look like this:
+ // /usr/bin/atos -p <pid> -printHeader <stack frame addresses>
+ NSMutableArray* args =
+ [NSMutableArray arrayWithCapacity:[stackTrace count] + 3];
+
+ [args addObject:@"-p"];
+ int pid = [[NSProcessInfo processInfo] processIdentifier];
+ [args addObject:[NSString stringWithFormat:@"%d", pid]];
+
+ [args addObject:@"-printHeader"];
+
+ unsigned int stackCount = [stackTrace count];
+ unsigned int stackIndex = 0;
+ for (; stackIndex < stackCount; stackIndex++) {
+ unsigned long address =
+ [[stackTrace objectAtIndex:stackIndex] unsignedLongValue];
+ [args addObject:[NSString stringWithFormat:@"0x%lx", address]];
+ }
+
+ NSPipe* outPipe = [NSPipe pipe];
+
+ NSTask* task = [[NSTask alloc] init];
+ [task setLaunchPath:@"/usr/bin/atos"];
+ [task setArguments:args];
+ [task setStandardOutput:outPipe];
+ [task setStandardError:outPipe];
+
+ NSLog(@"Generating stack trace for Obj-C exception...");
+
+ // This will throw an exception if the atos tool cannot be found, and in
+ // that case we'll just hit our @catch block below.
+ [task launch];
+
+ [task waitUntilExit];
+ [task release];
+
+ NSData* outData =
+ [[outPipe fileHandleForReading] readDataToEndOfFile];
+ NSString* outString =
+ [[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding];
+
+ NSLog(@"Stack trace:\n%@", outString);
+
+ [outString release];
+ } else {
+ NSLog(@"<No stack information available for Obj-C exception>");
+ }
+ }
+ @catch (NSException* exn) {
+ NSLog(@"Failed to generate stack trace for Obj-C exception [%@: %@]",
+ [exn name], [exn reason]);
+ }
+#endif
+}
+
+__attribute__((unused))
+static void
+nsObjCExceptionAbort()
+{
+ // We need to raise a mach-o signal here, the Mozilla crash reporter on
+ // Mac OS X does not respond to POSIX signals. Raising mach-o signals directly
+ // is tricky so we do it by just derefing a null pointer.
+ int* foo = nullptr;
+ *foo = 1;
+}
+
+__attribute__((unused))
+static void
+nsObjCExceptionLogAbort(NSException* aException)
+{
+ nsObjCExceptionLog(aException);
+ nsObjCExceptionAbort();
+}
+
+#define NS_OBJC_TRY(_e, _fail) \
+@try { _e; } \
+@catch(NSException *_exn) { \
+ nsObjCExceptionLog(_exn); \
+ _fail; \
+}
+
+#define NS_OBJC_TRY_EXPR(_e, _fail) \
+({ \
+ typeof(_e) _tmp; \
+ @try { _tmp = (_e); } \
+ @catch(NSException *_exn) { \
+ nsObjCExceptionLog(_exn); \
+ _fail; \
+ } \
+ _tmp; \
+})
+
+#define NS_OBJC_TRY_EXPR_NULL(_e) \
+NS_OBJC_TRY_EXPR(_e, 0)
+
+#define NS_OBJC_TRY_IGNORE(_e) \
+NS_OBJC_TRY(_e, )
+
+// To reduce code size the abort versions do not reuse above macros. This allows
+// catch blocks to only contain one call.
+
+#define NS_OBJC_TRY_ABORT(_e) \
+@try { _e; } \
+@catch(NSException *_exn) { \
+ nsObjCExceptionLog(_exn); \
+}
+
+#define NS_OBJC_TRY_EXPR_ABORT(_e) \
+({ \
+ typeof(_e) _tmp; \
+ @try { _tmp = (_e); } \
+ @catch(NSException *_exn) { \
+ nsObjCExceptionLog(_exn); \
+ } \
+ _tmp; \
+})
+
+// For wrapping blocks of Obj-C calls. Does not actually terminate.
+#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK @try {
+#define NS_OBJC_END_TRY_ABORT_BLOCK } @catch(NSException *_exn) { \
+ nsObjCExceptionLog(_exn); \
+ }
+
+// Same as above ABORT_BLOCK but returns a value after the try/catch block to
+// suppress compiler warnings. This allows us to avoid having to refactor code
+// to get scoping right when wrapping an entire method.
+
+#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL @try {
+#define NS_OBJC_END_TRY_ABORT_BLOCK_NIL } @catch(NSException *_exn) { \
+ nsObjCExceptionLog(_exn); \
+ } \
+ return nil;
+
+#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL @try {
+#define NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL } @catch(NSException *_exn) { \
+ nsObjCExceptionLog(_exn); \
+ } \
+ return nullptr;
+
+#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT @try {
+#define NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT } @catch(NSException *_exn) { \
+ nsObjCExceptionLog(_exn); \
+ } \
+ return NS_ERROR_FAILURE;
+
+#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN @try {
+#define NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(_rv) } @catch(NSException *_exn) { \
+ nsObjCExceptionLog(_exn);\
+ } \
+ return _rv;
+
+#endif // nsObjCExceptions_h_
diff --git a/xpcom/base/nsQueryObject.h b/xpcom/base/nsQueryObject.h
new file mode 100644
index 0000000000..a525463244
--- /dev/null
+++ b/xpcom/base/nsQueryObject.h
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsQueryObject_h
+#define nsQueryObject_h
+
+#include "mozilla/Attributes.h"
+
+#include "nsCOMPtr.h"
+#include "mozilla/RefPtr.h"
+
+/*****************************************************************************/
+
+template<class T>
+class MOZ_STACK_CLASS nsQueryObject final : public nsCOMPtr_helper
+{
+public:
+ explicit nsQueryObject(T* aRawPtr)
+ : mRawPtr(aRawPtr)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID& aIID,
+ void** aResult) const
+ {
+ nsresult status = mRawPtr ? mRawPtr->QueryInterface(aIID, aResult)
+ : NS_ERROR_NULL_POINTER;
+ return status;
+ }
+private:
+ T* MOZ_NON_OWNING_REF mRawPtr;
+};
+
+template<class T>
+class MOZ_STACK_CLASS nsQueryObjectWithError final : public nsCOMPtr_helper
+{
+public:
+ nsQueryObjectWithError(T* aRawPtr, nsresult* aErrorPtr)
+ : mRawPtr(aRawPtr), mErrorPtr(aErrorPtr)
+ {
+ }
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID& aIID,
+ void** aResult) const
+ {
+ nsresult status = mRawPtr ? mRawPtr->QueryInterface(aIID, aResult)
+ : NS_ERROR_NULL_POINTER;
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+ }
+private:
+ T* MOZ_NON_OWNING_REF mRawPtr;
+ nsresult* mErrorPtr;
+};
+
+/*****************************************************************************/
+
+/*****************************************************************************/
+
+template<class T>
+inline nsQueryObject<T>
+do_QueryObject(T* aRawPtr)
+{
+ return nsQueryObject<T>(aRawPtr);
+}
+
+template<class T>
+inline nsQueryObject<T>
+do_QueryObject(nsCOMPtr<T>& aRawPtr)
+{
+ return nsQueryObject<T>(aRawPtr);
+}
+
+template<class T>
+inline nsQueryObject<T>
+do_QueryObject(RefPtr<T>& aRawPtr)
+{
+ return nsQueryObject<T>(aRawPtr);
+}
+
+template<class T>
+inline nsQueryObjectWithError<T>
+do_QueryObject(T* aRawPtr, nsresult* aErrorPtr)
+{
+ return nsQueryObjectWithError<T>(aRawPtr, aErrorPtr);
+}
+
+template<class T>
+inline nsQueryObjectWithError<T>
+do_QueryObject(nsCOMPtr<T>& aRawPtr, nsresult* aErrorPtr)
+{
+ return nsQueryObjectWithError<T>(aRawPtr, aErrorPtr);
+}
+
+template<class T>
+inline nsQueryObjectWithError<T>
+do_QueryObject(RefPtr<T>& aRawPtr, nsresult* aErrorPtr)
+{
+ return nsQueryObjectWithError<T>(aRawPtr, aErrorPtr);
+}
+
+/*****************************************************************************/
+
+#endif // !defined(nsQueryObject_h)
diff --git a/xpcom/base/nsSecurityConsoleMessage.cpp b/xpcom/base/nsSecurityConsoleMessage.cpp
new file mode 100644
index 0000000000..3e6409cf32
--- /dev/null
+++ b/xpcom/base/nsSecurityConsoleMessage.cpp
@@ -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/. */
+
+#include "nsSecurityConsoleMessage.h"
+
+NS_IMPL_ISUPPORTS(nsSecurityConsoleMessage, nsISecurityConsoleMessage)
+
+nsSecurityConsoleMessage::nsSecurityConsoleMessage()
+{
+}
+
+nsSecurityConsoleMessage::~nsSecurityConsoleMessage()
+{
+}
+
+NS_IMETHODIMP
+nsSecurityConsoleMessage::GetTag(nsAString& aTag)
+{
+ aTag = mTag;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSecurityConsoleMessage::SetTag(const nsAString& aTag)
+{
+ mTag = aTag;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSecurityConsoleMessage::GetCategory(nsAString& aCategory)
+{
+ aCategory = mCategory;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSecurityConsoleMessage::SetCategory(const nsAString& aCategory)
+{
+ mCategory = aCategory;
+ return NS_OK;
+}
diff --git a/xpcom/base/nsSecurityConsoleMessage.h b/xpcom/base/nsSecurityConsoleMessage.h
new file mode 100644
index 0000000000..c93c13613f
--- /dev/null
+++ b/xpcom/base/nsSecurityConsoleMessage.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsSecurityConsoleMessage_h__
+#define nsSecurityConsoleMessage_h__
+#include "nsISecurityConsoleMessage.h"
+#include "nsString.h"
+
+class nsSecurityConsoleMessage final : public nsISecurityConsoleMessage
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISECURITYCONSOLEMESSAGE
+
+ nsSecurityConsoleMessage();
+
+private:
+ ~nsSecurityConsoleMessage();
+
+protected:
+ nsString mTag;
+ nsString mCategory;
+};
+
+#define NS_SECURITY_CONSOLE_MESSAGE_CID \
+ {0x43ebf210, 0x8a7b, 0x4ddb, {0xa8, 0x3d, 0xb8, 0x7c, 0x51, 0xa0, 0x58, 0xdb}}
+#endif //nsSecurityConsoleMessage_h__
diff --git a/xpcom/base/nsSetDllDirectory.h b/xpcom/base/nsSetDllDirectory.h
new file mode 100644
index 0000000000..0920d5936f
--- /dev/null
+++ b/xpcom/base/nsSetDllDirectory.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 nsSetDllDirectory_h
+#define nsSetDllDirectory_h
+
+#ifndef XP_WIN
+#error This file only makes sense on Windows.
+#endif
+
+#include <windows.h>
+#include <nscore.h>
+#include <stdlib.h>
+
+namespace mozilla {
+
+static void
+SanitizeEnvironmentVariables()
+{
+ DWORD bufferSize = GetEnvironmentVariableW(L"PATH", nullptr, 0);
+ if (bufferSize) {
+ wchar_t* originalPath = new wchar_t[bufferSize];
+ if (bufferSize - 1 == GetEnvironmentVariableW(L"PATH", originalPath,
+ bufferSize)) {
+ bufferSize = ExpandEnvironmentStringsW(originalPath, nullptr, 0);
+ if (bufferSize) {
+ wchar_t* newPath = new wchar_t[bufferSize];
+ if (ExpandEnvironmentStringsW(originalPath,
+ newPath,
+ bufferSize)) {
+ SetEnvironmentVariableW(L"PATH", newPath);
+ }
+ delete[] newPath;
+ }
+ }
+ delete[] originalPath;
+ }
+}
+
+}
+
+#endif
diff --git a/xpcom/base/nsStatusReporterManager.cpp b/xpcom/base/nsStatusReporterManager.cpp
new file mode 100644
index 0000000000..491e7d458b
--- /dev/null
+++ b/xpcom/base/nsStatusReporterManager.cpp
@@ -0,0 +1,320 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "nsStatusReporterManager.h"
+#include "nsCOMPtr.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsArrayEnumerator.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIFile.h"
+#include "nsDumpUtils.h"
+#include "nsIFileStreams.h"
+#include "nsPrintfCString.h"
+
+#ifdef XP_WIN
+#include <process.h>
+#define getpid _getpid
+#else
+#include <unistd.h>
+#endif
+
+#ifdef XP_UNIX
+#define DO_STATUS_REPORT 1
+#endif
+
+#ifdef DO_STATUS_REPORT // {
+namespace {
+
+class DumpStatusInfoToTempDirRunnable : public mozilla::Runnable
+{
+public:
+ DumpStatusInfoToTempDirRunnable()
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ nsCOMPtr<nsIStatusReporterManager> mgr =
+ do_GetService("@mozilla.org/status-reporter-manager;1");
+ mgr->DumpReports();
+ return NS_OK;
+ }
+};
+
+void
+doStatusReport(const nsCString& aInputStr)
+{
+ LOG("FifoWatcher(%s) dispatching status report runnable.", aInputStr.get());
+ RefPtr<DumpStatusInfoToTempDirRunnable> runnable =
+ new DumpStatusInfoToTempDirRunnable();
+ NS_DispatchToMainThread(runnable);
+}
+
+} //anonymous namespace
+#endif // DO_STATUS_REPORT }
+
+static bool gStatusReportProgress = 0;
+static int gNumReporters = 0;
+
+nsresult
+getStatus(nsACString& aDesc)
+{
+ if (!gStatusReportProgress) {
+ aDesc.AssignLiteral("Init");
+ } else {
+ aDesc.AssignLiteral("Running: There are ");
+ aDesc.AppendInt(gNumReporters);
+ aDesc.AppendLiteral(" reporters");
+ }
+ return NS_OK;
+}
+
+NS_STATUS_REPORTER_IMPLEMENT(StatusReporter, "StatusReporter State", getStatus)
+
+#define DUMP(o, s) \
+ do { \
+ const char* s2 = (s); \
+ uint32_t dummy; \
+ nsresult rvDump = (o)->Write((s2), strlen(s2), &dummy); \
+ if (NS_WARN_IF(NS_FAILED(rvDump))) \
+ return rvDump; \
+ } while (0)
+
+static nsresult
+DumpReport(nsIFileOutputStream* aOStream, const nsCString& aProcess,
+ const nsCString& aName, const nsCString& aDescription)
+{
+ if (aProcess.IsEmpty()) {
+ int pid = getpid();
+ nsPrintfCString pidStr("PID %u", pid);
+ DUMP(aOStream, "\n {\n \"Process\": \"");
+ DUMP(aOStream, pidStr.get());
+ } else {
+ DUMP(aOStream, "\n { \"Unknown Process\": \"");
+ }
+
+ DUMP(aOStream, "\",\n \"Reporter name\": \"");
+ DUMP(aOStream, aName.get());
+
+ DUMP(aOStream, "\",\n \"Status Description\": [\"");
+ nsCString desc = aDescription;
+ desc.ReplaceSubstring("|", "\",\"");
+ DUMP(aOStream, desc.get());
+
+ DUMP(aOStream, "\"]\n }");
+
+ return NS_OK;
+}
+
+/**
+ ** nsStatusReporterManager implementation
+ **/
+
+NS_IMPL_ISUPPORTS(nsStatusReporterManager, nsIStatusReporterManager)
+
+nsStatusReporterManager::nsStatusReporterManager()
+{
+}
+
+nsStatusReporterManager::~nsStatusReporterManager()
+{
+}
+
+NS_IMETHODIMP
+nsStatusReporterManager::Init()
+{
+ RegisterReporter(new NS_STATUS_REPORTER_NAME(StatusReporter));
+ gStatusReportProgress = 1;
+
+#ifdef DO_STATUS_REPORT
+ if (FifoWatcher::MaybeCreate()) {
+ FifoWatcher* fw = FifoWatcher::GetSingleton();
+ fw->RegisterCallback(NS_LITERAL_CSTRING("status report"), doStatusReport);
+ }
+#endif
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStatusReporterManager::DumpReports()
+{
+ static unsigned number = 1;
+ nsresult rv;
+
+ nsCString filename("status-reports-");
+ filename.AppendInt(getpid());
+ filename.Append('-');
+ filename.AppendInt(number++);
+ filename.AppendLiteral(".json");
+
+ // Open a file in NS_OS_TEMP_DIR for writing.
+ // The file is initialized as "incomplete-status-reports-pid-number.json" in the
+ // begining, it will be rename as "status-reports-pid-number.json" in the end.
+ nsCOMPtr<nsIFile> tmpFile;
+ rv = nsDumpUtils::OpenTempFile(NS_LITERAL_CSTRING("incomplete-") +
+ filename,
+ getter_AddRefs(tmpFile),
+ NS_LITERAL_CSTRING("status-reports"));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIFileOutputStream> ostream =
+ do_CreateInstance("@mozilla.org/network/file-output-stream;1");
+ rv = ostream->Init(tmpFile, -1, -1, 0);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ //Write the reports to the file
+
+ DUMP(ostream, "{\n\"subject\":\"about:service reports\",\n");
+ DUMP(ostream, "\"reporters\": [ ");
+
+ nsCOMPtr<nsISimpleEnumerator> e;
+ bool more, first = true;
+ EnumerateReporters(getter_AddRefs(e));
+ while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
+ nsCOMPtr<nsISupports> supports;
+ e->GetNext(getter_AddRefs(supports));
+ nsCOMPtr<nsIStatusReporter> r = do_QueryInterface(supports);
+
+ nsCString process;
+ rv = r->GetProcess(process);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCString name;
+ rv = r->GetName(name);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCString description;
+ rv = r->GetDescription(description);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (first) {
+ first = false;
+ } else {
+ DUMP(ostream, ",");
+ }
+
+ rv = DumpReport(ostream, process, name, description);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+ DUMP(ostream, "\n]\n}\n");
+
+ rv = ostream->Close();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Rename the status reports file
+ nsCOMPtr<nsIFile> srFinalFile;
+ rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(srFinalFile));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+#ifdef ANDROID
+ rv = srFinalFile->AppendNative(NS_LITERAL_CSTRING("status-reports"));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+#endif
+
+ rv = srFinalFile->AppendNative(filename);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = srFinalFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsAutoString srActualFinalFilename;
+ rv = srFinalFile->GetLeafName(srActualFinalFilename);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = tmpFile->MoveTo(/* directory */ nullptr, srActualFinalFilename);
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStatusReporterManager::EnumerateReporters(nsISimpleEnumerator** aResult)
+{
+ return NS_NewArrayEnumerator(aResult, mReporters);
+}
+
+NS_IMETHODIMP
+nsStatusReporterManager::RegisterReporter(nsIStatusReporter* aReporter)
+{
+ if (mReporters.IndexOf(aReporter) != -1) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mReporters.AppendObject(aReporter);
+ gNumReporters++;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStatusReporterManager::UnregisterReporter(nsIStatusReporter* aReporter)
+{
+ if (!mReporters.RemoveObject(aReporter)) {
+ return NS_ERROR_FAILURE;
+ }
+ gNumReporters--;
+ return NS_OK;
+}
+
+nsresult
+NS_RegisterStatusReporter(nsIStatusReporter* aReporter)
+{
+ nsCOMPtr<nsIStatusReporterManager> mgr =
+ do_GetService("@mozilla.org/status-reporter-manager;1");
+ if (!mgr) {
+ return NS_ERROR_FAILURE;
+ }
+ return mgr->RegisterReporter(aReporter);
+}
+
+nsresult
+NS_UnregisterStatusReporter(nsIStatusReporter* aReporter)
+{
+ nsCOMPtr<nsIStatusReporterManager> mgr =
+ do_GetService("@mozilla.org/status-reporter-manager;1");
+ if (!mgr) {
+ return NS_ERROR_FAILURE;
+ }
+ return mgr->UnregisterReporter(aReporter);
+}
+
+nsresult
+NS_DumpStatusReporter()
+{
+ nsCOMPtr<nsIStatusReporterManager> mgr =
+ do_GetService("@mozilla.org/status-reporter-manager;1");
+ if (!mgr) {
+ return NS_ERROR_FAILURE;
+ }
+ return mgr->DumpReports();
+}
diff --git a/xpcom/base/nsStatusReporterManager.h b/xpcom/base/nsStatusReporterManager.h
new file mode 100644
index 0000000000..84427cfcf6
--- /dev/null
+++ b/xpcom/base/nsStatusReporterManager.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/. */
+
+
+#include "nsIStatusReporter.h"
+#include "nsCOMArray.h"
+#include "nsString.h"
+
+class nsStatusReporter final : public nsIStatusReporter
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISTATUSREPORTER
+
+ nsStatusReporter(nsACString& aProcess, nsACString& aDesc);
+
+private:
+ nsCString sProcess;
+ nsCString sName;
+ nsCString sDesc;
+
+ virtual ~nsStatusReporter();
+};
+
+
+class nsStatusReporterManager : public nsIStatusReporterManager
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISTATUSREPORTERMANAGER
+
+ nsStatusReporterManager();
+
+private:
+ nsCOMArray<nsIStatusReporter> mReporters;
+
+ virtual ~nsStatusReporterManager();
+};
diff --git a/xpcom/base/nsSystemInfo.cpp b/xpcom/base/nsSystemInfo.cpp
new file mode 100644
index 0000000000..f6d9fd5ad3
--- /dev/null
+++ b/xpcom/base/nsSystemInfo.cpp
@@ -0,0 +1,985 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+
+#include "nsSystemInfo.h"
+#include "prsystem.h"
+#include "prio.h"
+#include "prprf.h"
+#include "mozilla/SSE.h"
+#include "mozilla/arm.h"
+#include "mozilla/Sprintf.h"
+
+#ifdef XP_WIN
+#include <time.h>
+#include <windows.h>
+#include <winioctl.h>
+#include "base/scoped_handle_win.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsIObserverService.h"
+#include "nsWindowsHelpers.h"
+#endif
+
+#ifdef XP_MACOSX
+#include "MacHelpers.h"
+#endif
+
+#ifdef MOZ_WIDGET_GTK
+#include <gtk/gtk.h>
+#include <dlfcn.h>
+#endif
+
+#if defined (XP_LINUX) && !defined (ANDROID)
+#include <unistd.h>
+#include <fstream>
+#include "mozilla/Tokenizer.h"
+#include "nsCharSeparatedTokenizer.h"
+
+#include <map>
+#include <string>
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#include "mozilla/dom/ContentChild.h"
+#endif
+
+#ifdef MOZ_WIDGET_GONK
+#include <sys/system_properties.h>
+#include "mozilla/Preferences.h"
+#include "nsPrintfCString.h"
+#endif
+
+#ifdef ANDROID
+extern "C" {
+NS_EXPORT int android_sdk_version;
+}
+#endif
+
+#ifdef XP_MACOSX
+#include <sys/sysctl.h>
+#endif
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+#include "mozilla/SandboxInfo.h"
+#endif
+
+// Slot for NS_InitXPCOM2 to pass information to nsSystemInfo::Init.
+// Only set to nonzero (potentially) if XP_UNIX. On such systems, the
+// system call to discover the appropriate value is not thread-safe,
+// so we must call it before going multithreaded, but nsSystemInfo::Init
+// only happens well after that point.
+uint32_t nsSystemInfo::gUserUmask = 0;
+
+#if defined (XP_LINUX) && !defined (ANDROID)
+static void
+SimpleParseKeyValuePairs(const std::string& aFilename,
+ std::map<nsCString, nsCString>& aKeyValuePairs)
+{
+ std::ifstream input(aFilename.c_str());
+ for (std::string line; std::getline(input, line); ) {
+ nsAutoCString key, value;
+
+ nsCCharSeparatedTokenizer tokens(nsDependentCString(line.c_str()), ':');
+ if (tokens.hasMoreTokens()) {
+ key = tokens.nextToken();
+ if (tokens.hasMoreTokens()) {
+ value = tokens.nextToken();
+ }
+ // We want the value even if there was just one token, to cover the
+ // case where we had the key, and the value was blank (seems to be
+ // a valid scenario some files.)
+ aKeyValuePairs[key] = value;
+ }
+ }
+}
+#endif
+
+#if defined(XP_WIN)
+namespace {
+nsresult
+GetHDDInfo(const char* aSpecialDirName, nsAutoCString& aModel,
+ nsAutoCString& aRevision)
+{
+ aModel.Truncate();
+ aRevision.Truncate();
+
+ nsCOMPtr<nsIFile> profDir;
+ nsresult rv = NS_GetSpecialDirectory(aSpecialDirName,
+ getter_AddRefs(profDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoString profDirPath;
+ rv = profDir->GetPath(profDirPath);
+ NS_ENSURE_SUCCESS(rv, rv);
+ wchar_t volumeMountPoint[MAX_PATH] = {L'\\', L'\\', L'.', L'\\'};
+ const size_t PREFIX_LEN = 4;
+ if (!::GetVolumePathNameW(profDirPath.get(), volumeMountPoint + PREFIX_LEN,
+ mozilla::ArrayLength(volumeMountPoint) -
+ PREFIX_LEN)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ size_t volumeMountPointLen = wcslen(volumeMountPoint);
+ // Since we would like to open a drive and not a directory, we need to
+ // remove any trailing backslash. A drive handle is valid for
+ // DeviceIoControl calls, a directory handle is not.
+ if (volumeMountPoint[volumeMountPointLen - 1] == L'\\') {
+ volumeMountPoint[volumeMountPointLen - 1] = L'\0';
+ }
+ ScopedHandle handle(::CreateFileW(volumeMountPoint, 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr, OPEN_EXISTING, 0, nullptr));
+ if (!handle.IsValid()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ STORAGE_PROPERTY_QUERY queryParameters = {
+ StorageDeviceProperty, PropertyStandardQuery
+ };
+ STORAGE_DEVICE_DESCRIPTOR outputHeader = {sizeof(STORAGE_DEVICE_DESCRIPTOR)};
+ DWORD bytesRead = 0;
+ if (!::DeviceIoControl(handle, IOCTL_STORAGE_QUERY_PROPERTY,
+ &queryParameters, sizeof(queryParameters),
+ &outputHeader, sizeof(outputHeader), &bytesRead,
+ nullptr)) {
+ return NS_ERROR_FAILURE;
+ }
+ PSTORAGE_DEVICE_DESCRIPTOR deviceOutput =
+ (PSTORAGE_DEVICE_DESCRIPTOR)malloc(outputHeader.Size);
+ if (!::DeviceIoControl(handle, IOCTL_STORAGE_QUERY_PROPERTY,
+ &queryParameters, sizeof(queryParameters),
+ deviceOutput, outputHeader.Size, &bytesRead,
+ nullptr)) {
+ free(deviceOutput);
+ return NS_ERROR_FAILURE;
+ }
+ // Some HDDs are including product ID info in the vendor field. Since PNP
+ // IDs include vendor info and product ID concatenated together, we'll do
+ // that here and interpret the result as a unique ID for the HDD model.
+ if (deviceOutput->VendorIdOffset) {
+ aModel = reinterpret_cast<char*>(deviceOutput) +
+ deviceOutput->VendorIdOffset;
+ }
+ if (deviceOutput->ProductIdOffset) {
+ aModel += reinterpret_cast<char*>(deviceOutput) +
+ deviceOutput->ProductIdOffset;
+ }
+ aModel.CompressWhitespace();
+ if (deviceOutput->ProductRevisionOffset) {
+ aRevision = reinterpret_cast<char*>(deviceOutput) +
+ deviceOutput->ProductRevisionOffset;
+ aRevision.CompressWhitespace();
+ }
+ free(deviceOutput);
+ return NS_OK;
+}
+
+nsresult GetInstallYear(uint32_t& aYear)
+{
+ HKEY hKey;
+ LONG status = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+ NS_LITERAL_STRING(
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"
+ ).get(),
+ 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
+
+ if (status != ERROR_SUCCESS) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsAutoRegKey key(hKey);
+
+ DWORD type = 0;
+ time_t raw_time = 0;
+ DWORD time_size = sizeof(time_t);
+
+ status = RegQueryValueExW(hKey, L"InstallDate",
+ nullptr, &type, (LPBYTE)&raw_time, &time_size);
+
+ if (status != ERROR_SUCCESS) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (type != REG_DWORD) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ tm time;
+ if (localtime_s(&time, &raw_time) != 0) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ aYear = 1900UL + time.tm_year;
+ return NS_OK;
+}
+
+nsresult GetCountryCode(nsAString& aCountryCode)
+{
+ GEOID geoid = GetUserGeoID(GEOCLASS_NATION);
+ if (geoid == GEOID_NOT_AVAILABLE) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ // Get required length
+ int numChars = GetGeoInfoW(geoid, GEO_ISO2, nullptr, 0, 0);
+ if (!numChars) {
+ return NS_ERROR_FAILURE;
+ }
+ // Now get the string for real
+ aCountryCode.SetLength(numChars);
+ numChars = GetGeoInfoW(geoid, GEO_ISO2, wwc(aCountryCode.BeginWriting()),
+ aCountryCode.Length(), 0);
+ if (!numChars) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // numChars includes null terminator
+ aCountryCode.Truncate(numChars - 1);
+ return NS_OK;
+}
+
+} // namespace
+#endif // defined(XP_WIN)
+
+using namespace mozilla;
+
+nsSystemInfo::nsSystemInfo()
+{
+}
+
+nsSystemInfo::~nsSystemInfo()
+{
+}
+
+// CPU-specific information.
+static const struct PropItems
+{
+ const char* name;
+ bool (*propfun)(void);
+} cpuPropItems[] = {
+ // x86-specific bits.
+ { "hasMMX", mozilla::supports_mmx },
+ { "hasSSE", mozilla::supports_sse },
+ { "hasSSE2", mozilla::supports_sse2 },
+ { "hasSSE3", mozilla::supports_sse3 },
+ { "hasSSSE3", mozilla::supports_ssse3 },
+ { "hasSSE4A", mozilla::supports_sse4a },
+ { "hasSSE4_1", mozilla::supports_sse4_1 },
+ { "hasSSE4_2", mozilla::supports_sse4_2 },
+ { "hasAVX", mozilla::supports_avx },
+ { "hasAVX2", mozilla::supports_avx2 },
+ // ARM-specific bits.
+ { "hasEDSP", mozilla::supports_edsp },
+ { "hasARMv6", mozilla::supports_armv6 },
+ { "hasARMv7", mozilla::supports_armv7 },
+ { "hasNEON", mozilla::supports_neon }
+};
+
+#ifdef XP_WIN
+// Lifted from media/webrtc/trunk/webrtc/base/systeminfo.cc,
+// so keeping the _ instead of switching to camel case for now.
+typedef BOOL (WINAPI *LPFN_GLPI)(
+ PSYSTEM_LOGICAL_PROCESSOR_INFORMATION,
+ PDWORD);
+static void
+GetProcessorInformation(int* physical_cpus, int* cache_size_L2, int* cache_size_L3)
+{
+ MOZ_ASSERT(physical_cpus && cache_size_L2 && cache_size_L3);
+
+ *physical_cpus = 0;
+ *cache_size_L2 = 0; // This will be in kbytes
+ *cache_size_L3 = 0; // This will be in kbytes
+
+ // GetLogicalProcessorInformation() is available on Windows XP SP3 and beyond.
+ LPFN_GLPI glpi = reinterpret_cast<LPFN_GLPI>(GetProcAddress(
+ GetModuleHandle(L"kernel32"),
+ "GetLogicalProcessorInformation"));
+ if (nullptr == glpi) {
+ return;
+ }
+ // Determine buffer size, allocate and get processor information.
+ // Size can change between calls (unlikely), so a loop is done.
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION info_buffer[32];
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION* infos = &info_buffer[0];
+ DWORD return_length = sizeof(info_buffer);
+ while (!glpi(infos, &return_length)) {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER && infos == &info_buffer[0]) {
+ infos = new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)];
+ } else {
+ return;
+ }
+ }
+
+ for (size_t i = 0;
+ i < return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ++i) {
+ if (infos[i].Relationship == RelationProcessorCore) {
+ ++*physical_cpus;
+ } else if (infos[i].Relationship == RelationCache) {
+ // Only care about L2 and L3 cache
+ switch (infos[i].Cache.Level) {
+ case 2:
+ *cache_size_L2 = static_cast<int>(infos[i].Cache.Size/1024);
+ break;
+ case 3:
+ *cache_size_L3 = static_cast<int>(infos[i].Cache.Size/1024);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (infos != &info_buffer[0]) {
+ delete [] infos;
+ }
+ return;
+}
+#endif
+
+nsresult
+nsSystemInfo::Init()
+{
+ nsresult rv;
+
+ static const struct
+ {
+ PRSysInfo cmd;
+ const char* name;
+ } items[] = {
+ { PR_SI_SYSNAME, "name" },
+ { PR_SI_HOSTNAME, "host" },
+ { PR_SI_ARCHITECTURE, "arch" },
+ { PR_SI_RELEASE, "version" }
+ };
+
+ for (uint32_t i = 0; i < (sizeof(items) / sizeof(items[0])); i++) {
+ char buf[SYS_INFO_BUFFER_LENGTH];
+ if (PR_GetSystemInfo(items[i].cmd, buf, sizeof(buf)) == PR_SUCCESS) {
+ rv = SetPropertyAsACString(NS_ConvertASCIItoUTF16(items[i].name),
+ nsDependentCString(buf));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ } else {
+ NS_WARNING("PR_GetSystemInfo failed");
+ }
+ }
+
+ rv = SetPropertyAsBool(NS_ConvertASCIItoUTF16("hasWindowsTouchInterface"),
+ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Additional informations not available through PR_GetSystemInfo.
+ SetInt32Property(NS_LITERAL_STRING("pagesize"), PR_GetPageSize());
+ SetInt32Property(NS_LITERAL_STRING("pageshift"), PR_GetPageShift());
+ SetInt32Property(NS_LITERAL_STRING("memmapalign"), PR_GetMemMapAlignment());
+ SetUint64Property(NS_LITERAL_STRING("memsize"), PR_GetPhysicalMemorySize());
+ SetUint32Property(NS_LITERAL_STRING("umask"), nsSystemInfo::gUserUmask);
+
+ uint64_t virtualMem = 0;
+ nsAutoCString cpuVendor;
+ int cpuSpeed = -1;
+ int cpuFamily = -1;
+ int cpuModel = -1;
+ int cpuStepping = -1;
+ int logicalCPUs = -1;
+ int physicalCPUs = -1;
+ int cacheSizeL2 = -1;
+ int cacheSizeL3 = -1;
+
+#if defined (XP_WIN)
+ // Virtual memory:
+ MEMORYSTATUSEX memStat;
+ memStat.dwLength = sizeof(memStat);
+ if (GlobalMemoryStatusEx(&memStat)) {
+ virtualMem = memStat.ullTotalVirtual;
+ }
+
+ // CPU speed
+ HKEY key;
+ static const WCHAR keyName[] =
+ L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName , 0, KEY_QUERY_VALUE, &key)
+ == ERROR_SUCCESS) {
+ DWORD data, len, vtype;
+ len = sizeof(data);
+
+ if (RegQueryValueEx(key, L"~Mhz", 0, 0, reinterpret_cast<LPBYTE>(&data),
+ &len) == ERROR_SUCCESS) {
+ cpuSpeed = static_cast<int>(data);
+ }
+
+ // Limit to 64 double byte characters, should be plenty, but create
+ // a buffer one larger as the result may not be null terminated. If
+ // it is more than 64, we will not get the value.
+ wchar_t cpuVendorStr[64+1];
+ len = sizeof(cpuVendorStr)-2;
+ if (RegQueryValueExW(key, L"VendorIdentifier",
+ 0, &vtype,
+ reinterpret_cast<LPBYTE>(cpuVendorStr),
+ &len) == ERROR_SUCCESS &&
+ vtype == REG_SZ && len % 2 == 0 && len > 1) {
+ cpuVendorStr[len/2] = 0; // In case it isn't null terminated
+ CopyUTF16toUTF8(nsDependentString(cpuVendorStr), cpuVendor);
+ }
+
+ RegCloseKey(key);
+ }
+
+ // Other CPU attributes:
+ SYSTEM_INFO si;
+ GetNativeSystemInfo(&si);
+ logicalCPUs = si.dwNumberOfProcessors;
+ GetProcessorInformation(&physicalCPUs, &cacheSizeL2, &cacheSizeL3);
+ if (physicalCPUs <= 0) {
+ physicalCPUs = logicalCPUs;
+ }
+ cpuFamily = si.wProcessorLevel;
+ cpuModel = si.wProcessorRevision >> 8;
+ cpuStepping = si.wProcessorRevision & 0xFF;
+#elif defined (XP_MACOSX)
+ // CPU speed
+ uint64_t sysctlValue64 = 0;
+ uint32_t sysctlValue32 = 0;
+ size_t len = 0;
+ len = sizeof(sysctlValue64);
+ if (!sysctlbyname("hw.cpufrequency_max", &sysctlValue64, &len, NULL, 0)) {
+ cpuSpeed = static_cast<int>(sysctlValue64/1000000);
+ }
+ MOZ_ASSERT(sizeof(sysctlValue64) == len);
+
+ len = sizeof(sysctlValue32);
+ if (!sysctlbyname("hw.physicalcpu_max", &sysctlValue32, &len, NULL, 0)) {
+ physicalCPUs = static_cast<int>(sysctlValue32);
+ }
+ MOZ_ASSERT(sizeof(sysctlValue32) == len);
+
+ len = sizeof(sysctlValue32);
+ if (!sysctlbyname("hw.logicalcpu_max", &sysctlValue32, &len, NULL, 0)) {
+ logicalCPUs = static_cast<int>(sysctlValue32);
+ }
+ MOZ_ASSERT(sizeof(sysctlValue32) == len);
+
+ len = sizeof(sysctlValue64);
+ if (!sysctlbyname("hw.l2cachesize", &sysctlValue64, &len, NULL, 0)) {
+ cacheSizeL2 = static_cast<int>(sysctlValue64/1024);
+ }
+ MOZ_ASSERT(sizeof(sysctlValue64) == len);
+
+ len = sizeof(sysctlValue64);
+ if (!sysctlbyname("hw.l3cachesize", &sysctlValue64, &len, NULL, 0)) {
+ cacheSizeL3 = static_cast<int>(sysctlValue64/1024);
+ }
+ MOZ_ASSERT(sizeof(sysctlValue64) == len);
+
+ if (!sysctlbyname("machdep.cpu.vendor", NULL, &len, NULL, 0)) {
+ char* cpuVendorStr = new char[len];
+ if (!sysctlbyname("machdep.cpu.vendor", cpuVendorStr, &len, NULL, 0)) {
+ cpuVendor = cpuVendorStr;
+ }
+ delete [] cpuVendorStr;
+ }
+
+ len = sizeof(sysctlValue32);
+ if (!sysctlbyname("machdep.cpu.family", &sysctlValue32, &len, NULL, 0)) {
+ cpuFamily = static_cast<int>(sysctlValue32);
+ }
+ MOZ_ASSERT(sizeof(sysctlValue32) == len);
+
+ len = sizeof(sysctlValue32);
+ if (!sysctlbyname("machdep.cpu.model", &sysctlValue32, &len, NULL, 0)) {
+ cpuModel = static_cast<int>(sysctlValue32);
+ }
+ MOZ_ASSERT(sizeof(sysctlValue32) == len);
+
+ len = sizeof(sysctlValue32);
+ if (!sysctlbyname("machdep.cpu.stepping", &sysctlValue32, &len, NULL, 0)) {
+ cpuStepping = static_cast<int>(sysctlValue32);
+ }
+ MOZ_ASSERT(sizeof(sysctlValue32) == len);
+
+#elif defined (XP_LINUX) && !defined (ANDROID)
+ // Get vendor, family, model, stepping, physical cores, L3 cache size
+ // from /proc/cpuinfo file
+ {
+ std::map<nsCString, nsCString> keyValuePairs;
+ SimpleParseKeyValuePairs("/proc/cpuinfo", keyValuePairs);
+
+ // cpuVendor from "vendor_id"
+ cpuVendor.Assign(keyValuePairs[NS_LITERAL_CSTRING("vendor_id")]);
+
+ {
+ // cpuFamily from "cpu family"
+ Tokenizer::Token t;
+ Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("cpu family")]);
+ if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+ t.AsInteger() <= INT32_MAX) {
+ cpuFamily = static_cast<int>(t.AsInteger());
+ }
+ }
+
+ {
+ // cpuModel from "model"
+ Tokenizer::Token t;
+ Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("model")]);
+ if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+ t.AsInteger() <= INT32_MAX) {
+ cpuModel = static_cast<int>(t.AsInteger());
+ }
+ }
+
+ {
+ // cpuStepping from "stepping"
+ Tokenizer::Token t;
+ Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("stepping")]);
+ if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+ t.AsInteger() <= INT32_MAX) {
+ cpuStepping = static_cast<int>(t.AsInteger());
+ }
+ }
+
+ {
+ // physicalCPUs from "cpu cores"
+ Tokenizer::Token t;
+ Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("cpu cores")]);
+ if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+ t.AsInteger() <= INT32_MAX) {
+ physicalCPUs = static_cast<int>(t.AsInteger());
+ }
+ }
+
+ {
+ // cacheSizeL3 from "cache size"
+ Tokenizer::Token t;
+ Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("cache size")]);
+ if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+ t.AsInteger() <= INT32_MAX) {
+ cacheSizeL3 = static_cast<int>(t.AsInteger());
+ if (p.Next(t) && t.Type() == Tokenizer::TOKEN_WORD &&
+ t.AsString() != NS_LITERAL_CSTRING("KB")) {
+ // If we get here, there was some text after the cache size value
+ // and that text was not KB. For now, just don't report the
+ // L3 cache.
+ cacheSizeL3 = -1;
+ }
+ }
+ }
+ }
+
+ {
+ // Get cpuSpeed from another file.
+ std::ifstream input("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
+ std::string line;
+ if (getline(input, line)) {
+ Tokenizer::Token t;
+ Tokenizer p(line.c_str());
+ if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+ t.AsInteger() <= INT32_MAX) {
+ cpuSpeed = static_cast<int>(t.AsInteger()/1000);
+ }
+ }
+ }
+
+ {
+ // Get cacheSizeL2 from yet another file
+ std::ifstream input("/sys/devices/system/cpu/cpu0/cache/index2/size");
+ std::string line;
+ if (getline(input, line)) {
+ Tokenizer::Token t;
+ Tokenizer p(line.c_str(), nullptr, "K");
+ if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
+ t.AsInteger() <= INT32_MAX) {
+ cacheSizeL2 = static_cast<int>(t.AsInteger());
+ }
+ }
+ }
+
+ SetInt32Property(NS_LITERAL_STRING("cpucount"), PR_GetNumberOfProcessors());
+#else
+ SetInt32Property(NS_LITERAL_STRING("cpucount"), PR_GetNumberOfProcessors());
+#endif
+
+ if (virtualMem) SetUint64Property(NS_LITERAL_STRING("virtualmemsize"), virtualMem);
+ if (cpuSpeed >= 0) SetInt32Property(NS_LITERAL_STRING("cpuspeed"), cpuSpeed);
+ if (!cpuVendor.IsEmpty()) SetPropertyAsACString(NS_LITERAL_STRING("cpuvendor"), cpuVendor);
+ if (cpuFamily >= 0) SetInt32Property(NS_LITERAL_STRING("cpufamily"), cpuFamily);
+ if (cpuModel >= 0) SetInt32Property(NS_LITERAL_STRING("cpumodel"), cpuModel);
+ if (cpuStepping >= 0) SetInt32Property(NS_LITERAL_STRING("cpustepping"), cpuStepping);
+
+ if (logicalCPUs >= 0) SetInt32Property(NS_LITERAL_STRING("cpucount"), logicalCPUs);
+ if (physicalCPUs >= 0) SetInt32Property(NS_LITERAL_STRING("cpucores"), physicalCPUs);
+
+ if (cacheSizeL2 >= 0) SetInt32Property(NS_LITERAL_STRING("cpucachel2"), cacheSizeL2);
+ if (cacheSizeL3 >= 0) SetInt32Property(NS_LITERAL_STRING("cpucachel3"), cacheSizeL3);
+
+ for (uint32_t i = 0; i < ArrayLength(cpuPropItems); i++) {
+ rv = SetPropertyAsBool(NS_ConvertASCIItoUTF16(cpuPropItems[i].name),
+ cpuPropItems[i].propfun());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+#ifdef XP_WIN
+ BOOL isWow64;
+ BOOL gotWow64Value = IsWow64Process(GetCurrentProcess(), &isWow64);
+ NS_WARNING_ASSERTION(gotWow64Value, "IsWow64Process failed");
+ if (gotWow64Value) {
+ rv = SetPropertyAsBool(NS_LITERAL_STRING("isWow64"), !!isWow64);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+ if (NS_FAILED(GetProfileHDDInfo())) {
+ // We might have been called before profile-do-change. We'll observe that
+ // event so that we can fill this in later.
+ nsCOMPtr<nsIObserverService> obsService =
+ do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ rv = obsService->AddObserver(this, "profile-do-change", false);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ nsAutoCString hddModel, hddRevision;
+ if (NS_SUCCEEDED(GetHDDInfo(NS_GRE_DIR, hddModel, hddRevision))) {
+ rv = SetPropertyAsACString(NS_LITERAL_STRING("binHDDModel"), hddModel);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = SetPropertyAsACString(NS_LITERAL_STRING("binHDDRevision"),
+ hddRevision);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ if (NS_SUCCEEDED(GetHDDInfo(NS_WIN_WINDOWS_DIR, hddModel, hddRevision))) {
+ rv = SetPropertyAsACString(NS_LITERAL_STRING("winHDDModel"), hddModel);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = SetPropertyAsACString(NS_LITERAL_STRING("winHDDRevision"),
+ hddRevision);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsAutoString countryCode;
+ if (NS_SUCCEEDED(GetCountryCode(countryCode))) {
+ rv = SetPropertyAsAString(NS_LITERAL_STRING("countryCode"), countryCode);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ uint32_t installYear = 0;
+ if (NS_SUCCEEDED(GetInstallYear(installYear))) {
+ rv = SetPropertyAsUint32(NS_LITERAL_STRING("installYear"), installYear);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+#endif
+
+#if defined(XP_MACOSX)
+ nsAutoString countryCode;
+ if (NS_SUCCEEDED(GetSelectedCityInfo(countryCode))) {
+ rv = SetPropertyAsAString(NS_LITERAL_STRING("countryCode"), countryCode);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+#endif
+
+#if defined(MOZ_WIDGET_GTK)
+ // This must be done here because NSPR can only separate OS's when compiled, not libraries.
+ // 64 bytes is going to be well enough for "GTK " followed by 3 integers
+ // separated with dots.
+ char gtkver[64];
+ ssize_t gtkver_len = 0;
+
+#if MOZ_WIDGET_GTK == 2
+ extern int gtk_read_end_of_the_pipe;
+
+ if (gtk_read_end_of_the_pipe != -1) {
+ do {
+ gtkver_len = read(gtk_read_end_of_the_pipe, &gtkver, sizeof(gtkver));
+ } while (gtkver_len < 0 && errno == EINTR);
+ close(gtk_read_end_of_the_pipe);
+ }
+#endif
+
+ if (gtkver_len <= 0) {
+ gtkver_len = SprintfLiteral(gtkver, "GTK %u.%u.%u", gtk_major_version,
+ gtk_minor_version, gtk_micro_version);
+ }
+
+ nsAutoCString secondaryLibrary;
+ if (gtkver_len > 0 && gtkver_len < int(sizeof(gtkver))) {
+ secondaryLibrary.Append(nsDependentCSubstring(gtkver, gtkver_len));
+ }
+
+ void* libpulse = dlopen("libpulse.so.0", RTLD_LAZY);
+ const char* libpulseVersion = "not-available";
+ if (libpulse) {
+ auto pa_get_library_version = reinterpret_cast<const char* (*)()>
+ (dlsym(libpulse, "pa_get_library_version"));
+
+ if (pa_get_library_version) {
+ libpulseVersion = pa_get_library_version();
+ }
+ }
+
+ secondaryLibrary.AppendPrintf(",libpulse %s", libpulseVersion);
+
+ if (libpulse) {
+ dlclose(libpulse);
+ }
+
+ rv = SetPropertyAsACString(NS_LITERAL_STRING("secondaryLibrary"),
+ secondaryLibrary);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+ AndroidSystemInfo info;
+ if (XRE_IsContentProcess()) {
+ dom::ContentChild* child = dom::ContentChild::GetSingleton();
+ if (child) {
+ child->SendGetAndroidSystemInfo(&info);
+ SetupAndroidInfo(info);
+ }
+ } else {
+ GetAndroidSystemInfo(&info);
+ SetupAndroidInfo(info);
+ }
+#endif
+
+#ifdef MOZ_WIDGET_GONK
+ char sdk[PROP_VALUE_MAX];
+ if (__system_property_get("ro.build.version.sdk", sdk)) {
+ android_sdk_version = atoi(sdk);
+ SetPropertyAsInt32(NS_LITERAL_STRING("sdk_version"), android_sdk_version);
+
+ SetPropertyAsACString(NS_LITERAL_STRING("secondaryLibrary"),
+ nsPrintfCString("SDK %u", android_sdk_version));
+ }
+
+ char characteristics[PROP_VALUE_MAX];
+ if (__system_property_get("ro.build.characteristics", characteristics)) {
+ if (!strcmp(characteristics, "tablet")) {
+ SetPropertyAsBool(NS_LITERAL_STRING("tablet"), true);
+ } else if (!strcmp(characteristics, "tv")) {
+ SetPropertyAsBool(NS_LITERAL_STRING("tv"), true);
+ }
+ }
+
+ nsAutoString str;
+ rv = GetPropertyAsAString(NS_LITERAL_STRING("version"), str);
+ if (NS_SUCCEEDED(rv)) {
+ SetPropertyAsAString(NS_LITERAL_STRING("kernel_version"), str);
+ }
+
+ const nsAdoptingString& b2g_os_name =
+ mozilla::Preferences::GetString("b2g.osName");
+ if (b2g_os_name) {
+ SetPropertyAsAString(NS_LITERAL_STRING("name"), b2g_os_name);
+ }
+
+ const nsAdoptingString& b2g_version =
+ mozilla::Preferences::GetString("b2g.version");
+ if (b2g_version) {
+ SetPropertyAsAString(NS_LITERAL_STRING("version"), b2g_version);
+ }
+#endif
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+ SandboxInfo sandInfo = SandboxInfo::Get();
+
+ SetPropertyAsBool(NS_LITERAL_STRING("hasSeccompBPF"),
+ sandInfo.Test(SandboxInfo::kHasSeccompBPF));
+ SetPropertyAsBool(NS_LITERAL_STRING("hasSeccompTSync"),
+ sandInfo.Test(SandboxInfo::kHasSeccompTSync));
+ SetPropertyAsBool(NS_LITERAL_STRING("hasUserNamespaces"),
+ sandInfo.Test(SandboxInfo::kHasUserNamespaces));
+ SetPropertyAsBool(NS_LITERAL_STRING("hasPrivilegedUserNamespaces"),
+ sandInfo.Test(SandboxInfo::kHasPrivilegedUserNamespaces));
+
+ if (sandInfo.Test(SandboxInfo::kEnabledForContent)) {
+ SetPropertyAsBool(NS_LITERAL_STRING("canSandboxContent"),
+ sandInfo.CanSandboxContent());
+ }
+
+ if (sandInfo.Test(SandboxInfo::kEnabledForMedia)) {
+ SetPropertyAsBool(NS_LITERAL_STRING("canSandboxMedia"),
+ sandInfo.CanSandboxMedia());
+ }
+#endif // XP_LINUX && MOZ_SANDBOX
+
+ return NS_OK;
+}
+
+#ifdef MOZ_WIDGET_ANDROID
+// Prerelease versions of Android use a letter instead of version numbers.
+// Unfortunately this breaks websites due to the user agent.
+// Chrome works around this by hardcoding an Android version when a
+// numeric version can't be obtained. We're doing the same.
+// This version will need to be updated whenever there is a new official
+// Android release.
+// See: https://cs.chromium.org/chromium/src/base/sys_info_android.cc?l=61
+#define DEFAULT_ANDROID_VERSION "6.0.99"
+
+/* static */
+void
+nsSystemInfo::GetAndroidSystemInfo(AndroidSystemInfo* aInfo)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (!mozilla::AndroidBridge::Bridge()) {
+ aInfo->sdk_version() = 0;
+ return;
+ }
+
+ nsAutoString str;
+ if (mozilla::AndroidBridge::Bridge()->GetStaticStringField(
+ "android/os/Build", "MODEL", str)) {
+ aInfo->device() = str;
+ }
+ if (mozilla::AndroidBridge::Bridge()->GetStaticStringField(
+ "android/os/Build", "MANUFACTURER", str)) {
+ aInfo->manufacturer() = str;
+ }
+ if (mozilla::AndroidBridge::Bridge()->GetStaticStringField(
+ "android/os/Build$VERSION", "RELEASE", str)) {
+ int major_version;
+ int minor_version;
+ int bugfix_version;
+ int num_read = sscanf(NS_ConvertUTF16toUTF8(str).get(), "%d.%d.%d", &major_version, &minor_version, &bugfix_version);
+ if (num_read == 0) {
+ aInfo->release_version() = NS_LITERAL_STRING(DEFAULT_ANDROID_VERSION);
+ } else {
+ aInfo->release_version() = str;
+ }
+ }
+ if (mozilla::AndroidBridge::Bridge()->GetStaticStringField(
+ "android/os/Build", "HARDWARE", str)) {
+ aInfo->hardware() = str;
+ }
+ int32_t sdk_version;
+ if (!mozilla::AndroidBridge::Bridge()->GetStaticIntField(
+ "android/os/Build$VERSION", "SDK_INT", &sdk_version)) {
+ sdk_version = 0;
+ }
+ aInfo->sdk_version() = sdk_version;
+ aInfo->isTablet() = java::GeckoAppShell::IsTablet();
+}
+
+void
+nsSystemInfo::SetupAndroidInfo(const AndroidSystemInfo& aInfo)
+{
+ if (!aInfo.device().IsEmpty()) {
+ SetPropertyAsAString(NS_LITERAL_STRING("device"), aInfo.device());
+ }
+ if (!aInfo.manufacturer().IsEmpty()) {
+ SetPropertyAsAString(NS_LITERAL_STRING("manufacturer"), aInfo.manufacturer());
+ }
+ if (!aInfo.release_version().IsEmpty()) {
+ SetPropertyAsAString(NS_LITERAL_STRING("release_version"), aInfo.release_version());
+ }
+ SetPropertyAsBool(NS_LITERAL_STRING("tablet"), aInfo.isTablet());
+ // NSPR "version" is the kernel version. For Android we want the Android version.
+ // Rename SDK version to version and put the kernel version into kernel_version.
+ nsAutoString str;
+ nsresult rv = GetPropertyAsAString(NS_LITERAL_STRING("version"), str);
+ if (NS_SUCCEEDED(rv)) {
+ SetPropertyAsAString(NS_LITERAL_STRING("kernel_version"), str);
+ }
+ // When AndroidBridge is not available (eg. in xpcshell tests), sdk_version is 0.
+ if (aInfo.sdk_version() != 0) {
+ android_sdk_version = aInfo.sdk_version();
+ if (android_sdk_version >= 8 && !aInfo.hardware().IsEmpty()) {
+ SetPropertyAsAString(NS_LITERAL_STRING("hardware"), aInfo.hardware());
+ }
+ SetPropertyAsInt32(NS_LITERAL_STRING("version"), android_sdk_version);
+ }
+}
+#endif // MOZ_WIDGET_ANDROID
+
+void
+nsSystemInfo::SetInt32Property(const nsAString& aPropertyName,
+ const int32_t aValue)
+{
+ NS_WARNING_ASSERTION(aValue > 0, "Unable to read system value");
+ if (aValue > 0) {
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ SetPropertyAsInt32(aPropertyName, aValue);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Unable to set property");
+ }
+}
+
+void
+nsSystemInfo::SetUint32Property(const nsAString& aPropertyName,
+ const uint32_t aValue)
+{
+ // Only one property is currently set via this function.
+ // It may legitimately be zero.
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ SetPropertyAsUint32(aPropertyName, aValue);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Unable to set property");
+}
+
+void
+nsSystemInfo::SetUint64Property(const nsAString& aPropertyName,
+ const uint64_t aValue)
+{
+ NS_WARNING_ASSERTION(aValue > 0, "Unable to read system value");
+ if (aValue > 0) {
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ SetPropertyAsUint64(aPropertyName, aValue);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Unable to set property");
+ }
+}
+
+#if defined(XP_WIN)
+NS_IMETHODIMP
+nsSystemInfo::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ if (!strcmp(aTopic, "profile-do-change")) {
+ nsresult rv;
+ nsCOMPtr<nsIObserverService> obsService =
+ do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = obsService->RemoveObserver(this, "profile-do-change");
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ return GetProfileHDDInfo();
+ }
+ return NS_OK;
+}
+
+nsresult
+nsSystemInfo::GetProfileHDDInfo()
+{
+ nsAutoCString hddModel, hddRevision;
+ nsresult rv = GetHDDInfo(NS_APP_USER_PROFILE_50_DIR, hddModel, hddRevision);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = SetPropertyAsACString(NS_LITERAL_STRING("profileHDDModel"), hddModel);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = SetPropertyAsACString(NS_LITERAL_STRING("profileHDDRevision"),
+ hddRevision);
+ return rv;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(nsSystemInfo, nsHashPropertyBag, nsIObserver)
+#endif // defined(XP_WIN)
+
diff --git a/xpcom/base/nsSystemInfo.h b/xpcom/base/nsSystemInfo.h
new file mode 100644
index 0000000000..5a7ef44240
--- /dev/null
+++ b/xpcom/base/nsSystemInfo.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _NSSYSTEMINFO_H_
+#define _NSSYSTEMINFO_H_
+
+#include "nsHashPropertyBag.h"
+#if defined(XP_WIN)
+#include "nsIObserver.h"
+#endif // defined(XP_WIN)
+
+#ifdef MOZ_WIDGET_ANDROID
+#include "mozilla/dom/PContent.h"
+#endif // MOZ_WIDGET_ANDROID
+
+class nsSystemInfo final
+ : public nsHashPropertyBag
+#if defined(XP_WIN)
+ , public nsIObserver
+#endif // defined(XP_WIN)
+{
+public:
+#if defined(XP_WIN)
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIOBSERVER
+#endif // defined(XP_WIN)
+
+ nsSystemInfo();
+
+ nsresult Init();
+
+ // Slot for NS_InitXPCOM2 to pass information to nsSystemInfo::Init.
+ // See comments above the variable definition and in NS_InitXPCOM2.
+ static uint32_t gUserUmask;
+
+#ifdef MOZ_WIDGET_ANDROID
+ static void GetAndroidSystemInfo(mozilla::dom::AndroidSystemInfo* aInfo);
+ protected:
+ void SetupAndroidInfo(const mozilla::dom::AndroidSystemInfo&);
+#endif
+
+protected:
+ void SetInt32Property(const nsAString& aPropertyName,
+ const int32_t aValue);
+ void SetUint32Property(const nsAString& aPropertyName,
+ const uint32_t aValue);
+ void SetUint64Property(const nsAString& aPropertyName,
+ const uint64_t aValue);
+
+private:
+ ~nsSystemInfo();
+
+#if defined(XP_WIN)
+ nsresult GetProfileHDDInfo();
+#endif // defined(XP_WIN)
+};
+
+#define NS_SYSTEMINFO_CONTRACTID "@mozilla.org/system-info;1"
+#define NS_SYSTEMINFO_CID \
+{ 0xd962398a, 0x99e5, 0x49b2, \
+{ 0x85, 0x7a, 0xc1, 0x59, 0x04, 0x9c, 0x7f, 0x6c } }
+
+#endif /* _NSSYSTEMINFO_H_ */
diff --git a/xpcom/base/nsTraceRefcnt.cpp b/xpcom/base/nsTraceRefcnt.cpp
new file mode 100644
index 0000000000..3b415174b4
--- /dev/null
+++ b/xpcom/base/nsTraceRefcnt.cpp
@@ -0,0 +1,1319 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "nsTraceRefcnt.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/StaticPtr.h"
+#include "nsXPCOMPrivate.h"
+#include "nscore.h"
+#include "nsISupports.h"
+#include "nsTArray.h"
+#include "nsTHashtable.h"
+#include "prenv.h"
+#include "plstr.h"
+#include "prlink.h"
+#include "nsCRT.h"
+#include <math.h>
+#include "nsHashKeys.h"
+#include "mozilla/StackWalk.h"
+#include "nsThreadUtils.h"
+#include "CodeAddressService.h"
+
+#include "nsXULAppAPI.h"
+#ifdef XP_WIN
+#include <process.h>
+#define getpid _getpid
+#else
+#include <unistd.h>
+#endif
+
+#include "mozilla/Atomics.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/BlockingResourceBase.h"
+#include "mozilla/PoisonIOInterposer.h"
+
+#include <string>
+#include <vector>
+
+#ifdef HAVE_DLOPEN
+#include <dlfcn.h>
+#endif
+
+#ifdef MOZ_DMD
+#include "base/process_util.h"
+#include "nsMemoryInfoDumper.h"
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+
+#include "plhash.h"
+#include "prmem.h"
+
+#include "prthread.h"
+
+// We use a spin lock instead of a regular mutex because this lock is usually
+// only held for a very short time, and gets grabbed at a very high frequency
+// (~100000 times per second). On Mac, the overhead of using a regular lock
+// is very high, see bug 1137963.
+static mozilla::Atomic<uintptr_t, mozilla::ReleaseAcquire> gTraceLogLocked;
+
+struct MOZ_STACK_CLASS AutoTraceLogLock final
+{
+ bool doRelease;
+ AutoTraceLogLock()
+ : doRelease(true)
+ {
+ uintptr_t currentThread = reinterpret_cast<uintptr_t>(PR_GetCurrentThread());
+ if (gTraceLogLocked == currentThread) {
+ doRelease = false;
+ } else {
+ while (!gTraceLogLocked.compareExchange(0, currentThread)) {
+ PR_Sleep(PR_INTERVAL_NO_WAIT); /* yield */
+ }
+ }
+ }
+ ~AutoTraceLogLock() { if (doRelease) gTraceLogLocked = 0; }
+};
+
+static PLHashTable* gBloatView;
+static PLHashTable* gTypesToLog;
+static PLHashTable* gObjectsToLog;
+static PLHashTable* gSerialNumbers;
+static intptr_t gNextSerialNumber;
+static bool gDumpedStatistics = false;
+
+// By default, debug builds only do bloat logging. Bloat logging
+// only tries to record when an object is created or destroyed, so we
+// optimize the common case in NS_LogAddRef and NS_LogRelease where
+// only bloat logging is enabled and no logging needs to be done.
+enum LoggingType
+{
+ NoLogging,
+ OnlyBloatLogging,
+ FullLogging
+};
+
+static LoggingType gLogging;
+
+static bool gLogLeaksOnly;
+
+#define BAD_TLS_INDEX ((unsigned)-1)
+
+// if gActivityTLS == BAD_TLS_INDEX, then we're
+// unitialized... otherwise this points to a NSPR TLS thread index
+// indicating whether addref activity is legal. If the PTR_TO_INT32 is 0 then
+// activity is ok, otherwise not!
+static unsigned gActivityTLS = BAD_TLS_INDEX;
+
+static bool gInitialized;
+static nsrefcnt gInitCount;
+
+static FILE* gBloatLog = nullptr;
+static FILE* gRefcntsLog = nullptr;
+static FILE* gAllocLog = nullptr;
+static FILE* gCOMPtrLog = nullptr;
+
+static void
+WalkTheStackSavingLocations(std::vector<void*>& aLocations);
+
+struct SerialNumberRecord
+{
+ SerialNumberRecord()
+ : serialNumber(++gNextSerialNumber)
+ , refCount(0)
+ , COMPtrCount(0)
+ {}
+
+ intptr_t serialNumber;
+ int32_t refCount;
+ int32_t COMPtrCount;
+ // We use std:: classes here rather than the XPCOM equivalents because the
+ // XPCOM equivalents do leak-checking, and if you try to leak-check while
+ // leak-checking, you're gonna have a bad time.
+ std::vector<void*> allocationStack;
+};
+
+struct nsTraceRefcntStats
+{
+ uint64_t mCreates;
+ uint64_t mDestroys;
+
+ bool HaveLeaks() const
+ {
+ return mCreates != mDestroys;
+ }
+
+ void Clear()
+ {
+ mCreates = 0;
+ mDestroys = 0;
+ }
+
+ int64_t NumLeaked() const
+ {
+ return (int64_t)(mCreates - mDestroys);
+ }
+};
+
+#ifdef DEBUG
+static const char kStaticCtorDtorWarning[] =
+ "XPCOM objects created/destroyed from static ctor/dtor";
+
+static void
+AssertActivityIsLegal()
+{
+ if (gActivityTLS == BAD_TLS_INDEX || PR_GetThreadPrivate(gActivityTLS)) {
+ if (PR_GetEnv("MOZ_FATAL_STATIC_XPCOM_CTORS_DTORS")) {
+ NS_RUNTIMEABORT(kStaticCtorDtorWarning);
+ } else {
+ NS_WARNING(kStaticCtorDtorWarning);
+ }
+ }
+}
+# define ASSERT_ACTIVITY_IS_LEGAL \
+ PR_BEGIN_MACRO \
+ AssertActivityIsLegal(); \
+ PR_END_MACRO
+#else
+# define ASSERT_ACTIVITY_IS_LEGAL PR_BEGIN_MACRO PR_END_MACRO
+#endif // DEBUG
+
+// These functions are copied from nsprpub/lib/ds/plhash.c, with changes
+// to the functions not called Default* to free the SerialNumberRecord or
+// the BloatEntry.
+
+static void*
+DefaultAllocTable(void* aPool, size_t aSize)
+{
+ return PR_MALLOC(aSize);
+}
+
+static void
+DefaultFreeTable(void* aPool, void* aItem)
+{
+ PR_Free(aItem);
+}
+
+static PLHashEntry*
+DefaultAllocEntry(void* aPool, const void* aKey)
+{
+ return PR_NEW(PLHashEntry);
+}
+
+static void
+SerialNumberFreeEntry(void* aPool, PLHashEntry* aHashEntry, unsigned aFlag)
+{
+ if (aFlag == HT_FREE_ENTRY) {
+ delete static_cast<SerialNumberRecord*>(aHashEntry->value);
+ PR_Free(aHashEntry);
+ }
+}
+
+static void
+TypesToLogFreeEntry(void* aPool, PLHashEntry* aHashEntry, unsigned aFlag)
+{
+ if (aFlag == HT_FREE_ENTRY) {
+ free(const_cast<char*>(static_cast<const char*>(aHashEntry->key)));
+ PR_Free(aHashEntry);
+ }
+}
+
+static const PLHashAllocOps serialNumberHashAllocOps = {
+ DefaultAllocTable, DefaultFreeTable,
+ DefaultAllocEntry, SerialNumberFreeEntry
+};
+
+static const PLHashAllocOps typesToLogHashAllocOps = {
+ DefaultAllocTable, DefaultFreeTable,
+ DefaultAllocEntry, TypesToLogFreeEntry
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+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;
+};
+
+struct CodeAddressServiceStringAlloc final
+{
+ static char* copy(const char* aStr) { return strdup(aStr); }
+ static void free(char* aPtr) { ::free(aPtr); }
+};
+
+// WalkTheStack does not hold any locks needed by MozDescribeCodeAddress, so
+// this class does not need to do anything.
+struct CodeAddressServiceLock final
+{
+ static void Unlock() {}
+ static void Lock() {}
+ static bool IsLocked() { return true; }
+};
+
+typedef mozilla::CodeAddressService<CodeAddressServiceStringTable,
+ CodeAddressServiceStringAlloc,
+ CodeAddressServiceLock> WalkTheStackCodeAddressService;
+
+mozilla::StaticAutoPtr<WalkTheStackCodeAddressService> gCodeAddressService;
+
+////////////////////////////////////////////////////////////////////////////////
+
+class BloatEntry
+{
+public:
+ BloatEntry(const char* aClassName, uint32_t aClassSize)
+ : mClassSize(aClassSize)
+ {
+ MOZ_ASSERT(strlen(aClassName) > 0, "BloatEntry name must be non-empty");
+ mClassName = PL_strdup(aClassName);
+ mStats.Clear();
+ mTotalLeaked = 0;
+ }
+
+ ~BloatEntry()
+ {
+ PL_strfree(mClassName);
+ }
+
+ uint32_t GetClassSize()
+ {
+ return (uint32_t)mClassSize;
+ }
+ const char* GetClassName()
+ {
+ return mClassName;
+ }
+
+ void Ctor()
+ {
+ mStats.mCreates++;
+ }
+
+ void Dtor()
+ {
+ mStats.mDestroys++;
+ }
+
+ static int DumpEntry(PLHashEntry* aHashEntry, int aIndex, void* aArg)
+ {
+ BloatEntry* entry = (BloatEntry*)aHashEntry->value;
+ if (entry) {
+ static_cast<nsTArray<BloatEntry*>*>(aArg)->AppendElement(entry);
+ }
+ return HT_ENUMERATE_NEXT;
+ }
+
+ static int TotalEntries(PLHashEntry* aHashEntry, int aIndex, void* aArg)
+ {
+ BloatEntry* entry = (BloatEntry*)aHashEntry->value;
+ if (entry && nsCRT::strcmp(entry->mClassName, "TOTAL") != 0) {
+ entry->Total((BloatEntry*)aArg);
+ }
+ return HT_ENUMERATE_NEXT;
+ }
+
+ void Total(BloatEntry* aTotal)
+ {
+ aTotal->mStats.mCreates += mStats.mCreates;
+ aTotal->mStats.mDestroys += mStats.mDestroys;
+ aTotal->mClassSize += mClassSize * mStats.mCreates; // adjust for average in DumpTotal
+ aTotal->mTotalLeaked += mClassSize * mStats.NumLeaked();
+ }
+
+ void DumpTotal(FILE* aOut)
+ {
+ mClassSize /= mStats.mCreates;
+ Dump(-1, aOut);
+ }
+
+ bool PrintDumpHeader(FILE* aOut, const char* aMsg)
+ {
+ fprintf(aOut, "\n== BloatView: %s, %s process %d\n", aMsg,
+ XRE_ChildProcessTypeToString(XRE_GetProcessType()), getpid());
+ if (gLogLeaksOnly && !mStats.HaveLeaks()) {
+ return false;
+ }
+
+ fprintf(aOut,
+ "\n" \
+ " |<----------------Class--------------->|<-----Bytes------>|<----Objects---->|\n" \
+ " | | Per-Inst Leaked| Total Rem|\n");
+
+ this->DumpTotal(aOut);
+
+ return true;
+ }
+
+ void Dump(int aIndex, FILE* aOut)
+ {
+ if (gLogLeaksOnly && !mStats.HaveLeaks()) {
+ return;
+ }
+
+ if (mStats.HaveLeaks() || mStats.mCreates != 0) {
+ fprintf(aOut, "%4d |%-38.38s| %8d %8" PRId64 "|%8" PRIu64 " %8" PRId64"|\n",
+ aIndex + 1, mClassName,
+ GetClassSize(),
+ nsCRT::strcmp(mClassName, "TOTAL") ? (mStats.NumLeaked() * GetClassSize()) : mTotalLeaked,
+ mStats.mCreates,
+ mStats.NumLeaked());
+ }
+ }
+
+protected:
+ char* mClassName;
+ double mClassSize; // This is stored as a double because of the way we compute the avg class size for total bloat.
+ int64_t mTotalLeaked; // Used only for TOTAL entry.
+ nsTraceRefcntStats mStats;
+};
+
+static void
+BloatViewFreeEntry(void* aPool, PLHashEntry* aHashEntry, unsigned aFlag)
+{
+ if (aFlag == HT_FREE_ENTRY) {
+ BloatEntry* entry = static_cast<BloatEntry*>(aHashEntry->value);
+ delete entry;
+ PR_Free(aHashEntry);
+ }
+}
+
+const static PLHashAllocOps bloatViewHashAllocOps = {
+ DefaultAllocTable, DefaultFreeTable,
+ DefaultAllocEntry, BloatViewFreeEntry
+};
+
+static void
+RecreateBloatView()
+{
+ gBloatView = PL_NewHashTable(256,
+ PL_HashString,
+ PL_CompareStrings,
+ PL_CompareValues,
+ &bloatViewHashAllocOps, nullptr);
+}
+
+static BloatEntry*
+GetBloatEntry(const char* aTypeName, uint32_t aInstanceSize)
+{
+ if (!gBloatView) {
+ RecreateBloatView();
+ }
+ BloatEntry* entry = nullptr;
+ if (gBloatView) {
+ entry = (BloatEntry*)PL_HashTableLookup(gBloatView, aTypeName);
+ if (!entry && aInstanceSize > 0) {
+
+ entry = new BloatEntry(aTypeName, aInstanceSize);
+ PLHashEntry* e = PL_HashTableAdd(gBloatView, aTypeName, entry);
+ if (!e) {
+ delete entry;
+ entry = nullptr;
+ }
+ } else {
+ MOZ_ASSERT(aInstanceSize == 0 || entry->GetClassSize() == aInstanceSize,
+ "Mismatched sizes were recorded in the memory leak logging table. "
+ "The usual cause of this is having a templated class that uses "
+ "MOZ_COUNT_{C,D}TOR in the constructor or destructor, respectively. "
+ "As a workaround, the MOZ_COUNT_{C,D}TOR calls can be moved to a "
+ "non-templated base class.");
+ }
+ }
+ return entry;
+}
+
+static int
+DumpSerialNumbers(PLHashEntry* aHashEntry, int aIndex, void* aClosure)
+{
+ SerialNumberRecord* record =
+ static_cast<SerialNumberRecord*>(aHashEntry->value);
+ auto* outputFile = static_cast<FILE*>(aClosure);
+#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
+ fprintf(outputFile, "%" PRIdPTR
+ " @%p (%d references; %d from COMPtrs)\n",
+ record->serialNumber,
+ aHashEntry->key,
+ record->refCount,
+ record->COMPtrCount);
+#else
+ fprintf(outputFile, "%" PRIdPTR
+ " @%p (%d references)\n",
+ record->serialNumber,
+ aHashEntry->key,
+ record->refCount);
+#endif
+ if (!record->allocationStack.empty()) {
+ static const size_t bufLen = 1024;
+ char buf[bufLen];
+ fprintf(outputFile, "allocation stack:\n");
+ for (size_t i = 0, length = record->allocationStack.size();
+ i < length;
+ ++i) {
+ gCodeAddressService->GetLocation(i, record->allocationStack[i],
+ buf, bufLen);
+ fprintf(outputFile, "%s\n", buf);
+ }
+ }
+ return HT_ENUMERATE_NEXT;
+}
+
+
+template<>
+class nsDefaultComparator<BloatEntry*, BloatEntry*>
+{
+public:
+ bool Equals(BloatEntry* const& aEntry1, BloatEntry* const& aEntry2) const
+ {
+ return PL_strcmp(aEntry1->GetClassName(), aEntry2->GetClassName()) == 0;
+ }
+ bool LessThan(BloatEntry* const& aEntry1, BloatEntry* const& aEntry2) const
+ {
+ return PL_strcmp(aEntry1->GetClassName(), aEntry2->GetClassName()) < 0;
+ }
+};
+
+
+nsresult
+nsTraceRefcnt::DumpStatistics()
+{
+ if (!gBloatLog || !gBloatView) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AutoTraceLogLock lock;
+
+ MOZ_ASSERT(!gDumpedStatistics,
+ "Calling DumpStatistics more than once may result in "
+ "bogus positive or negative leaks being reported");
+ gDumpedStatistics = true;
+
+ // Don't try to log while we hold the lock, we'd deadlock.
+ AutoRestore<LoggingType> saveLogging(gLogging);
+ gLogging = NoLogging;
+
+ BloatEntry total("TOTAL", 0);
+ PL_HashTableEnumerateEntries(gBloatView, BloatEntry::TotalEntries, &total);
+ const char* msg;
+ if (gLogLeaksOnly) {
+ msg = "ALL (cumulative) LEAK STATISTICS";
+ } else {
+ msg = "ALL (cumulative) LEAK AND BLOAT STATISTICS";
+ }
+ const bool leaked = total.PrintDumpHeader(gBloatLog, msg);
+
+ nsTArray<BloatEntry*> entries;
+ PL_HashTableEnumerateEntries(gBloatView, BloatEntry::DumpEntry, &entries);
+ const uint32_t count = entries.Length();
+
+ if (!gLogLeaksOnly || leaked) {
+ // Sort the entries alphabetically by classname.
+ entries.Sort();
+
+ for (uint32_t i = 0; i < count; ++i) {
+ BloatEntry* entry = entries[i];
+ entry->Dump(i, gBloatLog);
+ }
+
+ fprintf(gBloatLog, "\n");
+ }
+
+ fprintf(gBloatLog, "nsTraceRefcnt::DumpStatistics: %d entries\n", count);
+
+ if (gSerialNumbers) {
+ fprintf(gBloatLog, "\nSerial Numbers of Leaked Objects:\n");
+ PL_HashTableEnumerateEntries(gSerialNumbers, DumpSerialNumbers, gBloatLog);
+ }
+
+ return NS_OK;
+}
+
+void
+nsTraceRefcnt::ResetStatistics()
+{
+ AutoTraceLogLock lock;
+ if (gBloatView) {
+ PL_HashTableDestroy(gBloatView);
+ gBloatView = nullptr;
+ }
+}
+
+static bool
+LogThisType(const char* aTypeName)
+{
+ void* he = PL_HashTableLookup(gTypesToLog, aTypeName);
+ return he != nullptr;
+}
+
+static PLHashNumber
+HashNumber(const void* aKey)
+{
+ return PLHashNumber(NS_PTR_TO_INT32(aKey));
+}
+
+static intptr_t
+GetSerialNumber(void* aPtr, bool aCreate)
+{
+ PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers,
+ HashNumber(aPtr),
+ aPtr);
+ if (hep && *hep) {
+ MOZ_RELEASE_ASSERT(!aCreate, "If an object already has a serial number, we should be destroying it.");
+ return static_cast<SerialNumberRecord*>((*hep)->value)->serialNumber;
+ }
+
+ if (!aCreate) {
+ return 0;
+ }
+
+ SerialNumberRecord* record = new SerialNumberRecord();
+ WalkTheStackSavingLocations(record->allocationStack);
+ PL_HashTableRawAdd(gSerialNumbers, hep, HashNumber(aPtr),
+ aPtr, static_cast<void*>(record));
+ return gNextSerialNumber;
+}
+
+static int32_t*
+GetRefCount(void* aPtr)
+{
+ PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers,
+ HashNumber(aPtr),
+ aPtr);
+ if (hep && *hep) {
+ return &(static_cast<SerialNumberRecord*>((*hep)->value)->refCount);
+ } else {
+ return nullptr;
+ }
+}
+
+#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
+static int32_t*
+GetCOMPtrCount(void* aPtr)
+{
+ PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers,
+ HashNumber(aPtr),
+ aPtr);
+ if (hep && *hep) {
+ return &(static_cast<SerialNumberRecord*>((*hep)->value)->COMPtrCount);
+ }
+ return nullptr;
+}
+#endif // HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
+
+static void
+RecycleSerialNumberPtr(void* aPtr)
+{
+ PL_HashTableRemove(gSerialNumbers, aPtr);
+}
+
+static bool
+LogThisObj(intptr_t aSerialNumber)
+{
+ return (bool)PL_HashTableLookup(gObjectsToLog, (const void*)aSerialNumber);
+}
+
+#ifdef XP_WIN
+#define FOPEN_NO_INHERIT "N"
+#else
+#define FOPEN_NO_INHERIT
+#endif
+
+static bool
+InitLog(const char* aEnvVar, const char* aMsg, FILE** aResult)
+{
+ const char* value = getenv(aEnvVar);
+ if (value) {
+ if (nsCRT::strcmp(value, "1") == 0) {
+ *aResult = stdout;
+ fprintf(stdout, "### %s defined -- logging %s to stdout\n",
+ aEnvVar, aMsg);
+ return true;
+ } else if (nsCRT::strcmp(value, "2") == 0) {
+ *aResult = stderr;
+ fprintf(stdout, "### %s defined -- logging %s to stderr\n",
+ aEnvVar, aMsg);
+ return true;
+ } else {
+ FILE* stream;
+ nsAutoCString fname(value);
+ if (!XRE_IsParentProcess()) {
+ bool hasLogExtension =
+ fname.RFind(".log", true, -1, 4) == kNotFound ? false : true;
+ if (hasLogExtension) {
+ fname.Cut(fname.Length() - 4, 4);
+ }
+ fname.Append('_');
+ fname.Append((char*)XRE_ChildProcessTypeToString(XRE_GetProcessType()));
+ fname.AppendLiteral("_pid");
+ fname.AppendInt((uint32_t)getpid());
+ if (hasLogExtension) {
+ fname.AppendLiteral(".log");
+ }
+ }
+ stream = ::fopen(fname.get(), "w" FOPEN_NO_INHERIT);
+ if (stream) {
+ MozillaRegisterDebugFD(fileno(stream));
+ *aResult = stream;
+ fprintf(stdout, "### %s defined -- logging %s to %s\n",
+ aEnvVar, aMsg, fname.get());
+ } else {
+ fprintf(stdout, "### %s defined -- unable to log %s to %s\n",
+ aEnvVar, aMsg, fname.get());
+ MOZ_ASSERT(false, "Tried and failed to create an XPCOM log");
+ }
+ return stream != nullptr;
+ }
+ }
+ return false;
+}
+
+
+static void
+maybeUnregisterAndCloseFile(FILE*& aFile)
+{
+ if (!aFile) {
+ return;
+ }
+
+ MozillaUnRegisterDebugFILE(aFile);
+ fclose(aFile);
+ aFile = nullptr;
+}
+
+
+static void
+InitTraceLog()
+{
+ if (gInitialized) {
+ return;
+ }
+ gInitialized = true;
+
+ bool defined = InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog);
+ if (!defined) {
+ gLogLeaksOnly = InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog);
+ }
+ if (defined || gLogLeaksOnly) {
+ RecreateBloatView();
+ if (!gBloatView) {
+ NS_WARNING("out of memory");
+ maybeUnregisterAndCloseFile(gBloatLog);
+ gLogLeaksOnly = false;
+ }
+ }
+
+ InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog);
+
+ InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog);
+
+ const char* classes = getenv("XPCOM_MEM_LOG_CLASSES");
+
+#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
+ if (classes) {
+ InitLog("XPCOM_MEM_COMPTR_LOG", "nsCOMPtr", &gCOMPtrLog);
+ } else {
+ if (getenv("XPCOM_MEM_COMPTR_LOG")) {
+ fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n");
+ }
+ }
+#else
+ const char* comptr_log = getenv("XPCOM_MEM_COMPTR_LOG");
+ if (comptr_log) {
+ fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n");
+ }
+#endif // HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
+
+ if (classes) {
+ // if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted
+ // as a list of class names to track
+ gTypesToLog = PL_NewHashTable(256,
+ PL_HashString,
+ PL_CompareStrings,
+ PL_CompareValues,
+ &typesToLogHashAllocOps, nullptr);
+ if (!gTypesToLog) {
+ NS_WARNING("out of memory");
+ fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n");
+ } else {
+ fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
+ const char* cp = classes;
+ for (;;) {
+ char* cm = (char*)strchr(cp, ',');
+ if (cm) {
+ *cm = '\0';
+ }
+ PL_HashTableAdd(gTypesToLog, strdup(cp), (void*)1);
+ fprintf(stdout, "%s ", cp);
+ if (!cm) {
+ break;
+ }
+ *cm = ',';
+ cp = cm + 1;
+ }
+ fprintf(stdout, "\n");
+ }
+
+ gSerialNumbers = PL_NewHashTable(256,
+ HashNumber,
+ PL_CompareValues,
+ PL_CompareValues,
+ &serialNumberHashAllocOps, nullptr);
+
+
+ }
+
+ const char* objects = getenv("XPCOM_MEM_LOG_OBJECTS");
+ if (objects) {
+ gObjectsToLog = PL_NewHashTable(256,
+ HashNumber,
+ PL_CompareValues,
+ PL_CompareValues,
+ nullptr, nullptr);
+
+ if (!gObjectsToLog) {
+ NS_WARNING("out of memory");
+ fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- unable to log specific objects\n");
+ } else if (!(gRefcntsLog || gAllocLog || gCOMPtrLog)) {
+ fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n");
+ } else {
+ fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
+ const char* cp = objects;
+ for (;;) {
+ char* cm = (char*)strchr(cp, ',');
+ if (cm) {
+ *cm = '\0';
+ }
+ intptr_t top = 0;
+ intptr_t bottom = 0;
+ while (*cp) {
+ if (*cp == '-') {
+ bottom = top;
+ top = 0;
+ ++cp;
+ }
+ top *= 10;
+ top += *cp - '0';
+ ++cp;
+ }
+ if (!bottom) {
+ bottom = top;
+ }
+ for (intptr_t serialno = bottom; serialno <= top; serialno++) {
+ PL_HashTableAdd(gObjectsToLog, (const void*)serialno, (void*)1);
+ fprintf(stdout, "%" PRIdPTR " ", serialno);
+ }
+ if (!cm) {
+ break;
+ }
+ *cm = ',';
+ cp = cm + 1;
+ }
+ fprintf(stdout, "\n");
+ }
+ }
+
+
+ if (gBloatLog) {
+ gLogging = OnlyBloatLogging;
+ }
+
+ if (gRefcntsLog || gAllocLog || gCOMPtrLog) {
+ gLogging = FullLogging;
+ }
+}
+
+
+extern "C" {
+
+static void
+PrintStackFrame(uint32_t aFrameNumber, void* aPC, void* aSP, void* aClosure)
+{
+ FILE* stream = (FILE*)aClosure;
+ MozCodeAddressDetails details;
+ char buf[1024];
+
+ MozDescribeCodeAddress(aPC, &details);
+ MozFormatCodeAddressDetails(buf, sizeof(buf), aFrameNumber, aPC, &details);
+ fprintf(stream, "%s\n", buf);
+ fflush(stream);
+}
+
+static void
+PrintStackFrameCached(uint32_t aFrameNumber, void* aPC, void* aSP,
+ void* aClosure)
+{
+ auto stream = static_cast<FILE*>(aClosure);
+ static const size_t buflen = 1024;
+ char buf[buflen];
+ gCodeAddressService->GetLocation(aFrameNumber, aPC, buf, buflen);
+ fprintf(stream, " %s\n", buf);
+ fflush(stream);
+}
+
+static void
+RecordStackFrame(uint32_t /*aFrameNumber*/, void* aPC, void* /*aSP*/,
+ void* aClosure)
+{
+ auto locations = static_cast<std::vector<void*>*>(aClosure);
+ locations->push_back(aPC);
+}
+
+}
+
+void
+nsTraceRefcnt::WalkTheStack(FILE* aStream)
+{
+ MozStackWalk(PrintStackFrame, /* skipFrames */ 2, /* maxFrames */ 0, aStream,
+ 0, nullptr);
+}
+
+/**
+ * This is a variant of |WalkTheStack| that uses |CodeAddressService| to cache
+ * the results of |NS_DescribeCodeAddress|. If |WalkTheStackCached| is being
+ * called frequently, it will be a few orders of magnitude faster than
+ * |WalkTheStack|. However, the cache uses a lot of memory, which can cause
+ * OOM crashes. Therefore, this should only be used for things like refcount
+ * logging which walk the stack extremely frequently.
+ */
+static void
+WalkTheStackCached(FILE* aStream)
+{
+ if (!gCodeAddressService) {
+ gCodeAddressService = new WalkTheStackCodeAddressService();
+ }
+ MozStackWalk(PrintStackFrameCached, /* skipFrames */ 2, /* maxFrames */ 0,
+ aStream, 0, nullptr);
+}
+
+static void
+WalkTheStackSavingLocations(std::vector<void*>& aLocations)
+{
+ if (!gCodeAddressService) {
+ gCodeAddressService = new WalkTheStackCodeAddressService();
+ }
+ static const int kFramesToSkip =
+ 0 + // this frame gets inlined
+ 1 + // GetSerialNumber
+ 1; // NS_LogCtor
+ MozStackWalk(RecordStackFrame, kFramesToSkip, /* maxFrames */ 0,
+ &aLocations, 0, nullptr);
+}
+
+//----------------------------------------------------------------------
+
+EXPORT_XPCOM_API(void)
+NS_LogInit()
+{
+ NS_SetMainThread();
+
+ // FIXME: This is called multiple times, we should probably not allow that.
+ StackWalkInitCriticalAddress();
+ if (++gInitCount) {
+ nsTraceRefcnt::SetActivityIsLegal(true);
+ }
+}
+
+EXPORT_XPCOM_API(void)
+NS_LogTerm()
+{
+ mozilla::LogTerm();
+}
+
+#ifdef MOZ_DMD
+// If MOZ_DMD_SHUTDOWN_LOG is set, dump a DMD report to a file.
+// The value of this environment variable is used as the prefix
+// of the file name, so you probably want something like "/tmp/".
+// By default, this is run in all processes, but you can record a
+// log only for a specific process type by setting MOZ_DMD_LOG_PROCESS
+// to the process type you want to log, such as "default" or "tab".
+// This method can't use the higher level XPCOM file utilities
+// because it is run very late in shutdown to avoid recording
+// information about refcount logging entries.
+static void
+LogDMDFile()
+{
+ const char* dmdFilePrefix = PR_GetEnv("MOZ_DMD_SHUTDOWN_LOG");
+ if (!dmdFilePrefix) {
+ return;
+ }
+
+ const char* logProcessEnv = PR_GetEnv("MOZ_DMD_LOG_PROCESS");
+ if (logProcessEnv && !!strcmp(logProcessEnv, XRE_ChildProcessTypeToString(XRE_GetProcessType()))) {
+ return;
+ }
+
+ nsPrintfCString fileName("%sdmd-%d.log.gz", dmdFilePrefix, base::GetCurrentProcId());
+ FILE* logFile = fopen(fileName.get(), "w");
+ if (NS_WARN_IF(!logFile)) {
+ return;
+ }
+
+ nsMemoryInfoDumper::DumpDMDToFile(logFile);
+}
+#endif // MOZ_DMD
+
+namespace mozilla {
+void
+LogTerm()
+{
+ NS_ASSERTION(gInitCount > 0,
+ "NS_LogTerm without matching NS_LogInit");
+
+ if (--gInitCount == 0) {
+#ifdef DEBUG
+ /* FIXME bug 491977: This is only going to operate on the
+ * BlockingResourceBase which is compiled into
+ * libxul/libxpcom_core.so. Anyone using external linkage will
+ * have their own copy of BlockingResourceBase statics which will
+ * not be freed by this method.
+ *
+ * It sounds like what we really want is to be able to register a
+ * callback function to call at XPCOM shutdown. Note that with
+ * this solution, however, we need to guarantee that
+ * BlockingResourceBase::Shutdown() runs after all other shutdown
+ * functions.
+ */
+ BlockingResourceBase::Shutdown();
+#endif
+
+ if (gInitialized) {
+ nsTraceRefcnt::DumpStatistics();
+ nsTraceRefcnt::ResetStatistics();
+ }
+ nsTraceRefcnt::Shutdown();
+ nsTraceRefcnt::SetActivityIsLegal(false);
+ gActivityTLS = BAD_TLS_INDEX;
+
+#ifdef MOZ_DMD
+ LogDMDFile();
+#endif
+ }
+}
+
+} // namespace mozilla
+
+EXPORT_XPCOM_API(void)
+NS_LogAddRef(void* aPtr, nsrefcnt aRefcnt,
+ const char* aClass, uint32_t aClassSize)
+{
+ ASSERT_ACTIVITY_IS_LEGAL;
+ if (!gInitialized) {
+ InitTraceLog();
+ }
+ if (gLogging == NoLogging) {
+ return;
+ }
+ if (aRefcnt == 1 || gLogging == FullLogging) {
+ AutoTraceLogLock lock;
+
+ if (aRefcnt == 1 && gBloatLog) {
+ BloatEntry* entry = GetBloatEntry(aClass, aClassSize);
+ if (entry) {
+ entry->Ctor();
+ }
+ }
+
+ // Here's the case where MOZ_COUNT_CTOR was not used,
+ // yet we still want to see creation information:
+
+ bool loggingThisType = (!gTypesToLog || LogThisType(aClass));
+ intptr_t serialno = 0;
+ if (gSerialNumbers && loggingThisType) {
+ serialno = GetSerialNumber(aPtr, aRefcnt == 1);
+ MOZ_ASSERT(serialno != 0,
+ "Serial number requested for unrecognized pointer! "
+ "Are you memmoving a refcounted object?");
+ int32_t* count = GetRefCount(aPtr);
+ if (count) {
+ (*count)++;
+ }
+
+ }
+
+ bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
+ if (aRefcnt == 1 && gAllocLog && loggingThisType && loggingThisObject) {
+ fprintf(gAllocLog, "\n<%s> %p %" PRIdPTR " Create [thread %p]\n", aClass, aPtr, serialno, PR_GetCurrentThread());
+ WalkTheStackCached(gAllocLog);
+ }
+
+ if (gRefcntsLog && loggingThisType && loggingThisObject) {
+ // Can't use MOZ_LOG(), b/c it truncates the line
+ fprintf(gRefcntsLog, "\n<%s> %p %" PRIuPTR " AddRef %" PRIuPTR " [thread %p]\n",
+ aClass, aPtr, serialno, aRefcnt, PR_GetCurrentThread());
+ WalkTheStackCached(gRefcntsLog);
+ fflush(gRefcntsLog);
+ }
+ }
+}
+
+EXPORT_XPCOM_API(void)
+NS_LogRelease(void* aPtr, nsrefcnt aRefcnt, const char* aClass)
+{
+ ASSERT_ACTIVITY_IS_LEGAL;
+ if (!gInitialized) {
+ InitTraceLog();
+ }
+ if (gLogging == NoLogging) {
+ return;
+ }
+ if (aRefcnt == 0 || gLogging == FullLogging) {
+ AutoTraceLogLock lock;
+
+ if (aRefcnt == 0 && gBloatLog) {
+ BloatEntry* entry = GetBloatEntry(aClass, 0);
+ if (entry) {
+ entry->Dtor();
+ }
+ }
+
+ bool loggingThisType = (!gTypesToLog || LogThisType(aClass));
+ intptr_t serialno = 0;
+ if (gSerialNumbers && loggingThisType) {
+ serialno = GetSerialNumber(aPtr, false);
+ MOZ_ASSERT(serialno != 0,
+ "Serial number requested for unrecognized pointer! "
+ "Are you memmoving a refcounted object?");
+ int32_t* count = GetRefCount(aPtr);
+ if (count) {
+ (*count)--;
+ }
+
+ }
+
+ bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
+ if (gRefcntsLog && loggingThisType && loggingThisObject) {
+ // Can't use MOZ_LOG(), b/c it truncates the line
+ fprintf(gRefcntsLog,
+ "\n<%s> %p %" PRIuPTR " Release %" PRIuPTR " [thread %p]\n",
+ aClass, aPtr, serialno, aRefcnt, PR_GetCurrentThread());
+ WalkTheStackCached(gRefcntsLog);
+ fflush(gRefcntsLog);
+ }
+
+ // Here's the case where MOZ_COUNT_DTOR was not used,
+ // yet we still want to see deletion information:
+
+ if (aRefcnt == 0 && gAllocLog && loggingThisType && loggingThisObject) {
+ fprintf(gAllocLog, "\n<%s> %p %" PRIdPTR " Destroy [thread %p]\n", aClass, aPtr, serialno, PR_GetCurrentThread());
+ WalkTheStackCached(gAllocLog);
+ }
+
+ if (aRefcnt == 0 && gSerialNumbers && loggingThisType) {
+ RecycleSerialNumberPtr(aPtr);
+ }
+ }
+}
+
+EXPORT_XPCOM_API(void)
+NS_LogCtor(void* aPtr, const char* aType, uint32_t aInstanceSize)
+{
+ ASSERT_ACTIVITY_IS_LEGAL;
+ if (!gInitialized) {
+ InitTraceLog();
+ }
+
+ if (gLogging == NoLogging) {
+ return;
+ }
+
+ AutoTraceLogLock lock;
+
+ if (gBloatLog) {
+ BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
+ if (entry) {
+ entry->Ctor();
+ }
+ }
+
+ bool loggingThisType = (!gTypesToLog || LogThisType(aType));
+ intptr_t serialno = 0;
+ if (gSerialNumbers && loggingThisType) {
+ serialno = GetSerialNumber(aPtr, true);
+ MOZ_ASSERT(serialno != 0, "GetSerialNumber should never return 0 when passed true");
+ }
+
+ bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
+ if (gAllocLog && loggingThisType && loggingThisObject) {
+ fprintf(gAllocLog, "\n<%s> %p %" PRIdPTR " Ctor (%d)\n",
+ aType, aPtr, serialno, aInstanceSize);
+ WalkTheStackCached(gAllocLog);
+ }
+}
+
+
+EXPORT_XPCOM_API(void)
+NS_LogDtor(void* aPtr, const char* aType, uint32_t aInstanceSize)
+{
+ ASSERT_ACTIVITY_IS_LEGAL;
+ if (!gInitialized) {
+ InitTraceLog();
+ }
+
+ if (gLogging == NoLogging) {
+ return;
+ }
+
+ AutoTraceLogLock lock;
+
+ if (gBloatLog) {
+ BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
+ if (entry) {
+ entry->Dtor();
+ }
+ }
+
+ bool loggingThisType = (!gTypesToLog || LogThisType(aType));
+ intptr_t serialno = 0;
+ if (gSerialNumbers && loggingThisType) {
+ serialno = GetSerialNumber(aPtr, false);
+ MOZ_ASSERT(serialno != 0,
+ "Serial number requested for unrecognized pointer! "
+ "Are you memmoving a MOZ_COUNT_CTOR-tracked object?");
+ RecycleSerialNumberPtr(aPtr);
+ }
+
+ bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
+
+ // (If we're on a losing architecture, don't do this because we'll be
+ // using LogDeleteXPCOM instead to get file and line numbers.)
+ if (gAllocLog && loggingThisType && loggingThisObject) {
+ fprintf(gAllocLog, "\n<%s> %p %" PRIdPTR " Dtor (%d)\n",
+ aType, aPtr, serialno, aInstanceSize);
+ WalkTheStackCached(gAllocLog);
+ }
+}
+
+
+EXPORT_XPCOM_API(void)
+NS_LogCOMPtrAddRef(void* aCOMPtr, nsISupports* aObject)
+{
+#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
+ // Get the most-derived object.
+ void* object = dynamic_cast<void*>(aObject);
+
+ // This is a very indirect way of finding out what the class is
+ // of the object being logged. If we're logging a specific type,
+ // then
+ if (!gTypesToLog || !gSerialNumbers) {
+ return;
+ }
+ if (!gInitialized) {
+ InitTraceLog();
+ }
+ if (gLogging == FullLogging) {
+ AutoTraceLogLock lock;
+
+ intptr_t serialno = GetSerialNumber(object, false);
+ if (serialno == 0) {
+ return;
+ }
+
+ int32_t* count = GetCOMPtrCount(object);
+ if (count) {
+ (*count)++;
+ }
+
+ bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
+
+ if (gCOMPtrLog && loggingThisObject) {
+ fprintf(gCOMPtrLog, "\n<?> %p %" PRIdPTR " nsCOMPtrAddRef %d %p\n",
+ object, serialno, count ? (*count) : -1, aCOMPtr);
+ WalkTheStackCached(gCOMPtrLog);
+ }
+ }
+#endif // HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
+}
+
+
+EXPORT_XPCOM_API(void)
+NS_LogCOMPtrRelease(void* aCOMPtr, nsISupports* aObject)
+{
+#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
+ // Get the most-derived object.
+ void* object = dynamic_cast<void*>(aObject);
+
+ // This is a very indirect way of finding out what the class is
+ // of the object being logged. If we're logging a specific type,
+ // then
+ if (!gTypesToLog || !gSerialNumbers) {
+ return;
+ }
+ if (!gInitialized) {
+ InitTraceLog();
+ }
+ if (gLogging == FullLogging) {
+ AutoTraceLogLock lock;
+
+ intptr_t serialno = GetSerialNumber(object, false);
+ if (serialno == 0) {
+ return;
+ }
+
+ int32_t* count = GetCOMPtrCount(object);
+ if (count) {
+ (*count)--;
+ }
+
+ bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
+
+ if (gCOMPtrLog && loggingThisObject) {
+ fprintf(gCOMPtrLog, "\n<?> %p %" PRIdPTR " nsCOMPtrRelease %d %p\n",
+ object, serialno, count ? (*count) : -1, aCOMPtr);
+ WalkTheStackCached(gCOMPtrLog);
+ }
+ }
+#endif // HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
+}
+
+void
+nsTraceRefcnt::Shutdown()
+{
+ gCodeAddressService = nullptr;
+ if (gBloatView) {
+ PL_HashTableDestroy(gBloatView);
+ gBloatView = nullptr;
+ }
+ if (gTypesToLog) {
+ PL_HashTableDestroy(gTypesToLog);
+ gTypesToLog = nullptr;
+ }
+ if (gObjectsToLog) {
+ PL_HashTableDestroy(gObjectsToLog);
+ gObjectsToLog = nullptr;
+ }
+ if (gSerialNumbers) {
+ PL_HashTableDestroy(gSerialNumbers);
+ gSerialNumbers = nullptr;
+ }
+ maybeUnregisterAndCloseFile(gBloatLog);
+ maybeUnregisterAndCloseFile(gRefcntsLog);
+ maybeUnregisterAndCloseFile(gAllocLog);
+ maybeUnregisterAndCloseFile(gCOMPtrLog);
+}
+
+void
+nsTraceRefcnt::SetActivityIsLegal(bool aLegal)
+{
+ if (gActivityTLS == BAD_TLS_INDEX) {
+ PR_NewThreadPrivateIndex(&gActivityTLS, nullptr);
+ }
+
+ PR_SetThreadPrivate(gActivityTLS, reinterpret_cast<void*>(!aLegal));
+}
diff --git a/xpcom/base/nsTraceRefcnt.h b/xpcom/base/nsTraceRefcnt.h
new file mode 100644
index 0000000000..73e123a3fe
--- /dev/null
+++ b/xpcom/base/nsTraceRefcnt.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 nsTraceRefcnt_h___
+#define nsTraceRefcnt_h___
+
+#include <stdio.h> // for FILE
+#include "nscore.h"
+
+class nsTraceRefcnt
+{
+public:
+ static void Shutdown();
+
+ static nsresult DumpStatistics();
+
+ static void ResetStatistics();
+
+ static void WalkTheStack(FILE* aStream);
+
+ /**
+ * Tell nsTraceRefcnt whether refcounting, allocation, and destruction
+ * activity is legal. This is used to trigger assertions for any such
+ * activity that occurs because of static constructors or destructors.
+ */
+ static void SetActivityIsLegal(bool aLegal);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// And now for that utility that you've all been asking for...
+
+extern "C" void
+NS_MeanAndStdDev(double aNumberOfValues,
+ double aSumOfValues, double aSumOfSquaredValues,
+ double* aMeanResult, double* aStdDevResult);
+
+////////////////////////////////////////////////////////////////////////////////
+#endif
diff --git a/xpcom/base/nsUUIDGenerator.cpp b/xpcom/base/nsUUIDGenerator.cpp
new file mode 100644
index 0000000000..254a013227
--- /dev/null
+++ b/xpcom/base/nsUUIDGenerator.cpp
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if defined(XP_WIN)
+#include <windows.h>
+#include <objbase.h>
+#elif defined(XP_MACOSX)
+#include <CoreFoundation/CoreFoundation.h>
+#else
+#include <stdlib.h>
+#include "prrng.h"
+#endif
+
+#include "nsUUIDGenerator.h"
+
+#ifdef ANDROID
+extern "C" NS_EXPORT void arc4random_buf(void*, size_t);
+#endif
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS(nsUUIDGenerator, nsIUUIDGenerator)
+
+nsUUIDGenerator::nsUUIDGenerator()
+ : mLock("nsUUIDGenerator.mLock")
+{
+}
+
+nsUUIDGenerator::~nsUUIDGenerator()
+{
+}
+
+nsresult
+nsUUIDGenerator::Init()
+{
+ // We're a service, so we're guaranteed that Init() is not going
+ // to be reentered while we're inside Init().
+
+#if !defined(XP_WIN) && !defined(XP_MACOSX) && !defined(HAVE_ARC4RANDOM)
+ /* initialize random number generator using NSPR random noise */
+ unsigned int seed;
+
+ size_t bytes = 0;
+ while (bytes < sizeof(seed)) {
+ size_t nbytes = PR_GetRandomNoise(((unsigned char*)&seed) + bytes,
+ sizeof(seed) - bytes);
+ if (nbytes == 0) {
+ return NS_ERROR_FAILURE;
+ }
+ bytes += nbytes;
+ }
+
+ /* Initialize a new RNG state, and immediately switch
+ * back to the previous one -- we want to use mState
+ * only for our own calls to random().
+ */
+ mSavedState = initstate(seed, mState, sizeof(mState));
+ setstate(mSavedState);
+
+ mRBytes = 4;
+#ifdef RAND_MAX
+ if ((unsigned long)RAND_MAX < 0xffffffffUL) {
+ mRBytes = 3;
+ }
+ if ((unsigned long)RAND_MAX < 0x00ffffffUL) {
+ mRBytes = 2;
+ }
+ if ((unsigned long)RAND_MAX < 0x0000ffffUL) {
+ mRBytes = 1;
+ }
+ if ((unsigned long)RAND_MAX < 0x000000ffUL) {
+ return NS_ERROR_FAILURE;
+ }
+#endif
+
+#endif /* non XP_WIN and non XP_MACOSX and non ARC4RANDOM */
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUUIDGenerator::GenerateUUID(nsID** aRet)
+{
+ nsID* id = static_cast<nsID*>(moz_xmalloc(sizeof(nsID)));
+ if (!id) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsresult rv = GenerateUUIDInPlace(id);
+ if (NS_FAILED(rv)) {
+ free(id);
+ return rv;
+ }
+
+ *aRet = id;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsUUIDGenerator::GenerateUUIDInPlace(nsID* aId)
+{
+ // The various code in this method is probably not threadsafe, so lock
+ // across the whole method.
+ MutexAutoLock lock(mLock);
+
+#if defined(XP_WIN)
+ HRESULT hr = CoCreateGuid((GUID*)aId);
+ if (FAILED(hr)) {
+ return NS_ERROR_FAILURE;
+ }
+#elif defined(XP_MACOSX)
+ CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
+ if (!uuid) {
+ return NS_ERROR_FAILURE;
+ }
+
+ CFUUIDBytes bytes = CFUUIDGetUUIDBytes(uuid);
+ memcpy(aId, &bytes, sizeof(nsID));
+
+ CFRelease(uuid);
+#else /* not windows or OS X; generate randomness using random(). */
+ /* XXX we should be saving the return of setstate here and switching
+ * back to it; instead, we use the value returned when we called
+ * initstate, since older glibc's have broken setstate() return values
+ */
+#ifndef HAVE_ARC4RANDOM
+ setstate(mState);
+#endif
+
+#ifdef HAVE_ARC4RANDOM_BUF
+ arc4random_buf(aId, sizeof(nsID));
+#else /* HAVE_ARC4RANDOM_BUF */
+ size_t bytesLeft = sizeof(nsID);
+ while (bytesLeft > 0) {
+#ifdef HAVE_ARC4RANDOM
+ long rval = arc4random();
+ const size_t mRBytes = 4;
+#else
+ long rval = random();
+#endif
+
+
+ uint8_t* src = (uint8_t*)&rval;
+ // We want to grab the mRBytes least significant bytes of rval, since
+ // mRBytes less than sizeof(rval) means the high bytes are 0.
+#ifdef IS_BIG_ENDIAN
+ src += sizeof(rval) - mRBytes;
+#endif
+ uint8_t* dst = ((uint8_t*)aId) + (sizeof(nsID) - bytesLeft);
+ size_t toWrite = (bytesLeft < mRBytes ? bytesLeft : mRBytes);
+ for (size_t i = 0; i < toWrite; i++) {
+ dst[i] = src[i];
+ }
+
+ bytesLeft -= toWrite;
+ }
+#endif /* HAVE_ARC4RANDOM_BUF */
+
+ /* Put in the version */
+ aId->m2 &= 0x0fff;
+ aId->m2 |= 0x4000;
+
+ /* Put in the variant */
+ aId->m3[0] &= 0x3f;
+ aId->m3[0] |= 0x80;
+
+#ifndef HAVE_ARC4RANDOM
+ /* Restore the previous RNG state */
+ setstate(mSavedState);
+#endif
+#endif
+
+ return NS_OK;
+}
diff --git a/xpcom/base/nsUUIDGenerator.h b/xpcom/base/nsUUIDGenerator.h
new file mode 100644
index 0000000000..dd86093f8e
--- /dev/null
+++ b/xpcom/base/nsUUIDGenerator.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 _NSUUIDGENERATOR_H_
+#define _NSUUIDGENERATOR_H_
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Mutex.h"
+
+#include "nsIUUIDGenerator.h"
+
+class nsUUIDGenerator final : public nsIUUIDGenerator
+{
+public:
+ nsUUIDGenerator();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_DECL_NSIUUIDGENERATOR
+
+ nsresult Init();
+
+private:
+ ~nsUUIDGenerator();
+
+protected:
+
+ mozilla::Mutex mLock;
+#if !defined(XP_WIN) && !defined(XP_MACOSX) && !defined(HAVE_ARC4RANDOM)
+ char mState[128];
+ char* mSavedState;
+ uint8_t mRBytes;
+#endif
+};
+
+#define NS_UUID_GENERATOR_CONTRACTID "@mozilla.org/uuid-generator;1"
+#define NS_UUID_GENERATOR_CID \
+{ 0x706d36bb, 0xbf79, 0x4293, \
+{ 0x81, 0xf2, 0x8f, 0x68, 0x28, 0xc1, 0x8f, 0x9d } }
+
+#endif /* _NSUUIDGENERATOR_H_ */
diff --git a/xpcom/base/nsVersionComparatorImpl.cpp b/xpcom/base/nsVersionComparatorImpl.cpp
new file mode 100644
index 0000000000..8a37d8b1ac
--- /dev/null
+++ b/xpcom/base/nsVersionComparatorImpl.cpp
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "nsVersionComparatorImpl.h"
+#include "nsVersionComparator.h"
+#include "nsString.h"
+
+NS_IMPL_ISUPPORTS(nsVersionComparatorImpl, nsIVersionComparator)
+
+NS_IMETHODIMP
+nsVersionComparatorImpl::Compare(const nsACString& aStr1,
+ const nsACString& aStr2,
+ int32_t* aResult)
+{
+ *aResult = mozilla::CompareVersions(PromiseFlatCString(aStr1).get(),
+ PromiseFlatCString(aStr2).get());
+
+ return NS_OK;
+}
diff --git a/xpcom/base/nsVersionComparatorImpl.h b/xpcom/base/nsVersionComparatorImpl.h
new file mode 100644
index 0000000000..84a76d1bbc
--- /dev/null
+++ b/xpcom/base/nsVersionComparatorImpl.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Attributes.h"
+
+#include "nsIVersionComparator.h"
+
+class nsVersionComparatorImpl final : public nsIVersionComparator
+{
+ ~nsVersionComparatorImpl() {}
+
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIVERSIONCOMPARATOR
+};
+
+#define NS_VERSIONCOMPARATOR_CONTRACTID "@mozilla.org/xpcom/version-comparator;1"
+
+// c6e47036-ca94-4be3-963a-9abd8705f7a8
+#define NS_VERSIONCOMPARATOR_CID \
+{ 0xc6e47036, 0xca94, 0x4be3, \
+ { 0x96, 0x3a, 0x9a, 0xbd, 0x87, 0x05, 0xf7, 0xa8 } }
diff --git a/xpcom/base/nsWeakPtr.h b/xpcom/base/nsWeakPtr.h
new file mode 100644
index 0000000000..e2f7c37f17
--- /dev/null
+++ b/xpcom/base/nsWeakPtr.h
@@ -0,0 +1,15 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsWeakPtr_h__
+#define nsWeakPtr_h__
+
+#include "nsIWeakReference.h"
+#include "nsCOMPtr.h"
+
+// typedef nsCOMPtr<nsIWeakReference> nsWeakPtr;
+
+#endif
diff --git a/xpcom/base/nsWindowsHelpers.h b/xpcom/base/nsWindowsHelpers.h
new file mode 100644
index 0000000000..66505b3458
--- /dev/null
+++ b/xpcom/base/nsWindowsHelpers.h
@@ -0,0 +1,371 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 nsWindowsHelpers_h
+#define nsWindowsHelpers_h
+
+#include <windows.h>
+#include "nsAutoRef.h"
+#include "nscore.h"
+#include "mozilla/Assertions.h"
+
+// ----------------------------------------------------------------------------
+// Critical Section helper class
+// ----------------------------------------------------------------------------
+
+class AutoCriticalSection
+{
+public:
+ AutoCriticalSection(LPCRITICAL_SECTION aSection)
+ : mSection(aSection)
+ {
+ ::EnterCriticalSection(mSection);
+ }
+ ~AutoCriticalSection()
+ {
+ ::LeaveCriticalSection(mSection);
+ }
+private:
+ LPCRITICAL_SECTION mSection;
+};
+
+template<>
+class nsAutoRefTraits<HKEY>
+{
+public:
+ typedef HKEY RawRef;
+ static HKEY Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef aFD)
+ {
+ if (aFD != Void()) {
+ RegCloseKey(aFD);
+ }
+ }
+};
+
+template<>
+class nsAutoRefTraits<HDC>
+{
+public:
+ typedef HDC RawRef;
+ static HDC Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef aFD)
+ {
+ if (aFD != Void()) {
+ ::DeleteDC(aFD);
+ }
+ }
+};
+
+template<>
+class nsAutoRefTraits<HBRUSH>
+{
+public:
+ typedef HBRUSH RawRef;
+ static HBRUSH Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef aFD)
+ {
+ if (aFD != Void()) {
+ ::DeleteObject(aFD);
+ }
+ }
+};
+
+template<>
+class nsAutoRefTraits<HRGN>
+{
+public:
+ typedef HRGN RawRef;
+ static HRGN Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef aFD)
+ {
+ if (aFD != Void()) {
+ ::DeleteObject(aFD);
+ }
+ }
+};
+
+template<>
+class nsAutoRefTraits<HBITMAP>
+{
+public:
+ typedef HBITMAP RawRef;
+ static HBITMAP Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef aFD)
+ {
+ if (aFD != Void()) {
+ ::DeleteObject(aFD);
+ }
+ }
+};
+
+template<>
+class nsAutoRefTraits<SC_HANDLE>
+{
+public:
+ typedef SC_HANDLE RawRef;
+ static SC_HANDLE Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef aFD)
+ {
+ if (aFD != Void()) {
+ CloseServiceHandle(aFD);
+ }
+ }
+};
+
+template<>
+class nsSimpleRef<HANDLE>
+{
+protected:
+ typedef HANDLE RawRef;
+
+ nsSimpleRef() : mRawRef(nullptr)
+ {
+ }
+
+ nsSimpleRef(RawRef aRawRef) : mRawRef(aRawRef)
+ {
+ }
+
+ bool HaveResource() const
+ {
+ return mRawRef && mRawRef != INVALID_HANDLE_VALUE;
+ }
+
+public:
+ RawRef get() const
+ {
+ return mRawRef;
+ }
+
+ static void Release(RawRef aRawRef)
+ {
+ if (aRawRef && aRawRef != INVALID_HANDLE_VALUE) {
+ CloseHandle(aRawRef);
+ }
+ }
+ RawRef mRawRef;
+};
+
+
+template<>
+class nsAutoRefTraits<HMODULE>
+{
+public:
+ typedef HMODULE RawRef;
+ static RawRef Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef aFD)
+ {
+ if (aFD != Void()) {
+ FreeLibrary(aFD);
+ }
+ }
+};
+
+
+template<>
+class nsAutoRefTraits<DEVMODEW*>
+{
+public:
+ typedef DEVMODEW* RawRef;
+ static RawRef Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef aDevMode)
+ {
+ if (aDevMode != Void()) {
+ ::HeapFree(::GetProcessHeap(), 0, aDevMode);
+ }
+ }
+};
+
+
+// HGLOBAL is just a typedef of HANDLE which nsSimpleRef has a specialization of,
+// that means having a nsAutoRefTraits specialization for HGLOBAL is useless.
+// Therefore we create a wrapper class for HGLOBAL to make nsAutoRefTraits and
+// nsAutoRef work as intention.
+class nsHGLOBAL {
+public:
+ nsHGLOBAL(HGLOBAL hGlobal) : m_hGlobal(hGlobal)
+ {
+ }
+
+ operator HGLOBAL() const
+ {
+ return m_hGlobal;
+ }
+
+private:
+ HGLOBAL m_hGlobal;
+};
+
+
+template<>
+class nsAutoRefTraits<nsHGLOBAL>
+{
+public:
+ typedef nsHGLOBAL RawRef;
+ static RawRef Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef hGlobal)
+ {
+ ::GlobalFree(hGlobal);
+ }
+};
+
+
+// Because Printer's HANDLE uses ClosePrinter and we already have nsAutoRef<HANDLE>
+// which uses CloseHandle so we need to create a wrapper class for HANDLE to have
+// another specialization for nsAutoRefTraits.
+class nsHPRINTER {
+public:
+ nsHPRINTER(HANDLE hPrinter) : m_hPrinter(hPrinter)
+ {
+ }
+
+ operator HANDLE() const
+ {
+ return m_hPrinter;
+ }
+
+ HANDLE* operator&()
+ {
+ return &m_hPrinter;
+ }
+
+private:
+ HANDLE m_hPrinter;
+};
+
+
+// winspool.h header has AddMonitor macro, it conflicts with AddMonitor member
+// function in TaskbarPreview.cpp and TaskbarTabPreview.cpp. Beside, we only
+// need ClosePrinter here for Release function, so having its prototype is enough.
+extern "C" BOOL WINAPI ClosePrinter(HANDLE hPrinter);
+
+
+template<>
+class nsAutoRefTraits<nsHPRINTER>
+{
+public:
+ typedef nsHPRINTER RawRef;
+ static RawRef Void()
+ {
+ return nullptr;
+ }
+
+ static void Release(RawRef hPrinter)
+ {
+ ::ClosePrinter(hPrinter);
+ }
+};
+
+
+typedef nsAutoRef<HKEY> nsAutoRegKey;
+typedef nsAutoRef<HDC> nsAutoHDC;
+typedef nsAutoRef<HBRUSH> nsAutoBrush;
+typedef nsAutoRef<HRGN> nsAutoRegion;
+typedef nsAutoRef<HBITMAP> nsAutoBitmap;
+typedef nsAutoRef<SC_HANDLE> nsAutoServiceHandle;
+typedef nsAutoRef<HANDLE> nsAutoHandle;
+typedef nsAutoRef<HMODULE> nsModuleHandle;
+typedef nsAutoRef<DEVMODEW*> nsAutoDevMode;
+typedef nsAutoRef<nsHGLOBAL> nsAutoGlobalMem;
+typedef nsAutoRef<nsHPRINTER> nsAutoPrinter;
+
+namespace {
+
+// Construct a path "<system32>\<aModule>". return false if the output buffer
+// is too small.
+// Note: If the system path cannot be found, or doesn't fit in the output buffer
+// with the module name, we will just ignore the system path and output the
+// module name alone;
+// this may mean using a normal search path wherever the output is used.
+bool inline
+ConstructSystem32Path(LPCWSTR aModule, WCHAR* aSystemPath, UINT aSize)
+{
+ MOZ_ASSERT(aSystemPath);
+
+ size_t fileLen = wcslen(aModule);
+ if (fileLen >= aSize) {
+ // The module name alone cannot even fit!
+ return false;
+ }
+
+ size_t systemDirLen = GetSystemDirectoryW(aSystemPath, aSize);
+
+ if (systemDirLen) {
+ if (systemDirLen < aSize - fileLen) {
+ // Make the system directory path terminate with a slash.
+ if (aSystemPath[systemDirLen - 1] != L'\\') {
+ if (systemDirLen + 1 < aSize - fileLen) {
+ aSystemPath[systemDirLen] = L'\\';
+ ++systemDirLen;
+ // No need to re-nullptr terminate.
+ } else {
+ // Couldn't fit the system path with added slash.
+ systemDirLen = 0;
+ }
+ }
+ } else {
+ // Couldn't fit the system path.
+ systemDirLen = 0;
+ }
+ }
+
+ MOZ_ASSERT(systemDirLen + fileLen < aSize);
+
+ wcsncpy(aSystemPath + systemDirLen, aModule, fileLen);
+ aSystemPath[systemDirLen + fileLen] = L'\0';
+ return true;
+}
+
+HMODULE inline
+LoadLibrarySystem32(LPCWSTR aModule)
+{
+ WCHAR systemPath[MAX_PATH + 1];
+ if (!ConstructSystem32Path(aModule, systemPath, MAX_PATH + 1)) {
+ return NULL;
+ }
+ return LoadLibraryW(systemPath);
+}
+
+}
+
+#endif
diff --git a/xpcom/base/nscore.h b/xpcom/base/nscore.h
new file mode 100644
index 0000000000..f6e73c6bc7
--- /dev/null
+++ b/xpcom/base/nscore.h
@@ -0,0 +1,288 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 nscore_h___
+#define nscore_h___
+
+/**
+ * Make sure that we have the proper platform specific
+ * c++ definitions needed by nscore.h
+ */
+#ifndef _XPCOM_CONFIG_H_
+#include "xpcom-config.h"
+#endif
+
+/* Definitions of functions and operators that allocate memory. */
+#if !defined(XPCOM_GLUE) && !defined(NS_NO_XPCOM) && !defined(MOZ_NO_MOZALLOC)
+# include "mozilla/mozalloc.h"
+#endif
+
+/**
+ * Incorporate the integer data types which XPCOM uses.
+ */
+#include <stddef.h>
+#include <stdint.h>
+
+#include "mozilla/RefCountType.h"
+
+/* Core XPCOM declarations. */
+
+/*----------------------------------------------------------------------*/
+/* Import/export defines */
+
+#ifdef HAVE_VISIBILITY_HIDDEN_ATTRIBUTE
+#define NS_VISIBILITY_HIDDEN __attribute__ ((visibility ("hidden")))
+#else
+#define NS_VISIBILITY_HIDDEN
+#endif
+
+#if defined(HAVE_VISIBILITY_ATTRIBUTE)
+#define NS_VISIBILITY_DEFAULT __attribute__ ((visibility ("default")))
+#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
+#define NS_VISIBILITY_DEFAULT __global
+#else
+#define NS_VISIBILITY_DEFAULT
+#endif
+
+#define NS_HIDDEN_(type) NS_VISIBILITY_HIDDEN type
+#define NS_EXTERNAL_VIS_(type) NS_VISIBILITY_DEFAULT type
+
+#define NS_HIDDEN NS_VISIBILITY_HIDDEN
+#define NS_EXTERNAL_VIS NS_VISIBILITY_DEFAULT
+
+/**
+ * Mark a function as using a potentially non-standard function calling
+ * convention. This can be used on functions that are called very
+ * frequently, to reduce the overhead of the function call. It is still worth
+ * using the macro for C++ functions which take no parameters since it allows
+ * passing |this| in a register.
+ *
+ * - Do not use this on any scriptable interface method since xptcall won't be
+ * aware of the different calling convention.
+ * - This must appear on the declaration, not the definition.
+ * - Adding this to a public function _will_ break binary compatibility.
+ * - This may be used on virtual functions but you must ensure it is applied
+ * to all implementations - the compiler will _not_ warn but it will crash.
+ * - This has no effect for functions which take a variable number of
+ * arguments.
+ * - __fastcall on windows should not be applied to class
+ * constructors/destructors - use the NS_CONSTRUCTOR_FASTCALL macro for
+ * constructors/destructors.
+ *
+ * Examples: int NS_FASTCALL func1(char *foo);
+ * NS_HIDDEN_(int) NS_FASTCALL func2(char *foo);
+ */
+
+#if defined(__i386__) && defined(__GNUC__)
+#define NS_FASTCALL __attribute__ ((regparm (3), stdcall))
+#define NS_CONSTRUCTOR_FASTCALL __attribute__ ((regparm (3), stdcall))
+#elif defined(XP_WIN) && !defined(_WIN64)
+#define NS_FASTCALL __fastcall
+#define NS_CONSTRUCTOR_FASTCALL
+#else
+#define NS_FASTCALL
+#define NS_CONSTRUCTOR_FASTCALL
+#endif
+
+#ifdef XP_WIN
+
+#define NS_IMPORT __declspec(dllimport)
+#define NS_IMPORT_(type) __declspec(dllimport) type __stdcall
+#define NS_EXPORT __declspec(dllexport)
+#define NS_EXPORT_(type) __declspec(dllexport) type __stdcall
+#define NS_IMETHOD_(type) virtual type __stdcall
+#define NS_IMETHODIMP_(type) type __stdcall
+#define NS_METHOD_(type) type __stdcall
+#define NS_CALLBACK_(_type, _name) _type (__stdcall * _name)
+#ifndef _WIN64
+// Win64 has only one calling convention. __stdcall will be ignored by the compiler.
+#define NS_STDCALL __stdcall
+#define NS_HAVE_STDCALL
+#else
+#define NS_STDCALL
+#endif
+#define NS_FROZENCALL __cdecl
+
+#else
+
+#define NS_IMPORT NS_EXTERNAL_VIS
+#define NS_IMPORT_(type) NS_EXTERNAL_VIS_(type)
+#define NS_EXPORT NS_EXTERNAL_VIS
+#define NS_EXPORT_(type) NS_EXTERNAL_VIS_(type)
+#define NS_IMETHOD_(type) virtual type
+#define NS_IMETHODIMP_(type) type
+#define NS_METHOD_(type) type
+#define NS_CALLBACK_(_type, _name) _type (* _name)
+#define NS_STDCALL
+#define NS_FROZENCALL
+
+#endif
+
+/**
+ * Macro for creating typedefs for pointer-to-member types which are
+ * declared with stdcall. It is important to use this for any type which is
+ * declared as stdcall (i.e. NS_IMETHOD). For example, instead of writing:
+ *
+ * typedef nsresult (nsIFoo::*someType)(nsISupports* arg);
+ *
+ * you should write:
+ *
+ * typedef
+ * NS_STDCALL_FUNCPROTO(nsresult, someType, nsIFoo, typeFunc, (nsISupports*));
+ *
+ * where nsIFoo::typeFunc is any method declared as
+ * NS_IMETHOD typeFunc(nsISupports*);
+ *
+ * XXX this can be simplified to always use the non-typeof implementation
+ * when http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11893 is fixed.
+ */
+
+#ifdef __GNUC__
+#define NS_STDCALL_FUNCPROTO(ret, name, class, func, args) \
+ typeof(&class::func) name
+#else
+#define NS_STDCALL_FUNCPROTO(ret, name, class, func, args) \
+ ret (NS_STDCALL class::*name) args
+#endif
+
+/**
+ * Deprecated declarations.
+ */
+#ifdef __GNUC__
+# define MOZ_DEPRECATED __attribute__((deprecated))
+#elif defined(_MSC_VER)
+# define MOZ_DEPRECATED __declspec(deprecated)
+#else
+# define MOZ_DEPRECATED
+#endif
+
+/**
+ * Generic API modifiers which return the standard XPCOM nsresult type
+ *
+ * - NS_IMETHOD: use for in-class declarations and definitions.
+ * - NS_IMETHODIMP: use for out-of-class definitions.
+ * - NS_METHOD: usually used in conjunction with NS_CALLBACK.
+ * - NS_CALLBACK: used in some legacy situations. Best avoided.
+ */
+#define NS_IMETHOD NS_IMETHOD_(nsresult)
+#define NS_IMETHODIMP NS_IMETHODIMP_(nsresult)
+#define NS_METHOD NS_METHOD_(nsresult)
+#define NS_CALLBACK(_name) NS_CALLBACK_(nsresult, _name)
+
+/**
+ * Import/Export macros for XPCOM APIs
+ */
+
+#ifdef __cplusplus
+#define NS_EXTERN_C extern "C"
+#else
+#define NS_EXTERN_C
+#endif
+
+#define EXPORT_XPCOM_API(type) NS_EXTERN_C NS_EXPORT type NS_FROZENCALL
+#define IMPORT_XPCOM_API(type) NS_EXTERN_C NS_IMPORT type NS_FROZENCALL
+#define GLUE_XPCOM_API(type) NS_EXTERN_C NS_HIDDEN_(type) NS_FROZENCALL
+
+#ifdef IMPL_LIBXUL
+#define XPCOM_API(type) EXPORT_XPCOM_API(type)
+#elif defined(XPCOM_GLUE)
+#define XPCOM_API(type) GLUE_XPCOM_API(type)
+#else
+#define XPCOM_API(type) IMPORT_XPCOM_API(type)
+#endif
+
+#ifdef MOZILLA_INTERNAL_API
+ /*
+ The frozen string API has different definitions of nsAC?String
+ classes than the internal API. On systems that explicitly declare
+ dllexport symbols this is not a problem, but on ELF systems
+ internal symbols can accidentally "shine through"; we rename the
+ internal classes to avoid symbol conflicts.
+ */
+# define nsAString nsAString_internal
+# define nsACString nsACString_internal
+#endif
+
+#if (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
+/* Make refcnt logging part of the build. This doesn't mean that
+ * actual logging will occur (that requires a separate enable; see
+ * nsTraceRefcnt and nsISupportsImpl.h for more information). */
+#define NS_BUILD_REFCNT_LOGGING
+#endif
+
+/* If NO_BUILD_REFCNT_LOGGING is defined then disable refcnt logging
+ * in the build. This overrides FORCE_BUILD_REFCNT_LOGGING. */
+#if defined(NO_BUILD_REFCNT_LOGGING)
+#undef NS_BUILD_REFCNT_LOGGING
+#endif
+
+/* If a program allocates memory for the lifetime of the app, it doesn't make
+ * sense to touch memory pages and free that memory at shutdown,
+ * unless we are running leak stats.
+ */
+#if defined(NS_BUILD_REFCNT_LOGGING) || defined(MOZ_VALGRIND) || defined(MOZ_ASAN)
+#define NS_FREE_PERMANENT_DATA
+#endif
+
+/**
+ * NS_NO_VTABLE is emitted by xpidl in interface declarations whenever
+ * xpidl can determine that the interface can't contain a constructor.
+ * This results in some space savings and possible runtime savings -
+ * see bug 49416. We undefine it first, as xpidl-generated headers
+ * define it for IDL uses that don't include this file.
+ */
+#ifdef NS_NO_VTABLE
+#undef NS_NO_VTABLE
+#endif
+#if defined(_MSC_VER)
+#define NS_NO_VTABLE __declspec(novtable)
+#else
+#define NS_NO_VTABLE
+#endif
+
+
+/**
+ * Generic XPCOM result data type
+ */
+#include "nsError.h"
+
+typedef MozRefCountType nsrefcnt;
+
+/*
+ * Use these macros to do 64bit safe pointer conversions.
+ */
+
+#define NS_PTR_TO_INT32(x) ((int32_t)(intptr_t)(x))
+#define NS_PTR_TO_UINT32(x) ((uint32_t)(intptr_t)(x))
+#define NS_INT32_TO_PTR(x) ((void*)(intptr_t)(x))
+
+/*
+ * Use NS_STRINGIFY to form a string literal from the value of a macro.
+ */
+#define NS_STRINGIFY_HELPER(x_) #x_
+#define NS_STRINGIFY(x_) NS_STRINGIFY_HELPER(x_)
+
+/*
+ * If we're being linked as standalone glue, we don't want a dynamic
+ * dependency on NSPR libs, so we skip the debug thread-safety
+ * checks, and we cannot use the THREADSAFE_ISUPPORTS macros.
+ */
+#if defined(XPCOM_GLUE) && !defined(XPCOM_GLUE_USE_NSPR)
+#define XPCOM_GLUE_AVOID_NSPR
+#endif
+
+/*
+ * SEH exception macros.
+ */
+#ifdef HAVE_SEH_EXCEPTIONS
+#define MOZ_SEH_TRY __try
+#define MOZ_SEH_EXCEPT(expr) __except(expr)
+#else
+#define MOZ_SEH_TRY if(true)
+#define MOZ_SEH_EXCEPT(expr) else
+#endif
+
+#endif /* nscore_h___ */
diff --git a/xpcom/base/nsrootidl.idl b/xpcom/base/nsrootidl.idl
new file mode 100644
index 0000000000..55795b7bc6
--- /dev/null
+++ b/xpcom/base/nsrootidl.idl
@@ -0,0 +1,97 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Root idl declarations to be used by all.
+ */
+
+%{C++
+
+#include "nscore.h"
+typedef int64_t PRTime;
+
+/*
+ * Forward declarations for new string types
+ */
+class nsAString;
+class nsACString;
+
+/*
+ * Start commenting out the C++ versions of the below in the output header
+ */
+#if 0
+%}
+
+typedef boolean bool ;
+typedef octet uint8_t ;
+typedef unsigned short uint16_t ;
+typedef unsigned short char16_t;
+typedef unsigned long uint32_t ;
+typedef unsigned long long uint64_t ;
+typedef long long PRTime ;
+typedef short int16_t ;
+typedef long int32_t ;
+typedef long long int64_t ;
+
+typedef unsigned long nsrefcnt ;
+typedef unsigned long nsresult ;
+
+// XXX need this built into xpidl compiler so that it's really size_t or size_t
+// and it's scriptable:
+typedef unsigned long size_t;
+
+[ptr] native voidPtr(void);
+[ptr] native charPtr(char);
+[ptr] native unicharPtr(char16_t);
+
+[ref, nsid] native nsIDRef(nsID);
+[ref, nsid] native nsIIDRef(nsIID);
+[ref, nsid] native nsCIDRef(nsCID);
+
+[ptr, nsid] native nsIDPtr(nsID);
+[ptr, nsid] native nsIIDPtr(nsIID);
+[ptr, nsid] native nsCIDPtr(nsCID);
+
+// NOTE: Be careful in using the following 3 types. The *Ref and *Ptr variants
+// are more commonly used (and better supported). Those variants require
+// nsMemory alloc'd copies when used as 'out' params while these types do not.
+// However, currently these types can not be used for 'in' params. And, methods
+// that use them as 'out' params *must* be declared [notxpcom] (with an explicit
+// return type of nsresult). This makes such methods implicitly not scriptable.
+// Use of these types in methods without a [notxpcom] declaration will cause
+// the xpidl compiler to raise an error.
+// See: http://bugzilla.mozilla.org/show_bug.cgi?id=93792
+
+[nsid] native nsIID(nsIID);
+[nsid] native nsID(nsID);
+[nsid] native nsCID(nsCID);
+
+[ptr] native nsQIResult(void);
+
+[ref, domstring] native DOMString(ignored);
+[ref, domstring] native DOMStringRef(ignored);
+[ptr, domstring] native DOMStringPtr(ignored);
+
+[ref, utf8string] native AUTF8String(ignored);
+[ref, utf8string] native AUTF8StringRef(ignored);
+[ptr, utf8string] native AUTF8StringPtr(ignored);
+
+[ref, cstring] native ACString(ignored);
+[ref, cstring] native ACStringRef(ignored);
+[ptr, cstring] native ACStringPtr(ignored);
+
+[ref, astring] native AString(ignored);
+[ref, astring] native AStringRef(ignored);
+[ptr, astring] native AStringPtr(ignored);
+
+[ref, jsval] native jsval(jsval);
+ native jsid(jsid);
+
+%{C++
+/*
+ * End commenting out the C++ versions of the above in the output header
+ */
+#endif
+%}