Bug 1348273 - Convert crash annotations into a machine-readable list of constants; r=ted.mielczarek,njn,dholbert,mak,cpearce,mcmanus,froydnj,Dexter,jrmuizel,jchen,jimm,bz,surkov
authorGabriele Svelto <gsvelto@mozilla.com>
Thu, 05 Jul 2018 15:42:11 +0200
changeset 486027 42e2eeaca65d313926e962b6583d52d4bcab6d15
parent 486026 0e67d90ba707c8030c27fc2c488dfdaeb914cfb0
child 486028 64840ebaa8f1542641454d412eafdad3a77d9a01
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersted
bugs1348273
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1348273 - Convert crash annotations into a machine-readable list of constants; r=ted.mielczarek,njn,dholbert,mak,cpearce,mcmanus,froydnj,Dexter,jrmuizel,jchen,jimm,bz,surkov This introduces the machinery needed to generate crash annotations from a YAML file. The relevant C++ functions are updated to take a typed enum. JavaScript calls are unaffected but they will throw if the string argument does not correspond to one of the known entries in the C++ enum. The existing whitelists and blacklists of annotations are also generated from the YAML file and all duplicate code related to them has been consolidated. Once written out to the .extra file the annotations are converted in string form and are no different than the existing ones. All existing annotations have been included in the list (and some obsolete ones have been removed) and all call sites have been updated including tests where appropriate.
accessible/base/nsAccessibilityService.cpp
accessible/windows/msaa/Compatibility.cpp
accessible/windows/msaa/LazyInstantiator.cpp
accessible/windows/msaa/Platform.cpp
docshell/base/nsDocShell.cpp
dom/events/TouchEvent.cpp
dom/ipc/ContentChild.cpp
dom/ipc/ContentParent.cpp
dom/ipc/TabChild.cpp
dom/media/gmp/GMPChild.cpp
dom/media/gmp/GMPParent.cpp
dom/plugins/base/nsPluginHost.cpp
dom/plugins/base/nsPluginsDirDarwin.cpp
dom/plugins/ipc/PluginMessageUtils.h
dom/plugins/ipc/PluginModuleParent.cpp
gfx/ipc/GPUProcessManager.cpp
gfx/src/DriverCrashGuard.cpp
gfx/thebes/DeviceManagerDx.cpp
gfx/thebes/gfxPlatform.cpp
ipc/chromium/src/chrome/common/ipc_channel_posix.cc
ipc/glue/CrashReporterClient.cpp
ipc/glue/CrashReporterClient.h
ipc/glue/CrashReporterHost.cpp
ipc/glue/CrashReporterHost.h
ipc/glue/CrashReporterMetadataShmem.cpp
ipc/glue/CrashReporterMetadataShmem.h
ipc/glue/IPCMessageUtils.h
ipc/glue/MessageChannel.cpp
ipc/glue/MessageLink.cpp
ipc/glue/ProtocolUtils.cpp
ipc/glue/ProtocolUtils.h
ipc/glue/Transport_posix.cpp
ipc/mscom/COMPtrHolder.h
ipc/mscom/ProxyStream.cpp
ipc/mscom/RegistrationAnnotator.cpp
js/xpconnect/loader/mozJSComponentLoader.cpp
layout/style/ServoBindings.h
layout/style/nsLayoutStylesheetCache.cpp
mobile/android/base/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetryCrashPingBuilder.java
mobile/android/base/moz.build
mozglue/build/WindowsDllBlocklist.cpp
netwerk/ipc/NeckoMessageUtils.h
storage/mozStorageService.cpp
toolkit/components/crashes/CrashManager.jsm
toolkit/components/crashes/tests/xpcshell/crash.extra
toolkit/components/gfx/SanityTest.js
toolkit/components/telemetry/TelemetrySend.jsm
toolkit/components/telemetry/tests/unit/test_TelemetrySend.js
toolkit/components/terminator/nsTerminator.cpp
toolkit/crashreporter/CrashAnnotations.cpp
toolkit/crashreporter/CrashAnnotations.h.in
toolkit/crashreporter/CrashAnnotations.yaml
toolkit/crashreporter/client/moz.build
toolkit/crashreporter/client/ping.cpp
toolkit/crashreporter/generate_crash_reporter_sources.py
toolkit/crashreporter/moz.build
toolkit/crashreporter/nsDummyExceptionHandler.cpp
toolkit/crashreporter/nsExceptionHandler.cpp
toolkit/crashreporter/nsExceptionHandler.h
toolkit/crashreporter/test/unit/test_crash_after_js_large_allocation_failure.js
toolkit/crashreporter/test/unit/test_crash_after_js_large_allocation_failure_reporting.js
toolkit/crashreporter/test/unit/test_crash_after_js_oom_recovered.js
toolkit/crashreporter/test/unit/test_crash_after_js_oom_reported.js
toolkit/crashreporter/test/unit/test_crash_after_js_oom_reported_2.js
toolkit/crashreporter/test/unit/test_crash_oom.js
toolkit/crashreporter/test/unit/test_crashreporter.js
toolkit/crashreporter/test/unit/test_crashreporter_crash.js
toolkit/crashreporter/test/unit/test_oom_annotation_windows.js
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsEmbedFunctions.cpp
widget/android/GfxInfo.cpp
widget/android/jni/Utils.cpp
widget/android/nsAppShell.cpp
widget/cocoa/GfxInfo.mm
widget/windows/GfxInfo.cpp
xpcom/base/AvailableMemoryTracker.cpp
xpcom/base/CycleCollectedJSRuntime.cpp
xpcom/base/CycleCollectedJSRuntime.h
xpcom/base/nsCycleCollector.cpp
xpcom/base/nsDebugImpl.cpp
xpcom/system/nsICrashReporter.idl
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -1388,17 +1388,17 @@ nsAccessibilityService::Init()
     gApplicationAccessible = new ApplicationAccessible();
 #endif // defined(XP_WIN)
   }
 
   NS_ADDREF(gApplicationAccessible); // will release in Shutdown()
   gApplicationAccessible->Init();
 
   CrashReporter::
-    AnnotateCrashReport(NS_LITERAL_CSTRING("Accessibility"),
+    AnnotateCrashReport(CrashReporter::Annotation::Accessibility,
                         NS_LITERAL_CSTRING("Active"));
 
 #ifdef XP_WIN
   sPendingPlugins = new nsTArray<nsCOMPtr<nsIContent> >;
   sPluginTimers = new nsTArray<nsCOMPtr<nsITimer> >;
 #endif
 
   // Now its safe to start platform accessibility.
--- a/accessible/windows/msaa/Compatibility.cpp
+++ b/accessible/windows/msaa/Compatibility.cpp
@@ -237,17 +237,17 @@ Compatibility::HasKnownNonUiaConsumer()
 
 void
 Compatibility::Init()
 {
   // Note we collect some AT statistics/telemetry here for convenience.
   InitConsumers();
 
   CrashReporter::
-    AnnotateCrashReport(NS_LITERAL_CSTRING("AccessibilityInProcClient"),
+    AnnotateCrashReport(CrashReporter::Annotation::AccessibilityInProcClient,
                         nsPrintfCString("0x%X", sConsumers));
 
   // Gather telemetry
   uint32_t temp = sConsumers;
   for (int i = 0; temp; i++) {
     if (temp & 0x1)
       statistics::A11yConsumers(i);
 
@@ -431,18 +431,19 @@ UseIAccessibleProxyStub()
   // Otherwise we try the proxy/stub
   if (IsIAccessiblePSRegistered()) {
     return true;
   }
 
   // If we reach this point then something is seriously wrong with the
   // IAccessible configuration in the computer's registry. Let's annotate this
   // so that we can easily determine this condition during crash analysis.
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IAccessibleConfig"),
-                                     NS_LITERAL_CSTRING("NoSystemTypeLibOrPS"));
+  CrashReporter::AnnotateCrashReport(
+    CrashReporter::Annotation::IAccessibleConfig,
+    NS_LITERAL_CSTRING("NoSystemTypeLibOrPS"));
   return false;
 }
 
 #endif // !defined(HAVE_64BIT_BUILD)
 
 uint16_t
 Compatibility::GetActCtxResourceId()
 {
--- a/accessible/windows/msaa/LazyInstantiator.cpp
+++ b/accessible/windows/msaa/LazyInstantiator.cpp
@@ -12,17 +12,16 @@
 #include "mozilla/a11y/Platform.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/mscom/MainThreadRuntime.h"
 #include "mozilla/mscom/Registration.h"
 #include "mozilla/UniquePtr.h"
 #include "nsAccessibilityService.h"
 #include "nsWindowsHelpers.h"
 #include "nsCOMPtr.h"
-#include "nsExceptionHandler.h"
 #include "nsIFile.h"
 #include "nsXPCOM.h"
 #include "RootAccessibleWrap.h"
 #include "WinUtils.h"
 
 #include <oaidl.h>
 
 #if !defined(STATE_SYSTEM_NORMAL)
--- a/accessible/windows/msaa/Platform.cpp
+++ b/accessible/windows/msaa/Platform.cpp
@@ -353,17 +353,17 @@ AccumulateInstantiatorTelemetry(const ns
 
   if (!aValue.IsEmpty()) {
 #if defined(MOZ_TELEMETRY_REPORTING)
     Telemetry::ScalarSet(Telemetry::ScalarID::A11Y_INSTANTIATORS,
                          aValue);
 #endif // defined(MOZ_TELEMETRY_REPORTING)
 #if defined(MOZ_CRASHREPORTER)
     CrashReporter::
-      AnnotateCrashReport(NS_LITERAL_CSTRING("AccessibilityClient"),
+      AnnotateCrashReport(CrashReporter::Annotation::AccessibilityClient,
                           NS_ConvertUTF16toUTF8(aValue));
 #endif // defined(MOZ_CRASHREPORTER)
   }
 }
 
 static void
 GatherInstantiatorTelemetry(nsIFile* aClientExe)
 {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1695,18 +1695,18 @@ nsDocShell::GetUseRemoteTabs(bool* aUseR
   *aUseRemoteTabs = mUseRemoteTabs;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::SetRemoteTabs(bool aUseRemoteTabs)
 {
   if (aUseRemoteTabs) {
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("DOMIPCEnabled"),
-                                       NS_LITERAL_CSTRING("1"));
+    CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::DOMIPCEnabled,
+                                       true);
   }
 
   mUseRemoteTabs = aUseRemoteTabs;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::SetAffectPrivateSessionLifetime(bool aAffectLifetime)
--- a/dom/events/TouchEvent.cpp
+++ b/dom/events/TouchEvent.cpp
@@ -271,20 +271,18 @@ TouchEvent::PrefEnabled(nsIDocShell* aDo
   } else {
     if (sPrefCacheValue == 2) {
       enabled = PlatformSupportsTouch();
 
       static bool firstTime = true;
       // The touch screen data seems to be inaccurate in the parent process,
       // and we really need the crash annotation in child processes.
       if (firstTime && !XRE_IsParentProcess()) {
-        CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("HasDeviceTouchScreen"),
-                                           enabled ?
-                                             NS_LITERAL_CSTRING("1") :
-                                             NS_LITERAL_CSTRING("0"));
+        CrashReporter::AnnotateCrashReport(
+          CrashReporter::Annotation::HasDeviceTouchScreen, enabled);
         firstTime = false;
       }
 
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
       if (enabled && aDocShell) {
         // APZ might be disabled on this particular widget, in which case
         // TouchEvent support will also be disabled. Try to detect that.
         RefPtr<nsPresContext> pc;
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1772,26 +1772,23 @@ ContentChild::RecvSetProcessSandbox(cons
   }
 #elif defined(XP_WIN)
   mozilla::SandboxTarget::Instance()->StartSandbox();
 #elif defined(XP_MACOSX)
   sandboxEnabled = StartMacOSContentSandbox();
 #endif
 
   CrashReporter::AnnotateCrashReport(
-    NS_LITERAL_CSTRING("ContentSandboxEnabled"),
-    sandboxEnabled? NS_LITERAL_CSTRING("1") : NS_LITERAL_CSTRING("0"));
+    CrashReporter::Annotation::ContentSandboxEnabled, sandboxEnabled);
 #if defined(XP_LINUX) && !defined(OS_ANDROID)
-  nsAutoCString flagsString;
-  flagsString.AppendInt(SandboxInfo::Get().AsInteger());
-
   CrashReporter::AnnotateCrashReport(
-    NS_LITERAL_CSTRING("ContentSandboxCapabilities"), flagsString);
+    CrashReporter::Annotation::ContentSandboxCapabilities,
+    static_cast<int>(SandboxInfo::Get().AsInteger()));
 #endif /* XP_LINUX && !OS_ANDROID */
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("RemoteType"),
+  CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::RemoteType,
                                      NS_ConvertUTF16toUTF8(GetRemoteType()));
 #endif /* MOZ_CONTENT_SANDBOX */
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvBidiKeyboardNotify(const bool& aIsLangRTL,
@@ -2443,17 +2440,18 @@ ContentChild::ProcessingError(Result aCo
     case MsgValueError:
       break;
 
     default:
       MOZ_CRASH("not reached");
   }
 
   nsDependentCString reason(aReason);
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ipc_channel_error"), reason);
+  CrashReporter::AnnotateCrashReport(
+    CrashReporter::Annotation::ipc_channel_error, reason);
 
   MOZ_CRASH("Content child abort due to IPC error");
 }
 
 nsresult
 ContentChild::AddRemoteAlertObserver(const nsString& aData,
                                      nsIObserver* aObserver)
 {
@@ -3118,18 +3116,19 @@ ContentChild::RecvShutdown()
 
 void
 ContentChild::ShutdownInternal()
 {
   // If we receive the shutdown message from within a nested event loop, we want
   // to wait for that event loop to finish. Otherwise we could prematurely
   // terminate an "unload" or "pagehide" event handler (which might be doing a
   // sync XHR, for example).
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCShutdownState"),
-                                     NS_LITERAL_CSTRING("RecvShutdown"));
+  CrashReporter::AnnotateCrashReport(
+    CrashReporter::Annotation::IPCShutdownState,
+    NS_LITERAL_CSTRING("RecvShutdown"));
 
   MOZ_ASSERT(NS_IsMainThread());
   RefPtr<nsThread> mainThread = nsThreadManager::get().GetCurrentThread();
   // Note that we only have to check the recursion count for the current
   // cooperative thread. Since the Shutdown message is not labeled with a
   // SchedulerGroup, there can be no other cooperative threads doing work while
   // we're running.
   if (mainThread && mainThread->RecursionDepth() > 1) {
@@ -3177,20 +3176,21 @@ ContentChild::ShutdownInternal()
   }
 #endif
 
   // Start a timer that will insure we quickly exit after a reasonable
   // period of time. Prevents shutdown hangs after our connection to the
   // parent closes.
   StartForceKillTimer();
 
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCShutdownState"),
-                                     NS_LITERAL_CSTRING("SendFinishShutdown (sending)"));
+  CrashReporter::AnnotateCrashReport(
+    CrashReporter::Annotation::IPCShutdownState,
+    NS_LITERAL_CSTRING("SendFinishShutdown (sending)"));
   bool sent = SendFinishShutdown();
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCShutdownState"),
+  CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::IPCShutdownState,
                                      sent ? NS_LITERAL_CSTRING("SendFinishShutdown (sent)")
                                           : NS_LITERAL_CSTRING("SendFinishShutdown (failed)"));
 }
 
 PBrowserOrId
 ContentChild::GetBrowserOrId(TabChild* aTabChild)
 {
   if (!aTabChild ||
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -3365,23 +3365,21 @@ ContentParent::KillHard(const char* aRea
   // of the parent and child for submission to the crash server.
   if (mCrashReporter) {
     // GeneratePairedMinidump creates two minidumps for us - the main
     // one is for the content process we're about to kill, and the other
     // one is for the main browser process. That second one is the extra
     // minidump tagging along, so we have to tell the crash reporter that
     // it exists and is being appended.
     nsAutoCString additionalDumps("browser");
-    mCrashReporter->AddNote(
-      NS_LITERAL_CSTRING("additional_minidumps"),
-      additionalDumps);
+    mCrashReporter->AddAnnotation(
+      CrashReporter::Annotation::additional_minidumps, additionalDumps);
     nsDependentCString reason(aReason);
-    mCrashReporter->AddNote(
-      NS_LITERAL_CSTRING("ipc_channel_error"),
-      reason);
+    mCrashReporter->AddAnnotation(CrashReporter::Annotation::ipc_channel_error,
+                                  reason);
 
     Telemetry::Accumulate(Telemetry::SUBPROCESS_KILL_HARD, reason, 1);
 
     RefPtr<ContentParent> self = this;
     std::function<void(bool)> callback = [self](bool aResult) {
       self->OnGenerateMinidumpComplete(aResult);
     };
     // Generate the report and insert into the queue for submittal.
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1158,17 +1158,17 @@ TabChild::RecvLoadURL(const nsCString& a
     WebNavigation()->LoadURI(NS_ConvertUTF8toUTF16(aURI).get(),
                              nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
                              nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL,
                              nullptr, nullptr, nullptr, nsContentUtils::GetSystemPrincipal());
   if (NS_FAILED(rv)) {
       NS_WARNING("WebNavigation()->LoadURI failed. Eating exception, what else can I do?");
   }
 
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("URL"), aURI);
+  CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::URL, aURI);
 
   return IPC_OK();
 }
 
 void
 TabChild::DoFakeShow(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
                      const layers::LayersId& aLayersId,
                      const CompositorOptions& aCompositorOptions,
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -555,18 +555,19 @@ ToCString(const nsTArray<Pair<nsCString,
 
 mozilla::ipc::IPCResult
 GMPChild::AnswerStartPlugin(const nsString& aAdapter)
 {
   LOGD("%s", __FUNCTION__);
 
   nsCString libPath;
   if (!GetUTF8LibPath(libPath)) {
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("GMPLibraryPath"),
-                                       NS_ConvertUTF16toUTF8(mPluginPath));
+    CrashReporter::AnnotateCrashReport(
+      CrashReporter::Annotation::GMPLibraryPath,
+      NS_ConvertUTF16toUTF8(mPluginPath));
 
 #ifdef XP_WIN
     return IPC_FAIL(
       this,
       nsPrintfCString("Failed to get lib path with error(%d).", GetLastError())
         .get());
 #else
     return IPC_FAIL(
@@ -610,18 +611,19 @@ GMPChild::AnswerStartPlugin(const nsStri
   }
 
   if (!mGMPLoader->Load(libPath.get(),
                         libPath.Length(),
                         platformAPI,
                         adapter)) {
     NS_WARNING("Failed to load GMP");
     delete platformAPI;
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("GMPLibraryPath"),
-                                       NS_ConvertUTF16toUTF8(mPluginPath));
+    CrashReporter::AnnotateCrashReport(
+      CrashReporter::Annotation::GMPLibraryPath,
+      NS_ConvertUTF16toUTF8(mPluginPath));
 
 #ifdef XP_WIN
     return IPC_FAIL(
       this,
       nsPrintfCString("Failed to load GMP with error(%d).", GetLastError())
         .get());
 #else
     return IPC_FAIL(
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -458,20 +458,23 @@ GMPParent::EnsureProcessLoaded()
   nsresult rv = LoadProcess();
 
   return NS_SUCCEEDED(rv);
 }
 
 void
 GMPParent::WriteExtraDataForMinidump()
 {
-  mCrashReporter->AddNote(NS_LITERAL_CSTRING("GMPPlugin"), NS_LITERAL_CSTRING("1"));
-  mCrashReporter->AddNote(NS_LITERAL_CSTRING("PluginFilename"), NS_ConvertUTF16toUTF8(mName));
-  mCrashReporter->AddNote(NS_LITERAL_CSTRING("PluginName"), mDisplayName);
-  mCrashReporter->AddNote(NS_LITERAL_CSTRING("PluginVersion"), mVersion);
+  mCrashReporter->AddAnnotation(CrashReporter::Annotation::GMPPlugin, true);
+  mCrashReporter->AddAnnotation(CrashReporter::Annotation::PluginFilename,
+                                NS_ConvertUTF16toUTF8(mName));
+  mCrashReporter->AddAnnotation(CrashReporter::Annotation::PluginName,
+                                mDisplayName);
+  mCrashReporter->AddAnnotation(CrashReporter::Annotation::PluginVersion,
+                                mVersion);
 }
 
 bool
 GMPParent::GetCrashID(nsString& aResult)
 {
   if (!mCrashReporter) {
     return false;
   }
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -6,17 +6,16 @@
 /* nsPluginHost.cpp - top-level plugin management code */
 
 #include "nscore.h"
 #include "nsPluginHost.h"
 
 #include <cstdlib>
 #include <stdio.h>
 #include "prio.h"
-#include "nsExceptionHandler.h"
 #include "nsNPAPIPlugin.h"
 #include "nsNPAPIPluginStreamListener.h"
 #include "nsNPAPIPluginInstance.h"
 #include "nsPluginInstanceOwner.h"
 #include "nsObjectLoadingContent.h"
 #include "nsIHTTPHeaderListener.h"
 #include "nsIHttpHeaderVisitor.h"
 #include "nsIObserverService.h"
--- a/dom/plugins/base/nsPluginsDirDarwin.cpp
+++ b/dom/plugins/base/nsPluginsDirDarwin.cpp
@@ -405,53 +405,24 @@ nsresult nsPluginFile::GetPluginInfo(nsP
   // First look for data in a bundle plist
   if (bundle) {
     ParsePlistPluginInfo(info, bundle);
     ::CFRelease(bundle);
     if (info.fVariantCount > 0)
       return NS_OK;
   }
 
-  // Don't load "fbplugin" or any plugins whose name starts with "fbplugin_"
-  // (Facebook plugins) if we're running on OS X 10.10 (Yosemite) or later.
-  // A "fbplugin" file crashes on load, in the call to LoadPlugin() below.
-  // See bug 1086977.
-  if (nsCocoaFeatures::OnYosemiteOrLater()) {
-    if (fileName.EqualsLiteral("fbplugin") ||
-        StringBeginsWith(fileName, NS_LITERAL_CSTRING("fbplugin_"))) {
-      nsAutoCString msg;
-      msg.AppendPrintf("Preventing load of %s (see bug 1086977)",
-                       fileName.get());
-      NS_WARNING(msg.get());
-      return NS_ERROR_FAILURE;
-    }
-
-    // The block above assumes that "fbplugin" is the filename of the plugin
-    // to be blocked, or that the filename starts with "fbplugin_".  But we
-    // don't yet know for sure if this is always true.  So for the time being
-    // record extra information in our crash logs.
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Bug_1086977"),
-                                       fileName);
-  }
-
   // It's possible that our plugin has 2 entry points that'll give us mime type
   // info. Quicktime does this to get around the need of having admin rights to
   // change mime info in the resource fork. We need to use this info instead of
   // the resource. See bug 113464.
 
   // Sadly we have to load the library for this to work.
   rv = LoadPlugin(outLibrary);
 
-  if (nsCocoaFeatures::OnYosemiteOrLater()) {
-    // If we didn't crash in LoadPlugin(), change the previous annotation so we
-    // don't sow confusion.
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Bug_1086977"),
-                                       NS_LITERAL_CSTRING("Didn't crash, please ignore"));
-  }
-
   if (NS_FAILED(rv))
     return rv;
 
   // Try to get data from NP_GetMIMEDescription
   if (pLibrary) {
     NP_GETMIMEDESCRIPTION pfnGetMimeDesc = (NP_GETMIMEDESCRIPTION)PR_FindFunctionSymbol(pLibrary, NP_GETMIMEDESCRIPTION_NAME);
     if (pfnGetMimeDesc)
       ParsePluginMimeDescription(pfnGetMimeDesc(), info);
--- a/dom/plugins/ipc/PluginMessageUtils.h
+++ b/dom/plugins/ipc/PluginMessageUtils.h
@@ -18,17 +18,16 @@
 #include "gfxipc/ShadowLayerUtils.h"
 
 #include "npapi.h"
 #include "npruntime.h"
 #include "npfunctions.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "mozilla/Logging.h"
-#include "nsExceptionHandler.h"
 #include "nsHashKeys.h"
 
 #ifdef XP_MACOSX
 #include "PluginInterposeOSX.h"
 #else
 namespace mac_plugin_interposing { class NSCursorInfo { }; }
 #endif
 using mac_plugin_interposing::NSCursorInfo;
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -733,36 +733,46 @@ PluginModuleChromeParent::WriteExtraData
 
     // Get the plugin filename, try to get just the file leafname
     const std::string& pluginFile = mSubprocess->GetPluginFilePath();
     size_t filePos = pluginFile.rfind(FILE_PATH_SEPARATOR);
     if (filePos == std::string::npos)
         filePos = 0;
     else
         filePos++;
-    mCrashReporter->AddNote(NS_LITERAL_CSTRING("PluginFilename"), cstring(pluginFile.substr(filePos).c_str()));
-
-    mCrashReporter->AddNote(NS_LITERAL_CSTRING("PluginName"), mPluginName);
-    mCrashReporter->AddNote(NS_LITERAL_CSTRING("PluginVersion"), mPluginVersion);
+    mCrashReporter->AddAnnotation(CrashReporter::Annotation::PluginFilename,
+                                  cstring(pluginFile.substr(filePos).c_str()));
+    mCrashReporter->AddAnnotation(CrashReporter::Annotation::PluginName,
+                                  mPluginName);
+    mCrashReporter->AddAnnotation(CrashReporter::Annotation::PluginVersion,
+                                  mPluginVersion);
 
     if (mCrashReporter) {
 #ifdef XP_WIN
         if (mPluginCpuUsageOnHang.Length() > 0) {
-            mCrashReporter->AddNote(NS_LITERAL_CSTRING("NumberOfProcessors"),
-                                    nsPrintfCString("%d", PR_GetNumberOfProcessors()));
+            mCrashReporter->AddAnnotation(
+              CrashReporter::Annotation::NumberOfProcessors,
+              PR_GetNumberOfProcessors());
 
             nsCString cpuUsageStr;
             cpuUsageStr.AppendFloat(std::ceil(mPluginCpuUsageOnHang[0] * 100) / 100);
-            mCrashReporter->AddNote(NS_LITERAL_CSTRING("PluginCpuUsage"), cpuUsageStr);
+            mCrashReporter->AddAnnotation(
+              CrashReporter::Annotation::PluginCpuUsage,
+              cpuUsageStr);
 
 #ifdef MOZ_CRASHREPORTER_INJECTOR
-            for (uint32_t i=1; i<mPluginCpuUsageOnHang.Length(); ++i) {
+            for (uint32_t i = 1; i < mPluginCpuUsageOnHang.Length(); ++i) {
                 nsCString tempStr;
                 tempStr.AppendFloat(std::ceil(mPluginCpuUsageOnHang[i] * 100) / 100);
-                mCrashReporter->AddNote(nsPrintfCString("CpuUsageFlashProcess%d", i), tempStr);
+                // HACK: There can only be at most two flash processes hence
+                // the hardcoded annotations
+                CrashReporter::Annotation annotation =
+                  (i == 1) ? CrashReporter::Annotation::CpuUsageFlashProcess1
+                           : CrashReporter::Annotation::CpuUsageFlashProcess2;
+                mCrashReporter->AddAnnotation(annotation, tempStr);
             }
 #endif
         }
 #endif
     }
 }
 
 void
@@ -1239,18 +1249,18 @@ PluginModuleChromeParent::OnTakeFullMini
                 // Include the content process minidump
                 if (CreatePluginMinidump(aContentPid, 0,
                                          pluginDumpFile,
                                          NS_LITERAL_CSTRING("content"))) {
                     additionalDumps.AppendLiteral(",content");
                 }
             }
         }
-        mCrashReporter->AddNote(NS_LITERAL_CSTRING("additional_minidumps"),
-                                additionalDumps);
+        mCrashReporter->AddAnnotation(Annotation::additional_minidumps,
+                                      additionalDumps);
 
         mTakeFullMinidumpCallback.Invoke(mCrashReporter->MinidumpID());
     } else {
         mTakeFullMinidumpCallback.Invoke(EmptyString());
         NS_WARNING("failed to capture paired minidumps from hang");
     }
 }
 
@@ -1301,28 +1311,25 @@ PluginModuleChromeParent::TerminateChild
     mCrashReporterMutex.AssertCurrentThreadIn();
 
     if (!mCrashReporter) {
         // If mCrashReporter is null then the hang has ended, the plugin module
         // is shutting down. There's nothing to do here.
         mTerminateChildProcessCallback.Invoke(true);
         return;
     }
-    mCrashReporter->AddNote(NS_LITERAL_CSTRING("PluginHang"),
-                            NS_LITERAL_CSTRING("1"));
-    mCrashReporter->AddNote(NS_LITERAL_CSTRING("HangMonitorDescription"),
-                            aMonitorDescription);
+    mCrashReporter->AddAnnotation(Annotation::PluginHang, true);
+    mCrashReporter->AddAnnotation(Annotation::HangMonitorDescription,
+                                  aMonitorDescription);
 #ifdef XP_WIN
     if (mHangUIParent) {
         unsigned int hangUIDuration = mHangUIParent->LastShowDurationMs();
         if (hangUIDuration) {
-            nsPrintfCString strHangUIDuration("%u", hangUIDuration);
-            mCrashReporter->AddNote(
-                    NS_LITERAL_CSTRING("PluginHangUIDuration"),
-                    strHangUIDuration);
+            mCrashReporter->AddAnnotation(Annotation::PluginHangUIDuration,
+                                          hangUIDuration);
         }
     }
 #endif // XP_WIN
 
     mozilla::ipc::ScopedProcessHandle geckoChildProcess;
     bool childOpened = base::OpenProcessHandle(OtherPid(),
                                                &geckoChildProcess.rwget());
 
@@ -1578,17 +1585,18 @@ PluginModuleChromeParent::ProcessFirstMi
         NS_WARNING("[PluginModuleParent::ActorDestroy] abnormal shutdown without minidump!");
         return;
     }
 
     PLUGIN_LOG_DEBUG(("got child minidump: %s",
                       NS_ConvertUTF16toUTF8(mCrashReporter->MinidumpID()).get()));
 
     if (!flashProcessType.IsEmpty()) {
-        mCrashReporter->AddNote(NS_LITERAL_CSTRING("FlashProcessDump"), flashProcessType);
+        mCrashReporter->AddAnnotation(Annotation::FlashProcessDump,
+                                      flashProcessType);
     }
     mCrashReporter->FinalizeCrashReport();
 }
 
 void
 PluginModuleParent::ActorDestroy(ActorDestroyReason why)
 {
     switch (why) {
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -375,22 +375,21 @@ GPUProcessManager::OnProcessLaunchComple
     DisableGPUProcess("Failed to create PVsyncBridge endpoints");
     return;
   }
 
   mVsyncBridge = VsyncBridgeChild::Create(mVsyncIOThread, mProcessToken, std::move(vsyncChild));
   mGPUChild->SendInitVsyncBridge(std::move(vsyncParent));
 
   CrashReporter::AnnotateCrashReport(
-    NS_LITERAL_CSTRING("GPUProcessStatus"),
-    NS_LITERAL_CSTRING("Running"));
+    CrashReporter::Annotation::GPUProcessStatus, NS_LITERAL_CSTRING("Running"));
 
   CrashReporter::AnnotateCrashReport(
-    NS_LITERAL_CSTRING("GPUProcessLaunchCount"),
-    nsPrintfCString("%d", mNumProcessAttempts));
+    CrashReporter::Annotation::GPUProcessLaunchCount,
+    static_cast<int>(mNumProcessAttempts));
 }
 
 static bool
 ShouldLimitDeviceResets(uint32_t count, int32_t deltaMilliseconds)
 {
   // We decide to limit by comparing the amount of resets that have happened
   // and time since the last reset to two prefs.
   int32_t timeLimit = gfxPrefs::DeviceResetThresholdMilliseconds();
@@ -722,17 +721,17 @@ GPUProcessManager::DestroyProcess()
   mProcess = nullptr;
   mGPUChild = nullptr;
   if (mVsyncBridge) {
     mVsyncBridge->Close();
     mVsyncBridge = nullptr;
   }
 
   CrashReporter::AnnotateCrashReport(
-    NS_LITERAL_CSTRING("GPUProcessStatus"),
+    CrashReporter::Annotation::GPUProcessStatus,
     NS_LITERAL_CSTRING("Destroyed"));
 }
 
 already_AddRefed<CompositorSession>
 GPUProcessManager::CreateTopLevelCompositor(nsBaseWidget* aWidget,
                                             LayerManager* aLayerManager,
                                             CSSToLayoutDeviceScale aScale,
                                             const CompositorOptions& aOptions,
--- a/gfx/src/DriverCrashGuard.cpp
+++ b/gfx/src/DriverCrashGuard.cpp
@@ -160,19 +160,18 @@ DriverCrashGuard::~DriverCrashGuard()
     // proceed to mark the status as okay.
     if (GetStatus() != DriverInitStatus::Crashed) {
       SetStatus(DriverInitStatus::Okay);
     }
   } else {
     dom::ContentChild::GetSingleton()->SendEndDriverCrashGuard(uint32_t(mType));
   }
 
-  // Remove the crash report annotation.
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("GraphicsStartupTest"),
-                                     NS_LITERAL_CSTRING(""));
+  CrashReporter::RemoveCrashReportAnnotation(
+    CrashReporter::Annotation::GraphicsStartupTest);
 }
 
 bool
 DriverCrashGuard::Crashed()
 {
   InitializeIfNeeded();
 
   // Note, we read mCrashDetected instead of GetStatus(), since in child
@@ -205,18 +204,18 @@ void
 DriverCrashGuard::ActivateGuard()
 {
   mGuardActivated = true;
 
   // Anotate crash reports only if we're a real guard. Otherwise, we could
   // attribute a random parent process crash to a graphics problem in a child
   // process.
   if (mMode != Mode::Proxy) {
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("GraphicsStartupTest"),
-                                       NS_LITERAL_CSTRING("1"));
+    CrashReporter::AnnotateCrashReport(
+      CrashReporter::Annotation::GraphicsStartupTest, true);
   }
 
   // If we're in the content process, the rest of the guarding is handled
   // in the parent.
   if (XRE_IsContentProcess()) {
     return;
   }
 
--- a/gfx/thebes/DeviceManagerDx.cpp
+++ b/gfx/thebes/DeviceManagerDx.cpp
@@ -880,20 +880,18 @@ DeviceManagerDx::MaybeResetAndReacquireD
   if (!HasDeviceReset(&resetReason)) {
     return false;
   }
 
   if (resetReason != DeviceResetReason::FORCED_RESET) {
     Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON, uint32_t(resetReason));
   }
 
-  nsPrintfCString reasonString("%d", int(resetReason));
   CrashReporter::AnnotateCrashReport(
-    NS_LITERAL_CSTRING("DeviceResetReason"),
-    reasonString);
+    CrashReporter::Annotation::DeviceResetReason, int(resetReason));
 
   bool createCompositorDevice = !!mCompositorDevice;
   bool createContentDevice = !!mContentDevice;
 
   ResetDevices();
 
   if (createCompositorDevice && !CreateCompositorDevices()) {
     // Just stop, don't try anything more
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -194,38 +194,38 @@ public:
 /// if the capacity set with the method below is >= 2.  We always retain the
 /// very first critical message, and the latest capacity-1 messages are
 /// rotated through. Note that we don't expect the total number of times
 /// this gets called to be large - it is meant for critical errors only.
 
 class CrashStatsLogForwarder: public mozilla::gfx::LogForwarder
 {
 public:
-  explicit CrashStatsLogForwarder(const char* aKey);
+  explicit CrashStatsLogForwarder(CrashReporter::Annotation aKey);
   void Log(const std::string& aString) override;
   void CrashAction(LogReason aReason) override;
   bool UpdateStringsVector(const std::string& aString) override;
 
   LoggingRecord LoggingRecordCopy() override;
 
   void SetCircularBufferSize(uint32_t aCapacity);
 
 private:
   // Helper for the Log()
   void UpdateCrashReport();
 
 private:
   LoggingRecord mBuffer;
-  nsCString mCrashCriticalKey;
+  CrashReporter::Annotation mCrashCriticalKey;
   uint32_t mMaxCapacity;
   int32_t mIndex;
   Mutex mMutex;
 };
 
-CrashStatsLogForwarder::CrashStatsLogForwarder(const char* aKey)
+CrashStatsLogForwarder::CrashStatsLogForwarder(CrashReporter::Annotation aKey)
   : mBuffer()
   , mCrashCriticalKey(aKey)
   , mMaxCapacity(0)
   , mIndex(-1)
   , mMutex("CrashStatsLogForwarder")
 {
 }
 
@@ -293,21 +293,23 @@ void CrashStatsLogForwarder::UpdateCrash
     break;
   }
 
   for (auto& it : mBuffer) {
     message << logAnnotation << Get<0>(it) << "]" << Get<1>(it) << " (t=" << Get<2>(it) << ") ";
   }
 
   nsCString reportString(message.str().c_str());
-  nsresult annotated = CrashReporter::AnnotateCrashReport(mCrashCriticalKey, reportString);
+  nsresult annotated = CrashReporter::AnnotateCrashReport(mCrashCriticalKey,
+                                                          reportString);
 
   if (annotated != NS_OK) {
     printf("Crash Annotation %s: %s",
-           mCrashCriticalKey.get(), message.str().c_str());
+           CrashReporter::AnnotationToString(mCrashCriticalKey),
+           message.str().c_str());
   }
 }
 
 class LogForwarderEvent : public Runnable
 {
   ~LogForwarderEvent() override = default;
 
 public:
@@ -917,17 +919,18 @@ gfxPlatform::MaxAllocSize()
   // pref or whatnot.
   const int32_t kMinAllocPref = 10000000;
   return std::max(kMinAllocPref, gfxPrefs::MaxAllocSizeDoNotUseDirectly());
 }
 
 /* static */ void
 gfxPlatform::InitMoz2DLogging()
 {
-  auto fwd = new CrashStatsLogForwarder("GraphicsCriticalError");
+  auto fwd = new CrashStatsLogForwarder(
+    CrashReporter::Annotation::GraphicsCriticalError);
   fwd->SetCircularBufferSize(gfxPrefs::GfxLoggingCrashLength());
 
   mozilla::gfx::Config cfg;
   cfg.mLogForwarder = fwd;
   cfg.mMaxTextureSize = gfxPlatform::MaxTextureSize();
   cfg.mMaxAllocSize = gfxPlatform::MaxAllocSize();
 
   gfx::Factory::Init(cfg);
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
@@ -243,31 +243,34 @@ bool Channel::ChannelImpl::CreatePipe(co
                                       Mode mode) {
   DCHECK(server_listen_pipe_ == -1 && pipe_ == -1);
 
   // socketpair()
   pipe_name_ = WideToASCII(channel_id);
   if (mode == MODE_SERVER) {
     int pipe_fds[2];
     if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds) != 0) {
-      mozilla::ipc::AnnotateCrashReportWithErrno("IpcCreatePipeSocketPairErrno", errno);
+      mozilla::ipc::AnnotateCrashReportWithErrno(
+        CrashReporter::Annotation::IpcCreatePipeSocketPairErrno, errno);
       return false;
     }
     // Set both ends to be non-blocking.
     if (fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK) == -1 ||
         fcntl(pipe_fds[1], F_SETFL, O_NONBLOCK) == -1) {
-      mozilla::ipc::AnnotateCrashReportWithErrno("IpcCreatePipeFcntlErrno", errno);
+      mozilla::ipc::AnnotateCrashReportWithErrno(
+        CrashReporter::Annotation::IpcCreatePipeFcntlErrno, errno);
       IGNORE_EINTR(close(pipe_fds[0]));
       IGNORE_EINTR(close(pipe_fds[1]));
       return false;
     }
 
     if (!SetCloseOnExec(pipe_fds[0]) ||
         !SetCloseOnExec(pipe_fds[1])) {
-      mozilla::ipc::AnnotateCrashReportWithErrno("IpcCreatePipeCloExecErrno", errno);
+      mozilla::ipc::AnnotateCrashReportWithErrno(
+        CrashReporter::Annotation::IpcCreatePipeCloExecErrno, errno);
       IGNORE_EINTR(close(pipe_fds[0]));
       IGNORE_EINTR(close(pipe_fds[1]));
       return false;
     }
 
     pipe_ = pipe_fds[0];
     client_pipe_ = pipe_fds[1];
 
--- a/ipc/glue/CrashReporterClient.cpp
+++ b/ipc/glue/CrashReporterClient.cpp
@@ -21,17 +21,18 @@ CrashReporterClient::CrashReporterClient
 }
 
 CrashReporterClient::~CrashReporterClient()
 {
   MOZ_COUNT_DTOR(CrashReporterClient);
 }
 
 void
-CrashReporterClient::AnnotateCrashReport(const nsCString& aKey, const nsCString& aData)
+CrashReporterClient::AnnotateCrashReport(CrashReporter::Annotation aKey,
+                                         const nsCString& aData)
 {
   StaticMutexAutoLock lock(sLock);
   mMetadata->AnnotateCrashReport(aKey, aData);
 }
 
 void
 CrashReporterClient::AppendAppNotes(const nsCString& aData)
 {
--- a/ipc/glue/CrashReporterClient.h
+++ b/ipc/glue/CrashReporterClient.h
@@ -60,17 +60,18 @@ public:
       aOutShmem);
   }
 
   static void InitSingletonWithShmem(const Shmem& aShmem);
 
   static void DestroySingleton();
   static RefPtr<CrashReporterClient> GetSingleton();
 
-  void AnnotateCrashReport(const nsCString& aKey, const nsCString& aData);
+  void AnnotateCrashReport(CrashReporter::Annotation aKey,
+                           const nsCString& aData);
   void AppendAppNotes(const nsCString& aData);
 
 private:
   explicit CrashReporterClient(const Shmem& aShmem);
   ~CrashReporterClient();
 
 private:
   static StaticMutex sLock;
--- a/ipc/glue/CrashReporterHost.cpp
+++ b/ipc/glue/CrashReporterHost.cpp
@@ -60,17 +60,17 @@ CrashReporterHost::AdoptMinidump(nsIFile
 }
 
 bool
 CrashReporterHost::FinalizeCrashReport()
 {
   MOZ_ASSERT(!mFinalized);
   MOZ_ASSERT(HasMinidump());
 
-  CrashReporter::AnnotationTable notes;
+  CrashReporter::AnnotationTable annotations;
 
   nsAutoCString type;
   switch (mProcessType) {
     case GeckoProcessType_Content:
       type = NS_LITERAL_CSTRING("content");
       break;
     case GeckoProcessType_Plugin:
     case GeckoProcessType_GMPlugin:
@@ -78,32 +78,33 @@ CrashReporterHost::FinalizeCrashReport()
       break;
     case GeckoProcessType_GPU:
       type = NS_LITERAL_CSTRING("gpu");
       break;
     default:
       NS_ERROR("unknown process type");
       break;
   }
-  notes.Put(NS_LITERAL_CSTRING("ProcessType"), type);
+  annotations[CrashReporter::Annotation::ProcessType] = type;
 
   char startTime[32];
   SprintfLiteral(startTime, "%lld", static_cast<long long>(mStartTime));
-  notes.Put(NS_LITERAL_CSTRING("StartupTime"), nsDependentCString(startTime));
+  annotations[CrashReporter::Annotation::StartupTime] =
+    nsDependentCString(startTime);
 
   // We might not have shmem (for example, when running crashreporter tests).
   if (mShmem.IsReadable()) {
-    CrashReporterMetadataShmem::ReadAppNotes(mShmem, &notes);
+    CrashReporterMetadataShmem::ReadAppNotes(mShmem, annotations);
   }
-  CrashReporter::AppendExtraData(mDumpID, mExtraNotes);
-  CrashReporter::AppendExtraData(mDumpID, notes);
+  CrashReporter::AppendExtraData(mDumpID, mExtraAnnotations);
+  CrashReporter::AppendExtraData(mDumpID, annotations);
 
-  // Use mExtraNotes, since NotifyCrashService looks for "PluginHang" which is
-  // set in the parent process.
-  NotifyCrashService(mProcessType, mDumpID, &mExtraNotes);
+  // Use mExtraAnnotations, since NotifyCrashService looks for "PluginHang"
+  // which is set in the parent process.
+  NotifyCrashService(mProcessType, mDumpID, mExtraAnnotations);
 
   mFinalized = true;
   return true;
 }
 
 namespace {
 class GenerateMinidumpShutdownBlocker : public nsIAsyncShutdownBlocker {
 public:
@@ -216,21 +217,21 @@ CrashReporterHost::GenerateMinidumpAndPa
                                         getter_AddRefs(mTargetDump),
                                         std::move(callback),
                                         aAsync);
 }
 
 /* static */ void
 CrashReporterHost::NotifyCrashService(GeckoProcessType aProcessType,
                                       const nsString& aChildDumpID,
-                                      const AnnotationTable* aNotes)
+                                      const AnnotationTable& aNotes)
 {
   if (!NS_IsMainThread()) {
     RefPtr<Runnable> runnable = NS_NewRunnableFunction(
-      "ipc::CrashReporterHost::NotifyCrashService", [=]() -> void {
+      "ipc::CrashReporterHost::NotifyCrashService", [&]() -> void {
         CrashReporterHost::NotifyCrashService(
           aProcessType, aChildDumpID, aNotes);
       });
     RefPtr<nsIThread> mainThread = do_GetMainThread();
     SyncRunnable::DispatchToThread(mainThread, runnable);
     return;
   }
 
@@ -250,19 +251,18 @@ CrashReporterHost::NotifyCrashService(Ge
   switch (aProcessType) {
     case GeckoProcessType_Content:
       processType = nsICrashService::PROCESS_TYPE_CONTENT;
       telemetryKey.AssignLiteral("content");
       break;
     case GeckoProcessType_Plugin: {
       processType = nsICrashService::PROCESS_TYPE_PLUGIN;
       telemetryKey.AssignLiteral("plugin");
-      nsAutoCString val;
-      if (aNotes->Get(NS_LITERAL_CSTRING("PluginHang"), &val) &&
-        val.EqualsLiteral("1")) {
+      nsCString val = aNotes[CrashReporter::Annotation::PluginHang];
+      if (val.Equals(NS_LITERAL_CSTRING("1"))) {
         crashType = nsICrashService::CRASH_TYPE_HANG;
         telemetryKey.AssignLiteral("pluginhang");
       }
       break;
     }
     case GeckoProcessType_GMPlugin:
       processType = nsICrashService::PROCESS_TYPE_GMPLUGIN;
       telemetryKey.AssignLiteral("gmplugin");
@@ -277,15 +277,41 @@ CrashReporterHost::NotifyCrashService(Ge
   }
 
   RefPtr<Promise> promise;
   crashService->AddCrash(processType, crashType, aChildDumpID, getter_AddRefs(promise));
   Telemetry::Accumulate(Telemetry::SUBPROCESS_CRASHES_WITH_DUMP, telemetryKey, 1);
 }
 
 void
-CrashReporterHost::AddNote(const nsCString& aKey, const nsCString& aValue)
+CrashReporterHost::AddAnnotation(CrashReporter::Annotation aKey, bool aValue)
+{
+  mExtraAnnotations[aKey] = aValue ? NS_LITERAL_CSTRING("1")
+                                   : NS_LITERAL_CSTRING("0");
+}
+
+void
+CrashReporterHost::AddAnnotation(CrashReporter::Annotation aKey,
+                                 int aValue)
 {
-  mExtraNotes.Put(aKey, aValue);
+  nsAutoCString valueString;
+  valueString.AppendInt(aValue);
+  mExtraAnnotations[aKey] = valueString;
+}
+
+void
+CrashReporterHost::AddAnnotation(CrashReporter::Annotation aKey,
+                                 unsigned int aValue)
+{
+  nsAutoCString valueString;
+  valueString.AppendInt(aValue);
+  mExtraAnnotations[aKey] = valueString;
+}
+
+void
+CrashReporterHost::AddAnnotation(CrashReporter::Annotation aKey,
+                                 const nsCString& aValue)
+{
+  mExtraAnnotations[aKey] = aValue;
 }
 
 } // namespace ipc
 } // namespace mozilla
--- a/ipc/glue/CrashReporterHost.h
+++ b/ipc/glue/CrashReporterHost.h
@@ -125,19 +125,22 @@ public:
   // This is a static helper function to notify the crash service that a
   // crash has occurred. When PCrashReporter is removed, we can make this
   // a member function. This can be called from any thread, and if not
   // called from the main thread, will post a synchronous message to the
   // main thread.
   static void NotifyCrashService(
     GeckoProcessType aProcessType,
     const nsString& aChildDumpID,
-    const AnnotationTable* aNotes);
+    const AnnotationTable& aNotes);
 
-  void AddNote(const nsCString& aKey, const nsCString& aValue);
+  void AddAnnotation(CrashReporter::Annotation aKey, bool aValue);
+  void AddAnnotation(CrashReporter::Annotation aKey, int aValue);
+  void AddAnnotation(CrashReporter::Annotation aKey, unsigned int aValue);
+  void AddAnnotation(CrashReporter::Annotation aKey, const nsCString& aValue);
 
   bool HasMinidump() const {
     return !mDumpID.IsEmpty();
   }
   const nsString& MinidumpID() const {
     MOZ_ASSERT(HasMinidump());
     return mDumpID;
   }
@@ -147,17 +150,17 @@ private:
                             const nsString& aChildDumpID);
 
 private:
   CallbackWrapper<bool> mCreateMinidumpCallback;
   GeckoProcessType mProcessType;
   Shmem mShmem;
   ThreadId mThreadId;
   time_t mStartTime;
-  AnnotationTable mExtraNotes;
+  AnnotationTable mExtraAnnotations;
   nsString mDumpID;
   bool mFinalized;
   nsCOMPtr<nsIFile> mTargetDump;
 };
 
 } // namespace ipc
 } // namespace mozilla
 
--- a/ipc/glue/CrashReporterMetadataShmem.cpp
+++ b/ipc/glue/CrashReporterMetadataShmem.cpp
@@ -1,21 +1,24 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CrashReporterMetadataShmem.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/EnumeratedRange.h"
 #include "nsISupportsImpl.h"
 
 namespace mozilla {
 namespace ipc {
 
+using CrashReporter::Annotation;
+
 enum class EntryType : uint8_t {
   None,
   Annotation,
 };
 
 CrashReporterMetadataShmem::CrashReporterMetadataShmem(const Shmem& aShmem)
  : mShmem(aShmem)
 {
@@ -23,41 +26,42 @@ CrashReporterMetadataShmem::CrashReporte
 }
 
 CrashReporterMetadataShmem::~CrashReporterMetadataShmem()
 {
   MOZ_COUNT_DTOR(CrashReporterMetadataShmem);
 }
 
 void
-CrashReporterMetadataShmem::AnnotateCrashReport(const nsCString& aKey, const nsCString& aData)
+CrashReporterMetadataShmem::AnnotateCrashReport(Annotation aKey,
+                                                const nsCString& aData)
 {
-  mNotes.Put(aKey, aData);
+  mAnnotations[aKey] = aData;
   SyncNotesToShmem();
 }
 
 void
 CrashReporterMetadataShmem::AppendAppNotes(const nsCString& aData)
 {
   mAppNotes.Append(aData);
-  mNotes.Put(NS_LITERAL_CSTRING("Notes"), mAppNotes);
+  mAnnotations[Annotation::Notes] = mAppNotes;
   SyncNotesToShmem();
 }
 
 class MOZ_STACK_CLASS MetadataShmemWriter
 {
 public:
   explicit MetadataShmemWriter(const Shmem& aShmem)
    : mCursor(aShmem.get<uint8_t>()),
      mEnd(mCursor + aShmem.Size<uint8_t>())
   {
     *mCursor = uint8_t(EntryType::None);
   }
 
-  MOZ_MUST_USE bool WriteAnnotation(const nsCString& aKey, const nsCString& aValue) {
+  MOZ_MUST_USE bool WriteAnnotation(Annotation aKey, const nsCString& aValue) {
     // This shouldn't happen because Commit() guarantees mCursor < mEnd. But
     // we might as well be safe.
     if (mCursor >= mEnd) {
       return false;
     }
 
     // Save the current position so we can write the entry type if the entire
     // entry fits.
@@ -120,21 +124,21 @@ private:
   uint8_t* mEnd;
 };
 
 void
 CrashReporterMetadataShmem::SyncNotesToShmem()
 {
   MetadataShmemWriter writer(mShmem);
 
-  for (auto it = mNotes.Iter(); !it.Done(); it.Next()) {
-    nsCString key = nsCString(it.Key());
-    nsCString value = nsCString(it.Data());
-    if (!writer.WriteAnnotation(key, value)) {
-      return;
+  for (auto key : MakeEnumeratedRange(Annotation::Count)) {
+    if (!mAnnotations[key].IsEmpty()) {
+      if (!writer.WriteAnnotation(key, mAnnotations[key])) {
+        return;
+      }
     }
   }
 }
 
 // Helper class to iterate over metadata entries encoded in shmem.
 class MOZ_STACK_CLASS MetadataShmemReader
 {
 public:
@@ -157,36 +161,37 @@ public:
   void Next() {
     if (mCursor < mEnd) {
       mEntryType = EntryType(*mCursor++);
     } else {
       mEntryType = EntryType::None;
     }
   }
 
+  template <typename T>
+  bool Read(T* aOut) {
+    return Read(aOut, sizeof(T));
+  }
+
   bool Read(nsCString& aOut) {
     uint32_t length = 0;
     if (!Read(&length)) {
       return false;
     }
 
     const uint8_t* src = Read(length);
     if (!src) {
       return false;
     }
 
     aOut.Assign((const char *)src, length);
     return true;
   }
 
 private:
-  template <typename T>
-  bool Read(T* aOut) {
-    return Read(aOut, sizeof(T));
-  }
   bool Read(void* aOut, size_t aLength) {
     const uint8_t* src = Read(aLength);
     if (!src) {
       return false;
     }
     memcpy(aOut, src, aLength);
     return true;
   }
@@ -204,27 +209,29 @@ private:
 
 private:
   const uint8_t* mCursor;
   const uint8_t* mEnd;
   EntryType mEntryType;
 };
 
 void
-CrashReporterMetadataShmem::ReadAppNotes(const Shmem& aShmem, CrashReporter::AnnotationTable* aNotes)
+CrashReporterMetadataShmem::ReadAppNotes(const Shmem& aShmem,
+                                         AnnotationTable& aNotes)
 {
   for (MetadataShmemReader reader(aShmem); !reader.Done(); reader.Next()) {
     switch (reader.Type()) {
       case EntryType::Annotation: {
-        nsCString key, value;
-        if (!reader.Read(key) || !reader.Read(value)) {
+        Annotation key;
+        nsCString value;
+        if (!reader.Read(&key) || !reader.Read(value)) {
           return;
         }
 
-        aNotes->Put(key, value);
+        aNotes[key] = value;
         break;
       }
       default:
         NS_ASSERTION(false, "Unknown metadata entry type");
         break;
     }
   }
 }
--- a/ipc/glue/CrashReporterMetadataShmem.h
+++ b/ipc/glue/CrashReporterMetadataShmem.h
@@ -20,27 +20,29 @@ class CrashReporterMetadataShmem
   typedef mozilla::ipc::Shmem Shmem;
   typedef CrashReporter::AnnotationTable AnnotationTable;
 
 public:
   explicit CrashReporterMetadataShmem(const Shmem& aShmem);
   ~CrashReporterMetadataShmem();
 
   // Metadata writers. These must only be called in child processes.
-  void AnnotateCrashReport(const nsCString& aKey, const nsCString& aData);
+  void AnnotateCrashReport(CrashReporter::Annotation aKey,
+                           const nsCString& aData);
   void AppendAppNotes(const nsCString& aData);
 
-  static void ReadAppNotes(const Shmem& aShmem, CrashReporter::AnnotationTable* aNotes);
+  static void ReadAppNotes(const Shmem& aShmem,
+                           CrashReporter::AnnotationTable& aNotes);
 
 private:
   void SyncNotesToShmem();
 
 private:
   Shmem mShmem;
 
-  AnnotationTable mNotes;
+  AnnotationTable mAnnotations;
   nsCString mAppNotes;
 };
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_CrashReporterMetadataShmem_h
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -132,22 +132,24 @@ struct EnumSerializer {
   static void Write(Message* aMsg, const paramType& aValue) {
     MOZ_RELEASE_ASSERT(EnumValidator::IsLegalValue(aValue));
     WriteParam(aMsg, uintParamType(aValue));
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) {
     uintParamType value;
     if (!ReadParam(aMsg, aIter, &value)) {
-      CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCReadErrorReason"),
-                                         NS_LITERAL_CSTRING("Bad iter"));
+      CrashReporter::AnnotateCrashReport(
+        CrashReporter::Annotation::IPCReadErrorReason,
+        NS_LITERAL_CSTRING("Bad iter"));
       return false;
     } else if (!EnumValidator::IsLegalValue(paramType(value))) {
-      CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCReadErrorReason"),
-                                         NS_LITERAL_CSTRING("Illegal value"));
+      CrashReporter::AnnotateCrashReport(
+        CrashReporter::Annotation::IPCReadErrorReason,
+        NS_LITERAL_CSTRING("Illegal value"));
       return false;
     }
     *aResult = paramType(value);
     return true;
   }
 };
 
 template <typename E,
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -769,19 +769,18 @@ MessageChannel::CanSend() const
     MonitorAutoLock lock(*mMonitor);
     return Connected();
 }
 
 void
 MessageChannel::WillDestroyCurrentMessageLoop()
 {
 #if defined(DEBUG)
-    CrashReporter::AnnotateCrashReport(
-        NS_LITERAL_CSTRING("IPCFatalErrorProtocol"),
-        nsDependentCString(mName));
+    CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::IPCFatalErrorProtocol,
+                                       nsDependentCString(mName));
     MOZ_CRASH("MessageLoop destroyed before MessageChannel that's bound to it");
 #endif
 
     // Clear mWorkerThread to avoid posting to it in the future.
     MonitorAutoLock lock(*mMonitor);
     mWorkerLoop = nullptr;
 }
 
@@ -799,18 +798,17 @@ MessageChannel::Clear()
     // before mListener.  But just to be safe, mListener is a weak pointer.
 
 #if !defined(ANDROID)
     // KillHard shutdowns can occur with the channel in connected state. We are
     // already collecting crash dump data about KillHard shutdowns and we
     // shouldn't intentionally crash here.
     if (!Unsound_IsClosed() && !mInKillHardShutdown) {
         CrashReporter::AnnotateCrashReport(
-            NS_LITERAL_CSTRING("IPCFatalErrorProtocol"),
-            nsDependentCString(mName));
+          CrashReporter::Annotation::IPCFatalErrorProtocol, nsDependentCString(mName));
         switch (mChannelState) {
             case ChannelOpening:
                 MOZ_CRASH("MessageChannel destroyed without being closed " \
                           "(mChannelState == ChannelOpening).");
                 break;
             case ChannelConnected:
                 MOZ_CRASH("MessageChannel destroyed without being closed " \
                           "(mChannelState == ChannelConnected).");
--- a/ipc/glue/MessageLink.cpp
+++ b/ipc/glue/MessageLink.cpp
@@ -154,18 +154,22 @@ ProcessLink::EchoMessage(Message *msg)
                                            msg));
     // OnEchoMessage takes ownership of |msg|
 }
 
 void
 ProcessLink::SendMessage(Message *msg)
 {
     if (msg->size() > IPC::Channel::kMaximumMessageSize) {
-      CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCMessageName"), nsDependentCString(msg->name()));
-      CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCMessageSize"), nsPrintfCString("%d", msg->size()));
+      CrashReporter::AnnotateCrashReport(
+        CrashReporter::Annotation::IPCMessageName,
+        nsDependentCString(msg->name()));
+      CrashReporter::AnnotateCrashReport(
+        CrashReporter::Annotation::IPCMessageSize,
+        static_cast<int>(msg->size()));
       MOZ_CRASH("IPC message size is too large");
     }
 
     if (!mChan->mIsPostponingSends) {
         mChan->AssertWorkerThread();
     }
     mChan->mMonitor->AssertCurrentThreadOwns();
 
--- a/ipc/glue/ProtocolUtils.cpp
+++ b/ipc/glue/ProtocolUtils.cpp
@@ -206,17 +206,17 @@ bool DuplicateHandle(HANDLE aSourceHandl
 #endif
 
   // Finally, see if we already have access to the process.
   ScopedProcessHandle targetProcess(OpenProcess(PROCESS_DUP_HANDLE,
                                                 FALSE,
                                                 aTargetProcessId));
   if (!targetProcess) {
     CrashReporter::AnnotateCrashReport(
-      NS_LITERAL_CSTRING("IPCTransportFailureReason"),
+      CrashReporter::Annotation::IPCTransportFailureReason,
       NS_LITERAL_CSTRING("Failed to open target process."));
     return false;
   }
 
   return !!::DuplicateHandle(::GetCurrentProcess(), aSourceHandle,
                               targetProcess, aTargetHandle,
                               aDesiredAccess, FALSE, aOptions);
 }
@@ -228,30 +228,28 @@ AnnotateSystemError()
   int64_t error = 0;
 #if defined(XP_WIN)
   error = ::GetLastError();
 #elif defined(OS_POSIX)
   error = errno;
 #endif
   if (error) {
     CrashReporter::AnnotateCrashReport(
-      NS_LITERAL_CSTRING("IPCSystemError"),
+      CrashReporter::Annotation::IPCSystemError,
       nsPrintfCString("%" PRId64, error));
   }
 }
 
 #if defined(XP_MACOSX)
 void
-AnnotateCrashReportWithErrno(const char* tag, int error)
+AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, int error)
 {
-  CrashReporter::AnnotateCrashReport(
-    nsCString(tag),
-    nsPrintfCString("%d", error));
+  CrashReporter::AnnotateCrashReport(tag, error);
 }
-#endif
+#endif // defined(XP_MACOSX)
 
 void
 LogMessageForProtocol(const char* aTopLevelProtocol, base::ProcessId aOtherPid,
                       const char* aContextDescription,
                       uint32_t aMessageId,
                       MessageDirection aDirection)
 {
   nsPrintfCString logMessage("[time: %" PRId64 "][%d%s%d] [%s] %s %s\n",
@@ -285,18 +283,19 @@ FatalError(const char* aMsg, bool aIsPar
   nsAutoCString formattedMessage("IPDL error: \"");
   formattedMessage.AppendASCII(aMsg);
   if (aIsParent) {
     // We're going to crash the parent process because at this time
     // there's no other really nice way of getting a minidump out of
     // this process if we're off the main thread.
     formattedMessage.AppendLiteral("\". Intentionally crashing.");
     NS_ERROR(formattedMessage.get());
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCFatalErrorMsg"),
-                                       nsDependentCString(aMsg));
+    CrashReporter::AnnotateCrashReport(
+      CrashReporter::Annotation::IPCFatalErrorMsg,
+      nsDependentCString(aMsg));
     AnnotateSystemError();
 #ifndef FUZZING
     MOZ_CRASH("IPC FatalError in the parent process!");
 #endif
   } else {
     formattedMessage.AppendLiteral("\". abort()ing as a result.");
 #ifndef FUZZING
     MOZ_CRASH_UNSAFE_OOL(formattedMessage.get());
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -28,16 +28,17 @@
 #include "mozilla/LinkedList.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/NotNull.h"
 #include "mozilla/Scoped.h"
 #include "mozilla/UniquePtr.h"
 #include "MainThreadUtils.h"
+#include "nsICrashReporter.h"
 #include "nsILabelableRunnable.h"
 
 #if defined(ANDROID) && defined(DEBUG)
 #include <android/log.h>
 #endif
 
 template<typename T> class nsTHashtable;
 template<typename T> class nsPtrHashKey;
@@ -909,19 +910,19 @@ private:
 
     bool mValid;
     mozilla::ipc::Transport::Mode mMode;
     TransportDescriptor mTransport;
     ProcessId mMyPid, mOtherPid;
 };
 
 #if defined(XP_MACOSX)
-void AnnotateCrashReportWithErrno(const char* tag, int error);
+void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, int error);
 #else
-static inline void AnnotateCrashReportWithErrno(const char* tag, int error)
+static inline void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, int error)
 {}
 #endif
 
 // This function is used internally to create a pair of Endpoints. See the
 // comment above Endpoint for a description of how it might be used.
 template<class PFooParent, class PFooChild>
 nsresult
 CreateEndpoints(const PrivateIPDLInterface& aPrivate,
@@ -931,17 +932,18 @@ CreateEndpoints(const PrivateIPDLInterfa
                 Endpoint<PFooChild>* aChildEndpoint)
 {
   MOZ_RELEASE_ASSERT(aParentDestPid);
   MOZ_RELEASE_ASSERT(aChildDestPid);
 
   TransportDescriptor parentTransport, childTransport;
   nsresult rv;
   if (NS_FAILED(rv = CreateTransport(aParentDestPid, &parentTransport, &childTransport))) {
-    AnnotateCrashReportWithErrno("IpcCreateEndpointsNsresult", int(rv));
+    AnnotateCrashReportWithErrno(
+      CrashReporter::Annotation::IpcCreateEndpointsNsresult, int(rv));
     return rv;
   }
 
   *aParentEndpoint = Endpoint<PFooParent>(aPrivate, mozilla::ipc::Transport::MODE_SERVER,
                                           parentTransport, aParentDestPid, aChildDestPid);
 
   *aChildEndpoint = Endpoint<PFooChild>(aPrivate, mozilla::ipc::Transport::MODE_CLIENT,
                                         childTransport, aChildDestPid, aParentDestPid);
--- a/ipc/glue/Transport_posix.cpp
+++ b/ipc/glue/Transport_posix.cpp
@@ -35,21 +35,23 @@ CreateTransport(base::ProcessId aProcIdO
   if (fd1 < 0 || fd2 < 0) {
     return NS_ERROR_TRANSPORT_INIT;
   }
 
   // The Transport closes these fds when it goes out of scope, so we
   // dup them here
   fd1 = dup(fd1);
   if (fd1 < 0) {
-    AnnotateCrashReportWithErrno("IpcCreateTransportDupErrno", errno);
+    AnnotateCrashReportWithErrno(
+      CrashReporter::Annotation::IpcCreateTransportDupErrno, errno);
   }
   fd2 = dup(fd2);
   if (fd2 < 0) {
-    AnnotateCrashReportWithErrno("IpcCreateTransportDupErrno", errno);
+    AnnotateCrashReportWithErrno(
+      CrashReporter::Annotation::IpcCreateTransportDupErrno, errno);
   }
 
   if (fd1 < 0 || fd2 < 0) {
     IGNORE_EINTR(close(fd1));
     IGNORE_EINTR(close(fd2));
     return NS_ERROR_DUPLICATE_HANDLE;
   }
 
--- a/ipc/mscom/COMPtrHolder.h
+++ b/ipc/mscom/COMPtrHolder.h
@@ -205,18 +205,19 @@ struct ParamTraits<mozilla::mscom::COMPt
         return false;
       }
     }
 
     typename paramType::EnvType env;
 
     mozilla::mscom::ProxyStream proxyStream(_IID, buf.get(), length, &env);
     if (!proxyStream.IsValid()) {
-      CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProxyStreamValid"),
-                                         NS_LITERAL_CSTRING("false"));
+      CrashReporter::AnnotateCrashReport(
+        CrashReporter::Annotation::ProxyStreamValid,
+        NS_LITERAL_CSTRING("false"));
       return false;
     }
 
     typename paramType::COMPtrType ptr;
     if (!proxyStream.GetInterface(mozilla::mscom::getter_AddRefs(ptr))) {
       return false;
     }
 
--- a/ipc/mscom/ProxyStream.cpp
+++ b/ipc/mscom/ProxyStream.cpp
@@ -39,17 +39,18 @@ ProxyStream::ProxyStream()
 // reconstructing the stream from a buffer anyway.
 ProxyStream::ProxyStream(REFIID aIID, const BYTE* aInitBuf,
                          const int aInitBufSize, Environment* aEnv)
   : mGlobalLockedBuf(nullptr)
   , mHGlobal(nullptr)
   , mBufSize(aInitBufSize)
   , mPreserveStream(false)
 {
-  NS_NAMED_LITERAL_CSTRING(kCrashReportKey, "ProxyStreamUnmarshalStatus");
+  CrashReporter::Annotation kCrashReportKey =
+    CrashReporter::Annotation::ProxyStreamUnmarshalStatus;
 
   if (!aInitBufSize) {
     CrashReporter::AnnotateCrashReport(kCrashReportKey,
                                        NS_LITERAL_CSTRING("!aInitBufSize"));
     // We marshaled a nullptr. Nothing else to do here.
     return;
   }
 
@@ -131,43 +132,40 @@ ProxyStream::ProxyStream(REFIID aIID, co
     EnsureMTA mta(marshalFn);
   }
 
   mStream = nullptr;
 
   if (FAILED(unmarshalResult) || !mUnmarshaledProxy) {
     nsPrintfCString hrAsStr("0x%08X", unmarshalResult);
     CrashReporter::AnnotateCrashReport(
-        NS_LITERAL_CSTRING("CoUnmarshalInterfaceResult"), hrAsStr);
+        CrashReporter::Annotation::CoUnmarshalInterfaceResult, hrAsStr);
     AnnotateInterfaceRegistration(aIID);
     if (!mUnmarshaledProxy) {
       CrashReporter::AnnotateCrashReport(kCrashReportKey,
                                          NS_LITERAL_CSTRING("!mUnmarshaledProxy"));
     }
 
 #if defined(ACCESSIBILITY)
     AnnotateClassRegistration(CLSID_AccessibleHandler);
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("UnmarshalActCtx"),
-                                       strActCtx);
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("UnmarshalActCtxManifestPath"),
-                                       NS_ConvertUTF16toUTF8(manifestPath));
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("A11yHandlerRegistered"),
-                                       a11y::IsHandlerRegistered() ?
-                                       NS_LITERAL_CSTRING("true") :
-                                       NS_LITERAL_CSTRING("false"));
+    CrashReporter::AnnotateCrashReport(
+      CrashReporter::Annotation::UnmarshalActCtx, strActCtx);
+    CrashReporter::AnnotateCrashReport(
+      CrashReporter::Annotation::UnmarshalActCtxManifestPath,
+      NS_ConvertUTF16toUTF8(manifestPath));
+    CrashReporter::AnnotateCrashReport(
+      CrashReporter::Annotation::A11yHandlerRegistered,
+      a11y::IsHandlerRegistered() ? NS_LITERAL_CSTRING("true")
+                                  : NS_LITERAL_CSTRING("false"));
 
-    nsAutoCString strExpectedStreamLen;
-    strExpectedStreamLen.AppendInt(expectedStreamLen);
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ExpectedStreamLen"),
-                                       strExpectedStreamLen);
+    CrashReporter::AnnotateCrashReport(
+      CrashReporter::Annotation::ExpectedStreamLen, expectedStreamLen);
 
-    nsAutoCString actualStreamLen;
-    actualStreamLen.AppendInt(aInitBufSize);
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ActualStreamLen"),
-                                       actualStreamLen);
+    CrashReporter::AnnotateCrashReport(
+      CrashReporter::Annotation::ActualStreamLen, aInitBufSize);
 #endif // defined(ACCESSIBILITY)
   }
 }
 
 ProxyStream::ProxyStream(ProxyStream&& aOther)
   : mGlobalLockedBuf(nullptr)
   , mHGlobal(nullptr)
   , mBufSize(0)
@@ -355,69 +353,66 @@ ProxyStream::ProxyStream(REFIID aIID, IU
   } else {
     // When marshaling in child processes, we want to force the MTA.
     EnsureMTA mta(marshalFn);
   }
 
   if (FAILED(createStreamResult)) {
     nsPrintfCString hrAsStr("0x%08X", createStreamResult);
     CrashReporter::AnnotateCrashReport(
-        NS_LITERAL_CSTRING("CreateStreamOnHGlobalFailure"),
-        hrAsStr);
+      CrashReporter::Annotation::CreateStreamOnHGlobalFailure, hrAsStr);
   }
 
   if (FAILED(marshalResult)) {
     AnnotateInterfaceRegistration(aIID);
     nsPrintfCString hrAsStr("0x%08X", marshalResult);
     CrashReporter::AnnotateCrashReport(
-        NS_LITERAL_CSTRING("CoMarshalInterfaceFailure"), hrAsStr);
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("MarshalActCtxManifestPath"),
-                                       NS_ConvertUTF16toUTF8(manifestPath));
+      CrashReporter::Annotation::CoMarshalInterfaceFailure, hrAsStr);
+    CrashReporter::AnnotateCrashReport(
+      CrashReporter::Annotation::MarshalActCtxManifestPath,
+      NS_ConvertUTF16toUTF8(manifestPath));
   }
 
   if (FAILED(statResult)) {
     nsPrintfCString hrAsStr("0x%08X", statResult);
-    CrashReporter::AnnotateCrashReport(
-        NS_LITERAL_CSTRING("StatFailure"),
-        hrAsStr);
+    CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::StatFailure,
+                                       hrAsStr);
   }
 
   if (FAILED(getHGlobalResult)) {
     nsPrintfCString hrAsStr("0x%08X", getHGlobalResult);
     CrashReporter::AnnotateCrashReport(
-        NS_LITERAL_CSTRING("GetHGlobalFromStreamFailure"),
-        hrAsStr);
+      CrashReporter::Annotation::GetHGlobalFromStreamFailure, hrAsStr);
   }
 
   mStream = std::move(stream);
 
   if (streamSize) {
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProxyStreamSizeFrom"),
-                                       NS_LITERAL_CSTRING("IStream::Stat"));
+    CrashReporter::AnnotateCrashReport(
+      CrashReporter::Annotation::ProxyStreamSizeFrom,
+      NS_LITERAL_CSTRING("IStream::Stat"));
     mBufSize = streamSize;
   }
 
   if (!hglobal) {
     return;
   }
 
   mGlobalLockedBuf = reinterpret_cast<BYTE*>(::GlobalLock(hglobal));
   mHGlobal = hglobal;
 
   // If we couldn't get the stream size directly from mStream, we may use
   // the size of the memory block allocated by the HGLOBAL, though it might
   // be larger than the actual stream size.
   if (!streamSize) {
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProxyStreamSizeFrom"),
-                                       NS_LITERAL_CSTRING("GlobalSize"));
+    CrashReporter::AnnotateCrashReport(
+      CrashReporter::Annotation::ProxyStreamSizeFrom,
+      NS_LITERAL_CSTRING("GlobalSize"));
     mBufSize = static_cast<int>(::GlobalSize(hglobal));
   }
 
-  nsAutoCString strBufSize;
-  strBufSize.AppendInt(mBufSize);
-
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProxyStreamSize"),
-                                     strBufSize);
+  CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::ProxyStreamSize,
+                                     mBufSize);
 }
 
 } // namespace mscom
 } // namespace mozilla
 
--- a/ipc/mscom/RegistrationAnnotator.cpp
+++ b/ipc/mscom/RegistrationAnnotator.cpp
@@ -371,26 +371,25 @@ AnnotateInterfaceRegistration(REFIID aIi
   json.EndObject();
 
   json.StartObjectProperty("HKCU", style);
   AnnotateInterfaceRegistrationForHive(json, HKEY_CURRENT_USER, aIid, style);
   json.EndObject();
 
   json.End();
 
-  nsAutoCString annotationKey;
-  annotationKey.AppendLiteral("InterfaceRegistrationInfo");
+  CrashReporter::Annotation annotationKey;
   if (XRE_IsParentProcess()) {
-    annotationKey.AppendLiteral("Parent");
+    annotationKey = CrashReporter::Annotation::InterfaceRegistrationInfoParent;
   } else {
-    annotationKey.AppendLiteral("Child");
+    annotationKey = CrashReporter::Annotation::InterfaceRegistrationInfoChild;
   }
-
-  CrashReporter::AnnotateCrashReport(annotationKey,
-                                     static_cast<CStringWriter*>(json.WriteFunc())->Get());
+  CrashReporter::AnnotateCrashReport(
+    annotationKey,
+    static_cast<CStringWriter*>(json.WriteFunc())->Get());
 }
 
 void
 AnnotateClassRegistration(REFCLSID aClsid)
 {
 #if defined(DEBUG)
   const JSONWriter::CollectionStyle style = JSONWriter::MultiLineStyle;
 #else
@@ -409,22 +408,21 @@ AnnotateClassRegistration(REFCLSID aClsi
   json.EndObject();
 
   json.StartObjectProperty("HKCU", style);
   AnnotateClsidRegistrationForHive(json, HKEY_CURRENT_USER, strClsid, style);
   json.EndObject();
 
   json.End();
 
-  nsAutoCString annotationKey;
-  annotationKey.AppendLiteral("ClassRegistrationInfo");
+  CrashReporter::Annotation annotationKey;
   if (XRE_IsParentProcess()) {
-    annotationKey.AppendLiteral("Parent");
+    annotationKey = CrashReporter::Annotation::ClassRegistrationInfoParent;
   } else {
-    annotationKey.AppendLiteral("Child");
+    annotationKey = CrashReporter::Annotation::ClassRegistrationInfoChild;
   }
 
   CrashReporter::AnnotateCrashReport(annotationKey,
                                      static_cast<CStringWriter*>(json.WriteFunc())->Get());
 }
 
 } // namespace mscom
 } // namespace mozilla
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -15,16 +15,17 @@
 #ifdef XP_WIN
 #include <windows.h>
 #endif
 
 #include "jsapi.h"
 #include "js/Printf.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
+#include "nsExceptionHandler.h"
 #include "nsIComponentManager.h"
 #include "mozilla/Module.h"
 #include "nsIFile.h"
 #include "mozJSComponentLoader.h"
 #include "mozJSLoaderUtils.h"
 #include "nsIXPConnect.h"
 #include "nsIObserverService.h"
 #include "nsIScriptSecurityManager.h"
@@ -51,20 +52,16 @@
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/ScriptPreloader.h"
 #include "mozilla/dom/DOMPrefs.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/UniquePtrExtensions.h"
 #include "mozilla/Unused.h"
 
-#ifdef MOZ_CRASHREPORTER
-#include "mozilla/ipc/CrashReporterClient.h"
-#endif
-
 using namespace mozilla;
 using namespace mozilla::scache;
 using namespace mozilla::loader;
 using namespace xpc;
 using namespace JS;
 
 static const char kObserverServiceContractID[] = "@mozilla.org/observer-service;1";
 
@@ -358,51 +355,47 @@ ResolveModuleObjectProperty(JSContext* a
         }
         if (found) {
             return lexical;
         }
     }
     return aModObj;
 }
 
-#ifdef MOZ_CRASHREPORTER
 static mozilla::Result<nsCString, nsresult> ReadScript(ComponentLoaderInfo& aInfo);
 
 static nsresult
-AnnotateScriptContents(const nsACString& aName, const nsACString& aURI)
+AnnotateScriptContents(CrashReporter::Annotation aName, const nsACString& aURI)
 {
     ComponentLoaderInfo info(aURI);
 
     nsCString str;
     MOZ_TRY_VAR(str, ReadScript(info));
 
     // The crash reporter won't accept any strings with embedded nuls. We
     // shouldn't have any here, but if we do because of data corruption, we
     // still want the annotation. So replace any embedded nuls before
     // annotating.
     str.ReplaceSubstring(NS_LITERAL_CSTRING("\0"), NS_LITERAL_CSTRING("\\0"));
 
     CrashReporter::AnnotateCrashReport(aName, str);
 
     return NS_OK;
 }
-#endif // defined MOZ_CRASHREPORTER
 
 nsresult
 mozJSComponentLoader::AnnotateCrashReport()
 {
-#ifdef MOZ_CRASHREPORTER
     Unused << AnnotateScriptContents(
-        NS_LITERAL_CSTRING("nsAsyncShutdownComponent"),
+        CrashReporter::Annotation::nsAsyncShutdownComponent,
         NS_LITERAL_CSTRING("resource://gre/components/nsAsyncShutdown.js"));
 
     Unused << AnnotateScriptContents(
-        NS_LITERAL_CSTRING("AsyncShutdownModule"),
+        CrashReporter::Annotation::AsyncShutdownModule,
         NS_LITERAL_CSTRING("resource://gre/modules/AsyncShutdown.jsm"));
-#endif // defined MOZ_CRASHREPORTER
 
     return NS_OK;
 }
 
 const mozilla::Module*
 mozJSComponentLoader::LoadModule(FileLocation& aFile)
 {
     if (!NS_IsMainThread()) {
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -679,17 +679,17 @@ void Gecko_UnregisterProfilerThread();
 bool Gecko_DocumentRule_UseForPresentation(RawGeckoPresContextBorrowed,
                                            const nsACString* aPattern,
                                            mozilla::css::DocumentMatchingFunction);
 
 // Allocator hinting.
 void Gecko_SetJemallocThreadLocalArena(bool enabled);
 
 void Gecko_AddBufferToCrashReport(const void* addr, size_t len);
-void Gecko_AnnotateCrashReport(const char* key_str, const char* value_str);
+void Gecko_AnnotateCrashReport(uint32_t key, const char* value_str);
 
 // Pseudo-element flags.
 #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
   const uint32_t SERVO_CSS_PSEUDO_ELEMENT_FLAGS_##name_ = flags_;
 #include "nsCSSPseudoElementList.h"
 #undef CSS_PSEUDO_ELEMENT
 
 #define SERVO_BINDING_FUNC(name_, return_, ...) return_ name_(__VA_ARGS__);
--- a/layout/style/nsLayoutStylesheetCache.cpp
+++ b/layout/style/nsLayoutStylesheetCache.cpp
@@ -707,18 +707,19 @@ AnnotateCrashReport(nsIURI* aURI)
         }
       }
       delete find;
     }
   } else {
     annotation.AppendLiteral("No GRE omnijar\n");
   }
 
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("SheetLoadFailure"),
-                                     NS_ConvertUTF16toUTF8(annotation));
+  CrashReporter::AnnotateCrashReport(
+    CrashReporter::Annotation::SheetLoadFailure,
+    NS_ConvertUTF16toUTF8(annotation));
 }
 
 static void
 ErrorLoadingSheet(nsIURI* aURI, const char* aMsg, FailureAction aFailureAction)
 {
   nsPrintfCString errorMessage("%s loading built-in stylesheet '%s'",
                                aMsg,
                                aURI ? aURI->GetSpecOrDefault().get() : "");
--- a/mobile/android/base/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetryCrashPingBuilder.java
+++ b/mobile/android/base/java/org/mozilla/gecko/telemetry/pingbuilders/TelemetryCrashPingBuilder.java
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 package org.mozilla.gecko.telemetry.pingbuilders;
 
 import android.util.Log;
 
+import org.mozilla.gecko.CrashReporterConstants;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.NonObjectJSONException;
 import org.mozilla.gecko.util.StringUtils;
 
 import java.io.IOException;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
@@ -30,50 +31,16 @@ import java.util.TimeZone;
 public class TelemetryCrashPingBuilder extends TelemetryPingBuilder {
     private static final String LOGTAG = "GeckoTelemetryCrashPingBuilder";
 
     private static final int PING_VERSION = 1;
 
     private static final String ISO8601_DATE = "yyyy-MM-dd";
     private static final String ISO8601_DATE_HOURS = "yyyy-MM-dd'T'HH':00:00.000Z'";
 
-    // The following list should be kept in sync with the one in CrashManager.jsm
-    private static final String[] ANNOTATION_WHITELIST = {
-        "AsyncShutdownTimeout",
-        "AvailablePageFile",
-        "AvailablePhysicalMemory",
-        "AvailableVirtualMemory",
-        "BlockedDllList",
-        "BlocklistInitFailed",
-        "BuildID",
-        "ContainsMemoryReport",
-        "CrashTime",
-        "EventLoopNestingLevel",
-        "ipc_channel_error",
-        "IsGarbageCollecting",
-        "LowCommitSpaceEvents",
-        "MozCrashReason",
-        "OOMAllocationSize",
-        "ProductID",
-        "ProductName",
-        "ReleaseChannel",
-        "RemoteType",
-        "SecondsSinceLastCrash",
-        "ShutdownProgress",
-        "StartupCrash",
-        "SystemMemoryUsePercentage",
-        "TextureUsage",
-        "TotalPageFile",
-        "TotalPhysicalMemory",
-        "TotalVirtualMemory",
-        "UptimeTS",
-        "User32BeforeBlocklist",
-        "Version",
-    };
-
     public TelemetryCrashPingBuilder(String crashId, String clientId, HashMap<String, String> annotations) {
         super(TelemetryPingBuilder.UNIFIED_TELEMETRY_VERSION);
 
         payload.put("type", "crash");
         payload.put("id", docID);
         payload.put("version", TelemetryPingBuilder.UNIFIED_TELEMETRY_VERSION);
         payload.put("creationDate", currentDate(ISO8601_DATE_HOURS));
         payload.put("clientId", clientId);
@@ -173,17 +140,17 @@ public class TelemetryCrashPingBuilder e
      *
      * @param annotations A map holding the crash annotations
      * @returns A JSON object representing the ping's metadata node
      */
     private static ExtendedJSONObject createMetadataNode(HashMap<String, String> annotations) {
         ExtendedJSONObject node = new ExtendedJSONObject();
 
         for (Entry<String, String> pair : annotations.entrySet()) {
-            if (Arrays.binarySearch(ANNOTATION_WHITELIST, pair.getKey()) >= 0) {
+            if (Arrays.binarySearch(CrashReporterConstants.ANNOTATION_WHITELIST, pair.getKey()) >= 0) {
                 node.put(pair.getKey(), pair.getValue());
             }
         }
 
         return node;
     }
 
     /**
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -111,31 +111,37 @@ with Files('../app/src/*/res/menu/browse
 
 DEFINES['ANDROID_PACKAGE_NAME'] = CONFIG['ANDROID_PACKAGE_NAME']
 FINAL_TARGET_PP_FILES += ['package-name.txt.in']
 
 GENERATED_FILES += [
     'AndroidManifest.xml',
     'generated/preprocessed/org/mozilla/gecko/AdjustConstants.java',
     'generated/preprocessed/org/mozilla/gecko/AppConstants.java',
+    'generated/preprocessed/org/mozilla/gecko/CrashReporterConstants.java',
     'generated/preprocessed/org/mozilla/gecko/MmaConstants.java',
 ]
 x = GENERATED_FILES['generated/preprocessed/org/mozilla/gecko/AdjustConstants.java']
 x.script = 'generate_build_config.py:generate_java'
 x.inputs += ['AdjustConstants.java.in']
 y = GENERATED_FILES['generated/preprocessed/org/mozilla/gecko/AppConstants.java']
 y.script = 'generate_build_config.py:generate_java'
 y.inputs += ['AppConstants.java.in']
 y = GENERATED_FILES['generated/preprocessed/org/mozilla/gecko/MmaConstants.java']
 y.script = 'generate_build_config.py:generate_java'
 y.inputs += ['MmaConstants.java.in']
 z = GENERATED_FILES['AndroidManifest.xml']
 z.script = 'generate_build_config.py:generate_android_manifest'
 z.inputs += ['AndroidManifest.xml.in']
 
+# Generate CrashReporterConstants.java
+crash_reporter_constants = GENERATED_FILES['generated/preprocessed/org/mozilla/gecko/CrashReporterConstants.java']
+crash_reporter_constants.script = '/toolkit/crashreporter/generate_crash_reporter_sources.py:emit_class'
+crash_reporter_constants.inputs += ['/toolkit/crashreporter/CrashAnnotations.yaml']
+
 # Regular builds invoke `libs` targets that localize files with no AB_CD set
 # into the default resources (res/{values,raw}).
 #
 # Multi-locale builds invoke `chrome-%` targets that localize files into
 # locale-specific resources (res/{values,raw}-AB-rCD).  Single-locale repacks
 # invoke `libs AB_CD=$*` targets that localize files into the default resources
 # (res/{values,raw}).
 #
@@ -187,15 +193,16 @@ t = ('android_apks',
 
 GENERATED_FILES += [t]
 GENERATED_FILES[t].force = True
 GENERATED_FILES[t].script = '/mobile/android/gradle.py:assemble_app'
 GENERATED_FILES[t].inputs += [
     '!AndroidManifest.xml',
     '!generated/preprocessed/org/mozilla/gecko/AdjustConstants.java',
     '!generated/preprocessed/org/mozilla/gecko/AppConstants.java',
+    '!generated/preprocessed/org/mozilla/gecko/CrashReporterConstants.java',
     '!generated/preprocessed/org/mozilla/gecko/MmaConstants.java',
     # These all depend on AB_CD, which isn't captured in this definition.  Due
     # to subtle RecursiveMake details, everything works out.  In the future we
     # can try to express the APKs themselves as LOCALIZED_GENERATED_FILES.
     '!res/raw/suggestedsites.json',
     '!res/values/strings.xml',
 ]
--- a/mozglue/build/WindowsDllBlocklist.cpp
+++ b/mozglue/build/WindowsDllBlocklist.cpp
@@ -19,50 +19,42 @@
 #include <io.h>
 
 #pragma warning( push )
 #pragma warning( disable : 4275 4530 ) // See msvc-stl-wrapper.template.h
 #include <map>
 #pragma warning( pop )
 
 #include "Authenticode.h"
+#include "CrashAnnotations.h"
 #include "nsAutoPtr.h"
 #include "nsWindowsDllInterceptor.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/StackWalk_windows.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Vector.h"
 #include "mozilla/WindowsVersion.h"
 #include "nsWindowsHelpers.h"
 #include "WindowsDllBlocklist.h"
 #include "mozilla/AutoProfilerLabel.h"
 #include "mozilla/glue/WindowsDllServices.h"
 
 using namespace mozilla;
 
+using CrashReporter::Annotation;
+using CrashReporter::AnnotationToString;
+
 #define DLL_BLOCKLIST_ENTRY(name, ...) \
   { name, __VA_ARGS__ },
 #define DLL_BLOCKLIST_STRING_TYPE const char*
 #include "mozilla/WindowsDllBlocklistDefs.h"
 
 // define this for very verbose dll load debug spew
 #undef DEBUG_very_verbose
 
-static const char kBlockedDllsParameter[] = "BlockedDllList=";
-static const int kBlockedDllsParameterLen =
-  sizeof(kBlockedDllsParameter) - 1;
-
-static const char kBlocklistInitFailedParameter[] = "BlocklistInitFailed=1\n";
-static const int kBlocklistInitFailedParameterLen =
-  sizeof(kBlocklistInitFailedParameter) - 1;
-
-static const char kUser32BeforeBlocklistParameter[] = "User32BeforeBlocklist=1\n";
-static const int kUser32BeforeBlocklistParameterLen =
-  sizeof(kUser32BeforeBlocklistParameter) - 1;
-
 static uint32_t sInitFlags;
 static bool sBlocklistInitAttempted;
 static bool sBlocklistInitFailed;
 static bool sUser32BeforeBlocklist;
 
 // Duplicated from xpcom glue. Ideally this should be shared.
 void
 printf_stderr(const char *fmt, ...)
@@ -775,32 +767,40 @@ DllBlocklist_Initialize(uint32_t aInitFl
     if (pProc) {
       gStartAddressesToBlock->append(pProc);
     }
   }
 #endif
 }
 
 static void
+WriteAnnotation(HANDLE aFile, Annotation aAnnotation, const char* aValue,
+                DWORD* aNumBytes)
+{
+  const char* str = AnnotationToString(aAnnotation);
+  WriteFile(aFile, str, strlen(str), aNumBytes, nullptr);
+  WriteFile(aFile, "=", 1, aNumBytes, nullptr);
+  WriteFile(aFile, aValue, strlen(aValue), aNumBytes, nullptr);
+}
+
+static void
 InternalWriteNotes(HANDLE file)
 {
   DWORD nBytes;
 
-  WriteFile(file, kBlockedDllsParameter, kBlockedDllsParameterLen, &nBytes, nullptr);
+  WriteAnnotation(file, Annotation::BlockedDllList, "", &nBytes);
   DllBlockSet::Write(file);
   WriteFile(file, "\n", 1, &nBytes, nullptr);
 
   if (sBlocklistInitFailed) {
-    WriteFile(file, kBlocklistInitFailedParameter,
-              kBlocklistInitFailedParameterLen, &nBytes, nullptr);
+    WriteAnnotation(file, Annotation::BlocklistInitFailed, "1\n", &nBytes);
   }
 
   if (sUser32BeforeBlocklist) {
-    WriteFile(file, kUser32BeforeBlocklistParameter,
-              kUser32BeforeBlocklistParameterLen, &nBytes, nullptr);
+    WriteAnnotation(file, Annotation::User32BeforeBlocklist, "1\n", &nBytes);
   }
 }
 
 using WriterFn = void (*)(HANDLE);
 static WriterFn gWriterFn = &InternalWriteNotes;
 
 static void
 GetNativeNtBlockSetWriter()
--- a/netwerk/ipc/NeckoMessageUtils.h
+++ b/netwerk/ipc/NeckoMessageUtils.h
@@ -102,17 +102,18 @@ struct ParamTraits<mozilla::net::NetAddr
       // Train's already off the rails:  let's get a stack trace at least...
       MOZ_CRASH("Error: please post stack trace to "
                       "https://bugzilla.mozilla.org/show_bug.cgi?id=661158");
       aMsg->WriteBytes(aParam.local.path, sizeof(aParam.local.path));
 #endif
     } else {
       if (XRE_IsParentProcess()) {
         nsPrintfCString msg("%d", aParam.raw.family);
-        CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Unknown NetAddr socket family"), msg);
+        CrashReporter::AnnotateCrashReport(
+          CrashReporter::Annotation::UnknownNetAddrSocketFamily, msg);
       }
 
       MOZ_CRASH("Unknown socket family");
     }
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, mozilla::net::NetAddr* aResult)
   {
--- a/storage/mozStorageService.cpp
+++ b/storage/mozStorageService.cpp
@@ -791,17 +791,17 @@ Service::Observe(nsISupports *, const ch
     if (gShutdownChecks == SCM_CRASH) {
       nsTArray<RefPtr<Connection> > connections;
       getConnections(connections);
       for (uint32_t i = 0, n = connections.Length(); i < n; i++) {
         if (!connections[i]->isClosed()) {
           // getFilename is only the leaf name for the database file,
           // so it shouldn't contain privacy-sensitive information.
           CrashReporter::AnnotateCrashReport(
-            NS_LITERAL_CSTRING("StorageConnectionNotClosed"),
+            CrashReporter::Annotation::StorageConnectionNotClosed,
             connections[i]->getFilename());
 #ifdef DEBUG
           printf_stderr("Storage connection not closed: %s",
                         connections[i]->getFilename().get());
 #endif
           MOZ_CRASH();
         }
       }
--- a/toolkit/components/crashes/CrashManager.jsm
+++ b/toolkit/components/crashes/CrashManager.jsm
@@ -216,54 +216,16 @@ this.CrashManager.prototype = Object.fre
   // The following are return codes for individual event file processing.
   // File processed OK.
   EVENT_FILE_SUCCESS: "ok",
   // The event appears to be malformed.
   EVENT_FILE_ERROR_MALFORMED: "malformed",
   // The type of event is unknown.
   EVENT_FILE_ERROR_UNKNOWN_EVENT: "unknown-event",
 
-  // A whitelist of crash annotations which do not contain sensitive data
-  // and are saved in the crash record and sent with Firefox Health Report.
-  ANNOTATION_WHITELIST: [
-    "AsyncShutdownTimeout",
-    "BuildID",
-    "ipc_channel_error",
-    "LowCommitSpaceEvents",
-    "ProductID",
-    "ProductName",
-    "ReleaseChannel",
-    "RemoteType",
-    "SecondsSinceLastCrash",
-    "ShutdownProgress",
-    "StartupCrash",
-    "TelemetryEnvironment",
-    "Version",
-    // The following entries are not normal annotations that can be found in
-    // the .extra file but are included in the crash record/FHR:
-    "AvailablePageFile",
-    "AvailablePhysicalMemory",
-    "AvailableVirtualMemory",
-    "BlockedDllList",
-    "BlocklistInitFailed",
-    "ContainsMemoryReport",
-    "CrashTime",
-    "EventLoopNestingLevel",
-    "IsGarbageCollecting",
-    "MozCrashReason",
-    "OOMAllocationSize",
-    "SystemMemoryUsePercentage",
-    "TextureUsage",
-    "TotalPageFile",
-    "TotalPhysicalMemory",
-    "TotalVirtualMemory",
-    "UptimeTS",
-    "User32BeforeBlocklist",
-  ],
-
   /**
    * Obtain a list of all dumps pending upload.
    *
    * The returned value is a promise that resolves to an array of objects
    * on success. Each element in the array has the following properties:
    *
    *   id (string)
    *      The ID of the crash (a UUID).
@@ -632,20 +594,26 @@ this.CrashManager.prototype = Object.fre
       let payload = data.substring(start);
 
       return this._handleEventFilePayload(store, entry, type, date, payload);
     })();
   },
 
   _filterAnnotations(annotations) {
     let filteredAnnotations = {};
+    let crashReporter = Cc["@mozilla.org/toolkit/crash-reporter;1"]
+                          .getService(Ci.nsICrashReporter);
 
     for (let line in annotations) {
-      if (this.ANNOTATION_WHITELIST.includes(line)) {
-        filteredAnnotations[line] = annotations[line];
+      try {
+        if (crashReporter.isAnnotationWhitelistedForPing(line)) {
+          filteredAnnotations[line] = annotations[line];
+        }
+      } catch (e) {
+        // Silently drop unknown annotations
       }
     }
 
     return filteredAnnotations;
   },
 
   _sendCrashPing(crashId, type, date, metadata = {}) {
     // If we have a saved environment, use it. Otherwise report
--- a/toolkit/components/crashes/tests/xpcshell/crash.extra
+++ b/toolkit/components/crashes/tests/xpcshell/crash.extra
@@ -3,17 +3,16 @@ TelemetryEnvironment={"EscapedField":"Es
 EMCheckCompatibility=true
 ProductName=Firefox
 ContentSandboxCapabilities=119
 TelemetryClientId=
 Vendor=Mozilla
 InstallTime=1000000000
 Theme=classic/1.0
 ReleaseChannel=default
-AddonsShouldHaveBlockedE10s=1
 ServerURL=https://crash-reports.mozilla.com
 SafeMode=0
 ContentSandboxCapable=1
 useragent_locale=en-US
 Version=55.0a1
 BuildID=20170512114708
 ProductID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
 TelemetryServerURL=
--- a/toolkit/components/gfx/SanityTest.js
+++ b/toolkit/components/gfx/SanityTest.js
@@ -67,22 +67,30 @@ function reportResult(val) {
   Services.prefs.savePrefFile(null);
 }
 
 function reportTestReason(val) {
   let histogram = Services.telemetry.getHistogramById("GRAPHICS_SANITY_TEST_REASON");
   histogram.add(val);
 }
 
-function annotateCrashReport(value) {
+function annotateCrashReport() {
   try {
-    // "1" if we're annotating the crash report, "" to remove the annotation.
     var crashReporter = Cc["@mozilla.org/toolkit/crash-reporter;1"].
                           getService(Ci.nsICrashReporter);
-    crashReporter.annotateCrashReport("GraphicsSanityTest", value ? "1" : "");
+    crashReporter.annotateCrashReport("GraphicsSanityTest", "1");
+  } catch (e) {
+  }
+}
+
+function removeCrashReportAnnotation(value) {
+  try {
+    var crashReporter = Cc["@mozilla.org/toolkit/crash-reporter;1"].
+                          getService(Ci.nsICrashReporter);
+    crashReporter.removeCrashReportAnnotation("GraphicsSanityTest");
   } catch (e) {
   }
 }
 
 function setTimeout(aMs, aCallback) {
   var timer = Cc["@mozilla.org/timer;1"].
                 createInstance(Ci.nsITimer);
   timer.initWithCallback(aCallback, aMs, Ci.nsITimer.TYPE_ONE_SHOT);
@@ -237,17 +245,17 @@ var listener = {
         this.mm.removeMessageListener(msgName, this);
       });
 
       this.mm = null;
     }
 
     // Remove the annotation after we've cleaned everything up, to catch any
     // incidental crashes from having performed the sanity test.
-    annotateCrashReport(false);
+    removeCrashReportAnnotation();
   }
 };
 
 function SanityTest() {}
 SanityTest.prototype = {
   classID: Components.ID("{f3a8ca4d-4c83-456b-aee2-6a2cbf11e9bd}"),
   QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
                                           Ci.nsISupportsWeakReference]),
@@ -325,17 +333,17 @@ SanityTest.prototype = {
 
     // profile-after-change fires only at startup, so we won't need
     // to use the listener again.
     let tester = listener;
     listener = null;
 
     if (!this.shouldRunTest()) return;
 
-    annotateCrashReport(true);
+    annotateCrashReport();
 
     // Open a tiny window to render our test page, and notify us when it's loaded
     var sanityTest = Services.ww.openWindow(null,
         "chrome://gfxsanity/content/sanityparent.html",
         "Test Page",
         "width=" + PAGE_WIDTH + ",height=" + PAGE_HEIGHT + ",chrome,titlebar=0,scrollbars=0,popup=1",
         null);
 
--- a/toolkit/components/telemetry/TelemetrySend.jsm
+++ b/toolkit/components/telemetry/TelemetrySend.jsm
@@ -685,18 +685,18 @@ var TelemetrySendImpl = {
       if (cr) {
         const crs = cr.getService(Ci.nsICrashReporter);
 
         let clientId = ClientID.getCachedClientID();
         let server = this._server || Services.prefs.getStringPref(TelemetryUtils.Preferences.Server, undefined);
 
         if (!this.sendingEnabled() || !TelemetryReportingPolicy.canUpload()) {
           // If we cannot send pings then clear the crash annotations
-          crs.annotateCrashReport("TelemetryClientId", "");
-          crs.annotateCrashReport("TelemetryServerURL", "");
+          crs.removeCrashReportAnnotation("TelemetryClientId");
+          crs.removeCrashReportAnnotation("TelemetryServerURL");
         } else {
           crs.annotateCrashReport("TelemetryClientId", clientId);
           crs.annotateCrashReport("TelemetryServerURL", server);
         }
       }
     } catch (e) {
       // Ignore errors when crash reporting is disabled
     }
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySend.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySend.js
@@ -634,19 +634,24 @@ add_task(async function test_pref_observ
             reject(Error(`Crash report annotation with unexpected key: "${key}".`));
           }
 
           if (expectedValue && value == "") {
             MockRegistrar.unregister(gMockCrs);
             reject(Error("Crash report annotation without expected value."));
           }
 
-          if (!expectedValue && value != "") {
+          if (keys.size == 0) {
             MockRegistrar.unregister(gMockCrs);
-            reject(Error(`Crash report annotation ("${key}") with unexpected value: "${value}".`));
+            resolve();
+          }
+        },
+        removeCrashReportAnnotation(key) {
+          if (!keys.delete(key)) {
+            MockRegistrar.unregister(gMockCrs);
           }
 
           if (keys.size == 0) {
             MockRegistrar.unregister(gMockCrs);
             resolve();
           }
         },
         UpdateCrashEventsDir() {
--- a/toolkit/components/terminator/nsTerminator.cpp
+++ b/toolkit/components/terminator/nsTerminator.cpp
@@ -598,18 +598,18 @@ nsTerminator::UpdateTelemetry()
 }
 
 void
 nsTerminator::UpdateCrashReport(const char* aTopic)
 {
   // In case of crash, we wish to know where in shutdown we are
   nsAutoCString report(aTopic);
 
-  Unused << CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ShutdownProgress"),
-                                               report);
+  Unused << CrashReporter::AnnotateCrashReport(
+    CrashReporter::Annotation::ShutdownProgress, report);
 }
 
 void
 XPCOMShutdownNotified()
 {
   MOZ_DIAGNOSTIC_ASSERT(sShutdownNotified == false);
   sShutdownNotified = true;
 }
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/CrashAnnotations.cpp
@@ -0,0 +1,63 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "CrashAnnotations.h"
+
+#include <algorithm>
+#include <cstring>
+#include <iterator>
+
+using std::begin;
+using std::end;
+using std::find_if;
+
+namespace CrashReporter {
+
+bool
+AnnotationFromString(Annotation& aResult, const char* aValue)
+{
+  auto elem = find_if(
+    begin(kAnnotationStrings),
+    end(kAnnotationStrings),
+    [&aValue](const char* aString) {
+      return strcmp(aString, aValue) == 0;
+    }
+  );
+
+  if (elem == end(kAnnotationStrings)) {
+    return false;
+  }
+
+  aResult = static_cast<Annotation>(elem - begin(kAnnotationStrings));
+  return true;
+}
+
+bool
+IsAnnotationWhitelistedForPing(Annotation aAnnotation) {
+  auto elem = find_if(
+    begin(kCrashPingWhitelist),
+    end(kCrashPingWhitelist),
+    [&aAnnotation](Annotation aElement) {
+      return aElement == aAnnotation;
+    }
+  );
+
+  return elem != end(kCrashPingWhitelist);
+}
+
+bool
+IsAnnotationBlacklistedForContent(Annotation aAnnotation)
+{
+  auto elem = find_if(
+    begin(kContentProcessBlacklist),
+    end(kContentProcessBlacklist),
+    [&aAnnotation](Annotation aElement) {
+      return aElement == aAnnotation;
+    }
+  );
+
+  return elem != end(kContentProcessBlacklist);
+}
+
+} // namespace CrashReporter
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/CrashAnnotations.h.in
@@ -0,0 +1,76 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 CrashAnnotations_h
+#define CrashAnnotations_h
+
+#include <cstdint>
+
+namespace CrashReporter {
+
+// Typed enum representing all crash annotations
+enum class Annotation : uint32_t {
+${enum}
+};
+
+// Stringified crash annotation names
+const char* const kAnnotationStrings[] = {
+${strings}
+};
+
+// Whitelist of crash annotations that can be included in a crash ping
+const Annotation kCrashPingWhitelist[] = {
+${whitelist}
+};
+
+// Blacklist of crash annotations that shouldn't be read from a content process
+const Annotation kContentProcessBlacklist[] = {
+${blacklist}
+};
+
+/**
+ * Return the string representation of a crash annotation.
+ *
+ * @param aAnnotation a crash annotation
+ * @returns A constant string holding the annotation name
+ */
+static inline const char*
+AnnotationToString(Annotation aAnnotation) {
+  return kAnnotationStrings[static_cast<uint32_t>(aAnnotation)];
+}
+
+/**
+ * Converts a string to its corresponding crash annotation.
+ *
+ * @param aResult a reference where the annotation will be stored
+ * @param aValue the string to be converted
+ * @return true if the string was successfully converted, false if it did not
+ *         correspond to any known annotation
+ */
+bool AnnotationFromString(Annotation& aResult, const char* aValue);
+
+/**
+ * Checks if the given crash annotation is whitelisted for inclusion in the
+ * crash ping.
+ *
+ * @param aAnnotation the crash annotation to be checked
+ * @return true if the annotation can be included in the crash ping, false
+ *         otherwise
+ */
+bool IsAnnotationWhitelistedForPing(Annotation aAnnotation);
+
+/**
+ * Checks if the given crash annotation needs to be filtered out when reading
+ * a content process crash annotations. Blacklisted annotations will be
+ * replaced with ones provided by the parent process.
+ *
+ * @param aAnnotation the crash annotation to be checked
+ * @return true if the annotation needs to be filtered out when reading
+ *         annotations provided by a content process, false otherwise
+ */
+bool IsAnnotationBlacklistedForContent(Annotation aAnnotation);
+
+} // namespace CrashReporter
+
+#endif // CrashAnnotations_h
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/CrashAnnotations.yaml
@@ -0,0 +1,776 @@
+# This lists all the available crash annotations.
+#
+# Mandatory fields for each entry are:
+# - description: A string describing the annotation
+# - type: the annotation type, currently `string`, `integer` or `boolean`.
+#   The latter are stringified to `1` for true and `0` for false.
+#
+# Additionally a field can have the following optional fields:
+# - altname: A string that will be used when writing out the annotation to the
+#   .extra file instead of the annotation name
+# - ping: A boolean that indicates whether the annotation is whitelisted for
+#   going into the crash ping, if not specified this defaults to false
+# - content: A boolean that indicates whether the field will be included in
+#   subprocess reports, if not specified this defaults to true
+
+A11yHandlerRegistered:
+  description: >
+    Set to "true" if the accessibility handler is registered, "false" otherwise.
+  type: string
+
+AbortMessage:
+  description: >
+    Message passed to NS_DebugBreak().
+  type: string
+
+Accessibility:
+  description: >
+    Set to "Active" by the accessibility service when it is active.
+  type: string
+
+AccessibilityClient:
+  description: >
+    Accessibility client ID.
+  type: string
+
+AccessibilityInProcClient:
+  description: >
+    Hexadecimal mask of in-process accessibility consumers, see
+    accessible/windows/msaa/Compatibility.h for the mappings.
+  type: string
+
+ActualStreamLen:
+  description: >
+    Actual length of an IPC proxy stream.
+  type: integer
+
+AdapterDeviceID:
+  description: >
+    Graphics adapter name.
+  type: string
+
+AdapterDriverVersion:
+  description: >
+    Graphics adapter driver version.
+  type: string
+
+AdapterSubsysID:
+  description: >
+    Graphics adapter subsystem ID.
+  type: string
+
+AdapterVendorID:
+  description: >
+    Graphics adapter vendor name.
+  type: string
+
+additional_minidumps:
+  description: >
+    Comma separated list of additional minidumps for this crash, each element
+    in the list represent the suffix used in the dump filename. E.g. the
+    "browser" entry for crash fa909194-737b-4b93-b8da-da110ac785e0 implies the
+    existence of the fa909194-737b-4b93-b8da-da110ac785e0-browser.dmp file.
+  type: string
+
+Addons:
+  description: >
+    List of currently enabled add-ons.
+  type: string
+  altname: Add-ons
+
+AppInitDLLs:
+  description: >
+    List of DLLs loaded when launching any application on Windows, this
+    reflects the contents of the AppInit_DLLs registry key.
+  type: string
+
+AsyncShutdownModule:
+  description: >
+    Holds the contents of the AsyncShutdown.js script
+  type: string
+
+AsyncShutdownTimeout:
+  description: >
+    This annotation is present if a shutdown blocker was not released in time
+    and the browser was crashed instead of waiting for shutdown to finish. The
+    condition that caused the hang is contained in the annotation.
+  type: string
+  ping: true
+
+AvailablePageFile:
+  description: >
+    Windows-only, maximum amount of memory that can be committed.  This
+    annotation is populated with the contents of the MEMORYSTATUSEX's structure
+    ullAvailPageFile field.
+  type: string
+  ping: true
+
+AvailablePhysicalMemory:
+  description: >
+    Windows-only, amount of free physical memory in bytes. This annotation
+    is populated with the contents of the MEMORYSTATUSEX's structure
+    ullAvailPhys field.
+  type: string
+  ping: true
+
+AvailableVirtualMemory:
+  description: >
+    Windows-only, amount of free virtual memory in bytes. This annotation is
+    populated with the contents of the MEMORYSTATUSEX's structure
+    ullAvailVirtual field.
+  type: string
+  ping: true
+
+BIOS_Manufacturer:
+  description: >
+    Name of the BIOS manufacturer.
+  type: string
+
+BlockedDllList:
+  description: >
+    Comma-separated list of blocked DLLS, Windows-only
+  type: string
+  ping: true
+
+BlocklistInitFailed:
+  description: >
+    Set to 1 if the DLL blocklist could not be initialized.
+  type: boolean
+  ping: true
+
+BreakpadReserveAddress:
+  description: >
+    Address of the buffer reserved by Breakpad.
+  type: string
+
+BreakpadReserveSize:
+  description: >
+    Size of the buffer reserved by Breakpad.
+  type: string
+
+BuildID:
+  description: >
+    Application build ID, the format is YYYYMMDDHHMMSS.
+  type: string
+  ping: true
+
+ClassRegistrationInfoChild:
+  description: >
+    Microsoft COM class registration annotation for the child process.
+  type: string
+
+ClassRegistrationInfoParent:
+  description: >
+    Microsoft COM class registration annotation for the parent process.
+  type: string
+
+CoMarshalInterfaceFailure:
+  description: >
+    Annotation describing the error returned by trying to marshal an object
+    via CoMarshalInterface during the creation of an IPC proxy stream.
+  type: string
+
+ContainsMemoryReport:
+  description: >
+    Indicates that the crash dump contains a memory report.
+  type: boolean
+  ping: true
+
+ContentSandboxCapabilities:
+  description: >
+    List of capabilities of the content process sandbox.
+  type: string
+
+ContentSandboxEnabled:
+  description: >
+    Set to 1 when content process sandboxing is enabled.
+  type: boolean
+
+ContentSandboxCapable:
+  description: >
+    Set to 1 if the client is capable of content sandboxing.
+  type: boolean
+
+ContentSandboxLevel:
+  description: >
+    Content sandbox level.
+  type: integer
+
+CoUnmarshalInterfaceResult:
+  description: >
+    Annotation describing the error returned by trying to unmarshal an object
+    via CoUnmarshalInterface during the creation of an IPC proxy stream.
+  type: integer
+
+CPUMicrocodeVersion:
+  description: >
+    Version of the CPU microcode.
+  type: string
+
+CpuUsageFlashProcess1:
+  description: >
+    CPU usage of the first Adobe Flash plugin process.
+  type: string
+
+CpuUsageFlashProcess2:
+  description: >
+    CPU usage of the second Adobe Flash plugin process.
+  type: string
+
+CrashAddressLikelyWrong:
+  description: >
+    Set to 1 if signal handling is broken, in which case the crash address is
+    likely to be wrong.
+  type: boolean
+
+CrashTime:
+  description: >
+    Crash time in seconds since the Epoch.
+  type: string
+  ping: true
+
+CreateStreamOnHGlobalFailure:
+  description: >
+    Set when failing to obtain a global memory handle during the creation of an
+    IPC proxy stream.
+  type: string
+
+CycleCollector:
+  description: >
+    Reason why the cycle collector crashed.
+  type: string
+
+DeviceResetReason:
+  description: >
+    Reason why a DirectX device has been reset, Windows only.
+  type: string
+
+DOMIPCEnabled:
+  description: >
+    Set to 1 when a tab is running in a content process
+  type: boolean
+
+EMCheckCompatibility:
+  description: >
+    Set to 1 if add-on compatibility checking is enabled.
+  type: boolean
+
+EventLoopNestingLevel:
+  description: >
+    Present only if higher than 0, indicates that we're running in a nested
+    event loop and indicates the nesting level.
+  type: integer
+  ping: true
+
+ExpectedStreamLen:
+  description: >
+    Expected length of an IPC proxy stream.
+  type: integer
+
+FlashProcessDump:
+  description: >
+    Type of process the flash plugin is running in, can be either "Broker" or
+    "Sandbox".
+  type: string
+
+FramePoisonBase:
+  description: >
+    Base pointer of the memory area used for the poison value we place in freed
+    memory.
+  type: string
+  content: false
+
+FramePoisonSize:
+  description: >
+    Size of the memory area used for the poison value we place in freed
+    memory.
+  type: integer
+  content: false
+
+GetHGlobalFromStreamFailure:
+  description: >
+    Error returned when invoking GetHGlobalFromStreamFailure() during the
+    creation of an IPC stream proxy.
+  type: string
+
+GMPLibraryPath:
+  description: >
+    Holds the path to the GMP plugin library.
+  type: string
+
+GMPPlugin:
+  description: >
+    Set to 1 if the GMP plugin is enabled.
+  type: boolean
+
+GPUProcessLaunchCount:
+  description: >
+    Number of times the GPU process was launched.
+  type: integer
+
+GPUProcessStatus:
+  description: >
+    Status of the GPU process, can be set to "Running" or "Destroyed"
+  type: string
+
+GraphicsCriticalError:
+  description: >
+    Information of a critical error that occurred within the graphics code.
+  type: string
+
+GraphicsSanityTest:
+  description: >
+    Annotation used in tests.
+  type: string
+
+GraphicsStartupTest:
+  description: >
+    Set to 1 by the graphics driver crash guard when it's activated.
+  type: boolean
+
+HangMonitorDescription:
+  description: >
+    Name of the hang monitor that generated the crash.
+  type: string
+
+HasDeviceTouchScreen:
+  description: >
+    Set to 1 if the device had a touch-screen, this only applies to Firefox
+    desktop as on mobile devices we assume a touch-screen is always present.
+  type: boolean
+
+IAccessibleConfig:
+  description: >
+    Set when something is seriously wrong with the IAccessible configuration in
+    the computer's registry. The value is always set to "NoSystemTypeLibOrPS"
+  type: string
+
+InstallTime:
+  description: >
+    The time when Firefox was installed expressed as seconds since the Epoch
+  type: integer
+
+InterfaceRegistrationInfoChild:
+  description: >
+    Microsoft COM interface registration annotation for the child process.
+  type: string
+
+InterfaceRegistrationInfoParent:
+  description: >
+    Microsoft COM interface registration annotation for the parent process.
+  type: string
+
+ipc_channel_error:
+  description: >
+    Set before a content process crashes because of an IPC channel error, holds
+    a description of the error.
+  type: string
+  ping: true
+
+IpcCreateEndpointsNsresult:
+  description: >
+    errno value retrieved after failing to create an IPC transport object.
+  type: integer
+
+IpcCreatePipeCloExecErrno:
+  description: >
+    errno value retrieved after failing to set the O_CLOEXEC flag on a pipe
+    used for IPC.
+  type: integer
+
+IpcCreatePipeFcntlErrno:
+  description: >
+    errno value retrieved after a call to fcntl() on a pipe used for IPC failed.
+  type: integer
+
+IpcCreatePipeSocketPairErrno:
+  description: >
+    errno value retrieved after a socketpair() call failed while creating an IPC
+    transport object.
+  type: integer
+
+IpcCreateTransportDupErrno:
+  description: >
+    errno value retrieved after a dup() call failed while creating an IPC
+    transport object.
+  type: integer
+
+IPCFatalErrorMsg:
+  description: >
+    Describes a fatal error that occurred during IPC operation.
+  type: string
+
+IPCFatalErrorProtocol:
+  description: >
+    Name of the protocol used by IPC when a fatal error occurred.
+  type: string
+
+IPCMessageName:
+  description: >
+    Name of the IPC message that caused a crash because it was too large.
+  type: string
+
+IPCMessageSize:
+  description: >
+    Size of the IPC message that caused a crash because it was too large.
+  type: integer
+
+IPCReadErrorReason:
+  description: >
+    Reason why reading an object via IPC failed.
+  type: string
+
+IPCShutdownState:
+  description: >
+    IPC shutdown state, can be set to either "RecvShutdown" or
+    "SendFinishShutdown" by a content process while it's shutting down.
+  type: string
+
+IPCSystemError:
+  description: >
+    Description of the last system error that occurred during IPC operation.
+  type: string
+
+IPCTransportFailureReason:
+  description: >
+    Reason why creating an IPC channel failed.
+  type: string
+
+IsGarbageCollecting:
+  description: >
+    If true then the JavaScript garbage collector was running when the crash
+    occurred.
+  type: boolean
+  ping: true
+
+JavaStackTrace:
+  description: >
+    Java stack trace, only present on Firefox for Android if we encounter an
+    uncaught Java exception.
+  type: string
+
+JSLargeAllocationFailure:
+  description: >
+    A large allocation couldn't be satisfied, check the JSOutOfMemory
+    description for the possible values of this annotation.
+  type: string
+
+JSOutOfMemory:
+  description: >
+    A small allocation couldn't be satisfied, the annotation may contain the
+    "Reporting", "Reported" or "Recovered" value. The first one means that
+    we crashed while responding to the OOM condition (possibly while running a
+    memory-pressure observers), the second that we crashed after having tried to
+    free some memory, and the last that the GC had managed to free enough memory
+    to satisfy the allocation.
+  type: string
+
+LowCommitSpaceEvents:
+  description: >
+    Number of times the available memory tracker has detected a that
+    commit-space was running low. This is a Windows-specific annotation.
+  type: integer
+  ping: true
+
+MarshalActCtxManifestPath:
+  description: >
+    Proxy stream marshalling current activation context manifest path.
+  type: string
+
+MozCrashReason:
+  description: >
+    Plaintext description of why Firefox crashed, this is usually set by
+    assertions and the like.
+  type: string
+  ping: true
+
+Notes:
+  description: >
+    Miscellaneous notes that can be appended to a crash.
+  type: string
+
+nsAsyncShutdownComponent:
+  description: >
+    Holds the contents of the nsAsyncShutdown.js script
+  type: string
+
+NumberOfProcessors:
+  description: >
+    Number of logical processors in the system.
+  type: integer
+
+OOMAllocationSize:
+  description: >
+    Size of the allocation that caused an out-of-memory condition.
+  type: string
+  ping: true
+
+PluginCpuUsage:
+  description: >
+    CPU usage of the plugin process.
+  type: string
+
+PluginFilename:
+  description: >
+    Plugin filename, only the process holding the plugin has this annotation.
+  type: string
+
+PluginHang:
+  description: >
+    The presence of this annotation indicates that this crash was generated in
+    response to a plugin hanging.
+  type: boolean
+
+PluginHangUIDuration:
+  description: >
+    Duration in milliseconds of the plugin hang that caused this crash.
+  type: integer
+
+PluginName:
+  description: >
+    Display name of a plugin, only the process holding the plugin has this
+    annotation.
+  type: string
+
+PluginVersion:
+  description: >
+    Version of a plugin, only the process holding the plugin has this
+    annotation.
+  type: string
+
+ProcessType:
+  description: >
+    Type of the process that crashed, can hold the values "content", "plugin" or
+    "gpu" currently.
+  type: string
+
+ProductName:
+  description: >
+    Application name (e.g. Firefox).
+  type: string
+  ping: true
+
+ProductID:
+  description: >
+    Application UUID (e.g. ec8030f7-c20a-464f-9b0e-13a3a9e97384).
+  type: string
+  ping: true
+
+ProxyStreamSize:
+  description: >
+    Size of an IPC proxy stream.
+  type: integer
+
+ProxyStreamSizeFrom:
+  description: >
+    Describes how the size of a proxy stream was obtained. It can be set to
+    either Stream::Stat or GlobalSize.
+  type: string
+
+ProxyStreamUnmarshalStatus:
+  description: >
+    Status of the proxy stream unmarshalling, see ipc/mscom/ProxyStream.cpp for
+    the various value this annotation can take.
+  type: string
+
+ProxyStreamValid:
+  description: >
+    Set to "false" when encountering an invalid IPC proxy stream.
+  type: string
+
+ReleaseChannel:
+  description: >
+    Application release channel (e.g. default, beta, ...)
+  type: string
+  ping: true
+
+RemoteType:
+  description: >
+    Type of the content process, can be set to "web", "file" or "extension".
+  type: string
+  ping: true
+
+SafeMode:
+  description: >
+    Set to 1 if the browser was started in safe mode.
+  type: boolean
+
+SecondsSinceLastCrash:
+  description: >
+    Time in seconds since the last crash occurred.
+  type: string
+  ping: true
+
+ServerURL:
+  description: >
+    URL used to post the crash report.
+  type: string
+
+SheetLoadFailure:
+  description: >
+    Set when failing to load a built-in style sheet. This can contain a
+    potentially very large amount of diagnostic information.
+  type: string
+
+ShutdownProgress:
+  description: >
+   Shutdown step at which the browser crashed, can be set to "quit-application",
+   "profile-change-teardown", "profile-before-change", "xpcom-will-shutdown" or
+   "xpcom-shutdown".
+  type: string
+  ping: true
+
+StartupCrash:
+  description: >
+    If set to 1 then this crash occurred during startup.
+  type: boolean
+  content: false
+  ping: true
+
+StartupTime:
+  description: >
+    The time when Firefox was launched expressed in seconds since the Epoch.
+  type: integer
+  content: false
+
+StatFailure:
+  description: >
+    Error returned when invoking IStream's Stat function during the creation
+    of an IPC proxy stream.
+  type: string
+
+StorageConnectionNotClosed:
+  description: >
+    This annotation is added when a mozStorage connection has not been properly
+    closed during shutdown. The annotation holds the filename of the database
+    associated with the connection.
+  type: string
+
+SystemMemoryUsePercentage:
+  description: >
+    Windows-only, percentage of physical memory in use. This annotation is
+    populated with the contents of the MEMORYSTATUSEX's structure dwMemoryLoad
+    field.
+  type: integer
+  ping: true
+
+TelemetryClientId:
+  description: >
+    Telemetry client ID.
+  type: string
+
+TelemetryEnvironment:
+  description: >
+    The telemetry environment in JSON format.
+  type: string
+
+TelemetryServerURL:
+  description: >
+    Telemetry server URL. Used to send main process crash pings directly from
+    the crashreporter client.
+  type: string
+
+TelemetrySessionId:
+  description: >
+    Telemetry session ID.
+  type: string
+
+TestKey:
+  description: >
+    Annotation used in tests.
+  type: string
+
+TestUnicode:
+  description: >
+    Annotation used in tests.
+  type: string
+
+TextureUsage:
+  description: >
+    Amount of memory in bytes consumed by textures.
+  type: string
+  ping: true
+
+ThreadIdNameMapping:
+  description: >
+    List of thread names with their corresponding thread IDs.
+  type: string
+
+TotalPageFile:
+  description: >
+    Windows-only, current committed memory limit. This annotation is
+    populated with the contents of the MEMORYSTATUSEX's structure
+    ullTotalPageFile field.
+  type: string
+  ping: true
+
+TotalPhysicalMemory:
+  description: >
+    Windows-only, amount of physical memory in bytes. This annotation is
+    populated with the contents of the MEMORYSTATUSEX's structure
+    ullTotalPhys field.
+  type: string
+  ping: true
+
+TotalVirtualMemory:
+  description: >
+    Windows-only, size of the virtual address space. This annotation is
+    populated with the contents of the MEMORYSTATUSEX's structure
+    ullTotalVirtual field.
+  type: string
+  ping: true
+
+UnknownNetAddrSocketFamily:
+  description: >
+    An unknown network address family was requested to Necko. The value is the
+    requested family number.
+  type: integer
+
+UnmarshalActCtx:
+  description: >
+    Proxy stream unmarshalling current activation context.
+  type: string
+
+UnmarshalActCtxManifestPath:
+  description: >
+    Proxy stream unmarshalling current activation context manifest path.
+  type: string
+
+UptimeTS:
+  description: >
+    Uptime in seconds. This annotation uses a string instead of an integer
+    because it has a fractional component.
+  type: string
+  ping: true
+
+URL:
+  description: >
+    URL being loaded.
+  type: string
+  content: false
+
+User32BeforeBlocklist:
+  description: >
+    Set to 1 if user32.dll was loaded before we could install the DLL blocklist.
+  type: boolean
+  ping: true
+
+useragent_locale:
+  description: >
+    User-agent locale.
+  type: string
+
+Vendor:
+  description: >
+    Application vendor (e.g. Mozilla).
+  type: string
+
+Version:
+  description: >
+    Product version.
+  type: string
+
+Winsock_LSP:
+  description: >
+    Information on winsock LSPs injected in our networking stack.
+  type: string
--- a/toolkit/crashreporter/client/moz.build
+++ b/toolkit/crashreporter/client/moz.build
@@ -3,16 +3,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.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 CONFIG['OS_TARGET'] != 'Android':
     Program('crashreporter')
 
     UNIFIED_SOURCES += [
+        '../CrashAnnotations.cpp',
         'crashreporter.cpp',
         'ping.cpp',
     ]
 
     LOCAL_INCLUDES += [
         '/toolkit/components/jsoncpp/include',
     ]
 
--- a/toolkit/crashreporter/client/ping.cpp
+++ b/toolkit/crashreporter/client/ping.cpp
@@ -15,16 +15,18 @@
 #elif defined(XP_MACOSX)
 #include <CoreFoundation/CoreFoundation.h>
 #elif defined(XP_WIN)
 #include <objbase.h>
 #endif
 
 #include "json/json.h"
 
+#include "CrashAnnotations.h"
+
 using std::string;
 
 namespace CrashReporter {
 
 struct UUID {
     uint32_t m0;
     uint16_t m1;
     uint16_t m2;
@@ -115,55 +117,25 @@ const char kTelemetryUrl[]       = "Tele
 const char kTelemetrySessionId[] = "TelemetrySessionId";
 const int  kTelemetryVersion     = 4;
 
 // Create the payload.metadata node of the crash ping using fields extracted
 // from the .extra file
 static Json::Value
 CreateMetadataNode(StringTable& strings)
 {
-  // The following list should be kept in sync with the one in CrashManager.jsm
-  const char *entries[] = {
-    "AsyncShutdownTimeout",
-    "AvailablePageFile",
-    "AvailablePhysicalMemory",
-    "AvailableVirtualMemory",
-    "BlockedDllList",
-    "BlocklistInitFailed",
-    "BuildID",
-    "ContainsMemoryReport",
-    "CrashTime",
-    "EventLoopNestingLevel",
-    "ipc_channel_error",
-    "IsGarbageCollecting",
-    "LowCommitSpaceEvents",
-    "MozCrashReason",
-    "OOMAllocationSize",
-    "ProductID",
-    "ProductName",
-    "ReleaseChannel",
-    "RemoteType",
-    "SecondsSinceLastCrash",
-    "ShutdownProgress",
-    "StartupCrash",
-    "SystemMemoryUsePercentage",
-    "TextureUsage",
-    "TotalPageFile",
-    "TotalPhysicalMemory",
-    "TotalVirtualMemory",
-    "UptimeTS",
-    "User32BeforeBlocklist",
-    "Version",
-  };
-
   Json::Value node;
 
-  for (auto entry : entries) {
-    if ((strings.find(entry) != strings.end()) && !strings[entry].empty()) {
-      node[entry] = strings[entry];
+  for (auto line : strings) {
+    Annotation annotation;
+
+    if (AnnotationFromString(annotation, line.first.c_str())) {
+      if (IsAnnotationWhitelistedForPing(annotation)) {
+        node[line.first] = line.second;
+      }
     }
   }
 
   return node;
 }
 
 // Create the payload node of the crash ping
 static Json::Value
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/generate_crash_reporter_sources.py
@@ -0,0 +1,200 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import string
+import sys
+import textwrap
+import yaml
+
+###############################################################################
+# Language-agnostic functionality                                             #
+###############################################################################
+
+template_header = "/* This file was autogenerated by " \
+  "toolkit/crashreporter/generate_crash_reporter_sources.py. DO NOT EDIT */\n\n"
+
+
+def validate_annotations(annotations):
+    """ Ensure that the annotations have all the required fields """
+
+    for (name, data) in sorted(annotations.items()):
+        if "description" not in data:
+            print("Annotation " + name + " does not have a description\n")
+            sys.exit(1)
+        if "type" not in data:
+            print("Annotation " + name + " does not have a type\n")
+            sys.exit(1)
+        else:
+            annotation_type = data.get("type")
+            valid_types = ["boolean", "integer", "string"]
+            if not any(annotation_type == t for t in valid_types):
+                print("Annotation " + name + " has an unknown type: " + annotation_type + "\n")
+                sys.exit(1)
+
+
+def read_annotations(annotations_filename):
+    """Read the annotations from a YAML file.
+    If an error is encountered quit the program."""
+
+    try:
+        with open(annotations_filename, "r") as annotations_file:
+            annotations = yaml.safe_load(annotations_file)
+    except (IOError, ValueError) as e:
+        print("Error parsing " + annotations_filename + ":\n" + str(e) + "\n")
+        sys.exit(1)
+
+    validate_annotations(annotations)
+
+    return annotations
+
+
+def read_template(template_filename):
+    """Read the contents of the template.
+    If an error is encountered quit the program."""
+
+    try:
+        with open(template_filename, "r") as template_file:
+            template = template_file.read()
+    except IOError as ex:
+        print("Error when reading " + template_filename + ":\n" + str(ex) + "\n")
+        sys.exit(1)
+
+    return template
+
+
+def extract_crash_ping_whitelist(annotations):
+    """Extract an array holding the names of the annotations whitelisted for
+    inclusion in the crash ping."""
+
+    return [name
+            for (name, data)
+            in sorted(annotations.items())
+            if data.get("ping", False)]
+
+
+def extract_content_process_blacklist(annotations):
+    """Extract an array holding the names of the annotations blacklisted when
+    read from a content process."""
+
+    return [name
+            for (name, data)
+            in sorted(annotations.items())
+            if not data.get("content", True)]
+
+###############################################################################
+# C++ code generation                                                         #
+###############################################################################
+
+
+def generate_strings(annotations):
+    """Generate strings corresponding to every annotation."""
+
+    names = ["  \"" + data.get("altname", name) + "\""
+             for (name, data)
+             in sorted(annotations.items())]
+
+    return ",\n".join(names)
+
+
+def generate_enum(annotations):
+    """Generate the C++ typed enum holding all the annotations and return it
+    as a string."""
+
+    enum = ""
+
+    for i, (name, _) in enumerate(sorted(annotations.items())):
+        enum += "  " + name + " = " + str(i) + ",\n"
+
+    enum += "  Count = " + str(len(annotations))
+
+    return enum
+
+
+def generate_array_initializer(contents):
+    """Generates the initializer for a C++ array of annotations."""
+
+    initializer = ["  Annotation::" + name for name in contents]
+
+    return ",\n".join(initializer)
+
+
+def generate_header(template, annotations):
+    """Generate a header by filling the template with the the list of
+    annotations and return it as a string."""
+
+    whitelist = extract_crash_ping_whitelist(annotations)
+    blacklist = extract_content_process_blacklist(annotations)
+
+    return template_header + string.Template(template).substitute({
+               "enum": generate_enum(annotations),
+               "strings": generate_strings(annotations),
+               "whitelist": generate_array_initializer(whitelist),
+               "blacklist": generate_array_initializer(blacklist),
+           })
+
+
+def emit_header(output, template_filename, annotations_filename):
+    """Generate the C++ header from the template and write it out."""
+
+    annotations = read_annotations(annotations_filename)
+    template = read_template(template_filename)
+    generated_header = generate_header(template, annotations)
+
+    try:
+        output.write(generated_header)
+    except IOError as ex:
+        print("Error while writing out the generated file:\n" + str(ex) + "\n")
+        sys.exit(1)
+
+###############################################################################
+# Java code generation                                                        #
+###############################################################################
+
+
+def generate_java_array_initializer(contents):
+    """Generates the initializer for an array of strings.
+    Effectively turns `["a", "b"]` into '   \"a\",\n   \"b\"\n'."""
+
+    initializer = ""
+
+    for name in contents:
+        initializer += "        \"" + name + "\",\n"
+
+    return initializer.strip(",\n")
+
+
+def generate_class(template, annotations):
+    """Fill the class template from the list of annotations."""
+
+    whitelist = extract_crash_ping_whitelist(annotations)
+
+    return template_header + string.Template(template).substitute({
+               "whitelist": generate_java_array_initializer(whitelist),
+           })
+
+
+def emit_class(output, annotations_filename):
+    """Generate the CrashReporterConstants.java file."""
+
+    template = textwrap.dedent("""\
+    package org.mozilla.gecko;
+
+    /**
+     * Constants used by the crash reporter. These are generated so that they
+     * are kept in sync with the other C++ and JS users.
+     */
+    public class CrashReporterConstants {
+        public static final String[] ANNOTATION_WHITELIST = {
+    ${whitelist}
+        };
+    }""")
+
+    annotations = read_annotations(annotations_filename)
+    generated_class = generate_class(template, annotations)
+
+    try:
+        output.write(generated_class)
+    except IOError as ex:
+        print("Error while writing out the generated file:\n" + str(ex) + "\n")
+        sys.exit(1)
--- a/toolkit/crashreporter/moz.build
+++ b/toolkit/crashreporter/moz.build
@@ -4,23 +4,29 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 SPHINX_TREES['crashreporter'] = 'docs'
 
 with Files('docs/**'):
     SCHEDULES.exclusive = ['docs']
 
+GENERATED_FILES += [
+    'CrashAnnotations.h',
+]
+
 EXPORTS += [
+    '!CrashAnnotations.h',
     'nsExceptionHandler.h',
 ]
 
 JAR_MANIFESTS += ['jar.mn']
 
 UNIFIED_SOURCES = [
+    'CrashAnnotations.cpp',
     'nsExceptionHandlerUtils.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['MOZ_CRASHREPORTER']:
     if CONFIG['OS_ARCH'] == 'WINNT':
         DIRS += [
@@ -120,14 +126,21 @@ if CONFIG['MOZ_CRASHREPORTER']:
 
     if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
         CXXFLAGS += ['-Wno-shadow']
 else:
     UNIFIED_SOURCES += [
         'nsDummyExceptionHandler.cpp',
     ]
 
+# Generate CrashAnnotations.h
+crash_annotations = GENERATED_FILES['CrashAnnotations.h']
+crash_annotations.script = 'generate_crash_reporter_sources.py:emit_header'
+crash_annotations.inputs = [
+    'CrashAnnotations.h.in',
+    'CrashAnnotations.yaml',
+]
 
 with Files('**'):
     BUG_COMPONENT = ('Toolkit', 'Crash Reporting')
 
 if CONFIG['CC_TYPE'] == 'clang-cl':
     AllowCompilerWarnings()  # workaround for bug 1090497
--- a/toolkit/crashreporter/nsDummyExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsDummyExceptionHandler.cpp
@@ -61,24 +61,41 @@ SetupExtraData(nsIFile* aAppDataDirector
 
 nsresult
 UnsetExceptionHandler()
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 nsresult
-AnnotateCrashReport(const nsACString& key,
-                    const nsACString& data)
+AnnotateCrashReport(Annotation key, bool data)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+AnnotateCrashReport(Annotation key, int data)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 nsresult
-RemoveCrashReportAnnotation(const nsACString& key)
+AnnotateCrashReport(Annotation key, unsigned int data)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+AnnotateCrashReport(Annotation key, const nsACString& data)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+RemoveCrashReportAnnotation(Annotation key)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 nsresult
 SetGarbageCollecting(bool collecting)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -7,16 +7,17 @@
 #include "nsExceptionHandler.h"
 #include "nsExceptionHandlerUtils.h"
 
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryService.h"
 #include "nsDataHashtable.h"
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/EnumeratedRange.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
 #include "mozilla/Unused.h"
 #include "mozilla/Printf.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/ipc/CrashReporterClient.h"
@@ -228,17 +229,17 @@ static const char* androidStartServiceCo
 // After targeting API 26 (Oreo) we ned to use a JobIntentService for the background
 // work regarding crash reporting. That Service needs a unique Job Id.
 static const char* androidCrashReporterJobId = nullptr;
 #endif
 
 // this holds additional data sent via the API
 static Mutex* crashReporterAPILock;
 static Mutex* notesFieldLock;
-static AnnotationTable* crashReporterAPIData_Hash;
+static AnnotationTable crashReporterAPIData_Table;
 static nsCString* crashReporterAPIData = nullptr;
 static nsCString* crashEventAPIData = nullptr;
 static nsCString* notesField = nullptr;
 static bool isGarbageCollecting;
 static uint32_t eventloopNestingLevel = 0;
 
 // Avoid a race during application termination.
 static Mutex* dumpSafetyLock;
@@ -319,25 +320,16 @@ public:
 
   NS_IMETHOD Run() override;
 
 private:
   uint32_t mPID;
 };
 #endif // MOZ_CRASHREPORTER_INJECTOR
 
-// Crashreporter annotations that we don't send along in subprocess reports.
-static const char* kSubprocessBlacklist[] = {
-  "FramePoisonBase",
-  "FramePoisonSize",
-  "StartupCrash",
-  "StartupTime",
-  "URL"
-};
-
 // If annotations are attempted before the crash reporter is enabled,
 // they queue up here.
 class DelayedNote;
 nsTArray<nsAutoPtr<DelayedNote> >* gDelayedAnnotations;
 
 #if defined(XP_WIN)
 // the following are used to prevent other DLLs reverting the last chance
 // exception handler to the windows default. Any attempt to change the
@@ -674,23 +666,26 @@ WriteString(PlatformWriter& pw, const ch
   size_t len = my_strlen(str);
 #else
   size_t len = strlen(str);
 #endif
 
   pw.WriteBuffer(str, len);
 }
 
-template<int N>
 static void
-WriteAnnotation(PlatformWriter& pw, const char (&name)[N],
-                const char* value) {
-  WriteLiteral(pw, name);
+WriteAnnotation(PlatformWriter& pw, const Annotation name,
+                const char* value, size_t len = 0) {
+  WriteString(pw, AnnotationToString(name));
   WriteLiteral(pw, "=");
-  WriteString(pw, value);
+  if (len == 0) {
+    WriteString(pw, value);
+  } else {
+    pw.WriteBuffer(value, len);
+  }
   WriteLiteral(pw, "\n");
 };
 
 /**
  * If minidump_id is null, we assume that dump_path contains the full
  * dump file path.
  */
 static void
@@ -732,23 +727,28 @@ WriteGlobalMemoryStatus(PlatformWriter* 
     conversionFunc(statex.field, buffer, 10);                  \
     if (apiData) {                                             \
       WriteAnnotation(*apiData, name, buffer);                  \
     }                                                          \
     if (eventFile) {                                           \
       WriteAnnotation(*eventFile, name, buffer);                \
     }
 
-    WRITE_STATEX_FIELD(dwMemoryLoad, "SystemMemoryUsePercentage", ltoa);
-    WRITE_STATEX_FIELD(ullTotalVirtual, "TotalVirtualMemory", _ui64toa);
-    WRITE_STATEX_FIELD(ullAvailVirtual, "AvailableVirtualMemory", _ui64toa);
-    WRITE_STATEX_FIELD(ullTotalPageFile, "TotalPageFile", _ui64toa);
-    WRITE_STATEX_FIELD(ullAvailPageFile, "AvailablePageFile", _ui64toa);
-    WRITE_STATEX_FIELD(ullTotalPhys, "TotalPhysicalMemory", _ui64toa);
-    WRITE_STATEX_FIELD(ullAvailPhys, "AvailablePhysicalMemory", _ui64toa);
+    WRITE_STATEX_FIELD(dwMemoryLoad, Annotation::SystemMemoryUsePercentage,
+                       ltoa);
+    WRITE_STATEX_FIELD(ullTotalVirtual, Annotation::TotalVirtualMemory,
+                       _ui64toa);
+    WRITE_STATEX_FIELD(ullAvailVirtual, Annotation::AvailableVirtualMemory,
+                       _ui64toa);
+    WRITE_STATEX_FIELD(ullTotalPageFile, Annotation::TotalPageFile, _ui64toa);
+    WRITE_STATEX_FIELD(ullAvailPageFile, Annotation::AvailablePageFile,
+                       _ui64toa);
+    WRITE_STATEX_FIELD(ullTotalPhys, Annotation::TotalPhysicalMemory, _ui64toa);
+    WRITE_STATEX_FIELD(ullAvailPhys, Annotation::AvailablePhysicalMemory,
+                       _ui64toa);
 
 #undef WRITE_STATEX_FIELD
   }
 }
 #endif
 
 #if !defined(MOZ_WIDGET_ANDROID)
 
@@ -1030,101 +1030,99 @@ MinidumpCallback(
       OpenAPIData(apiData, descriptor.path());
 #else
       OpenAPIData(apiData, dump_path, minidump_id);
 #endif
       apiData.WriteBuffer(crashReporterAPIData->get(), crashReporterAPIData->Length());
     }
 
     if (currentSessionId) {
-      WriteAnnotation(apiData, "TelemetrySessionId", currentSessionId);
-      WriteAnnotation(eventFile, "TelemetrySessionId", currentSessionId);
+      WriteAnnotation(apiData, Annotation::TelemetrySessionId,
+                      currentSessionId);
+      WriteAnnotation(eventFile, Annotation::TelemetrySessionId,
+                      currentSessionId);
     }
 
-    WriteAnnotation(apiData, "CrashTime", crashTimeString);
-    WriteAnnotation(eventFile, "CrashTime", crashTimeString);
-
-    WriteAnnotation(apiData, "UptimeTS", uptimeTSString);
-    WriteAnnotation(eventFile, "UptimeTS", uptimeTSString);
+    WriteAnnotation(apiData, Annotation::CrashTime, crashTimeString);
+    WriteAnnotation(eventFile, Annotation::CrashTime, crashTimeString);
+
+    WriteAnnotation(apiData, Annotation::UptimeTS, uptimeTSString);
+    WriteAnnotation(eventFile, Annotation::UptimeTS, uptimeTSString);
 
     if (timeSinceLastCrash != 0) {
-      WriteAnnotation(apiData, "SecondsSinceLastCrash",
+      WriteAnnotation(apiData, Annotation::SecondsSinceLastCrash,
                       timeSinceLastCrashString);
-      WriteAnnotation(eventFile, "SecondsSinceLastCrash",
+      WriteAnnotation(eventFile, Annotation::SecondsSinceLastCrash,
                       timeSinceLastCrashString);
     }
     if (isGarbageCollecting) {
-      WriteAnnotation(apiData, "IsGarbageCollecting", "1");
-      WriteAnnotation(eventFile, "IsGarbageCollecting", "1");
+      WriteAnnotation(apiData, Annotation::IsGarbageCollecting, "1");
+      WriteAnnotation(eventFile, Annotation::IsGarbageCollecting, "1");
     }
 
     char buffer[128];
 
     if (eventloopNestingLevel > 0) {
       XP_STOA(eventloopNestingLevel, buffer, 10);
-      WriteAnnotation(apiData, "EventLoopNestingLevel", buffer);
-      WriteAnnotation(eventFile, "EventLoopNestingLevel", buffer);
+      WriteAnnotation(apiData, Annotation::EventLoopNestingLevel, buffer);
+      WriteAnnotation(eventFile, Annotation::EventLoopNestingLevel, buffer);
     }
 
 #ifdef XP_WIN
     if (gBreakpadReservedVM) {
       _ui64toa(uintptr_t(gBreakpadReservedVM), buffer, 10);
-      WriteAnnotation(apiData, "BreakpadReserveAddress", buffer);
+      WriteAnnotation(apiData, Annotation::BreakpadReserveAddress, buffer);
       _ui64toa(kReserveSize, buffer, 10);
-      WriteAnnotation(apiData, "BreakpadReserveSize", buffer);
+      WriteAnnotation(apiData, Annotation::BreakpadReserveSize, buffer);
     }
 
 #ifdef HAS_DLL_BLOCKLIST
     if (apiData.Valid()) {
       DllBlocklist_WriteNotes(apiData.Handle());
       DllBlocklist_WriteNotes(eventFile.Handle());
     }
 #endif
     WriteGlobalMemoryStatus(&apiData, &eventFile);
 #endif // XP_WIN
 
     char* rust_panic_reason;
     size_t rust_panic_len;
     if (get_rust_panic_reason(&rust_panic_reason, &rust_panic_len)) {
       // rust_panic_reason is not null-terminated.
-      WriteLiteral(apiData, "MozCrashReason=");
-      apiData.WriteBuffer(rust_panic_reason, rust_panic_len);
-      WriteLiteral(apiData, "\n");
-      WriteLiteral(eventFile, "MozCrashReason=");
-      eventFile.WriteBuffer(rust_panic_reason, rust_panic_len);
-      WriteLiteral(eventFile, "\n");
+      WriteAnnotation(apiData, Annotation::MozCrashReason, rust_panic_reason,
+                      rust_panic_len);
+      WriteAnnotation(eventFile, Annotation::MozCrashReason, rust_panic_reason,
+                      rust_panic_len);
     } else if (gMozCrashReason) {
-      WriteAnnotation(apiData, "MozCrashReason", gMozCrashReason);
-      WriteAnnotation(eventFile, "MozCrashReason", gMozCrashReason);
+      WriteAnnotation(apiData, Annotation::MozCrashReason, gMozCrashReason);
+      WriteAnnotation(eventFile, Annotation::MozCrashReason, gMozCrashReason);
     }
 
     if (oomAllocationSizeBuffer[0]) {
-      WriteAnnotation(apiData, "OOMAllocationSize", oomAllocationSizeBuffer);
-      WriteAnnotation(eventFile, "OOMAllocationSize", oomAllocationSizeBuffer);
+      WriteAnnotation(apiData, Annotation::OOMAllocationSize,
+                      oomAllocationSizeBuffer);
+      WriteAnnotation(eventFile, Annotation::OOMAllocationSize,
+                      oomAllocationSizeBuffer);
     }
 
     if (texturesSizeBuffer[0]) {
-      WriteAnnotation(apiData, "TextureUsage", texturesSizeBuffer);
-      WriteAnnotation(eventFile, "TextureUsage", texturesSizeBuffer);
+      WriteAnnotation(apiData, Annotation::TextureUsage, texturesSizeBuffer);
+      WriteAnnotation(eventFile, Annotation::TextureUsage, texturesSizeBuffer);
     }
 
     if (memoryReportPath) {
-      WriteLiteral(apiData, "ContainsMemoryReport=1\n");
-      WriteLiteral(eventFile, "ContainsMemoryReport=1\n");
+      WriteAnnotation(apiData, Annotation::ContainsMemoryReport, "1");
+      WriteAnnotation(eventFile, Annotation::ContainsMemoryReport, "1");
     }
 
     std::function<void(const char*)> getThreadAnnotationCB =
-      [&] (const char * aAnnotation) -> void {
-      if (aAnnotation) {
-        WriteLiteral(apiData, "ThreadIdNameMapping=");
-        WriteLiteral(eventFile, "ThreadIdNameMapping=");
-        WriteString(apiData, aAnnotation);
-        WriteString(eventFile, aAnnotation);
-        WriteLiteral(apiData, "\n");
-        WriteLiteral(eventFile, "\n");
+      [&] (const char* aValue) -> void {
+      if (aValue) {
+        WriteAnnotation(apiData, Annotation::ThreadIdNameMapping, aValue);
+        WriteAnnotation(eventFile, Annotation::ThreadIdNameMapping, aValue);
       }
     };
     GetFlatThreadAnnotation(getThreadAnnotationCB, false);
   }
 
   if (!doReport) {
 #ifdef XP_WIN
     TerminateProcess(GetCurrentProcess(), 1);
@@ -1281,36 +1279,34 @@ PrepareChildExceptionTimeAnnotations(voi
 #endif
 
   char oomAllocationSizeBuffer[32] = "";
   if (gOOMAllocationSize) {
     XP_STOA(gOOMAllocationSize, oomAllocationSizeBuffer, 10);
   }
 
   if (oomAllocationSizeBuffer[0]) {
-    WriteAnnotation(apiData, "OOMAllocationSize", oomAllocationSizeBuffer);
+    WriteAnnotation(apiData, Annotation::OOMAllocationSize,
+                    oomAllocationSizeBuffer);
   }
 
   char* rust_panic_reason;
   size_t rust_panic_len;
   if (get_rust_panic_reason(&rust_panic_reason, &rust_panic_len)) {
     // rust_panic_reason is not null-terminated.
-    WriteLiteral(apiData, "MozCrashReason=");
-    apiData.WriteBuffer(rust_panic_reason, rust_panic_len);
-    WriteLiteral(apiData, "\n");
+    WriteAnnotation(apiData, Annotation::MozCrashReason, rust_panic_reason,
+                    rust_panic_len);
   } else if (gMozCrashReason) {
-    WriteAnnotation(apiData, "MozCrashReason", gMozCrashReason);
+    WriteAnnotation(apiData, Annotation::MozCrashReason, gMozCrashReason);
   }
 
   std::function<void(const char*)> getThreadAnnotationCB =
-    [&] (const char * aAnnotation) -> void {
-    if (aAnnotation) {
-      WriteLiteral(apiData, "ThreadIdNameMapping=");
-      WriteString(apiData, aAnnotation);
-      WriteLiteral(apiData, "\n");
+    [&] (const char * aValue) -> void {
+    if (aValue) {
+      WriteAnnotation(apiData, Annotation::ThreadIdNameMapping, aValue);
     }
   };
   GetFlatThreadAnnotation(getThreadAnnotationCB, true);
 }
 
 #ifdef XP_WIN
 static void
 ReserveBreakpadVM()
@@ -1503,20 +1499,16 @@ nsresult SetExceptionHandler(nsIFile* aX
   crashReporterAPIData = new nsCString();
   crashEventAPIData = new nsCString();
 
   NS_ASSERTION(!crashReporterAPILock, "Shouldn't have a lock yet");
   crashReporterAPILock = new Mutex("crashReporterAPILock");
   NS_ASSERTION(!notesFieldLock, "Shouldn't have a lock yet");
   notesFieldLock = new Mutex("notesFieldLock");
 
-  crashReporterAPIData_Hash =
-    new nsDataHashtable<nsCStringHashKey,nsCString>();
-  NS_ENSURE_TRUE(crashReporterAPIData_Hash, NS_ERROR_OUT_OF_MEMORY);
-
   notesField = new nsCString();
   NS_ENSURE_TRUE(notesField, NS_ERROR_OUT_OF_MEMORY);
 
 #if !defined(MOZ_WIDGET_ANDROID)
   // Locate the crash reporter executable
   nsAutoString crashReporterPath_temp;
   nsresult rv = LocateExecutable(aXREDirectory,
                                  NS_LITERAL_CSTRING(CRASH_REPORTER_FILENAME),
@@ -1673,18 +1665,17 @@ nsresult SetExceptionHandler(nsIFile* aX
     printf_stderr ("SetUnhandledExceptionFilter hook failed; crash reporter is vulnerable.\n");
 #endif
 #endif
 
   // store application start time
   char timeString[32];
   time_t startupTime = time(nullptr);
   XP_TTOA(startupTime, timeString, 10);
-  AnnotateCrashReport(NS_LITERAL_CSTRING("StartupTime"),
-                      nsDependentCString(timeString));
+  AnnotateCrashReport(Annotation::StartupTime, nsDependentCString(timeString));
 
 #if defined(XP_MACOSX)
   // On OS X, many testers like to see the OS crash reporting dialog
   // since it offers immediate stack traces.  We allow them to set
   // a default to pass exceptions to the OS handler.
   Boolean keyExistsAndHasValidFormat = false;
   Boolean prefValue = ::CFPreferencesGetAppBooleanValue(CFSTR("OSCrashReporter"),
                                                         kCFPreferencesCurrentApplication,
@@ -1928,17 +1919,17 @@ nsresult SetupExtraData(nsIFile* aAppDat
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsAutoCString data;
   if(NS_SUCCEEDED(GetOrInit(dataDirectory,
                             NS_LITERAL_CSTRING("InstallTime") + aBuildID,
                             data, InitInstallTime)))
-    AnnotateCrashReport(NS_LITERAL_CSTRING("InstallTime"), data);
+    AnnotateCrashReport(Annotation::InstallTime, data);
 
   // this is a little different, since we can't init it with anything,
   // since it's stored at crash time, and we can't annotate the
   // crash report with the stored value, since we really want
   // (now - LastCrash), so we just get a value if it exists,
   // and store it in a time_t value.
   if(NS_SUCCEEDED(GetOrInit(dataDirectory, NS_LITERAL_CSTRING("LastCrash"),
                             data, nullptr))) {
@@ -1986,18 +1977,19 @@ nsresult UnsetExceptionHandler()
   // allow SetUnhandledExceptionFilter
   gBlockUnhandledExceptionFilter = false;
 #endif
 
   delete gExceptionHandler;
 
   // do this here in the unlikely case that we succeeded in allocating
   // our strings but failed to allocate gExceptionHandler.
-  delete crashReporterAPIData_Hash;
-  crashReporterAPIData_Hash = nullptr;
+  std::fill(crashReporterAPIData_Table.begin(),
+            crashReporterAPIData_Table.end(),
+            EmptyCString());
 
   delete crashReporterAPILock;
   crashReporterAPILock = nullptr;
 
   delete notesFieldLock;
   notesFieldLock = nullptr;
 
   delete crashReporterAPIData;
@@ -2074,22 +2066,18 @@ static void ReplaceChar(nsCString& str, 
 
     str.BeginReading(iter);
     iter.advance(pos + replacement.Length() - 1);
     str.EndReading(end);
   }
 }
 
 static nsresult
-EscapeAnnotation(const nsACString& key, const nsACString& data, nsCString& escapedData)
+EscapeAnnotation(const nsACString& data, nsCString& escapedData)
 {
-  if (FindInReadable(NS_LITERAL_CSTRING("="), key) ||
-      FindInReadable(NS_LITERAL_CSTRING("\n"), key))
-    return NS_ERROR_INVALID_ARG;
-
   if (FindInReadable(NS_LITERAL_CSTRING("\0"), data))
     return NS_ERROR_INVALID_ARG;
 
   escapedData = data;
 
   // escape backslashes
   ReplaceChar(escapedData, NS_LITERAL_CSTRING("\\"),
               NS_LITERAL_CSTRING("\\\\"));
@@ -2097,35 +2085,35 @@ EscapeAnnotation(const nsACString& key, 
   ReplaceChar(escapedData, NS_LITERAL_CSTRING("\n"),
               NS_LITERAL_CSTRING("\\n"));
   return NS_OK;
 }
 
 class DelayedNote
 {
  public:
-  DelayedNote(const nsACString& aKey, const nsACString& aData)
-  : mKey(aKey), mData(aData), mType(Annotation) {}
+  DelayedNote(Annotation aKey, const nsACString& aData)
+  : mKey(aKey), mData(aData), mType(CrashAnnotation) {}
 
   explicit DelayedNote(const nsACString& aData)
   : mData(aData), mType(AppNote) {}
 
   void Run()
   {
-    if (mType == Annotation) {
+    if (mType == CrashAnnotation) {
       AnnotateCrashReport(mKey, mData);
     } else {
       AppendAppNotesToCrashReport(mData);
     }
   }
 
  private:
-  nsCString mKey;
+  Annotation mKey;
   nsCString mData;
-  enum AnnotationType { Annotation, AppNote } mType;
+  enum AnnotationType { CrashAnnotation, AppNote } mType;
 };
 
 static void
 EnqueueDelayedNote(DelayedNote* aNote)
 {
   if (!gDelayedAnnotations) {
     gDelayedAnnotations = new nsTArray<nsAutoPtr<DelayedNote> >();
   }
@@ -2139,66 +2127,88 @@ RunAndCleanUpDelayedNotes()
     for (nsAutoPtr<DelayedNote>& note : *gDelayedAnnotations) {
       note->Run();
     }
     delete gDelayedAnnotations;
     gDelayedAnnotations = nullptr;
   }
 }
 
-nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data)
+nsresult AnnotateCrashReport(Annotation key, bool data)
+{
+  return AnnotateCrashReport(key, data ? NS_LITERAL_CSTRING("1")
+                                       : NS_LITERAL_CSTRING("0"));
+}
+
+nsresult AnnotateCrashReport(Annotation key, int data)
+{
+  nsAutoCString dataString;
+  dataString.AppendInt(data);
+
+  return AnnotateCrashReport(key, dataString);
+}
+
+nsresult AnnotateCrashReport(Annotation key, unsigned int data)
+{
+  nsAutoCString dataString;
+  dataString.AppendInt(data);
+
+  return AnnotateCrashReport(key, dataString);
+}
+
+nsresult AnnotateCrashReport(Annotation key, const nsACString& data)
 {
   if (!GetEnabled())
     return NS_ERROR_NOT_INITIALIZED;
 
   nsCString escapedData;
-  nsresult rv = EscapeAnnotation(key, data, escapedData);
+  nsresult rv = EscapeAnnotation(data, escapedData);
   if (NS_FAILED(rv))
     return rv;
 
   if (!XRE_IsParentProcess()) {
     // The newer CrashReporterClient can be used from any thread.
     if (RefPtr<CrashReporterClient> client = CrashReporterClient::GetSingleton()) {
-      client->AnnotateCrashReport(nsCString(key), escapedData);
+      client->AnnotateCrashReport(key, escapedData);
       return NS_OK;
     }
 
     // EnqueueDelayedNote() can only be called on the main thread.
     MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
     EnqueueDelayedNote(new DelayedNote(key, data));
     return NS_OK;
   }
 
   MutexAutoLock lock(*crashReporterAPILock);
 
-  crashReporterAPIData_Hash->Put(key, escapedData);
+  crashReporterAPIData_Table[key] = escapedData;
 
   // now rebuild the file contents
   crashReporterAPIData->Truncate(0);
   crashEventAPIData->Truncate(0);
-  for (auto it = crashReporterAPIData_Hash->Iter(); !it.Done(); it.Next()) {
-    const nsACString& key = it.Key();
-    nsCString entry = it.Data();
+  for (auto key : MakeEnumeratedRange(Annotation::Count)) {
+    nsDependentCString str(AnnotationToString(key));
+    nsCString entry = crashReporterAPIData_Table[key];
     if (!entry.IsEmpty()) {
       NS_NAMED_LITERAL_CSTRING(kEquals, "=");
       NS_NAMED_LITERAL_CSTRING(kNewline, "\n");
-      nsAutoCString line = key + kEquals + entry + kNewline;
+      nsAutoCString line = str + kEquals + entry + kNewline;
 
       crashReporterAPIData->Append(line);
       crashEventAPIData->Append(line);
     }
   }
 
   return NS_OK;
 }
 
-nsresult RemoveCrashReportAnnotation(const nsACString& key)
+nsresult RemoveCrashReportAnnotation(Annotation key)
 {
-  return AnnotateCrashReport(key, NS_LITERAL_CSTRING(""));
+  return AnnotateCrashReport(key, EmptyCString());
 }
 
 nsresult SetGarbageCollecting(bool collecting)
 {
   if (!GetEnabled())
     return NS_ERROR_NOT_INITIALIZED;
 
   isGarbageCollecting = collecting;
@@ -2225,17 +2235,17 @@ nsresult AppendAppNotesToCrashReport(con
   if (FindInReadable(NS_LITERAL_CSTRING("\0"), data))
     return NS_ERROR_INVALID_ARG;
 
   if (!XRE_IsParentProcess()) {
     // Since we don't go through AnnotateCrashReport in the parent process,
     // we must ensure that the data is escaped and valid before the parent
     // sees it.
     nsCString escapedData;
-    nsresult rv = EscapeAnnotation(NS_LITERAL_CSTRING("Notes"), data, escapedData);
+    nsresult rv = EscapeAnnotation(data, escapedData);
     if (NS_FAILED(rv))
       return rv;
 
     if (RefPtr<CrashReporterClient> client = CrashReporterClient::GetSingleton()) {
       client->AppendAppNotes(escapedData);
       return NS_OK;
     }
 
@@ -2244,30 +2254,31 @@ nsresult AppendAppNotesToCrashReport(con
 
     EnqueueDelayedNote(new DelayedNote(data));
     return NS_OK;
   }
 
   MutexAutoLock lock(*notesFieldLock);
 
   notesField->Append(data);
-  return AnnotateCrashReport(NS_LITERAL_CSTRING("Notes"), *notesField);
+  return AnnotateCrashReport(Annotation::Notes, *notesField);
 }
 
 // Returns true if found, false if not found.
 static bool
-GetAnnotation(const nsACString& key, nsACString& data)
+GetAnnotation(CrashReporter::Annotation key, nsACString& data)
 {
   if (!gExceptionHandler)
     return false;
 
   MutexAutoLock lock(*crashReporterAPILock);
-  nsAutoCString entry;
-  if (!crashReporterAPIData_Hash->Get(key, &entry))
+  nsCString entry = crashReporterAPIData_Table[key];
+  if (entry.IsEmpty()) {
     return false;
+  }
 
   data = entry;
   return true;
 }
 
 nsresult RegisterAppMemory(void* ptr, size_t length)
 {
   if (!GetEnabled())
@@ -2305,25 +2316,24 @@ void SetIncludeContextHeap(bool aValue)
 #endif
 }
 
 bool GetServerURL(nsACString& aServerURL)
 {
   if (!gExceptionHandler)
     return false;
 
-  return GetAnnotation(NS_LITERAL_CSTRING("ServerURL"), aServerURL);
+  return GetAnnotation(CrashReporter::Annotation::ServerURL, aServerURL);
 }
 
 nsresult SetServerURL(const nsACString& aServerURL)
 {
   // store server URL with the API data
   // the client knows to handle this specially
-  return AnnotateCrashReport(NS_LITERAL_CSTRING("ServerURL"),
-                             aServerURL);
+  return AnnotateCrashReport(Annotation::ServerURL, aServerURL);
 }
 
 nsresult
 SetRestartArgs(int argc, char** argv)
 {
   if (!gExceptionHandler)
     return NS_OK;
 
@@ -2936,35 +2946,21 @@ AppendExtraData(const nsAString& id, con
   if (!GetExtraFileForID(id, getter_AddRefs(extraFile)))
     return false;
   return AppendExtraData(extraFile, data);
 }
 
 //-----------------------------------------------------------------------------
 // Helpers for AppendExtraData()
 //
-struct Blacklist {
-  Blacklist() : mItems(nullptr), mLen(0) { }
-  Blacklist(const char** items, int len) : mItems(items), mLen(len) { }
-
-  bool Contains(const nsACString& key) const {
-    for (int i = 0; i < mLen; ++i)
-      if (key.EqualsASCII(mItems[i]))
-        return true;
-    return false;
-  }
-
-  const char** mItems;
-  const int mLen;
-};
-
 static void
-WriteAnnotation(PRFileDesc* fd, const nsACString& key, const nsACString& value)
+WriteAnnotation(PRFileDesc* fd, const Annotation key, const nsACString& value)
 {
-  PR_Write(fd, key.BeginReading(), key.Length());
+  const char* annotation = AnnotationToString(key);
+  PR_Write(fd, annotation, strlen(annotation));
   PR_Write(fd, "=", 1);
   PR_Write(fd, value.BeginReading(), value.Length());
   PR_Write(fd, "\n", 1);
 }
 
 template<int N>
 static void
 WriteLiteral(PRFileDesc* fd, const char (&str)[N])
@@ -2974,68 +2970,70 @@ WriteLiteral(PRFileDesc* fd, const char 
 
 /*
  * If accessing the AnnotationTable |data| argument requires locks, the
  * caller should ensure the required locks are already held.
  */
 static bool
 WriteExtraData(nsIFile* extraFile,
                const AnnotationTable& data,
-               const Blacklist& blacklist,
                bool writeCrashTime=false,
-               bool truncate=false)
+               bool truncate=false,
+               bool content=false)
 {
   PRFileDesc* fd;
   int truncOrAppend = truncate ? PR_TRUNCATE : PR_APPEND;
   nsresult rv =
     extraFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | truncOrAppend,
                                 0600, &fd);
   if (NS_FAILED(rv))
     return false;
 
-  for (auto iter = data.ConstIter(); !iter.Done(); iter.Next()) {
-    // Skip entries in the blacklist.
-    const nsACString& key = iter.Key();
-    if (blacklist.Contains(key)) {
-        continue;
+  for (auto key : MakeEnumeratedRange(Annotation::Count)) {
+    nsCString value = data[key];
+    // Skip entries in the blacklist and empty entries.
+    if ((content && IsAnnotationBlacklistedForContent(key)) ||
+         value.IsEmpty()) {
+      continue;
     }
-    WriteAnnotation(fd, key, iter.Data());
+    WriteAnnotation(fd, key, value);
   }
 
   if (writeCrashTime) {
     time_t crashTime = time(nullptr);
     char crashTimeString[32];
     XP_TTOA(crashTime, crashTimeString, 10);
 
     WriteAnnotation(fd,
-                    nsDependentCString("CrashTime"),
+                    Annotation::CrashTime,
                     nsDependentCString(crashTimeString));
 
     double uptimeTS = (TimeStamp::NowLoRes() -
                        TimeStamp::ProcessCreation()).ToSecondsSigDigits();
     char uptimeTSString[64];
     SimpleNoCLibDtoA(uptimeTS, uptimeTSString, sizeof(uptimeTSString));
 
     WriteAnnotation(fd,
-                    nsDependentCString("UptimeTS"),
+                    Annotation::UptimeTS,
                     nsDependentCString(uptimeTSString));
   }
 
   if (memoryReportPath) {
-    WriteLiteral(fd, "ContainsMemoryReport=1\n");
+    WriteAnnotation(fd, Annotation::ContainsMemoryReport,
+                    NS_LITERAL_CSTRING("1"));
   }
 
   PR_Close(fd);
   return true;
 }
 
 bool
 AppendExtraData(nsIFile* extraFile, const AnnotationTable& data)
 {
-  return WriteExtraData(extraFile, data, Blacklist());
+  return WriteExtraData(extraFile, data);
 }
 
 static bool
 IsDataEscaped(char* aData)
 {
   if (strchr(aData, '\n')) {
     // There should not be any newlines
     return false;
@@ -3070,47 +3068,55 @@ ReadAndValidateExceptionTimeAnnotations(
     if (dataLen > 0 && data[dataLen - 1] == '\n') {
       data[dataLen - 1] = 0;
       --dataLen;
     }
     // There should not be any newlines in the key
     if (strchr(line, '\n')) {
       break;
     }
+    // The annotation sould be known
+    Annotation annotation;
+    if (!AnnotationFromString(annotation, line)) {
+      break;
+    }
     // Data should have been escaped by the child
     if (!IsDataEscaped(data)) {
       break;
     }
     // Looks good, save the (line,data) pair
-    aAnnotations.Put(nsDependentCString(line),
-                     nsDependentCString(data, dataLen));
+    aAnnotations[annotation] = nsDependentCString(data, dataLen);
   }
 }
 
 /**
+ * Writes extra data in the .extra file corresponding to the specified
+ * minidump. If `content` is set to true then this assumes that of a child
+ * process.
+ *
  * NOTE: One side effect of this function is that it deletes the
  * GeckoChildCrash<pid>.extra file if it exists, once processed.
  */
 static bool
 WriteExtraForMinidump(nsIFile* minidump,
                       uint32_t pid,
-                      const Blacklist& blacklist,
+                      bool content,
                       nsIFile** extraFile)
 {
   nsCOMPtr<nsIFile> extra;
   if (!GetExtraFileForMinidump(minidump, getter_AddRefs(extra))) {
     return false;
   }
 
   {
     MutexAutoLock lock(*crashReporterAPILock);
-    if (!WriteExtraData(extra, *crashReporterAPIData_Hash,
-                        blacklist,
+    if (!WriteExtraData(extra, crashReporterAPIData_Table,
                         true /*write crash time*/,
-                        true /*truncate*/)) {
+                        true /*truncate*/,
+                        content)) {
       return false;
     }
   }
 
   if (pid && processToCrashFd.count(pid)) {
     PRFileDesc* prFd = processToCrashFd[pid];
     processToCrashFd.erase(pid);
     FILE* fd;
@@ -3205,21 +3211,20 @@ OnChildProcessDumpRequested(void* aConte
 
   uint32_t pid =
 #ifdef XP_MACOSX
     aClientInfo.pid();
 #else
     aClientInfo->pid();
 #endif
 
-  if (!WriteExtraForMinidump(minidump, pid,
-                             Blacklist(kSubprocessBlacklist,
-                                       ArrayLength(kSubprocessBlacklist)),
-                             getter_AddRefs(extraFile)))
+  if (!WriteExtraForMinidump(minidump, pid, /* content */ true,
+                             getter_AddRefs(extraFile))) {
     return;
+  }
 
   if (ShouldReport()) {
     nsCOMPtr<nsIFile> memoryReport;
     if (memoryReportPath) {
       CreateFileFromPath(memoryReportPath, getter_AddRefs(memoryReport));
       MOZ_ASSERT(memoryReport);
     }
     MoveToPending(minidump, extraFile, memoryReport);
@@ -3732,17 +3737,18 @@ PairedDumpCallbackExtra(
 #ifdef XP_WIN32
                      nullptr, nullptr,
 #endif
                      succeeded);
 
   nsCOMPtr<nsIFile>& minidump = *static_cast< nsCOMPtr<nsIFile>* >(context);
 
   nsCOMPtr<nsIFile> extra;
-  return WriteExtraForMinidump(minidump, 0, Blacklist(), getter_AddRefs(extra));
+  return WriteExtraForMinidump(minidump, 0, /* content */ false,
+                               getter_AddRefs(extra));
 }
 
 ThreadId
 CurrentThreadId()
 {
 #if defined(XP_WIN)
   return ::GetCurrentThreadId();
 #elif defined(XP_LINUX)
--- a/toolkit/crashreporter/nsExceptionHandler.h
+++ b/toolkit/crashreporter/nsExceptionHandler.h
@@ -8,16 +8,19 @@
 // configured with --disable-crashreporter. If you add or remove a function
 // from this header you must update both implementations otherwise you'll break
 // builds that disable the crash reporter.
 
 #ifndef nsExceptionHandler_h__
 #define nsExceptionHandler_h__
 
 #include "mozilla/Assertions.h"
+#include "mozilla/EnumeratedArray.h"
+
+#include "CrashAnnotations.h"
 
 #include <functional>
 #include <stddef.h>
 #include <stdint.h>
 #include "nsError.h"
 #include "nsString.h"
 #include "prio.h"
 
@@ -32,18 +35,16 @@
 #include <mach/mach.h>
 #endif
 
 #if defined(XP_LINUX)
 #include <signal.h>
 #endif
 
 class nsIFile;
-template<class KeyClass, class DataType> class nsDataHashtable;
-class nsCStringHashKey;
 
 namespace CrashReporter {
 
 /**
  * Returns true if the crash reporter is using the dummy implementation.
  */
 static inline bool
 IsDummy() {
@@ -87,18 +88,21 @@ bool     GetServerURL(nsACString& aServe
 nsresult SetServerURL(const nsACString& aServerURL);
 bool     GetMinidumpPath(nsAString& aPath);
 nsresult SetMinidumpPath(const nsAString& aPath);
 
 
 // AnnotateCrashReport, RemoveCrashReportAnnotation and
 // AppendAppNotesToCrashReport may be called from any thread in a chrome
 // process, but may only be called from the main thread in a content process.
-nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data);
-nsresult RemoveCrashReportAnnotation(const nsACString& key);
+nsresult AnnotateCrashReport(Annotation key, bool data);
+nsresult AnnotateCrashReport(Annotation key, int data);
+nsresult AnnotateCrashReport(Annotation key, unsigned int data);
+nsresult AnnotateCrashReport(Annotation key, const nsACString& data);
+nsresult RemoveCrashReportAnnotation(Annotation key);
 nsresult AppendAppNotesToCrashReport(const nsACString& data);
 
 void AnnotateOOMAllocationSize(size_t size);
 void AnnotateTexturesSize(size_t size);
 nsresult SetGarbageCollecting(bool collecting);
 void SetEventloopNestingLevel(uint32_t level);
 void SetMinidumpAnalysisAllThreads();
 
@@ -108,17 +112,18 @@ nsresult SetupExtraData(nsIFile* aAppDat
 // Registers an additional memory region to be included in the minidump
 nsresult RegisterAppMemory(void* ptr, size_t length);
 nsresult UnregisterAppMemory(void* ptr);
 
 // Include heap regions of the crash context.
 void SetIncludeContextHeap(bool aValue);
 
 // Functions for working with minidumps and .extras
-typedef nsDataHashtable<nsCStringHashKey, nsCString> AnnotationTable;
+typedef mozilla::EnumeratedArray<Annotation, Annotation::Count, nsCString>
+        AnnotationTable;
 
 void DeleteMinidumpFilesForID(const nsAString& id);
 bool GetMinidumpForID(const nsAString& id, nsIFile** minidump);
 bool GetIDFromMinidump(nsIFile* minidump, nsAString& id);
 bool GetExtraFileForID(const nsAString& id, nsIFile** extraFile);
 bool GetExtraFileForMinidump(nsIFile* minidump, nsIFile** extraFile);
 bool AppendExtraData(const nsAString& id, const AnnotationTable& data);
 bool AppendExtraData(nsIFile* extraFile, const AnnotationTable& data);
--- a/toolkit/crashreporter/test/unit/test_crash_after_js_large_allocation_failure.js
+++ b/toolkit/crashreporter/test/unit/test_crash_after_js_large_allocation_failure.js
@@ -2,19 +2,19 @@ function run_test() {
   if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
     dump("INFO | test_crash_after_js_large_allocation_failure.js | Can't test crashreporter in a non-libxul build.\n");
     return;
   }
 
   do_crash(
     function() {
       crashType = CrashTestUtils.CRASH_MOZ_CRASH;
-      crashReporter.annotateCrashReport("TestingOOMCrash", "Yes");
+      crashReporter.annotateCrashReport("TestKey", "Yes");
       Cu.getJSTestingFunctions().reportLargeAllocationFailure();
       Cu.forceGC();
     },
     function(mdump, extra) {
-      Assert.equal(extra.TestingOOMCrash, "Yes");
+      Assert.equal(extra.TestKey, "Yes");
       Assert.equal(false, "JSOutOfMemory" in extra);
       Assert.equal(extra.JSLargeAllocationFailure, "Recovered");
     },
     true);
 }
--- a/toolkit/crashreporter/test/unit/test_crash_after_js_large_allocation_failure_reporting.js
+++ b/toolkit/crashreporter/test/unit/test_crash_after_js_large_allocation_failure_reporting.js
@@ -2,23 +2,23 @@ function run_test() {
   if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
     dump("INFO | test_crash_after_js_oom_reporting.js | Can't test crashreporter in a non-libxul build.\n");
     return;
   }
 
   do_crash(
     function() {
       crashType = CrashTestUtils.CRASH_MOZ_CRASH;
-      crashReporter.annotateCrashReport("TestingOOMCrash", "Yes");
+      crashReporter.annotateCrashReport("TestKey", "Yes");
 
       function crashWhileReporting() {
         CrashTestUtils.crash(crashType);
       }
 
       Services.obs.addObserver(crashWhileReporting, "memory-pressure");
       Cu.getJSTestingFunctions().reportLargeAllocationFailure();
     },
     function(mdump, extra) {
-      Assert.equal(extra.TestingOOMCrash, "Yes");
+      Assert.equal(extra.TestKey, "Yes");
       Assert.equal(extra.JSLargeAllocationFailure, "Reporting");
     },
     true);
 }
--- a/toolkit/crashreporter/test/unit/test_crash_after_js_oom_recovered.js
+++ b/toolkit/crashreporter/test/unit/test_crash_after_js_oom_recovered.js
@@ -2,18 +2,18 @@ function run_test() {
   if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
     dump("INFO | test_crash_after_js_oom_recovered.js | Can't test crashreporter in a non-libxul build.\n");
     return;
   }
 
   do_crash(
     function() {
       crashType = CrashTestUtils.CRASH_MOZ_CRASH;
-      crashReporter.annotateCrashReport("TestingOOMCrash", "Yes");
+      crashReporter.annotateCrashReport("TestKey", "Yes");
       Cu.getJSTestingFunctions().reportOutOfMemory();
       Cu.forceGC();
     },
     function(mdump, extra) {
-      Assert.equal(extra.TestingOOMCrash, "Yes");
+      Assert.equal(extra.TestKey, "Yes");
       Assert.equal(extra.JSOutOfMemory, "Recovered");
     },
     true);
 }
--- a/toolkit/crashreporter/test/unit/test_crash_after_js_oom_reported.js
+++ b/toolkit/crashreporter/test/unit/test_crash_after_js_oom_reported.js
@@ -2,26 +2,26 @@ function run_test() {
   if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
     dump("INFO | test_crash_after_js_oom_reported.js | Can't test crashreporter in a non-libxul build.\n");
     return;
   }
 
   do_crash(
     function() {
       crashType = CrashTestUtils.CRASH_MOZ_CRASH;
-      crashReporter.annotateCrashReport("TestingOOMCrash", "Yes");
+      crashReporter.annotateCrashReport("TestKey", "Yes");
 
       // GC now to avoid having it happen randomly later, which would make the
       // test bogusly fail. See comment below.
       Cu.forceGC();
 
       Cu.getJSTestingFunctions().reportOutOfMemory();
     },
     function(mdump, extra) {
-      Assert.equal(extra.TestingOOMCrash, "Yes");
+      Assert.equal(extra.TestKey, "Yes");
 
       // The JSOutOfMemory field is absent if the JS engine never reported OOM,
       // "Reported" if it did, and "Recovered" if it reported OOM but
       // subsequently completed a full GC cycle. Since this test calls
       // reportOutOfMemory() and then crashes, we expect "Reported".
       //
       // Theoretically, GC can happen any time, so it is just possible that
       // this property could be "Recovered" even if the implementation is
--- a/toolkit/crashreporter/test/unit/test_crash_after_js_oom_reported_2.js
+++ b/toolkit/crashreporter/test/unit/test_crash_after_js_oom_reported_2.js
@@ -2,23 +2,23 @@ function run_test() {
   if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
     dump("INFO | test_crash_after_js_oom_reported_2.js | Can't test crashreporter in a non-libxul build.\n");
     return;
   }
 
   do_crash(
     function() {
       crashType = CrashTestUtils.CRASH_MOZ_CRASH;
-      crashReporter.annotateCrashReport("TestingOOMCrash", "Yes");
+      crashReporter.annotateCrashReport("TestKey", "Yes");
       Cu.getJSTestingFunctions().reportOutOfMemory();
       Cu.forceGC(); // recover from first OOM
       Cu.getJSTestingFunctions().reportOutOfMemory();
     },
     function(mdump, extra) {
-      Assert.equal(extra.TestingOOMCrash, "Yes");
+      Assert.equal(extra.TestKey, "Yes");
 
       // Technically, GC can happen at any time, but it would be really
       // peculiar for it to happen again heuristically right after a GC was
       // forced. If extra.JSOutOfMemory is "Recovered" here, that's most
       // likely a bug in the error reporting machinery.
       Assert.equal(extra.JSOutOfMemory, "Reported");
     },
     true);
--- a/toolkit/crashreporter/test/unit/test_crash_oom.js
+++ b/toolkit/crashreporter/test/unit/test_crash_oom.js
@@ -2,17 +2,17 @@ function run_test() {
   if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
     dump("INFO | test_crash_oom.js | Can't test crashreporter in a non-libxul build.\n");
     return;
   }
 
   do_crash(
     function() {
       crashType = CrashTestUtils.CRASH_OOM;
-      crashReporter.annotateCrashReport("TestingOOMCrash", "Yes");
+      crashReporter.annotateCrashReport("TestKey", "Yes");
     },
     function(mdump, extra) {
-      Assert.equal(extra.TestingOOMCrash, "Yes");
+      Assert.equal(extra.TestKey, "Yes");
       Assert.ok("OOMAllocationSize" in extra);
       Assert.ok(Number(extra.OOMAllocationSize) > 0);
     },
     true);
 }
--- a/toolkit/crashreporter/test/unit/test_crashreporter.js
+++ b/toolkit/crashreporter/test/unit/test_crashreporter.js
@@ -34,43 +34,60 @@ function run_test() {
   // check getting/setting minidumpPath
   // it should be $TEMP by default, but I'm not sure if we can exactly test that
   // this will at least test that it doesn't throw
   Assert.notEqual(cr.minidumpPath.path, "");
   var cwd = do_get_cwd();
   cr.minidumpPath = cwd;
   Assert.equal(cr.minidumpPath.path, cwd.path);
 
+  // Test annotateCrashReport()
   try {
-    cr.annotateCrashReport("equal=equal", "");
-    do_throw("Calling annotateCrashReport() with an '=' in key should have thrown!");
+    cr.annotateCrashReport(undefined, "");
+    do_throw("Calling annotateCrashReport() with an undefined key should have thrown!");
   } catch (ex) {
     Assert.equal(ex.result, Cr.NS_ERROR_INVALID_ARG);
   }
   try {
-    cr.annotateCrashReport("new\nline", "");
-    do_throw("Calling annotateCrashReport() with a '\\n' in key should have thrown!");
+    cr.annotateCrashReport("foobar", "");
+    do_throw("Calling annotateCrashReport() with a bogus key should have thrown!");
   } catch (ex) {
     Assert.equal(ex.result, Cr.NS_ERROR_INVALID_ARG);
   }
   try {
-    cr.annotateCrashReport("", "da\0ta");
+    cr.annotateCrashReport("TestKey", "da\0ta");
     do_throw("Calling annotateCrashReport() with a '\\0' in data should have thrown!");
   } catch (ex) {
     Assert.equal(ex.result, Cr.NS_ERROR_INVALID_ARG);
   }
-  cr.annotateCrashReport("testKey", "testData1");
+  cr.annotateCrashReport("TestKey", "testData1");
   // Replace previous data.
-  cr.annotateCrashReport("testKey", "testData2");
+  cr.annotateCrashReport("TestKey", "testData2");
 
   try {
     cr.appendAppNotesToCrashReport("da\0ta");
     do_throw("Calling appendAppNotesToCrashReport() with a '\\0' in data should have thrown!");
   } catch (ex) {
     Assert.equal(ex.result, Cr.NS_ERROR_INVALID_ARG);
   }
   cr.appendAppNotesToCrashReport("additional testData3");
   // Add more data.
   cr.appendAppNotesToCrashReport("additional testData4");
 
+  // Test removeCrashReportAnnotation()
+  try {
+    cr.removeCrashReportAnnotation(undefined);
+    do_throw("Calling removeCrashReportAnnotation() with an undefined key should have thrown!");
+  } catch (ex) {
+    Assert.equal(ex.result, Cr.NS_ERROR_INVALID_ARG);
+  }
+  try {
+    cr.removeCrashReportAnnotation("foobar");
+    do_throw("Calling removeCrashReportAnnotation() with a bogus key should have thrown!");
+  } catch (ex) {
+    Assert.equal(ex.result, Cr.NS_ERROR_INVALID_ARG);
+  }
+  cr.removeCrashReportAnnotation("TestKey");
+
+  // Testing setting the minidumpPath field
   cr.minidumpPath = cwd;
   Assert.equal(cr.minidumpPath.path, cwd.path);
 }
--- a/toolkit/crashreporter/test/unit/test_crashreporter_crash.js
+++ b/toolkit/crashreporter/test/unit/test_crashreporter_crash.js
@@ -29,29 +29,31 @@ function run_test() {
              if (is_win7_or_newer)
                Assert.ok(CrashTestUtils.dumpHasStream(mdump.path, CrashTestUtils.MD_MEMORY_INFO_LIST_STREAM));
            });
 
   // check setting some basic data
   do_crash(function() {
              // Add various annotations
              crashReporter.annotateCrashReport("TestKey", "TestValue");
-             crashReporter.annotateCrashReport("\u2665", "\u{1F4A9}");
+             crashReporter.annotateCrashReport("TestUnicode", "\u{1F4A9}");
+             crashReporter.annotateCrashReport("Add-ons", "test%40mozilla.org:0.1");
              crashReporter.appendAppNotesToCrashReport("Junk");
              crashReporter.appendAppNotesToCrashReport("MoreJunk");
 
              // TelemetrySession setup will trigger the session annotation
              let scope = {};
              ChromeUtils.import("resource://gre/modules/TelemetryController.jsm", scope);
              scope.TelemetryController.testSetup();
            },
            function(mdump, extra) {
              Assert.equal(extra.TestKey, "TestValue");
-             Assert.equal(extra["\u2665"], "\u{1F4A9}");
+             Assert.equal(extra.TestUnicode, "\u{1F4A9}");
              Assert.equal(extra.Notes, "JunkMoreJunk");
+             Assert.equal(extra["Add-ons"], "test%40mozilla.org:0.1");
              const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
              Assert.ok("TelemetrySessionId" in extra,
                        "The TelemetrySessionId field is present in the extra file");
              Assert.ok(UUID_REGEX.test(extra.TelemetrySessionId),
                        "The TelemetrySessionId is a UUID");
              Assert.ok(!("TelemetryClientId" in extra),
                        "The TelemetryClientId field is omitted by default");
              Assert.ok(!("TelemetryServerURL" in extra),
--- a/toolkit/crashreporter/test/unit/test_oom_annotation_windows.js
+++ b/toolkit/crashreporter/test/unit/test_oom_annotation_windows.js
@@ -2,20 +2,20 @@ function run_test() {
   if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
     dump("INFO | test_crash_oom.js | Can't test crashreporter in a non-libxul build.\n");
     return;
   }
 
   do_crash(
     function() {
       crashType = CrashTestUtils.CRASH_OOM;
-      crashReporter.annotateCrashReport("TestingOOMCrash", "Yes");
+      crashReporter.annotateCrashReport("TestKey", "Yes");
     },
     function(mdump, extra) {
-      Assert.equal(extra.TestingOOMCrash, "Yes");
+      Assert.equal(extra.TestKey, "Yes");
       Assert.ok("OOMAllocationSize" in extra);
       Assert.ok(Number(extra.OOMAllocationSize) > 0);
       Assert.ok("SystemMemoryUsePercentage" in extra);
       Assert.ok("TotalVirtualMemory" in extra);
       Assert.ok("AvailableVirtualMemory" in extra);
       Assert.ok("TotalPageFile" in extra);
       Assert.ok("AvailablePageFile" in extra);
       Assert.ok("TotalPhysicalMemory" in extra);
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -1230,17 +1230,50 @@ nsXULAppInfo::GetExtraFileForID(const ns
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXULAppInfo::AnnotateCrashReport(const nsACString& key,
                                   const nsACString& data)
 {
-  return CrashReporter::AnnotateCrashReport(key, data);
+  CrashReporter::Annotation annotation;
+
+  if (!AnnotationFromString(annotation, PromiseFlatCString(key).get())) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  return CrashReporter::AnnotateCrashReport(annotation, data);
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::RemoveCrashReportAnnotation(const nsACString& key)
+{
+  CrashReporter::Annotation annotation;
+
+  if (!AnnotationFromString(annotation, PromiseFlatCString(key).get())) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  return CrashReporter::RemoveCrashReportAnnotation(annotation);
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::IsAnnotationWhitelistedForPing(const nsACString& aValue,
+                                             bool* aIsWhitelisted)
+{
+  CrashReporter::Annotation annotation;
+
+  if (!AnnotationFromString(annotation, PromiseFlatCString(aValue).get())) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  *aIsWhitelisted = CrashReporter::IsAnnotationWhitelistedForPing(annotation);
+
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXULAppInfo::AppendAppNotesToCrashReport(const nsACString& data)
 {
   return CrashReporter::AppendAppNotesToCrashReport(data);
 }
 
@@ -3385,49 +3418,49 @@ XREMain::XRE_mainInit(bool* aExitFlag)
     rv = nsXREDirProvider::GetUserAppDataDirectory(getter_AddRefs(file));
     if (NS_SUCCEEDED(rv)) {
       CrashReporter::SetUserAppDataDirectory(file);
     }
     if (mAppData->crashReporterURL)
       CrashReporter::SetServerURL(nsDependentCString(mAppData->crashReporterURL));
 
     // We overwrite this once we finish starting up.
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("StartupCrash"),
-                                       NS_LITERAL_CSTRING("1"));
+    CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::StartupCrash,
+                                       true);
 
     // pass some basic info from the app data
     if (mAppData->vendor)
-      CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Vendor"),
+      CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::Vendor,
                                          nsDependentCString(mAppData->vendor));
     if (mAppData->name)
-      CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProductName"),
+      CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::ProductName,
                                          nsDependentCString(mAppData->name));
     if (mAppData->ID)
-      CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProductID"),
+      CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::ProductID,
                                          nsDependentCString(mAppData->ID));
     if (mAppData->version)
-      CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Version"),
+      CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::Version,
                                          nsDependentCString(mAppData->version));
     if (mAppData->buildID)
-      CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("BuildID"),
+      CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::BuildID,
                                          nsDependentCString(mAppData->buildID));
 
     nsDependentCString releaseChannel(NS_STRINGIFY(MOZ_UPDATE_CHANNEL));
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ReleaseChannel"),
-                                       releaseChannel);
+    CrashReporter::AnnotateCrashReport(
+      CrashReporter::Annotation::ReleaseChannel, releaseChannel);
 #ifdef MOZ_LINKER
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("CrashAddressLikelyWrong"),
-                                       IsSignalHandlingBroken() ? NS_LITERAL_CSTRING("1")
-                                                                : NS_LITERAL_CSTRING("0"));
+    CrashReporter::AnnotateCrashReport(
+      CrashReporter::Annotation::CrashAddressLikelyWrong,
+      IsSignalHandlingBroken());
 #endif
 
 #ifdef XP_WIN
     nsAutoString appInitDLLs;
     if (widget::WinUtils::GetAppInitDLLs(appInitDLLs)) {
-      CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AppInitDLLs"),
+      CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AppInitDLLs,
                                          NS_ConvertUTF16toUTF8(appInitDLLs));
     }
 #endif
 
     CrashReporter::SetRestartArgs(gArgc, gArgv);
 
     // annotate other data (user id etc)
     nsCOMPtr<nsIFile> userAppDataDir;
@@ -3578,26 +3611,25 @@ XREMain::XRE_mainInit(bool* aExitFlag)
             cpuUpdateRevision = static_cast<int>(updateRevision[0]);
             break;
           }
         }
       }
     }
 
     if (cpuUpdateRevision > 0) {
-      CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("CPUMicrocodeVersion"),
-                                         nsPrintfCString("0x%x",
-                                                         cpuUpdateRevision));
+      CrashReporter::AnnotateCrashReport(
+        CrashReporter::Annotation::CPUMicrocodeVersion,
+        nsPrintfCString("0x%x", cpuUpdateRevision));
     }
   }
 #endif
 
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("SafeMode"),
-                                       gSafeMode ? NS_LITERAL_CSTRING("1") :
-                                                   NS_LITERAL_CSTRING("0"));
+    CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::SafeMode,
+                                       gSafeMode);
 
   // Handle --no-remote and --new-instance command line arguments. Setup
   // the environment to better accommodate other components and various
   // restart scenarios.
   ar = CheckArg("no-remote", nullptr, CheckArgFlag::CheckOSInt | CheckArgFlag::RemoveArg);
   if (ar == ARG_BAD) {
     PR_fprintf(PR_STDERR, "Error: argument --no-remote is invalid when argument --osint is specified\n");
     return 1;
@@ -3710,18 +3742,19 @@ static void AnnotateSystemManufacturer()
   }
 
   VARIANT value;
   VariantInit(&value);
 
   hr = classObject->Get(L"Manufacturer", 0, &value, 0, 0);
 
   if (SUCCEEDED(hr) && V_VT(&value) == VT_BSTR) {
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("BIOS_Manufacturer"),
-                                       NS_ConvertUTF16toUTF8(V_BSTR(&value)));
+    CrashReporter::AnnotateCrashReport(
+      CrashReporter::Annotation::BIOS_Manufacturer,
+      NS_ConvertUTF16toUTF8(V_BSTR(&value)));
   }
 
   VariantClear(&value);
 }
 
 static void PR_CALLBACK AnnotateSystemManufacturer_ThreadStart(void*)
 {
   HRESULT hr = CoInitialize(nullptr);
@@ -4355,34 +4388,33 @@ void AddSandboxAnnotations()
 {
   // Include the sandbox content level, regardless of platform
   int level = GetEffectiveContentSandboxLevel();
 
   nsAutoCString levelString;
   levelString.AppendInt(level);
 
   CrashReporter::AnnotateCrashReport(
-    NS_LITERAL_CSTRING("ContentSandboxLevel"), levelString);
+    CrashReporter::Annotation::ContentSandboxLevel, levelString);
 
   // Include whether or not this instance is capable of content sandboxing
   bool sandboxCapable = false;
 
 #if defined(XP_WIN)
   // All supported Windows versions support some level of content sandboxing
   sandboxCapable = true;
 #elif defined(XP_MACOSX)
   // All supported OS X versions are capable
   sandboxCapable = true;
 #elif defined(XP_LINUX)
   sandboxCapable = SandboxInfo::Get().CanSandboxContent();
 #endif
 
   CrashReporter::AnnotateCrashReport(
-    NS_LITERAL_CSTRING("ContentSandboxCapable"),
-    sandboxCapable ? NS_LITERAL_CSTRING("1") : NS_LITERAL_CSTRING("0"));
+    CrashReporter::Annotation::ContentSandboxCapable, sandboxCapable);
 }
 #endif /* MOZ_CONTENT_SANDBOX */
 
 /*
  * XRE_mainRun - Command line startup, profile migration, and
  * the calling of appStartup->Run().
  */
 nsresult
@@ -4426,26 +4458,28 @@ XREMain::XRE_mainRun()
   if (NS_SUCCEEDED(rv)) {
     nsCOMPtr<nsIPrefBranch> defaultPrefBranch;
     rv = prefs->GetDefaultBranch(nullptr, getter_AddRefs(defaultPrefBranch));
 
     if (NS_SUCCEEDED(rv)) {
       nsAutoCString sval;
       rv = defaultPrefBranch->GetCharPref("app.update.channel", sval);
       if (NS_SUCCEEDED(rv)) {
-        CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ReleaseChannel"),
-                                            sval);
+        CrashReporter::AnnotateCrashReport(
+          CrashReporter::Annotation::ReleaseChannel, sval);
       }
     }
   }
   // Needs to be set after xpcom initialization.
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("FramePoisonBase"),
-                                     nsPrintfCString("%.16" PRIu64, uint64_t(gMozillaPoisonBase)));
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("FramePoisonSize"),
-                                     nsPrintfCString("%" PRIu32, uint32_t(gMozillaPoisonSize)));
+  CrashReporter::AnnotateCrashReport(
+    CrashReporter::Annotation::FramePoisonBase,
+    nsPrintfCString("%.16" PRIu64, uint64_t(gMozillaPoisonBase)));
+  CrashReporter::AnnotateCrashReport(
+    CrashReporter::Annotation::FramePoisonSize,
+    uint32_t(gMozillaPoisonSize));
 
   bool includeContextHeap =
     Preferences::GetBool("toolkit.crashreporter.include_context_heap", false);
   CrashReporter::SetIncludeContextHeap(includeContextHeap);
 
 #ifdef XP_WIN
   PR_CreateThread(PR_USER_THREAD, AnnotateSystemManufacturer_ThreadStart, 0,
                   PR_PRIORITY_LOW, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0);
@@ -4594,17 +4628,18 @@ XREMain::XRE_mainRun()
   // As FilePreferences need the profile directory, we must initialize right here.
   mozilla::FilePreferences::InitDirectoriesWhitelist();
   mozilla::FilePreferences::InitPrefs();
 
   OverrideDefaultLocaleIfNeeded();
 
   nsCString userAgentLocale;
   LocaleService::GetInstance()->GetAppLocaleAsLangTag(userAgentLocale);
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("useragent_locale"), userAgentLocale);
+  CrashReporter::AnnotateCrashReport(
+    CrashReporter::Annotation::useragent_locale, userAgentLocale);
 
   appStartup->GetShuttingDown(&mShuttingDown);
 
   nsCOMPtr<nsICommandLineRunner> cmdLine;
 
   nsCOMPtr<nsIFile> workingDir;
   rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, getter_AddRefs(workingDir));
   NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
@@ -4690,18 +4725,18 @@ XREMain::XRE_mainRun()
 
     nsCOMPtr<nsIObserverService> obsService =
       mozilla::services::GetObserverService();
     if (obsService)
       obsService->NotifyObservers(nullptr, "final-ui-startup", nullptr);
 
     (void)appStartup->DoneStartingUp();
 
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("StartupCrash"),
-                                       NS_LITERAL_CSTRING("0"));
+    CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::StartupCrash,
+                                       false);
 
     appStartup->GetShuttingDown(&mShuttingDown);
   }
 
   if (!mShuttingDown) {
     rv = cmdLine->Run();
     NS_ENSURE_SUCCESS_LOG(rv, NS_ERROR_FAILURE);
 
@@ -4748,17 +4783,17 @@ XREMain::XRE_mainRun()
   Telemetry::Accumulate(Telemetry::SANDBOX_CONTENT_ENABLED,
                         sandboxInfo.Test(SandboxInfo::kEnabledForContent));
   Telemetry::Accumulate(Telemetry::SANDBOX_MEDIA_ENABLED,
                         sandboxInfo.Test(SandboxInfo::kEnabledForMedia));
   nsAutoCString flagsString;
   flagsString.AppendInt(sandboxInfo.AsInteger());
 
   CrashReporter::AnnotateCrashReport(
-    NS_LITERAL_CSTRING("ContentSandboxCapabilities"), flagsString);
+    CrashReporter::Annotation::ContentSandboxCapabilities, flagsString);
 #endif /* MOZ_SANDBOX && XP_LINUX */
 
 #if defined(MOZ_CONTENT_SANDBOX)
   AddSandboxAnnotations();
 #endif /* MOZ_CONTENT_SANDBOX */
 
   {
     rv = appStartup->Run();
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -320,20 +320,18 @@ SetTaskbarGroupId(const nsString& aId)
 #endif
 
 #if defined(MOZ_CONTENT_SANDBOX)
 void
 AddContentSandboxLevelAnnotation()
 {
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     int level = GetEffectiveContentSandboxLevel();
-    nsAutoCString levelString;
-    levelString.AppendInt(level);
     CrashReporter::AnnotateCrashReport(
-      NS_LITERAL_CSTRING("ContentSandboxLevel"), levelString);
+      CrashReporter::Annotation::ContentSandboxLevel, level);
   }
 }
 #endif /* MOZ_CONTENT_SANDBOX */
 
 namespace {
 
 int GetDebugChildPauseTime() {
   auto pauseStr = PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE");
--- a/widget/android/GfxInfo.cpp
+++ b/widget/android/GfxInfo.cpp
@@ -343,29 +343,22 @@ GfxInfo::GetIsGPU2Active(bool* aIsGPU2Ac
 {
   EnsureInitialized();
   return NS_ERROR_FAILURE;
 }
 
 void
 GfxInfo::AddCrashReportAnnotations()
 {
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AdapterVendorID"),
+  CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterVendorID,
                                      mGLStrings->Vendor());
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AdapterDeviceID"),
+  CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterDeviceID,
                                      mGLStrings->Renderer());
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AdapterDriverVersion"),
-                                     mGLStrings->Version());
-
-  /* Add an App Note for now so that we get the data immediately. These
-   * can go away after we store the above in the socorro db */
-  nsAutoCString note;
-  note.AppendPrintf("AdapterDescription: '%s'\n", mAdapterDescription.get());
-
-  CrashReporter::AppendAppNotesToCrashReport(note);
+  CrashReporter::AnnotateCrashReport(
+    CrashReporter::Annotation::AdapterDriverVersion, mGLStrings->Version());
 }
 
 const nsTArray<GfxDriverInfo>&
 GfxInfo::GetGfxDriverInfo()
 {
   if (sDriverInfo->IsEmpty()) {
     APPEND_TO_DRIVER_BLOCKLIST2(OperatingSystem::Android,
       (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorAll), GfxDriverInfo::allDevices,
--- a/widget/android/jni/Utils.cpp
+++ b/widget/android/jni/Utils.cpp
@@ -199,18 +199,18 @@ bool HandleUncaughtException(JNIEnv* aEn
     return true;
 }
 
 bool ReportException(JNIEnv* aEnv, jthrowable aExc, jstring aStack)
 {
     bool result = true;
 
     result &= NS_SUCCEEDED(CrashReporter::AnnotateCrashReport(
-            NS_LITERAL_CSTRING("JavaStackTrace"),
-            String::Ref::From(aStack)->ToCString()));
+                           CrashReporter::Annotation::JavaStackTrace,
+                           String::Ref::From(aStack)->ToCString()));
 
     auto appNotes = java::GeckoAppShell::GetAppNotes();
     if (NS_WARN_IF(aEnv->ExceptionCheck())) {
         aEnv->ExceptionDescribe();
         aEnv->ExceptionClear();
     } else if (appNotes) {
         CrashReporter::AppendAppNotesToCrashReport(NS_LITERAL_CSTRING("\n") +
                                                    appNotes->ToCString());
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -4,23 +4,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsAppShell.h"
 
 #include "base/basictypes.h"
 #include "base/message_loop.h"
 #include "base/task.h"
 #include "mozilla/Hal.h"
-#include "nsExceptionHandler.h"
 #include "nsIScreen.h"
 #include "nsIScreenManager.h"
 #include "nsWindow.h"
 #include "nsThreadUtils.h"
 #include "nsICommandLineRunner.h"
-#include "nsICrashReporter.h"
 #include "nsIObserverService.h"
 #include "nsIAppStartup.h"
 #include "nsIGeolocationProvider.h"
 #include "nsCacheService.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMWakeLockListener.h"
 #include "nsIPowerManagerService.h"
 #include "nsISpeculativeConnect.h"
--- a/widget/cocoa/GfxInfo.mm
+++ b/widget/cocoa/GfxInfo.mm
@@ -278,31 +278,22 @@ GfxInfo::AddCrashReportAnnotations()
 
   GetAdapterDeviceID(deviceID);
   CopyUTF16toUTF8(deviceID, narrowDeviceID);
   GetAdapterVendorID(vendorID);
   CopyUTF16toUTF8(vendorID, narrowVendorID);
   GetAdapterDriverVersion(driverVersion);
   CopyUTF16toUTF8(driverVersion, narrowDriverVersion);
 
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AdapterVendorID"),
+  CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterVendorID,
                                      narrowVendorID);
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AdapterDeviceID"),
+  CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterDeviceID,
                                      narrowDeviceID);
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AdapterDriverVersion"),
-                                     narrowDriverVersion);
-  /* Add an App Note for now so that we get the data immediately. These
-   * can go away after we store the above in the socorro db */
-  nsAutoCString note;
-  /* AppendPrintf only supports 32 character strings, mrghh. */
-  note.AppendLiteral("AdapterVendorID: ");
-  note.Append(narrowVendorID);
-  note.AppendLiteral(", AdapterDeviceID: ");
-  note.Append(narrowDeviceID);
-  CrashReporter::AppendAppNotesToCrashReport(note);
+  CrashReporter::AnnotateCrashReport(
+    CrashReporter::Annotation::AdapterDriverVersion, narrowDriverVersion);
 }
 
 // We don't support checking driver versions on Mac.
 #define IMPLEMENT_MAC_DRIVER_BLOCKLIST(os, vendor, device, features, blockOn, ruleId) \
   APPEND_TO_DRIVER_BLOCKLIST(os, vendor, device, features, blockOn,           \
                              DRIVER_COMPARISON_IGNORED, V(0,0,0,0), ruleId, "")
 
 
--- a/widget/windows/GfxInfo.cpp
+++ b/widget/windows/GfxInfo.cpp
@@ -888,45 +888,34 @@ GfxInfo::AddCrashReportAnnotations()
   CopyUTF16toUTF8(deviceID, narrowDeviceID);
   GetAdapterVendorID(vendorID);
   CopyUTF16toUTF8(vendorID, narrowVendorID);
   GetAdapterDriverVersion(driverVersion);
   CopyUTF16toUTF8(driverVersion, narrowDriverVersion);
   GetAdapterSubsysID(subsysID);
   CopyUTF16toUTF8(subsysID, narrowSubsysID);
 
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AdapterVendorID"),
+  CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterVendorID,
                                      narrowVendorID);
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AdapterDeviceID"),
+  CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterDeviceID,
                                      narrowDeviceID);
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AdapterDriverVersion"),
-                                     narrowDriverVersion);
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AdapterSubsysID"),
+  CrashReporter::AnnotateCrashReport(
+    CrashReporter::Annotation::AdapterDriverVersion, narrowDriverVersion);
+  CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterSubsysID,
                                      narrowSubsysID);
 
-  /* Add an App Note for now so that we get the data immediately. These
-   * can go away after we store the above in the socorro db */
+  /* Add an App Note, this contains extra information. */
   nsAutoCString note;
-  /* AppendPrintf only supports 32 character strings, mrghh. */
-  note.AppendLiteral("AdapterVendorID: ");
-  note.Append(narrowVendorID);
-  note.AppendLiteral(", AdapterDeviceID: ");
-  note.Append(narrowDeviceID);
-  note.AppendLiteral(", AdapterSubsysID: ");
-  note.Append(narrowSubsysID);
-  note.AppendLiteral(", AdapterDriverVersion: ");
-  note.Append(NS_LossyConvertUTF16toASCII(driverVersion));
 
+  // TODO: We should probably convert this into a proper annotation
   if (vendorID == GfxDriverInfo::GetDeviceVendor(VendorAll)) {
     /* if we didn't find a valid vendorID lets append the mDeviceID string to try to find out why */
-    note.AppendLiteral(", ");
     LossyAppendUTF16toASCII(mDeviceID[mActiveGPUIndex], note);
     note.AppendLiteral(", ");
     LossyAppendUTF16toASCII(mDeviceKeyDebug, note);
-    LossyAppendUTF16toASCII(mDeviceKeyDebug, note);
   }
   note.AppendLiteral("\n");
 
   if (mHasDualGPU) {
     nsString deviceID2, vendorID2, subsysID2;
     nsAutoString adapterDriverVersionString2;
     nsCString narrowDeviceID2, narrowVendorID2, narrowSubsysID2;
 
--- a/xpcom/base/AvailableMemoryTracker.cpp
+++ b/xpcom/base/AvailableMemoryTracker.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/AvailableMemoryTracker.h"
 
 #if defined(XP_WIN)
 #include "nsExceptionHandler.h"
 #include "nsIMemoryReporter.h"
 #include "nsMemoryPressure.h"
-#include "nsPrintfCString.h"
 #endif
 
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIRunnable.h"
 #include "nsISupports.h"
 #include "nsITimer.h"
 #include "nsThreadUtils.h"
@@ -146,18 +145,18 @@ nsAvailableMemoryWatcher::IsVirtualMemor
 
 /* static */ bool
 nsAvailableMemoryWatcher::IsCommitSpaceLow(const MEMORYSTATUSEX& aStat)
 {
   if ((kLowCommitSpaceThreshold != 0) &&
       (aStat.ullAvailPageFile < kLowCommitSpaceThreshold)) {
     sNumLowCommitSpaceEvents++;
     CrashReporter::AnnotateCrashReport(
-      NS_LITERAL_CSTRING("LowCommitSpaceEvents"),
-      nsPrintfCString("%" PRIu32, uint32_t(sNumLowCommitSpaceEvents)));
+      CrashReporter::Annotation::LowCommitSpaceEvents,
+      uint32_t(sNumLowCommitSpaceEvents));
     return true;
   }
 
   return false;
 }
 
 /* static */ bool
 nsAvailableMemoryWatcher::IsPhysicalMemoryLow(const MEMORYSTATUSEX& aStat)
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -1468,29 +1468,45 @@ CycleCollectedJSRuntime::FinalizeDeferre
   if (aType == CycleCollectedJSContext::FinalizeIncrementally) {
     NS_IdleDispatchToCurrentThread(do_AddRef(mFinalizeRunnable), 2500);
   } else {
     mFinalizeRunnable->ReleaseNow(false);
     MOZ_ASSERT(!mFinalizeRunnable);
   }
 }
 
+const char*
+CycleCollectedJSRuntime::OOMStateToString(const OOMState aOomState) const
+{
+  switch (aOomState) {
+    case OOMState::OK:
+      return "OK";
+    case OOMState::Reporting:
+      return "Reporting";
+    case OOMState::Reported:
+      return "Reported";
+    case OOMState::Recovered:
+      return "Recovered";
+    default:
+      MOZ_ASSERT_UNREACHABLE("OOMState holds an invalid value");
+      return "Unknown";
+  }
+}
+
 void
 CycleCollectedJSRuntime::AnnotateAndSetOutOfMemory(OOMState* aStatePtr,
                                                    OOMState aNewState)
 {
   *aStatePtr = aNewState;
-  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"));
+  CrashReporter::Annotation annotation = (aStatePtr == &mOutOfMemoryState)
+    ? CrashReporter::Annotation::JSOutOfMemory
+    : CrashReporter::Annotation::JSLargeAllocationFailure;
+
+  CrashReporter::AnnotateCrashReport(
+    annotation, nsDependentCString(OOMStateToString(aNewState)));
 }
 
 void
 CycleCollectedJSRuntime::OnGC(JSContext* aContext,
                               JSGCStatus aStatus)
 {
   switch (aStatus) {
     case JSGC_BEGIN:
--- a/xpcom/base/CycleCollectedJSRuntime.h
+++ b/xpcom/base/CycleCollectedJSRuntime.h
@@ -232,16 +232,18 @@ public:
 
     // 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
   };
 
+  const char* OOMStateToString(const OOMState aOomState) const;
+
   void SetLargeAllocationFailure(OOMState aNewState);
 
   void AnnotateAndSetOutOfMemory(OOMState* aStatePtr, OOMState aNewState);
   void OnGC(JSContext* aContext, JSGCStatus aStatus);
   void OnOutOfMemory();
   void OnLargeAllocationFailure();
 
   JSRuntime* Runtime() { return mJSRuntime; }
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -670,17 +670,18 @@ PtrInfo::AnnotatedReleaseAssert(bool aCo
     return;
   }
 
   const char* piName = "Unknown";
   if (mParticipant) {
     piName = mParticipant->ClassName();
   }
   nsPrintfCString msg("%s, for class %s", aMessage, piName);
-  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("CycleCollector"), msg);
+  CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::CycleCollector,
+                                     msg);
 
   MOZ_CRASH();
 }
 
 /**
  * A structure designed to be used like a linked list of PtrInfo, except
  * it allocates many PtrInfos at a time.
  */
--- a/xpcom/base/nsDebugImpl.cpp
+++ b/xpcom/base/nsDebugImpl.cpp
@@ -404,18 +404,19 @@ NS_DebugBreak(uint32_t aSeverity, const 
       // 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));
+        CrashReporter::AnnotateCrashReport(
+          CrashReporter::Annotation::AbortMessage,
+          nsDependentCString(nonPIDBuf.buffer));
       }
 
 #if defined(DEBUG) && defined(_WIN32)
       RealBreak();
 #endif
 #if defined(DEBUG)
       nsTraceRefcnt::WalkTheStack(stderr);
 #endif
--- a/xpcom/system/nsICrashReporter.idl
+++ b/xpcom/system/nsICrashReporter.idl
@@ -71,28 +71,51 @@ interface nsICrashReporter : nsISupports
    * @throw NS_ERROR_FILE_NOT_FOUND if the extra file could not be found
    */
   nsIFile getExtraFileForID(in AString id);
 
   /**
    * Add some extra data to be submitted with a crash report.
    *
    * @param key
-   *        Name of the data to be added.
+   *        Name of a known crash annotation constant.
    * @param data
    *        Data to be added.
    *
    * @throw NS_ERROR_NOT_INITIALIZED if crash reporting not initialized
-   * @throw NS_ERROR_INVALID_ARG if key or data contain invalid characters.
-   *                             Invalid characters for key are '=' and
-   *                             '\n'.  Invalid character for data is '\0'.
+   * @throw NS_ERROR_INVALID_ARG if key contains an invalid value or data
+   *                             contains invalid characters.  Invalid
+   *                             character for data is '\0'.
    */
   void annotateCrashReport(in AUTF8String key, in AUTF8String data);
 
   /**
+   * Remove a crash report annotation.
+   *
+   * @param key
+   *        Name of a known crash annotation constant.
+   *
+   * @throw NS_ERROR_NOT_INITIALIZED if crash reporting not initialized
+   * @throw NS_ERROR_INVALID_ARG if key contains an invalid value.
+   */
+  void removeCrashReportAnnotation(in AUTF8String key);
+
+  /**
+   * Checks if an annotation is whitelisted for inclusion in the crash ping.
+   *
+   * @param key
+   *        Name of a known crash annotation constant.
+   *
+   * @return True if the specified value is a valid annotation and can be
+             included in the crash ping, false otherwise.
+   * @throw NS_ERROR_INVALID_ARG if key contains an invalid value.
+   */
+  boolean isAnnotationWhitelistedForPing(in ACString value);
+
+  /**
    * Append some data to the "Notes" field, to be submitted with a crash report.
    * Unlike annotateCrashReport, this method will append to existing data.
    *
    * @param data
    *        Data to be added.
    *
    * @throw NS_ERROR_NOT_INITIALIZED if crash reporting not initialized
    * @throw NS_ERROR_INVALID_ARG if data contains invalid characters.
@@ -116,17 +139,17 @@ interface nsICrashReporter : nsISupports
   /**
    * Write a minidump immediately, with the user-supplied exception
    * information. This is implemented on Windows only, because
    * SEH (structured exception handling) exists on Windows only.
    *
    * @param aExceptionInfo  EXCEPTION_INFO* provided by Window's SEH
    */
   [noscript] void writeMinidumpForException(in voidPtr aExceptionInfo);
-  
+
   /**
    * Append note containing an Obj-C exception's info.
    *
    * @param aException  NSException object to append note for
    */
   [noscript] void appendObjCExceptionInfoToAppNotes(in voidPtr aException);
 
   /**