Bug 1411469 - Statically allocate static atoms. r=froydnj
authorNicholas Nethercote <nnethercote@mozilla.com>
Thu, 08 Mar 2018 12:59:11 +1100
changeset 463461 51793742d8289c192dc2b90d5f64c4ec26928822
parent 463460 92d99c50967f25655aabe7f085c4f1165dc83de9
child 463462 f37f0b1702464a0b925141a51e443d40a9ec5491
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1411469
milestone61.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 1411469 - Statically allocate static atoms. r=froydnj Currently static atoms are stored on the heap, but their char buffers are stored in read-only static memory. This patch changes the representation of nsStaticAtom (thus making it a non-trivial subclass of nsAtom). Instead of a pointer to the string, it now has an mStringOffset field which is a 32-bit offset to the string. (This requires placement of the string and the atom within the same object so that the offset is known to be small. The docs and macros in nsStaticAtom.h handle that.) Static and dynamic atoms now store their chars in different ways: nsStaticAtom stores them inline, nsDynamicAtom has a pointer to separate storage. So `mString` and GetStringBuffer() move from nsAtom to nsDynamicAtom. The change to static atoms means they can be made constexpr and stored in read-only memory instead of on the heap. On 64-bit this reduces the per-process overhead by 16 bytes; on 32-bit the saving is 12 bytes. (Further reductions will be possible in follow-up patches.) The increased use of constexpr required multiple workarounds for MSVC. - Multiple uses of MOZ_{PUSH,POP}_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING to disable warnings about (well-defined!) overflow of unsigned integer arithmetic. - The use of -Zc:externConstexpr on all files defining static atoms, to make MSVC follow the C++ standard(!) and let constexpr variables have external linkage. - The use of -constexpr:steps300000 to increase the number of operations allowed in a constexpr value, in order to handle gGkAtoms, which requires hashing ~2,500 atom strings. The patch also changes how HTML5 atoms are handled. They are now treated as dynamic atoms, i.e. we have "dynamic normal" atoms and "dynamic HTML5 atoms", and "dynamic atoms" covers both cases, and both are represented via nsDynamicAtom. The main difference between the two kinds is that dynamic HTML5 atoms still aren't allowed to be used in various operations, most notably AddRef()/Release(). All this also required moving nsDynamicAtom into the header file. There is a slight performance cost to all these changes: now that nsStaticAtom and nsDynamicAtom store their chars in different ways, a conditional branch is required in the following functions: Equals(), GetUTF16String(), WeakAtom::as_slice(). Finally, in about:memory the "explicit/atoms/static/atom-objects" value is no longer needed, because that memory is static instead of heap-allocated. MozReview-Commit-ID: 4AxPv05ngZy
dom/bindings/DOMString.h
layout/style/ServoBindings.toml
layout/style/moz.build
layout/style/nsCSSAnonBoxes.cpp
layout/style/nsCSSAnonBoxes.h
layout/style/nsCSSPseudoClasses.cpp
layout/style/nsCSSPseudoElements.cpp
layout/style/nsCSSPseudoElements.h
parser/html/nsHtml5AtomTable.cpp
parser/html/nsHtml5AtomTable.h
toolkit/components/aboutmemory/tests/test_memoryReporters.xul
xpcom/base/nsMemoryReporterManager.cpp
xpcom/ds/moz.build
xpcom/ds/nsAtom.h
xpcom/ds/nsAtomTable.cpp
xpcom/ds/nsAtomTable.h
xpcom/ds/nsGkAtoms.cpp
xpcom/ds/nsGkAtoms.h
xpcom/ds/nsStaticAtom.h
xpcom/io/moz.build
xpcom/io/nsDirectoryService.cpp
xpcom/io/nsDirectoryService.h
--- a/dom/bindings/DOMString.h
+++ b/dom/bindings/DOMString.h
@@ -218,17 +218,18 @@ public:
     MOZ_ASSERT(aAtom || aNullHandling != eNullNotExpected);
     if (aNullHandling == eNullNotExpected || aAtom) {
       if (aAtom->IsStatic()) {
         // Static atoms are backed by literals.
         SetLiteralInternal(aAtom->GetUTF16String(), aAtom->GetLength());
       } else {
         // Dynamic atoms always have a string buffer and never have 0 length,
         // because nsGkAtoms::_empty is a static atom.
-        SetKnownLiveStringBuffer(aAtom->GetStringBuffer(), aAtom->GetLength());
+        SetKnownLiveStringBuffer(
+          aAtom->AsDynamic()->GetStringBuffer(), aAtom->GetLength());
       }
     } else if (aNullHandling == eTreatNullAsNull) {
       SetNull();
     }
   }
 
   void SetNull()
   {
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -300,16 +300,17 @@ whitelist-types = [
     "nsCSSValuePair_heap",
     "nsCSSValuePairList",
     "nsCSSValuePairList_heap",
     "nsCSSValueTokenStream",
     "nsCSSValueTriplet_heap",
     "nsCursorImage",
     "nsFont",
     "nsAtom",
+    "nsDynamicAtom",
     "nsMainThreadPtrHandle",
     "nsMainThreadPtrHolder",
     "nsMargin",
     "nsMediaFeature",
     "nsMediaFeatures",
     "nsMediaList",
     "nsRect",
     "nsRestyleHint",
--- a/layout/style/moz.build
+++ b/layout/style/moz.build
@@ -181,17 +181,16 @@ UNIFIED_SOURCES += [
     'GroupRule.cpp',
     'ImageLoader.cpp',
     'LayerAnimationInfo.cpp',
     'Loader.cpp',
     'MediaList.cpp',
     'MediaQueryList.cpp',
     'nsAnimationManager.cpp',
     'nsComputedDOMStyle.cpp',
-    'nsCSSAnonBoxes.cpp',
     'nsCSSCounterStyleRule.cpp',
     'nsCSSFontFaceRule.cpp',
     'nsCSSKeywords.cpp',
     'nsCSSProps.cpp',
     'nsCSSPseudoClasses.cpp',
     'nsCSSScanner.cpp',
     'nsCSSValue.cpp',
     'nsDOMCSSAttrDeclaration.cpp',
@@ -236,23 +235,29 @@ UNIFIED_SOURCES += [
     'StreamLoader.cpp',
     'StyleAnimationValue.cpp',
     'StylePrefs.cpp',
     'StyleSheet.cpp',
     'URLExtraData.cpp',
 ]
 
 SOURCES += [
+    'nsCSSAnonBoxes.cpp',
     # Both nsCSSPseudoElements.cpp and nsCSSPseudoClasses.cpp defined a
     # 'mozPlaceholder' static atom.
     'nsCSSPseudoElements.cpp',
     # nsLayoutStylesheetCache.cpp uses nsExceptionHandler.h, which includes
     # windows.h.
     'nsLayoutStylesheetCache.cpp',
 ]
+if CONFIG['CC_TYPE'] == 'msvc':
+    # Needed for gCSSAnonBoxAtoms.
+    SOURCES['nsCSSAnonBoxes.cpp'].flags += ['-Zc:externConstexpr']
+    # Needed for gCSSPseudoElementAtoms.
+    SOURCES['nsCSSPseudoElements.cpp'].flags += ['-Zc:externConstexpr']
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '../base',
     '../generic',
--- a/layout/style/nsCSSAnonBoxes.cpp
+++ b/layout/style/nsCSSAnonBoxes.cpp
@@ -8,36 +8,54 @@
 
 #include "mozilla/ArrayUtils.h"
 
 #include "nsCSSAnonBoxes.h"
 #include "nsStaticAtom.h"
 
 using namespace mozilla;
 
+namespace mozilla {
+namespace detail {
+
+MOZ_PUSH_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
+extern constexpr CSSAnonBoxAtoms gCSSAnonBoxAtoms = {
+  #define CSS_ANON_BOX(name_, value_) \
+    NS_STATIC_ATOM_INIT_STRING(value_)
+  #include "nsCSSAnonBoxList.h"
+  #undef CSS_ANON_BOX
+
+  #define CSS_ANON_BOX(name_, value_) \
+    NS_STATIC_ATOM_INIT_ATOM(CSSAnonBoxAtoms, name_, value_)
+  #include "nsCSSAnonBoxList.h"
+  #undef CSS_ANON_BOX
+};
+MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
+
+} // namespace detail
+} // namespace mozilla
+
 #define CSS_ANON_BOX(name_, value_) \
-  NS_STATIC_ATOM_SUBCLASS_DEFN(nsICSSAnonBoxPseudo, nsCSSAnonBoxes, name_)
-#include "nsCSSAnonBoxList.h"
-#undef CSS_ANON_BOX
-
-#define CSS_ANON_BOX(name_, value_) NS_STATIC_ATOM_BUFFER(name_, value_)
+  NS_STATIC_ATOM_SUBCLASS_DEFN_PTR(nsICSSAnonBoxPseudo, nsCSSAnonBoxes, name_)
 #include "nsCSSAnonBoxList.h"
 #undef CSS_ANON_BOX
 
 static const nsStaticAtomSetup sCSSAnonBoxAtomSetup[] = {
   // Put the non-inheriting anon boxes first, so we can index into them easily.
   #define CSS_ANON_BOX(name_, value_) /* nothing */
   #define CSS_NON_INHERITING_ANON_BOX(name_, value_) \
-    NS_STATIC_ATOM_SUBCLASS_SETUP(nsCSSAnonBoxes, name_)
+    NS_STATIC_ATOM_SUBCLASS_SETUP( \
+      mozilla::detail::gCSSAnonBoxAtoms, nsCSSAnonBoxes, name_)
   #include "nsCSSAnonBoxList.h"
   #undef CSS_NON_INHERITING_ANON_BOX
   #undef CSS_ANON_BOX
 
   #define CSS_ANON_BOX(name_, value_) \
-    NS_STATIC_ATOM_SUBCLASS_SETUP(nsCSSAnonBoxes, name_)
+    NS_STATIC_ATOM_SUBCLASS_SETUP( \
+      mozilla::detail::gCSSAnonBoxAtoms, nsCSSAnonBoxes, name_)
   #define CSS_NON_INHERITING_ANON_BOX(name_, value_) /* nothing */
   #include "nsCSSAnonBoxList.h"
   #undef CSS_NON_INHERITING_ANON_BOX
   #undef CSS_ANON_BOX
 };
 
 void nsCSSAnonBoxes::RegisterStaticAtoms()
 {
--- a/layout/style/nsCSSAnonBoxes.h
+++ b/layout/style/nsCSSAnonBoxes.h
@@ -11,33 +11,52 @@
 
 #include "nsAtom.h"
 #include "nsStaticAtom.h"
 
 // Empty class derived from nsAtom so that function signatures can
 // require an atom from this atom list.
 class nsICSSAnonBoxPseudo : public nsAtom {};
 
+namespace mozilla {
+namespace detail {
+
+struct CSSAnonBoxAtoms
+{
+  #define CSS_ANON_BOX(name_, value_) NS_STATIC_ATOM_DECL_STRING(name_, value_)
+  #include "nsCSSAnonBoxList.h"
+  #undef CSS_ANON_BOX
+
+  #define CSS_ANON_BOX(name_, value_) NS_STATIC_ATOM_DECL_ATOM(name_)
+  #include "nsCSSAnonBoxList.h"
+  #undef CSS_ANON_BOX
+};
+
+extern const CSSAnonBoxAtoms gCSSAnonBoxAtoms;
+
+} // namespace detail
+} // namespace mozilla
+
 class nsCSSAnonBoxes {
 public:
 
   static void RegisterStaticAtoms();
 
   static bool IsAnonBox(nsAtom *aAtom);
 #ifdef MOZ_XUL
   static bool IsTreePseudoElement(nsAtom* aPseudo);
 #endif
   static bool IsNonElement(nsAtom* aPseudo)
   {
     return aPseudo == mozText || aPseudo == oofPlaceholder ||
            aPseudo == firstLetterContinuation;
   }
 
 #define CSS_ANON_BOX(name_, value_) \
-  NS_STATIC_ATOM_SUBCLASS_DECL(nsICSSAnonBoxPseudo, name_)
+  NS_STATIC_ATOM_SUBCLASS_DECL_PTR(nsICSSAnonBoxPseudo, name_)
 #include "nsCSSAnonBoxList.h"
 #undef CSS_ANON_BOX
 
   typedef uint8_t NonInheritingBase;
   enum class NonInheriting : NonInheritingBase {
 #define CSS_ANON_BOX(_name, _value) /* nothing */
 #define CSS_NON_INHERITING_ANON_BOX(_name, _value) _name,
 #include "nsCSSAnonBoxList.h"
--- a/layout/style/nsCSSPseudoClasses.cpp
+++ b/layout/style/nsCSSPseudoClasses.cpp
@@ -23,38 +23,65 @@ using namespace mozilla;
 #define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \
   static_assert(!((flags_) & CSS_PSEUDO_CLASS_ENABLED_IN_CHROME) || \
                 ((flags_) & CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), \
                 "Pseudo-class '" #name_ "' is enabled in chrome, so it " \
                 "should also be enabled in UA sheets");
 #include "nsCSSPseudoClassList.h"
 #undef CSS_PSEUDO_CLASS
 
+namespace detail {
+
+struct CSSPseudoClassAtoms
+{
+  #define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \
+    NS_STATIC_ATOM_DECL_STRING(name_, value_)
+  #include "nsCSSPseudoClassList.h"
+  #undef CSS_PSEUDO_CLASS
+
+  #define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \
+    NS_STATIC_ATOM_DECL_ATOM(name_)
+  #include "nsCSSPseudoClassList.h"
+  #undef CSS_PSEUDO_CLASS
+};
+
+MOZ_PUSH_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
+static constexpr CSSPseudoClassAtoms sCSSPseudoClassAtoms = {
+  #define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \
+    NS_STATIC_ATOM_INIT_STRING(value_)
+  #include "nsCSSPseudoClassList.h"
+  #undef CSS_PSEUDO_CLASS
+
+  #define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \
+    NS_STATIC_ATOM_INIT_ATOM(CSSPseudoClassAtoms, name_, value_)
+  #include "nsCSSPseudoClassList.h"
+  #undef CSS_PSEUDO_CLASS
+};
+MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
+
+} // namespace detail
+
 class CSSPseudoClassAtoms
 {
 public:
   #define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \
-    NS_STATIC_ATOM_DECL(name_)
+    NS_STATIC_ATOM_DECL_PTR(name_)
   #include "nsCSSPseudoClassList.h"
   #undef CSS_PSEUDO_CLASS
 };
 
 #define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \
-  NS_STATIC_ATOM_DEFN(CSSPseudoClassAtoms, name_)
-#include "nsCSSPseudoClassList.h"
-#undef CSS_PSEUDO_CLASS
-
-#define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \
-  NS_STATIC_ATOM_BUFFER(name_, value_)
+  NS_STATIC_ATOM_DEFN_PTR(CSSPseudoClassAtoms, name_)
 #include "nsCSSPseudoClassList.h"
 #undef CSS_PSEUDO_CLASS
 
 static const nsStaticAtomSetup sCSSPseudoClassAtomSetup[] = {
   #define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \
-    NS_STATIC_ATOM_SETUP(CSSPseudoClassAtoms, name_)
+    NS_STATIC_ATOM_SETUP( \
+      ::detail::sCSSPseudoClassAtoms, CSSPseudoClassAtoms, name_)
   #include "nsCSSPseudoClassList.h"
   #undef CSS_PSEUDO_CLASS
 };
 
 // Flags data for each of the pseudo-classes, which must be separate
 // from the previous array since there's no place for it in
 // nsStaticAtomSetup.
 /* static */ const uint32_t
--- a/layout/style/nsCSSPseudoElements.cpp
+++ b/layout/style/nsCSSPseudoElements.cpp
@@ -11,30 +11,47 @@
 #include "mozilla/ArrayUtils.h"
 
 #include "nsStaticAtom.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsDOMString.h"
 
 using namespace mozilla;
 
-#define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
-  NS_STATIC_ATOM_BUFFER(name_, value_)
-#include "nsCSSPseudoElementList.h"
-#undef CSS_PSEUDO_ELEMENT
+namespace mozilla {
+namespace detail {
+
+MOZ_PUSH_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
+extern constexpr CSSPseudoElementAtoms gCSSPseudoElementAtoms = {
+  #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
+    NS_STATIC_ATOM_INIT_STRING(value_)
+  #include "nsCSSPseudoElementList.h"
+  #undef CSS_PSEUDO_ELEMENT
+
+  #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
+    NS_STATIC_ATOM_INIT_ATOM(CSSPseudoElementAtoms, name_, value_)
+  #include "nsCSSPseudoElementList.h"
+  #undef CSS_PSEUDO_ELEMENT
+};
+MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
+
+} // namespace detail
+} // namespace mozilla
 
 #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
-  NS_STATIC_ATOM_SUBCLASS_DEFN(nsICSSPseudoElement, nsCSSPseudoElements, name_)
+  NS_STATIC_ATOM_SUBCLASS_DEFN_PTR( \
+    nsICSSPseudoElement, nsCSSPseudoElements, name_)
 #include "nsCSSPseudoElementList.h"
 #undef CSS_PSEUDO_ELEMENT
 
 // Array of nsStaticAtomSetup for each of the pseudo-elements.
 static const nsStaticAtomSetup sCSSPseudoElementAtomSetup[] = {
   #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
-    NS_STATIC_ATOM_SUBCLASS_SETUP(nsCSSPseudoElements, name_)
+    NS_STATIC_ATOM_SUBCLASS_SETUP( \
+      detail::gCSSPseudoElementAtoms, nsCSSPseudoElements, name_)
   #include "nsCSSPseudoElementList.h"
   #undef CSS_PSEUDO_ELEMENT
 };
 
 // Flags data for each of the pseudo-elements, which must be separate
 // from the previous array since there's no place for it in
 // nsStaticAtomSetup.
 /* static */ const uint32_t
--- a/layout/style/nsCSSPseudoElements.h
+++ b/layout/style/nsCSSPseudoElements.h
@@ -74,16 +74,35 @@ enum class CSSPseudoElementType : CSSPse
   NonInheritingAnonBox, // from nsCSSAnonBoxes, IsNonInheritingAnonBox true.
 #ifdef MOZ_XUL
   XULTree,
 #endif
   NotPseudo,
   MAX
 };
 
+namespace detail {
+
+struct CSSPseudoElementAtoms
+{
+  #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
+    NS_STATIC_ATOM_DECL_STRING(name_, value_)
+  #include "nsCSSPseudoElementList.h"
+  #undef CSS_PSEUDO_ELEMENT
+
+  #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
+    NS_STATIC_ATOM_DECL_ATOM(name_)
+  #include "nsCSSPseudoElementList.h"
+  #undef CSS_PSEUDO_ELEMENT
+};
+
+extern const CSSPseudoElementAtoms gCSSPseudoElementAtoms;
+
+} // namespace detail
+
 } // namespace mozilla
 
 // Empty class derived from nsAtom so that function signatures can
 // require an atom from this atom list.
 class nsICSSPseudoElement : public nsAtom {};
 
 class nsCSSPseudoElements
 {
@@ -100,18 +119,18 @@ public:
   // This must match EAGER_PSEUDO_COUNT in Rust code.
   static const size_t kEagerPseudoCount = 4;
 
   static bool IsEagerlyCascadedInServo(const Type aType)
   {
     return PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_IS_CSS2);
   }
 
-#define CSS_PSEUDO_ELEMENT(_name, _value, _flags) \
-  NS_STATIC_ATOM_SUBCLASS_DECL(nsICSSPseudoElement, _name)
+#define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
+  NS_STATIC_ATOM_SUBCLASS_DECL_PTR(nsICSSPseudoElement, name_)
 #include "nsCSSPseudoElementList.h"
 #undef CSS_PSEUDO_ELEMENT
 
   static Type GetPseudoType(nsAtom* aAtom, EnabledState aEnabledState);
 
   // Get the atom for a given Type. aType must be < CSSPseudoElementType::Count.
   // This only ever returns static atoms, so it's fine to return a raw pointer.
   static nsAtom* GetPseudoAtom(Type aType);
--- a/parser/html/nsHtml5AtomTable.cpp
+++ b/parser/html/nsHtml5AtomTable.cpp
@@ -2,17 +2,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/. */
 
 #include "nsHtml5AtomTable.h"
 #include "nsThreadUtils.h"
 
 nsHtml5AtomEntry::nsHtml5AtomEntry(KeyTypePointer aStr)
   : nsStringHashKey(aStr)
-  , mAtom(new nsAtom(nsAtom::AtomKind::HTML5Atom, *aStr, 0))
+  , mAtom(new nsDynamicAtom(*aStr))
 {
 }
 
 nsHtml5AtomEntry::nsHtml5AtomEntry(const nsHtml5AtomEntry& aOther)
   : nsStringHashKey(aOther)
   , mAtom(nullptr)
 {
   NS_NOTREACHED("nsHtml5AtomTable is broken and tried to copy an entry");
--- a/parser/html/nsHtml5AtomTable.h
+++ b/parser/html/nsHtml5AtomTable.h
@@ -16,17 +16,17 @@ class nsHtml5AtomEntry : public nsString
 {
 public:
   explicit nsHtml5AtomEntry(KeyTypePointer aStr);
   nsHtml5AtomEntry(const nsHtml5AtomEntry& aOther);
   ~nsHtml5AtomEntry();
   inline nsAtom* GetAtom() { return mAtom; }
 
 private:
-  nsAtom* mAtom;
+  nsDynamicAtom* mAtom;
 };
 
 /**
  * nsHtml5AtomTable provides non-locking lookup and creation of atoms for
  * nsHtml5Parser or nsHtml5StreamParser.
  *
  * The hashtable holds dynamically allocated atoms that are private to an
  * instance of nsHtml5Parser or nsHtml5StreamParser. (Static atoms are used on
--- a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
+++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
@@ -98,18 +98,18 @@
     } else if (aPath.search(/^explicit\/window-objects\/top\(.*\/js-compartment\(/) >= 0) {
       present.windowObjectsJsCompartments = true;
     } else if (aPath.search(/^explicit\/storage\/sqlite\/places.sqlite/) >= 0) {
       present.places = true;
     } else if (aPath.search(/^explicit\/images/) >= 0) {
       present.images = true;
     } else if (aPath.search(/^explicit\/xpti-working-set$/) >= 0) {
       present.xptiWorkingSet = true;
-    } else if (aPath.search(/^explicit\/atoms\/static\/atom-objects$/) >= 0) {
-      present.staticAtomObjects = true;
+    } else if (aPath.search(/^explicit\/atoms\/dynamic\/atom-objects$/) >= 0) {
+      present.dynamicAtomObjects = true;
     } else if (/\[System Principal\].*this-is-a-sandbox-name/.test(aPath)) {
       // A system compartment with a location (such as a sandbox) should
       // show that location.
       present.sandboxLocation = true;
     } else if (aPath.includes(bigStringPrefix)) {
       present.bigString = true;
     } else if (aPath.includes("!)(*&")) {
       present.smallString1 = true;
@@ -256,17 +256,17 @@
     checkSizeReasonable("js-main-runtime-gc-heap-committed/used/gc-things",
                         jsGcHeapUsedGcThingsTotal);
 
     ok(present.jsNonWindowCompartments,     "js-non-window compartments are present");
     ok(present.windowObjectsJsCompartments, "window-objects/.../js compartments are present");
     ok(present.places,                      "places is present");
     ok(present.images,                      "images is present");
     ok(present.xptiWorkingSet,              "xpti-working-set is present");
-    ok(present.staticAtomObjects,           "static/atom-objects is present");
+    ok(present.dynamicAtomObjects,          "dynamic/atom-objects is present");
     ok(present.sandboxLocation,             "sandbox locations are present");
     ok(present.bigString,                   "large string is present");
     ok(present.smallString1,                "small string 1 is present");
     ok(present.smallString2,                "small string 2 is present");
 
     ok(!present.anonymizedWhenUnnecessary,
        "anonymized paths are not present when unnecessary. Failed case: " +
        present.anonymizedWhenUnnecessary);
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -1416,21 +1416,16 @@ public:
     AtomsSizes sizes;
     NS_AddSizeOfAtoms(MallocSizeOf, sizes);
 
     MOZ_COLLECT_REPORT(
       "explicit/atoms/table", KIND_HEAP, UNITS_BYTES, sizes.mTable,
       "Memory used by the atom table.");
 
     MOZ_COLLECT_REPORT(
-      "explicit/atoms/static/atom-objects", KIND_HEAP, UNITS_BYTES,
-      sizes.mStaticAtomObjects,
-      "Memory used by static atom objects.");
-
-    MOZ_COLLECT_REPORT(
       "explicit/atoms/dynamic/atom-objects", KIND_HEAP, UNITS_BYTES,
       sizes.mDynamicAtomObjects,
       "Memory used by dynamic atom objects.");
 
     MOZ_COLLECT_REPORT(
       "explicit/atoms/dynamic/unshared-buffers", KIND_HEAP, UNITS_BYTES,
       sizes.mDynamicUnsharedBuffers,
       "Memory used by unshared string buffers pointed to by dynamic atom "
--- a/xpcom/ds/moz.build
+++ b/xpcom/ds/moz.build
@@ -113,20 +113,25 @@ UNIFIED_SOURCES += [
     'nsSupportsPrimitives.cpp',
     'nsTArray.cpp',
     'nsTObserverArray.cpp',
     'nsVariant.cpp',
     'PLDHashTable.cpp',
     'Tokenizer.cpp',
 ]
 
-# XXX: will be moved to UNIFIED_SOURCES in the next patch
 SOURCES += [
     'nsGkAtoms.cpp',
 ]
+if CONFIG['CC_TYPE'] == 'msvc':
+    # Needed for gGkAtoms.
+    SOURCES['nsGkAtoms.cpp'].flags += [
+        '-constexpr:steps300000',
+        '-Zc:externConstexpr',
+    ]
 
 EXTRA_COMPONENTS += [
     'nsINIProcessor.js',
     'nsINIProcessor.manifest',
 ]
 
 LOCAL_INCLUDES += [
     '../io',
--- a/xpcom/ds/nsAtom.h
+++ b/xpcom/ds/nsAtom.h
@@ -5,134 +5,202 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsAtom_h
 #define nsAtom_h
 
 #include "nsISupportsImpl.h"
 #include "nsString.h"
 #include "nsStringBuffer.h"
+#include "mozilla/HashFunctions.h"
 
 namespace mozilla {
 struct AtomsSizes;
 }
 
+class nsStaticAtom;
+class nsDynamicAtom;
+
+// This class encompasses both static and dynamic atoms.
+//
+// - In places where static and dynamic atoms can be used, use RefPtr<nsAtom>.
+//   This is by far the most common case. (The exception to this is the HTML5
+//   parser, which does its own weird thing, and uses non-refcounted dynamic
+//   atoms.)
+//
+// - In places where only static atoms can appear, use nsStaticAtom* to avoid
+//   unnecessary refcounting. This is a moderately common case.
+//
+// - In places where only dynamic atoms can appear, it doesn't matter much
+//   whether you use RefPtr<nsAtom> or RefPtr<nsDynamicAtom>. This is an
+//   extremely rare case.
+//
 class nsAtom
 {
 public:
   void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                               mozilla::AtomsSizes& aSizes) const;
 
+  // Dynamic HTML5 atoms are just like vanilla dynamic atoms, but we disallow
+  // various operations, the most important of which is AddRef/Release.
+  // XXX: we'd like to get rid of dynamic HTML5 atoms. See bug 1392185 for
+  // details.
   enum class AtomKind : uint8_t {
-    DynamicAtom = 0,
-    StaticAtom = 1,
-    HTML5Atom = 2,
+    Static = 0,
+    DynamicNormal = 1,
+    DynamicHTML5 = 2,
   };
 
   bool Equals(char16ptr_t aString, uint32_t aLength) const
   {
     return mLength == aLength &&
-           memcmp(mString, aString, mLength * sizeof(char16_t)) == 0;
+           memcmp(GetUTF16String(), aString, mLength * sizeof(char16_t)) == 0;
   }
 
   bool Equals(const nsAString& aString) const
   {
     return Equals(aString.BeginReading(), aString.Length());
   }
 
   AtomKind Kind() const { return static_cast<AtomKind>(mKind); }
 
-  bool IsDynamic() const { return Kind() == AtomKind::DynamicAtom; }
-  bool IsHTML5()   const { return Kind() == AtomKind::HTML5Atom; }
-  bool IsStatic()  const { return Kind() == AtomKind::StaticAtom; }
+  bool IsStatic() const { return Kind() == AtomKind::Static; }
+  bool IsDynamic() const
+  {
+    return Kind() == AtomKind::DynamicNormal ||
+           Kind() == AtomKind::DynamicHTML5;
+  }
+  bool IsDynamicHTML5() const
+  {
+    return Kind() == AtomKind::DynamicHTML5;
+  }
 
-  char16ptr_t GetUTF16String() const { return mString; }
+  const nsStaticAtom* AsStatic() const;
+  const nsDynamicAtom* AsDynamic() const;
+  nsDynamicAtom* AsDynamic();
+
+  char16ptr_t GetUTF16String() const;
 
   uint32_t GetLength() const { return mLength; }
 
   void ToString(nsAString& aString) const;
   void ToUTF8String(nsACString& aString) const;
 
-  // This is not valid for static atoms. The caller must *not* mutate the
-  // string buffer, otherwise all hell will break loose.
-  nsStringBuffer* GetStringBuffer() const
-  {
-    // See the comment on |mString|'s declaration.
-    MOZ_ASSERT(IsDynamic() || IsHTML5());
-    return nsStringBuffer::FromData(const_cast<char16_t*>(mString));
-  }
-
   // A hashcode that is better distributed than the actual atom pointer, for
   // use in situations that need a well-distributed hashcode. It's called hash()
   // rather than Hash() so we can use mozilla::BloomFilter<N, nsAtom>, because
   // BloomFilter requires elements to implement a function called hash().
   //
   uint32_t hash() const
   {
-    MOZ_ASSERT(!IsHTML5());
+    MOZ_ASSERT(!IsDynamicHTML5());
     return mHash;
   }
 
   // We can't use NS_INLINE_DECL_THREADSAFE_REFCOUNTING because the refcounting
   // of this type is special.
   MozExternalRefCountType AddRef();
   MozExternalRefCountType Release();
 
   typedef mozilla::TrueType HasThreadSafeRefCnt;
 
-private:
-  friend class nsAtomTable;
-  friend class nsAtomSubTable;
-  friend class nsHtml5AtomEntry;
+protected:
+  // Used by nsStaticAtom.
+  constexpr nsAtom(const char16_t* aStr, uint32_t aLength)
+    : mLength(aLength)
+    , mKind(static_cast<uint32_t>(nsAtom::AtomKind::Static))
+    , mHash(mozilla::ConstExprHashString(aStr))
+  {}
 
-protected:
-  // Used by nsDynamicAtom and directly (by nsHtml5AtomEntry) for HTML5 atoms.
-  nsAtom(AtomKind aKind, const nsAString& aString, uint32_t aHash);
+  // Used by nsDynamicAtom.
+  nsAtom(AtomKind aKind, const nsAString& aString, uint32_t aHash)
+    : mLength(aString.Length())
+    , mKind(static_cast<uint32_t>(aKind))
+    , mHash(aHash)
+  {
+    MOZ_ASSERT(aKind == AtomKind::DynamicNormal ||
+               aKind == AtomKind::DynamicHTML5);
+  }
 
-  // Used by nsStaticAtom.
-  nsAtom(const char16_t* aString, uint32_t aLength, uint32_t aHash);
-
-  ~nsAtom();
+  ~nsAtom() = default;
 
   const uint32_t mLength:30;
   const uint32_t mKind:2; // nsAtom::AtomKind
   const uint32_t mHash;
-  // WARNING! For static atoms, this is a pointer to a static char buffer. For
-  // non-static atoms it points to the chars in an nsStringBuffer. This means
-  // that nsStringBuffer::FromData(mString) calls are only valid for non-static
-  // atoms.
-  const char16_t* const mString;
 };
 
-// A trivial subclass of nsAtom that can be used for known static atoms. The
-// main advantage of this class is that it doesn't require refcounting, so you
-// can use |nsStaticAtom*| in contrast with |RefPtr<nsAtom>|.
-//
 // This class would be |final| if it wasn't for nsICSSAnonBoxPseudo and
 // nsICSSPseudoElement, which are trivial subclasses used to ensure only
-// certain atoms are passed to certain functions.
+// certain static atoms are passed to certain functions.
 class nsStaticAtom : public nsAtom
 {
 public:
   // These are deleted so it's impossible to RefPtr<nsStaticAtom>. Raw
   // nsStaticAtom pointers should be used instead.
   MozExternalRefCountType AddRef() = delete;
   MozExternalRefCountType Release() = delete;
 
+  constexpr nsStaticAtom(const char16_t* aStr, uint32_t aLength,
+                         uint32_t aStringOffset)
+    : nsAtom(aStr, aLength)
+    , mStringOffset(aStringOffset)
+  {}
+
+  const char16_t* String() const
+  {
+    return reinterpret_cast<const char16_t*>(uintptr_t(this) - mStringOffset);
+  }
+
   already_AddRefed<nsAtom> ToAddRefed() {
     return already_AddRefed<nsAtom>(static_cast<nsAtom*>(this));
   }
 
 private:
-  friend class nsAtomTable;
+  // This is an offset to the string chars, which must be at a lower address in
+  // memory. This should be achieved by using the macros in nsStaticAtom.h.
+  uint32_t mStringOffset;
+};
+
+class nsDynamicAtom : public nsAtom
+{
+public:
+  // We can't use NS_INLINE_DECL_THREADSAFE_REFCOUNTING because the refcounting
+  // of this type is special.
+  MozExternalRefCountType AddRef();
+  MozExternalRefCountType Release();
+
+  ~nsDynamicAtom();
+
+  const char16_t* String() const { return mString; }
 
-  // Construction is done entirely by |friend|s.
-  nsStaticAtom(const char16_t* aString, uint32_t aLength, uint32_t aHash)
-    : nsAtom(aString, aLength, aHash)
-  {}
+  // The caller must *not* mutate the string buffer, otherwise all hell will
+  // break loose.
+  nsStringBuffer* GetStringBuffer() const
+  {
+    // See the comment on |mString|'s declaration.
+    MOZ_ASSERT(IsDynamic());
+    return nsStringBuffer::FromData(const_cast<char16_t*>(mString));
+  }
+
+private:
+  friend class nsAtomTable;
+  friend class nsAtomSubTable;
+  // XXX: we'd like to remove nsHtml5AtomEntry. See bug 1392185.
+  friend class nsHtml5AtomEntry;
+
+  // Construction is done by |friend|s.
+  // The first constructor is for dynamic normal atoms, the second is for
+  // dynamic HTML5 atoms.
+  nsDynamicAtom(const nsAString& aString, uint32_t aHash);
+  explicit nsDynamicAtom(const nsAString& aString);
+
+  mozilla::ThreadSafeAutoRefCnt mRefCnt;
+  // Note: this points to the chars in an nsStringBuffer, which is obtained
+  // with nsStringBuffer::FromData(mString).
+  const char16_t* const mString;
 };
 
 // The four forms of NS_Atomize (for use with |RefPtr<nsAtom>|) return the
 // atom for the string given. At any given time there will always be one atom
 // representing a given string. Atoms are intended to make string comparison
 // cheaper by simplifying it to pointer equality. A pointer to the atom that
 // does not own a reference is not guaranteed to be valid.
 
--- a/xpcom/ds/nsAtomTable.cpp
+++ b/xpcom/ds/nsAtomTable.cpp
@@ -32,20 +32,18 @@
 //
 // - Dynamic: the atom itself is heap allocated, as is the nsStringBuffer it
 //   points to. |gAtomTable| holds weak references to dynamic atoms. When the
 //   refcount of a dynamic atom drops to zero, we increment a static counter.
 //   When that counter reaches a certain threshold, we iterate over the atom
 //   table, removing and deleting dynamic atoms with refcount zero. This allows
 //   us to avoid acquiring the atom table lock during normal refcounting.
 //
-// - Static: the atom itself is heap allocated, but it points to a static
-//   nsStringBuffer. |gAtomTable| effectively owns static atoms, because such
-//   atoms ignore all AddRef/Release calls, which ensures they stay alive until
-//   |gAtomTable| itself is destroyed whereupon they are explicitly deleted.
+// - Static: both the atom and its chars are statically allocated and
+//   immutable, so it ignores all AddRef/Release calls.
 //
 // Note that gAtomTable is used on multiple threads, and has internal
 // synchronization.
 
 using namespace mozilla;
 
 //----------------------------------------------------------------------
 
@@ -60,44 +58,16 @@ enum class GCKind {
 // (and thus turned into unused state), and decremented when an unused
 // atom gets a reference again. The atom table relies on this value to
 // schedule GC. This value can temporarily go below zero when multiple
 // threads are operating the same atom, so it has to be signed so that
 // we wouldn't use overflow value for comparison.
 // See nsAtom::AddRef() and nsAtom::Release().
 static Atomic<int32_t, ReleaseAcquire> gUnusedAtomCount(0);
 
-// Dynamic atoms need a ref count; this class adds that to nsAtom.
-class nsDynamicAtom : public nsAtom
-{
-public:
-  // We can't use NS_INLINE_DECL_THREADSAFE_REFCOUNTING because the refcounting
-  // of this type is special.
-  MozExternalRefCountType AddRef();
-  MozExternalRefCountType Release();
-
-  static nsDynamicAtom* As(nsAtom* aAtom)
-  {
-    MOZ_ASSERT(aAtom->IsDynamic());
-    return static_cast<nsDynamicAtom*>(aAtom);
-  }
-
-private:
-  friend class nsAtomTable;
-  friend class nsAtomSubTable;
-
-  // Construction is done by |friend|s.
-  nsDynamicAtom(const nsAString& aString, uint32_t aHash)
-    : nsAtom(AtomKind::DynamicAtom, aString, aHash)
-    , mRefCnt(1)
-  {}
-
-  mozilla::ThreadSafeAutoRefCnt mRefCnt;
-};
-
 static char16_t*
 FromStringBuffer(const nsAString& aString)
 {
   char16_t* str;
   size_t length = aString.Length();
   RefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aString);
   if (buf) {
     str = static_cast<char16_t*>(buf->Data());
@@ -116,95 +86,119 @@ FromStringBuffer(const nsAString& aStrin
              "enough storage");
 
   // Take ownership of the string buffer.
   mozilla::Unused << buf.forget();
 
   return str;
 }
 
-// This constructor is for dynamic atoms and HTML5 atoms.
-nsAtom::nsAtom(AtomKind aKind, const nsAString& aString, uint32_t aHash)
-  : mLength(aString.Length())
-  , mKind(static_cast<uint32_t>(aKind))
-  , mHash(aHash)
+nsDynamicAtom::nsDynamicAtom(const nsAString& aString, uint32_t aHash)
+  : nsAtom(AtomKind::DynamicNormal, aString, aHash)
+  , mRefCnt(1)
   , mString(FromStringBuffer(aString))
 {
-  MOZ_ASSERT(aKind == AtomKind::DynamicAtom || aKind == AtomKind::HTML5Atom);
-
-  MOZ_ASSERT_IF(!IsHTML5(), mHash == HashString(mString, mLength));
+  MOZ_ASSERT(mHash == HashString(mString, mLength));
 
   MOZ_ASSERT(mString[mLength] == char16_t(0), "null terminated");
   MOZ_ASSERT(Equals(aString), "correct data");
 }
 
-// This constructor is for static atoms.
-nsAtom::nsAtom(const char16_t* aString, uint32_t aLength, uint32_t aHash)
-  : mLength(aLength)
-  , mKind(static_cast<uint32_t>(AtomKind::StaticAtom))
-  , mHash(aHash)
-  , mString(const_cast<char16_t*>(aString))
+nsDynamicAtom::nsDynamicAtom(const nsAString& aString)
+  : nsAtom(AtomKind::DynamicHTML5, aString, 0)
+  , mRefCnt(1)
+  , mString(FromStringBuffer(aString))
 {
-  MOZ_ASSERT(mHash == HashString(mString, mLength));
+  MOZ_ASSERT(mString[mLength] == char16_t(0), "null terminated");
+  MOZ_ASSERT(Equals(aString), "correct data");
+}
 
-  MOZ_ASSERT(mString[mLength] == char16_t(0), "null terminated");
-  MOZ_ASSERT(NS_strlen(mString) == mLength, "correct storage");
+nsDynamicAtom::~nsDynamicAtom()
+{
+  GetStringBuffer()->Release();
 }
 
-nsAtom::~nsAtom()
+const nsStaticAtom*
+nsAtom::AsStatic() const
+{
+  MOZ_ASSERT(IsStatic());
+  return static_cast<const nsStaticAtom*>(this);
+}
+
+const nsDynamicAtom*
+nsAtom::AsDynamic() const
 {
-  if (!IsStatic()) {
-    MOZ_ASSERT(IsDynamic() || IsHTML5());
-    GetStringBuffer()->Release();
-  }
+  MOZ_ASSERT(IsDynamic());
+  return static_cast<const nsDynamicAtom*>(this);
+}
+
+nsDynamicAtom*
+nsAtom::AsDynamic()
+{
+  MOZ_ASSERT(IsDynamic());
+  return static_cast<nsDynamicAtom*>(this);
 }
 
 void
 nsAtom::ToString(nsAString& aString) const
 {
   // See the comment on |mString|'s declaration.
   if (IsStatic()) {
     // AssignLiteral() lets us assign without copying. This isn't a string
     // literal, but it's a static atom and thus has an unbounded lifetime,
     // which is what's important.
-    aString.AssignLiteral(mString, mLength);
+    aString.AssignLiteral(AsStatic()->String(), mLength);
   } else {
-    GetStringBuffer()->ToString(mLength, aString);
+    AsDynamic()->GetStringBuffer()->ToString(mLength, aString);
   }
 }
 
 void
 nsAtom::ToUTF8String(nsACString& aBuf) const
 {
-  MOZ_ASSERT(!IsHTML5(), "Called ToUTF8String() on an HTML5 atom");
-  CopyUTF16toUTF8(nsDependentString(mString, mLength), aBuf);
+  MOZ_ASSERT(!IsDynamicHTML5(),
+             "Called ToUTF8String() on a dynamic HTML5 atom");
+  CopyUTF16toUTF8(nsDependentString(GetUTF16String(), mLength), aBuf);
 }
 
 void
 nsAtom::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, AtomsSizes& aSizes)
   const
 {
-  MOZ_ASSERT(!IsHTML5(),
-             "Called AddSizeOfIncludingThis() on an HTML5 atom");
-  size_t thisSize = aMallocSizeOf(this);
-  if (IsStatic()) {
-    // String buffers pointed to by static atoms are in static memory, and so
-    // are not measured here.
-    aSizes.mStaticAtomObjects += thisSize;
-  } else {
-    aSizes.mDynamicAtomObjects += thisSize;
+  MOZ_ASSERT(!IsDynamicHTML5(),
+             "Called AddSizeOfIncludingThis() on a dynamic HTML5 atom");
+
+  // Static atoms are in static memory, and so are not measured here.
+  if (IsDynamic()) {
+    aSizes.mDynamicAtomObjects += aMallocSizeOf(this);
     aSizes.mDynamicUnsharedBuffers +=
-      GetStringBuffer()->SizeOfIncludingThisIfUnshared(aMallocSizeOf);
+      AsDynamic()->GetStringBuffer()->SizeOfIncludingThisIfUnshared(
+        aMallocSizeOf);
   }
 }
 
+char16ptr_t
+nsAtom::GetUTF16String() const
+{
+  return IsStatic() ? AsStatic()->String() : AsDynamic()->String();
+}
+
 //----------------------------------------------------------------------
 
 struct AtomTableKey
 {
+  explicit AtomTableKey(const nsStaticAtom* aAtom)
+    : mUTF16String(aAtom->String())
+    , mUTF8String(nullptr)
+    , mLength(aAtom->GetLength())
+    , mHash(aAtom->hash())
+  {
+    MOZ_ASSERT(HashString(mUTF16String, mLength) == mHash);
+  }
+
   AtomTableKey(const char16_t* aUTF16String, uint32_t aLength,
                uint32_t* aHashOut)
     : mUTF16String(aUTF16String)
     , mUTF8String(nullptr)
     , mLength(aLength)
   {
     mHash = HashString(mUTF16String, mLength);
     *aHashOut = mHash;
@@ -355,23 +349,17 @@ AtomTableMatchKey(const PLDHashEntryHdr*
 
   return he->mAtom->Equals(k->mUTF16String, k->mLength);
 }
 
 void
 nsAtomTable::AtomTableClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
 {
   auto entry = static_cast<AtomTableEntry*>(aEntry);
-  nsAtom* atom = entry->mAtom;
-  if (atom->IsStatic()) {
-    // This case -- when the entry being cleared holds a static atom -- only
-    // occurs when gAtomTable is destroyed, whereupon all static atoms within it
-    // must be explicitly deleted.
-    delete atom;
-  }
+  entry->mAtom = nullptr;
 }
 
 static void
 AtomTableInitEntry(PLDHashEntryHdr* aEntry, const void* aKey)
 {
   static_cast<AtomTableEntry*>(aEntry)->mAtom = nullptr;
 }
 
@@ -505,20 +493,20 @@ nsAtomSubTable::GCLocked(GCKind aKind)
   uint32_t nonZeroRefcountAtomsCount = 0;
   for (auto i = mTable.Iter(); !i.Done(); i.Next()) {
     auto entry = static_cast<AtomTableEntry*>(i.Get());
     if (entry->mAtom->IsStatic()) {
       continue;
     }
 
     nsAtom* atom = entry->mAtom;
-    MOZ_ASSERT(!atom->IsHTML5());
-    if (atom->IsDynamic() && nsDynamicAtom::As(atom)->mRefCnt == 0) {
+    MOZ_ASSERT(!atom->IsDynamicHTML5());
+    if (atom->IsDynamic() && atom->AsDynamic()->mRefCnt == 0) {
       i.Remove();
-      delete atom;
+      delete atom->AsDynamic();
       ++removedCount;
     }
 #ifdef NS_FREE_PERMANENT_DATA
     else if (aKind == GCKind::Shutdown && PR_GetEnv("XPCOM_MEM_BLOAT_LOG")) {
       // Only report leaking atoms in leak-checking builds in a run where we
       // are checking for leaks, during shutdown. If something is anomalous,
       // then we'll assert later in this function.
       nsAutoCString name;
@@ -584,27 +572,27 @@ nsDynamicAtom::Release()
   }
 
   return count;
 }
 
 MozExternalRefCountType
 nsAtom::AddRef()
 {
-  MOZ_ASSERT(!IsHTML5(), "Attempt to AddRef an HTML5 atom");
+  MOZ_ASSERT(!IsDynamicHTML5(), "Attempt to AddRef a dynamic HTML5 atom");
 
-  return IsStatic() ? 2 : nsDynamicAtom::As(this)->AddRef();
+  return IsStatic() ? 2 : AsDynamic()->AddRef();
 }
 
 MozExternalRefCountType
 nsAtom::Release()
 {
-  MOZ_ASSERT(!IsHTML5(), "Attempt to Release an HTML5 atom");
+  MOZ_ASSERT(!IsDynamicHTML5(), "Attempt to Release a dynamic HTML5 atom");
 
-  return IsStatic() ? 1 : nsDynamicAtom::As(this)->Release();
+  return IsStatic() ? 1 : AsDynamic()->Release();
 }
 
 //----------------------------------------------------------------------
 
 // Have the static atoms been inserted into the table?
 static bool gStaticAtomsDone = false;
 
 void
@@ -661,46 +649,42 @@ nsAtomSubTable::AddSizeOfExcludingThisLo
 void
 nsAtomTable::RegisterStaticAtoms(const nsStaticAtomSetup* aSetup,
                                  uint32_t aCount)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_RELEASE_ASSERT(!gStaticAtomsDone, "Static atom insertion is finished!");
 
   for (uint32_t i = 0; i < aCount; ++i) {
-    const char16_t* string = aSetup[i].mString;
+    const nsStaticAtom* atom = aSetup[i].mAtom;
     nsStaticAtom** atomp = aSetup[i].mAtomp;
 
-    MOZ_ASSERT(nsCRT::IsAscii(string));
+    MOZ_ASSERT(nsCRT::IsAscii(atom->String()));
+    MOZ_ASSERT(NS_strlen(atom->String()) == atom->GetLength());
 
-    uint32_t stringLen = NS_strlen(string);
-
-    uint32_t hash;
-    AtomTableKey key(string, stringLen, &hash);
+    AtomTableKey key(atom);
     nsAtomSubTable& table = SelectSubTable(key);
     MutexAutoLock lock(table.mLock);
     AtomTableEntry* he = table.Add(key);
 
-    nsStaticAtom* atom;
     if (he->mAtom) {
       // Disallow creating a dynamic atom, and then later, while the dynamic
       // atom is still alive, registering that same atom as a static atom.  It
       // causes subtle bugs, and we're programming in C++ here, not Smalltalk.
       if (!he->mAtom->IsStatic()) {
         nsAutoCString name;
         he->mAtom->ToUTF8String(name);
         MOZ_CRASH_UNSAFE_PRINTF(
           "Static atom registration for %s should be pushed back", name.get());
       }
-      atom = static_cast<nsStaticAtom*>(he->mAtom);
+      // XXX: A duplicate static atom! Bug 1445113 will disallow this case.
+      *atomp = static_cast<nsStaticAtom*>(he->mAtom);
     } else {
-      atom = new nsStaticAtom(string, stringLen, hash);
-      he->mAtom = atom;
+      he->mAtom = *atomp = const_cast<nsStaticAtom*>(atom);
     }
-    *atomp = atom;
   }
 }
 
 void
 RegisterStaticAtoms(const nsStaticAtomSetup* aSetup, uint32_t aCount)
 {
   MOZ_ASSERT(gAtomTable);
   gAtomTable->RegisterStaticAtoms(aSetup, aCount);
--- a/xpcom/ds/nsAtomTable.h
+++ b/xpcom/ds/nsAtomTable.h
@@ -12,23 +12,21 @@
 
 void NS_InitAtomTable();
 void NS_ShutdownAtomTable();
 
 namespace mozilla {
 struct AtomsSizes
 {
   size_t mTable;
-  size_t mStaticAtomObjects;
   size_t mDynamicAtomObjects;
   size_t mDynamicUnsharedBuffers;
 
   AtomsSizes()
    : mTable(0)
-   , mStaticAtomObjects(0)
    , mDynamicAtomObjects(0)
    , mDynamicUnsharedBuffers(0)
   {}
 };
 } // namespace mozilla
 
 void NS_AddSizeOfAtoms(mozilla::MallocSizeOf aMallocSizeOf,
                        mozilla::AtomsSizes& aSizes);
--- a/xpcom/ds/nsGkAtoms.cpp
+++ b/xpcom/ds/nsGkAtoms.cpp
@@ -1,27 +1,42 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsGkAtoms.h"
-#include "nsStaticAtom.h"
+
+namespace mozilla {
+namespace detail {
+
+MOZ_PUSH_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
+extern constexpr GkAtoms gGkAtoms = {
+  #define GK_ATOM(name_, value_) NS_STATIC_ATOM_INIT_STRING(value_)
+  #include "nsGkAtomList.h"
+  #undef GK_ATOM
 
-#define GK_ATOM(name_, value_) NS_STATIC_ATOM_DEFN(nsGkAtoms, name_)
-#include "nsGkAtomList.h"
-#undef GK_ATOM
+  #define GK_ATOM(name_, value_) \
+    NS_STATIC_ATOM_INIT_ATOM(GkAtoms, name_, value_)
+  #include "nsGkAtomList.h"
+  #undef GK_ATOM
+};
+MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
 
-#define GK_ATOM(name_, value_) NS_STATIC_ATOM_BUFFER(name_, value_)
+} // namespace detail
+} // namespace mozilla
+
+#define GK_ATOM(name_, value_) NS_STATIC_ATOM_DEFN_PTR(nsGkAtoms, name_)
 #include "nsGkAtomList.h"
 #undef GK_ATOM
 
 static const nsStaticAtomSetup sGkAtomSetup[] = {
-  #define GK_ATOM(name_, value_) NS_STATIC_ATOM_SETUP(nsGkAtoms, name_)
+  #define GK_ATOM(name_, value_) \
+    NS_STATIC_ATOM_SETUP(mozilla::detail::gGkAtoms, nsGkAtoms, name_)
   #include "nsGkAtomList.h"
   #undef GK_ATOM
 };
 
 void nsGkAtoms::RegisterStaticAtoms()
 {
   NS_RegisterStaticAtoms(sGkAtomSetup);
 }
--- a/xpcom/ds/nsGkAtoms.h
+++ b/xpcom/ds/nsGkAtoms.h
@@ -2,21 +2,41 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.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 nsGkAtoms_h___
 #define nsGkAtoms_h___
 
+#include "nsAtom.h"
 #include "nsStaticAtom.h"
 
+namespace mozilla {
+namespace detail {
+
+struct GkAtoms
+{
+  #define GK_ATOM(name_, value_) NS_STATIC_ATOM_DECL_STRING(name_, value_)
+  #include "nsGkAtomList.h"
+  #undef GK_ATOM
+
+  #define GK_ATOM(name_, value_) NS_STATIC_ATOM_DECL_ATOM(name_)
+  #include "nsGkAtomList.h"
+  #undef GK_ATOM
+};
+
+extern const GkAtoms gGkAtoms;
+
+} // namespace detail
+} // namespace mozilla
+
 class nsGkAtoms
 {
 public:
   static void RegisterStaticAtoms();
 
-  #define GK_ATOM(_name, _value) NS_STATIC_ATOM_DECL(_name)
+  #define GK_ATOM(name_, value_) NS_STATIC_ATOM_DECL_PTR(name_)
   #include "nsGkAtomList.h"
   #undef GK_ATOM
 };
 
 #endif /* nsGkAtoms_h___ */
--- a/xpcom/ds/nsStaticAtom.h
+++ b/xpcom/ds/nsStaticAtom.h
@@ -15,109 +15,198 @@
 // conjunction with a .h file that defines the names and values of the atoms.
 //
 // For example, the .h file might be called MyAtomList.h and look like this:
 //
 //   MY_ATOM(one, "one")
 //   MY_ATOM(two, "two")
 //   MY_ATOM(three, "three")
 //
-// The code defining the static atoms might look like this:
+// The code defining the static atoms should look something like this:
+//
+//   ====> MyAtoms.h <====
+//
+//   namespace detail {
 //
-//   class MyAtoms {
-//   public:
-//     #define MY_ATOM(_name, _value) NS_STATIC_ATOM_DECL(_name)
+//   struct MyAtoms
+//   {
+//     #define MY_ATOM(name_, value_) NS_STATIC_ATOM_DECL_STRING(name_, value_)
+//     #include "MyAtomList.h"
+//     #undef MY_ATOM
+//
+//     #define MY_ATOM(name_, value_) NS_STATIC_ATOM_DECL_ATOM(name_)
 //     #include "MyAtomList.h"
 //     #undef MY_ATOM
 //   };
 //
-//   #define MY_ATOM(name_, value_) NS_STATIC_ATOM_DEFN(MyAtoms, name_)
-//   #include "MyAtomList.h"
-//   #undef MY_ATOM
+//   extern const MyAtoms gMyAtoms;
+//
+//   } // namespace detail
+//
+//   class nsMyAtoms
+//   {
+//   public:
+//     #define MY_ATOM(name_, value_) NS_STATIC_ATOM_DECL_PTR(name_)
+//     #include "MyAtomList.h"
+//     #undef MY_ATOM
+//   };
+//
+//   ====> MyAtoms.cpp <====
 //
-//   #define MY_ATOM(name_, value_) NS_STATIC_ATOM_BUFFER(name_, value_)
+//   MOZ_PUSH_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
+//   static constexpr MyAtoms gMyAtoms = {
+//     #define MY_ATOM(name_, value_) NS_STATIC_ATOM_INIT_STRING(value_)
+//     #include "MyAtomList.h"
+//     #undef MY_ATOM
+//
+//     #define MY_ATOM(name_, value_) NS_STATIC_ATOM_INIT_ATOM(MyAtoms, name_, value_)
+//     #include "MyAtomList.h"
+//     #undef MY_ATOM
+//   };
+//   MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
+//
+//   #define MY_ATOM(name_, value_) NS_STATIC_ATOM_DEFN_PTR(nsMyAtoms, name_)
 //   #include "MyAtomList.h"
 //   #undef MY_ATOM
 //
 //   static const nsStaticAtomSetup sMyAtomSetup[] = {
-//     #define MY_ATOM(name_, value_) NS_STATIC_ATOM_SETUP(MyAtoms, name_)
+//     #define MY_ATOM(name_, value_) NS_STATIC_ATOM_SETUP(mozilla::detail::gMyAtoms, nsMyAtoms, name_)
 //     #include "MyAtomList.h"
 //     #undef MY_ATOM
 //   };
 //
 // The macros expand to the following:
 //
-//   class MyAtoms
+//   ====> MyAtoms.h <====
+//
+//   // A `detail` namespace is used because the things within it aren't
+//   // directly referenced by external users of these static atoms.
+//   namespace detail {
+//
+//   // This `detail` class contains the atom strings and the atom objects.
+//   // Because they are together in a class, the mStringOffset field of the
+//   // atoms will be small and can be initialized at compile time.
+//   struct MyAtoms
+//   {
+//     const char16_t one_string[4];
+//     const char16_t two_string[4];
+//     const char16_t three_string[6];
+//
+//     const nsStaticAtom one_atom;
+//     const nsStaticAtom two_atom;
+//     const nsStaticAtom three_atom;
+//   };
+//
+//   extern const MyAtoms gMyAtoms;
+//
+//   } // namespace detail
+//
+//   // This class holds the pointers to the individual atoms.
+//   class nsMyAtoms
 //   {
 //   public:
 //     static nsStaticAtom* one;
 //     static nsStaticAtom* two;
 //     static nsStaticAtom* three;
 //   };
 //
+//   ====> MyAtoms.cpp <====
+//
+//   // Need to suppress some MSVC warning weirdness with WrappingMultiply().
+//   MOZ_PUSH_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
+//   // Because this is `constexpr` it ends up in read-only memory where it can
+//   // be shared between processes.
+//   extern constexpr MyAtoms gMyAtoms = {
+//     u"one",
+//     u"two",
+//     u"three",
+//
+//     nsStaticAtom(u"one", 3, offsetof(MyAtoms, one_atom) -
+//                             offsetof(MyAtoms, one_string)),
+//     nsStaticAtom(u"two", 3, offsetof(MyAtoms, two_atom) -
+//                             offsetof(MyAtoms, two_string)),
+//     nsStaticAtom(u"three", 3, offsetof(MyAtoms, three_atom) -
+//                               offsetof(MyAtoms, three_string)),
+//   };
+//   MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
+//
 //   nsStaticAtom* MyAtoms::one;
 //   nsStaticAtom* MyAtoms::two;
 //   nsStaticAtom* MyAtoms::three;
 //
-//   static const char16_t one_buffer[4] = u"one";     // plus a static_assert
-//   static const char16_t two_buffer[4] = u"two";     // plus a static_assert
-//   static const char16_t three_buffer[6] = u"three"; // plus a static_assert
-//
 //   static const nsStaticAtomSetup sMyAtomSetup[] = {
-//     { one_buffer, &MyAtoms::one },
-//     { two_buffer, &MyAtoms::two },
-//     { three_buffer, &MyAtoms::three },
+//     { &detail::gMyAtoms.one_atom, &nsMyAtoms::one },
+//     { &detail::gMyAtoms.two_atom, &nsMyAtoms::two },
+//     { &detail::gMyAtoms.three_atom, &nsMyAtoms::three },
 //   };
 //
 // When RegisterStaticAtoms(sMyAtomSetup) is called it iterates over
 // sMyAtomSetup[]. E.g. for the first atom it does roughly the following:
-// - MyAtoms::one = new nsStaticAtom(one_buffer)
-// - inserts MyAtoms::one into the atom table
+// - MyAtoms::one = one_atom
+// - inserts one_atom into the atom table
+
+// The declaration of the atom's string, which must be within the same `detail`
+// class as NS_STATIC_ATOM_DECL_ATOM.
+#define NS_STATIC_ATOM_DECL_STRING(name_, value_) \
+  const char16_t name_##_string[sizeof(value_)];
 
-// The declaration of the pointer to the static atom, which must be within a
-// class.
-#define NS_STATIC_ATOM_DECL(name_) \
+// The declaration of the static atom itself, which must be within the same
+// `detail` class as NS_STATIC_ATOM_DECL_STRING.
+#define NS_STATIC_ATOM_DECL_ATOM(name_) \
+  const nsStaticAtom name_##_atom;
+
+// The declaration of the pointer to the static atom, which must be within
+// a class.
+#define NS_STATIC_ATOM_DECL_PTR(name_) \
   static nsStaticAtom* name_;
 
 // Like NS_STATIC_ATOM_DECL, but for sub-classes of nsStaticAtom.
-#define NS_STATIC_ATOM_SUBCLASS_DECL(type_, name_) \
+#define NS_STATIC_ATOM_SUBCLASS_DECL_PTR(type_, name_) \
   static type_* name_;
 
+// The initialization of the atom's string, which must be within a `constexpr`
+// instance of the `detail` class.
+#define NS_STATIC_ATOM_INIT_STRING(value_) \
+  u"" value_,
+
+// The initialization of the static atom itself, which must be within a
+// `constexpr` instance of the `detail` class.
+//
+// Note that |value_| is an 8-bit string, and so |sizeof(value_)| is equal
+// to the number of chars (including the terminating '\0'). The |u""| prefix
+// converts |value_| to a 16-bit string.
+#define NS_STATIC_ATOM_INIT_ATOM(class_, name_, value_) \
+  nsStaticAtom(u"" value_, \
+               sizeof(value_) - 1, \
+               offsetof(class_, name_##_atom) - \
+               offsetof(class_, name_##_string)),
+
 // The definition of the pointer to the static atom. Initially null, it is
 // set by RegisterStaticAtoms() to point to a heap-allocated nsStaticAtom.
-#define NS_STATIC_ATOM_DEFN(class_, name_) \
+#define NS_STATIC_ATOM_DEFN_PTR(class_, name_) \
   nsStaticAtom* class_::name_;
 
 // Like NS_STATIC_ATOM_DEFN, but for sub-classes of nsStaticAtom.
-#define NS_STATIC_ATOM_SUBCLASS_DEFN(type_, class_, name_) \
+#define NS_STATIC_ATOM_SUBCLASS_DEFN_PTR(type_, class_, name_) \
   type_* class_::name_;
 
-// The buffer of 16-bit chars that constitute the static atom.
-//
-// Note that |value_| is an 8-bit string, and so |sizeof(value_)| is equal
-// to the number of chars (including the terminating '\0'). The |u""| prefix
-// converts |value_| to a 16-bit string, which is what is assigned.
-#define NS_STATIC_ATOM_BUFFER(name_, value_) \
-  static const char16_t name_##_buffer[sizeof(value_)] = u"" value_; \
-  static_assert(sizeof(value_[0]) == 1, "non-8-bit static atom literal");
-
-// The StaticAtomSetup. Used only during start-up.
-#define NS_STATIC_ATOM_SETUP(class_, name_) \
-  { name_##_buffer, &class_::name_ },
+// The StaticAtomSetup. Used during start-up, and in some cases afterwards.
+#define NS_STATIC_ATOM_SETUP(detailObj_, class_, name_) \
+  { &detailObj_.name_##_atom, &class_::name_ },
 
 // Like NS_STATIC_ATOM_SUBCLASS, but for sub-classes of nsStaticAtom.
-#define NS_STATIC_ATOM_SUBCLASS_SETUP(class_, name_) \
-  { name_##_buffer, reinterpret_cast<nsStaticAtom**>(&class_::name_) },
+#define NS_STATIC_ATOM_SUBCLASS_SETUP(detailObj_, class_, name_) \
+  { &detailObj_.name_##_atom, \
+    reinterpret_cast<nsStaticAtom**>(&class_::name_) },
 
 // Holds data used to initialize large number of atoms during startup. Use
-// NS_STATIC_ATOM_SETUP to initialize these structs. They should never be
-// accessed directly other than from nsAtomTable.cpp.
+// NS_STATIC_ATOM_SETUP to initialize these structs.
 struct nsStaticAtomSetup
 {
-  const char16_t* const mString;
+  const nsStaticAtom* const mAtom;
   nsStaticAtom** const mAtomp;
 };
 
 // Register an array of static atoms with the atom table.
 template<uint32_t N>
 void
 NS_RegisterStaticAtoms(const nsStaticAtomSetup (&aSetup)[N])
 {
--- a/xpcom/io/moz.build
+++ b/xpcom/io/moz.build
@@ -93,17 +93,16 @@ EXPORTS.mozilla += [
 UNIFIED_SOURCES += [
     'Base64.cpp',
     'crc32c.c',
     'FileDescriptorFile.cpp',
     'NonBlockingAsyncInputStream.cpp',
     'nsAnonymousTemporaryFile.cpp',
     'nsAppFileLocationProvider.cpp',
     'nsBinaryStream.cpp',
-    'nsDirectoryService.cpp',
     'nsEscape.cpp',
     'nsInputStreamTee.cpp',
     'nsIOUtil.cpp',
     'nsLinebreakConverter.cpp',
     'nsLocalFileCommon.cpp',
     'nsMultiplexInputStream.cpp',
     'nsNativeCharsetUtils.cpp',
     'nsPipe3.cpp',
@@ -117,16 +116,23 @@ UNIFIED_SOURCES += [
     'nsWildCard.cpp',
     'SlicedInputStream.cpp',
     'SnappyCompressOutputStream.cpp',
     'SnappyFrameUtils.cpp',
     'SnappyUncompressInputStream.cpp',
     'SpecialSystemDirectory.cpp',
 ]
 
+SOURCES += [
+    'nsDirectoryService.cpp',
+]
+if CONFIG['CC_TYPE'] == 'msvc':
+    # Needed for gDirectoryAtoms.
+    SOURCES['nsDirectoryService.cpp'].flags += ['-Zc:externConstexpr']
+
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     SOURCES += [
         'CocoaFileUtils.mm',
     ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
--- a/xpcom/io/nsDirectoryService.cpp
+++ b/xpcom/io/nsDirectoryService.cpp
@@ -4,19 +4,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ArrayUtils.h"
 
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsDirectoryService.h"
-#include "nsDirectoryServiceDefs.h"
 #include "nsLocalFile.h"
 #include "nsDebug.h"
+#include "nsGkAtoms.h"
 #include "nsStaticAtom.h"
 #include "nsEnumeratorUtils.h"
 
 #include "nsICategoryManager.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIStringEnumerator.h"
 
 #if defined(XP_WIN)
@@ -100,27 +100,44 @@ nsDirectoryService::Create(nsISupports* 
 
   if (!gService) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   return gService->QueryInterface(aIID, aResult);
 }
 
-#define DIR_ATOM(name_, value_) NS_STATIC_ATOM_DEFN(nsDirectoryService, name_)
-#include "nsDirectoryServiceAtomList.h"
-#undef DIR_ATOM
+namespace mozilla {
+namespace detail {
+
+MOZ_PUSH_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
+extern constexpr DirectoryAtoms gDirectoryAtoms = {
+  #define DIR_ATOM(name_, value_) NS_STATIC_ATOM_INIT_STRING(value_)
+  #include "nsDirectoryServiceAtomList.h"
+  #undef DIR_ATOM
 
-#define DIR_ATOM(name_, value_) NS_STATIC_ATOM_BUFFER(name_, value_)
+  #define DIR_ATOM(name_, value_) \
+    NS_STATIC_ATOM_INIT_ATOM(DirectoryAtoms, name_, value_)
+  #include "nsDirectoryServiceAtomList.h"
+  #undef DIR_ATOM
+};
+MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
+
+} // namespace detail
+} // namespace mozilla
+
+#define DIR_ATOM(name_, value_) \
+  NS_STATIC_ATOM_DEFN_PTR(nsDirectoryService, name_)
 #include "nsDirectoryServiceAtomList.h"
 #undef DIR_ATOM
 
 static const nsStaticAtomSetup sDirectoryServiceAtomSetup[] = {
   #define DIR_ATOM(name_, value_) \
-    NS_STATIC_ATOM_SETUP(nsDirectoryService, name_)
+    NS_STATIC_ATOM_SETUP( \
+      mozilla::detail::gDirectoryAtoms, nsDirectoryService, name_)
   #include "nsDirectoryServiceAtomList.h"
   #undef DIR_ATOM
 };
 
 NS_IMETHODIMP
 nsDirectoryService::Init()
 {
   NS_NOTREACHED("nsDirectoryService::Init() for internal use only!");
--- a/xpcom/io/nsDirectoryService.h
+++ b/xpcom/io/nsDirectoryService.h
@@ -6,25 +6,45 @@
 
 #ifndef nsDirectoryService_h___
 #define nsDirectoryService_h___
 
 #include "nsIDirectoryService.h"
 #include "nsInterfaceHashtable.h"
 #include "nsIFile.h"
 #include "nsAtom.h"
+#include "nsDirectoryServiceDefs.h"
 #include "nsStaticAtom.h"
 #include "nsTArray.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/StaticPtr.h"
 
 #define NS_XPCOM_INIT_CURRENT_PROCESS_DIR       "MozBinD"   // Can be used to set NS_XPCOM_CURRENT_PROCESS_DIR
                                                             // CANNOT be used to GET a location
 #define NS_DIRECTORY_SERVICE_CID  {0xf00152d0,0xb40b,0x11d3,{0x8c, 0x9c, 0x00, 0x00, 0x64, 0x65, 0x73, 0x74}}
 
+namespace mozilla {
+namespace detail {
+
+struct DirectoryAtoms
+{
+  #define DIR_ATOM(name_, value_) NS_STATIC_ATOM_DECL_STRING(name_, value_)
+  #include "nsDirectoryServiceAtomList.h"
+  #undef DIR_ATOM
+
+  #define DIR_ATOM(name_, value_) NS_STATIC_ATOM_DECL_ATOM(name_)
+  #include "nsDirectoryServiceAtomList.h"
+  #undef DIR_ATOM
+};
+
+extern const DirectoryAtoms gDirectoryAtoms;
+
+} // namespace detail
+} // namespace mozilla
+
 class nsDirectoryService final
   : public nsIDirectoryService
   , public nsIProperties
   , public nsIDirectoryServiceProvider2
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
@@ -50,14 +70,14 @@ private:
   ~nsDirectoryService();
 
   nsresult GetCurrentProcessDirectory(nsIFile** aFile);
 
   nsInterfaceHashtable<nsCStringHashKey, nsIFile> mHashtable;
   nsTArray<nsCOMPtr<nsIDirectoryServiceProvider>> mProviders;
 
 public:
-  #define DIR_ATOM(name_, value_) NS_STATIC_ATOM_DECL(name_)
+  #define DIR_ATOM(name_, value_) NS_STATIC_ATOM_DECL_PTR(name_)
   #include "nsDirectoryServiceAtomList.h"
   #undef DIR_ATOM
 };
 
 #endif