Bug 1482782 - Part 4: Move CSS pseudo-element atoms to nsGkAtoms. r=njn,emilio
☠☠ backed out by f3f6f10edab2 ☠ ☠
authorCameron McCormack <cam@mcc.id.au>
Wed, 15 Aug 2018 15:46:00 +1000
changeset 486704 018fdb50a6bec77b3022b2431334bae21339c4cb
parent 486703 33a8aa8096c94e9be06d4aec7a8ad274f5dcf9de
child 486705 887de0efbb67f436a241a60fea0fc4a001656cdd
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)
reviewersnjn, emilio
bugs1482782
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 1482782 - Part 4: Move CSS pseudo-element atoms to nsGkAtoms. r=njn,emilio Summary: Depends On D3282 Reviewers: njn!, emilio! Tags: #secure-revision Bug #: 1482782 Differential Revision: https://phabricator.services.mozilla.com/D3283
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsGenConList.cpp
layout/base/nsGenConList.h
layout/build/nsLayoutStatics.cpp
layout/generic/nsBlockFrame.cpp
layout/generic/nsFirstLetterFrame.cpp
layout/generic/nsInlineFrame.cpp
layout/generic/nsTextFrame.cpp
layout/style/nsCSSPseudoElementList.h
layout/style/nsCSSPseudoElements.cpp
layout/style/nsCSSPseudoElements.h
layout/style/nsComputedDOMStyle.cpp
servo/components/style/gecko/pseudo_element_definition.mako.rs
servo/components/style/gecko/regen_atoms.py
xpcom/ds/Atom.py
xpcom/ds/StaticAtoms.py
xpcom/ds/moz.build
xpcom/ds/nsGkAtoms.h
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -3387,18 +3387,18 @@ nsCSSFrameConstructor::ConstructDetailsF
 
 static nsIFrame*
 FindAncestorWithGeneratedContentPseudo(nsIFrame* aFrame)
 {
   for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
     NS_ASSERTION(f->IsGeneratedContentFrame(),
                  "should not have exited generated content");
     nsAtom* pseudo = f->Style()->GetPseudo();
-    if (pseudo == nsCSSPseudoElements::before ||
-        pseudo == nsCSSPseudoElements::after)
+    if (pseudo == nsCSSPseudoElements::before() ||
+        pseudo == nsCSSPseudoElements::after())
       return f;
   }
   return nullptr;
 }
 
 #define SIMPLE_FCDATA(_func) FCDATA_DECL(0, _func)
 #define FULL_CTOR_FCDATA(_flags, _func)                             \
   { _flags | FCDATA_FUNC_IS_FULL_CTOR, { nullptr }, _func, nullptr }
@@ -10270,17 +10270,17 @@ nsCSSFrameConstructor::WrapFramesInFirst
     // Nothing is supposed to go into the first-line; nothing to do
     return;
   }
 
   if (!aLineFrame) {
     // Create line frame
     ComputedStyle* parentStyle =
       nsFrame::CorrectStyleParentFrame(aBlockFrame,
-                                       nsCSSPseudoElements::firstLine)->
+                                       nsCSSPseudoElements::firstLine())->
         Style();
     RefPtr<ComputedStyle> firstLineStyle = GetFirstLineStyle(aBlockContent,
                                                                 parentStyle);
 
     aLineFrame = NS_NewFirstLineFrame(mPresShell, firstLineStyle);
 
     // Initialize the line frame
     InitAndRestoreFrame(aState, aBlockContent, aBlockFrame, aLineFrame);
@@ -10534,33 +10534,33 @@ nsCSSFrameConstructor::CreateLetterFrame
   NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlockFrame),
                  "Not a block frame?");
 
   // Get a ComputedStyle for the first-letter-frame.
   //
   // Keep this in sync with nsBlockFrame::UpdatePseudoElementStyles.
   nsIFrame* parentFrame =
     nsFrame::CorrectStyleParentFrame(aParentFrame,
-                                     nsCSSPseudoElements::firstLetter);
+                                     nsCSSPseudoElements::firstLetter());
 
   ComputedStyle* parentComputedStyle = parentFrame->Style();
 
   // Use content from containing block so that we can actually
   // find a matching style rule.
   nsIContent* blockContent = aBlockFrame->GetContent();
 
   // Create first-letter style rule
   RefPtr<ComputedStyle> sc =
     GetFirstLetterStyle(blockContent, parentComputedStyle);
 
   if (sc) {
     if (parentFrame->IsLineFrame()) {
       nsIFrame* parentIgnoringFirstLine =
         nsFrame::CorrectStyleParentFrame(aBlockFrame,
-                                         nsCSSPseudoElements::firstLetter);
+                                         nsCSSPseudoElements::firstLetter());
 
       sc =
         mPresShell->StyleSet()->ReparentComputedStyle(
           sc,
           parentComputedStyle,
           parentIgnoringFirstLine->Style(),
           parentComputedStyle,
           blockContent->AsElement());
--- a/layout/base/nsGenConList.cpp
+++ b/layout/base/nsGenConList.cpp
@@ -54,21 +54,21 @@ nsGenConList::DestroyNodesFor(nsIFrame* 
  * @param aContent the content to use is stored here; it's the element
  * that generated the ::before or ::after content, or (if not for generated
  * content), the frame's own element
  * @return -1 for ::before, +1 for ::after, and 0 otherwise.
  */
 inline int32_t PseudoCompareType(nsIFrame* aFrame, nsIContent** aContent)
 {
   nsAtom *pseudo = aFrame->Style()->GetPseudo();
-  if (pseudo == nsCSSPseudoElements::before) {
+  if (pseudo == nsCSSPseudoElements::before()) {
     *aContent = aFrame->GetContent()->GetParent();
     return -1;
   }
-  if (pseudo == nsCSSPseudoElements::after) {
+  if (pseudo == nsCSSPseudoElements::after()) {
     *aContent = aFrame->GetContent()->GetParent();
     return 1;
   }
   *aContent = aFrame->GetContent();
   return 0;
 }
 
 /* static */ bool
--- a/layout/base/nsGenConList.h
+++ b/layout/base/nsGenConList.h
@@ -68,19 +68,19 @@ protected:
     NS_ASSERTION(mContentIndex <
                    int32_t(mPseudoFrame->StyleContent()->ContentCount()),
                  "index out of range");
       // We allow negative values of mContentIndex for 'counter-reset' and
       // 'counter-increment'.
 
     NS_ASSERTION(mContentIndex < 0 ||
                  mPseudoFrame->Style()->GetPseudo() ==
-                   nsCSSPseudoElements::before ||
+                   nsCSSPseudoElements::before() ||
                  mPseudoFrame->Style()->GetPseudo() ==
-                   nsCSSPseudoElements::after,
+                   nsCSSPseudoElements::after(),
                  "not :before/:after generated content and not counter change");
     NS_ASSERTION(mContentIndex < 0 ||
                  mPseudoFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT,
                  "not generated content and not counter change");
   }
 };
 
 class nsGenConList {
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -132,23 +132,26 @@ nsLayoutStatics::Initialize()
 
   nsresult rv;
 
   ContentParent::StartUp();
 
   // Register static atoms. Note that nsGkAtoms must be initialized earlier
   // than here, so it's done in NS_InitAtomTable() instead.
   nsCSSAnonBoxes::RegisterStaticAtoms();
-  nsCSSPseudoElements::RegisterStaticAtoms();
   nsCSSKeywords::AddRefTable();
   nsCSSProps::AddRefTable();
   nsColorNames::AddRefTable();
 
   NS_SetStaticAtomsDone();
 
+#ifdef DEBUG
+  nsCSSPseudoElements::AssertAtoms();
+#endif
+
   StartupJSEnvironment();
   nsJSContext::EnsureStatics();
 
   nsGlobalWindowInner::Init();
   nsGlobalWindowOuter::Init();
   Navigator::Init();
   nsXBLService::Init();
 
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -5709,17 +5709,17 @@ nsBlockFrame::UpdateFirstLetterStyle(Ser
   // Figure out what the right style parent is.  This needs to match
   // nsCSSFrameConstructor::CreateLetterFrame.
   nsIFrame* inFlowFrame = letterFrame;
   if (inFlowFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
     inFlowFrame = inFlowFrame->GetPlaceholderFrame();
   }
   nsIFrame* styleParent =
     CorrectStyleParentFrame(inFlowFrame->GetParent(),
-                            nsCSSPseudoElements::firstLetter);
+                            nsCSSPseudoElements::firstLetter());
   ComputedStyle* parentStyle = styleParent->Style();
   RefPtr<ComputedStyle> firstLetterStyle =
     aRestyleState.StyleSet()
                  .ResolvePseudoElementStyle(mContent->AsElement(),
                                             CSSPseudoElementType::firstLetter,
                                             parentStyle,
                                             nullptr);
   // Note that we don't need to worry about changehints for the continuation
@@ -7640,17 +7640,17 @@ nsBlockFrame::UpdatePseudoElementStyles(
     RefPtr<ComputedStyle> newBulletStyle =
       ResolveBulletStyle(type, &aRestyleState.StyleSet());
     UpdateStyleOfOwnedChildFrame(bullet, newBulletStyle, aRestyleState);
   }
 
   if (nsIFrame* firstLineFrame = GetFirstLineFrame()) {
     nsIFrame* styleParent =
       CorrectStyleParentFrame(firstLineFrame->GetParent(),
-                              nsCSSPseudoElements::firstLine);
+                              nsCSSPseudoElements::firstLine());
 
     ComputedStyle* parentStyle = styleParent->Style();
     RefPtr<ComputedStyle> firstLineStyle =
       aRestyleState.StyleSet()
                    .ResolvePseudoElementStyle(mContent->AsElement(),
                                               CSSPseudoElementType::firstLine,
                                               parentStyle,
                                               nullptr);
--- a/layout/generic/nsFirstLetterFrame.cpp
+++ b/layout/generic/nsFirstLetterFrame.cpp
@@ -54,17 +54,17 @@ nsFirstLetterFrame::Init(nsIContent*    
                          nsIFrame*         aPrevInFlow)
 {
   RefPtr<ComputedStyle> newSC;
   if (aPrevInFlow) {
     // Get proper ComputedStyle for ourselves.  We're creating the frame
     // that represents everything *except* the first letter, so just create
     // a ComputedStyle that inherits from our style parent, with no extra rules.
     nsIFrame* styleParent =
-      CorrectStyleParentFrame(aParent, nsCSSPseudoElements::firstLetter);
+      CorrectStyleParentFrame(aParent, nsCSSPseudoElements::firstLetter());
     ComputedStyle* parentComputedStyle = styleParent->Style();
     newSC = PresContext()->StyleSet()->
       ResolveStyleForFirstLetterContinuation(parentComputedStyle);
     SetComputedStyleWithoutNotification(newSC);
   }
 
   nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
 }
@@ -231,17 +231,17 @@ nsFirstLetterFrame::Reflow(nsPresContext
 
     FinishAndStoreOverflow(&aMetrics, aReflowInput.mStyleDisplay);
   } else {
     // Pretend we are a span and reflow the child frame
     nsLineLayout* ll = aReflowInput.mLineLayout;
     bool          pushedFrame;
 
     ll->SetInFirstLetter(
-      mComputedStyle->GetPseudo() == nsCSSPseudoElements::firstLetter);
+      mComputedStyle->GetPseudo() == nsCSSPseudoElements::firstLetter());
     ll->BeginSpan(this, &aReflowInput, bp.IStart(wm),
                   availSize.ISize(wm), &mBaseline);
     ll->ReflowFrame(kid, aReflowStatus, &kidMetrics, pushedFrame);
     NS_ASSERTION(lineWM.IsVertical() == wm.IsVertical(),
                  "we're assuming we can mix sizes between lineWM and wm "
                  "since we shouldn't have orthogonal writing modes within "
                  "a line.");
     aMetrics.ISize(lineWM) = ll->EndSpan(this) + bp.IStartEnd(wm);
@@ -379,17 +379,17 @@ nsFirstLetterFrame::DrainOverflowFrames(
     if (kidContent) {
       NS_ASSERTION(kidContent->IsText(),
                    "should contain only text nodes");
       ComputedStyle* parentSC;
       if (prevInFlow) {
         // This is for the rest of the content not in the first-letter.
         nsIFrame* styleParent =
           CorrectStyleParentFrame(GetParent(),
-                                  nsCSSPseudoElements::firstLetter);
+                                  nsCSSPseudoElements::firstLetter());
         parentSC = styleParent->Style();
       } else {
         // And this for the first-letter style.
         parentSC = mComputedStyle;
       }
       RefPtr<ComputedStyle> sc =
         aPresContext->StyleSet()->ResolveStyleForText(kidContent, parentSC);
       kid->SetComputedStyle(sc);
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -1013,23 +1013,23 @@ NS_IMPL_FRAMEARENA_HELPERS(nsFirstLineFr
 
 void
 nsFirstLineFrame::Init(nsIContent*       aContent,
                        nsContainerFrame* aParent,
                        nsIFrame*         aPrevInFlow)
 {
   nsInlineFrame::Init(aContent, aParent, aPrevInFlow);
   if (!aPrevInFlow) {
-    MOZ_ASSERT(Style()->GetPseudo() == nsCSSPseudoElements::firstLine);
+    MOZ_ASSERT(Style()->GetPseudo() == nsCSSPseudoElements::firstLine());
     return;
   }
 
   // This frame is a continuation - fixup the computed style if aPrevInFlow
   // is the first-in-flow (the only one with a ::first-line pseudo).
-  if (aPrevInFlow->Style()->GetPseudo() == nsCSSPseudoElements::firstLine) {
+  if (aPrevInFlow->Style()->GetPseudo() == nsCSSPseudoElements::firstLine()) {
     MOZ_ASSERT(FirstInFlow() == aPrevInFlow);
     // Create a new ComputedStyle that is a child of the parent
     // ComputedStyle thus removing the ::first-line style. This way
     // we behave as if an anonymous (unstyled) span was the child
     // of the parent frame.
     ComputedStyle* parentContext = aParent->Style();
     RefPtr<ComputedStyle> newSC = PresContext()->StyleSet()->
       ResolveInheritingAnonymousBoxStyle(nsCSSAnonBoxes::mozLineFrame,
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -5198,17 +5198,17 @@ nsTextFrame::BuildDisplayList(nsDisplayL
     MakeDisplayItem<nsDisplayText>(aBuilder, this, isSelected));
 }
 
 static nsIFrame*
 GetGeneratedContentOwner(nsIFrame* aFrame, bool* aIsBefore)
 {
   *aIsBefore = false;
   while (aFrame && (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT)) {
-    if (aFrame->Style()->GetPseudo() == nsCSSPseudoElements::before) {
+    if (aFrame->Style()->GetPseudo() == nsCSSPseudoElements::before()) {
       *aIsBefore = true;
     }
     aFrame = aFrame->GetParent();
   }
   return aFrame;
 }
 
 UniquePtr<SelectionDetails>
--- a/layout/style/nsCSSPseudoElementList.h
+++ b/layout/style/nsCSSPseudoElementList.h
@@ -1,31 +1,34 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* atom list for CSS pseudo-elements */
+/* list of CSS pseudo-elements */
 
 /*
- * This file contains the list of nsAtoms and their values for CSS
- * pseudo-elements.  It is designed to be used as inline input to
- * nsCSSPseudoElements.cpp *only* through the magic of C preprocessing.  All
- * entries must be enclosed either in the macro CSS_PSEUDO_ELEMENT;
- * these macros will have cruel and unusual things done to them.  The
- * entries should be kept in some sort of logical order.
+ * This file contains the list of support CSS pseudo-elements and some flags.
+ * It is designed to be used as inline input to nsCSSPseudoElements.cpp *only*
+ * through the magic of C preprocessing.  All entries must be enclosed either
+ * in the macro CSS_PSEUDO_ELEMENT; these macros will have cruel and unusual
+ * things done to them.  The entries should be kept in some sort of logical
+ * order.
  *
  * Code including this file MUST define CSS_PSEUDO_ELEMENT, which takes
  * three parameters:
  * name_  : The C++ identifier used for the atom (which will be a member
  *          of nsCSSPseudoElements)
  * value_ : The pseudo-element as a string, with single-colon syntax,
  *          used as the string value of the atom.
  * flags_ : A bitfield containing flags defined in nsCSSPseudoElements.h
+ *
+ * A corresponding atom must also be defined in StaticAtoms.py with a name of
+ * "PseudoElement_<name_>" and whose value matches the definition in this file.
  */
 
 // OUTPUT_CLASS=nsCSSPseudoElements
 // MACRO_NAME=CSS_PSEUDO_ELEMENT
 
 CSS_PSEUDO_ELEMENT(after, ":after", CSS_PSEUDO_ELEMENT_IS_CSS2 |
                                     CSS_PSEUDO_ELEMENT_IS_FLEX_OR_GRID_ITEM)
 CSS_PSEUDO_ELEMENT(before, ":before", CSS_PSEUDO_ELEMENT_IS_CSS2 |
--- a/layout/style/nsCSSPseudoElements.cpp
+++ b/layout/style/nsCSSPseudoElements.cpp
@@ -7,94 +7,68 @@
 /* atom list for CSS pseudo-elements */
 
 #include "nsCSSPseudoElements.h"
 
 #include "mozilla/ArrayUtils.h"
 
 #include "nsCSSAnonBoxes.h"
 #include "nsDOMString.h"
+#include "nsGkAtomConsts.h"
 
 using namespace mozilla;
 
-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( \
-        nsICSSPseudoElement, CSSPseudoElementAtoms, name_, value_)
-    #include "nsCSSPseudoElementList.h"
-    #undef CSS_PSEUDO_ELEMENT
-  }
-};
-MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFLOW_WARNING
-
-} // namespace detail
-} // namespace mozilla
-
-const nsStaticAtom* const nsCSSPseudoElements::sAtoms =
-  mozilla::detail::gCSSPseudoElementAtoms.mAtoms;
-
-#define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
-  NS_STATIC_ATOM_DEFN_PTR( \
-    nsICSSPseudoElement, mozilla::detail::CSSPseudoElementAtoms, \
-    mozilla::detail::gCSSPseudoElementAtoms, nsCSSPseudoElements, name_);
-#include "nsCSSPseudoElementList.h"
-#undef CSS_PSEUDO_ELEMENT
-
 // Flags data for each of the pseudo-elements.
 /* static */ const uint32_t
 nsCSSPseudoElements::kPseudoElementFlags[] = {
 #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
   flags_,
 #include "nsCSSPseudoElementList.h"
 #undef CSS_PSEUDO_ELEMENT
 };
 
-void nsCSSPseudoElements::RegisterStaticAtoms()
+static nsStaticAtom*
+GetAtomBase()
 {
-  NS_RegisterStaticAtoms(sAtoms, sAtomsLen);
+  return const_cast<nsStaticAtom*>(
+      nsGkAtoms::GetAtomByIndex(kAtomIndex_PseudoElements));
 }
 
-bool nsCSSPseudoElements::IsPseudoElement(nsAtom *aAtom)
+bool
+nsCSSPseudoElements::IsPseudoElement(nsAtom* aAtom)
 {
-  return nsStaticAtomUtils::IsMember(aAtom, sAtoms, sAtomsLen);
+  return nsStaticAtomUtils::IsMember(aAtom, GetAtomBase(),
+                                     kAtomCount_PseudoElements);
 }
 
 /* static */ bool
 nsCSSPseudoElements::IsCSS2PseudoElement(nsAtom *aAtom)
 {
   // We don't implement this using PseudoElementHasFlags because callers
   // want to pass things that could be anon boxes.
   NS_ASSERTION(nsCSSPseudoElements::IsPseudoElement(aAtom) ||
                nsCSSAnonBoxes::IsAnonBox(aAtom),
                "must be pseudo element or anon box");
-  bool result = aAtom == nsCSSPseudoElements::after ||
-                  aAtom == nsCSSPseudoElements::before ||
-                  aAtom == nsCSSPseudoElements::firstLetter ||
-                  aAtom == nsCSSPseudoElements::firstLine;
+  bool result = aAtom == nsCSSPseudoElements::after() ||
+                  aAtom == nsCSSPseudoElements::before() ||
+                  aAtom == nsCSSPseudoElements::firstLetter() ||
+                  aAtom == nsCSSPseudoElements::firstLine();
   NS_ASSERTION(nsCSSAnonBoxes::IsAnonBox(aAtom) ||
                result == PseudoElementHasFlags(
                    GetPseudoType(aAtom, EnabledState::eIgnoreEnabledState),
                    CSS_PSEUDO_ELEMENT_IS_CSS2),
                "result doesn't match flags");
   return result;
 }
 
 /* static */ CSSPseudoElementType
 nsCSSPseudoElements::GetPseudoType(nsAtom* aAtom, EnabledState aEnabledState)
 {
-  Maybe<uint32_t> index = nsStaticAtomUtils::Lookup(aAtom, sAtoms, sAtomsLen);
+  Maybe<uint32_t> index =
+    nsStaticAtomUtils::Lookup(aAtom, GetAtomBase(), kAtomCount_PseudoElements);
   if (index.isSome()) {
     auto type = static_cast<Type>(*index);
     return IsEnabled(type, aEnabledState) ? type : Type::NotPseudo;
   }
 
   if (nsCSSAnonBoxes::IsAnonBox(aAtom)) {
 #ifdef MOZ_XUL
     if (nsCSSAnonBoxes::IsTreePseudoElement(aAtom)) {
@@ -111,18 +85,18 @@ nsCSSPseudoElements::GetPseudoType(nsAto
 
   return Type::NotPseudo;
 }
 
 /* static */ nsAtom*
 nsCSSPseudoElements::GetPseudoAtom(Type aType)
 {
   MOZ_ASSERT(aType < Type::Count, "Unexpected type");
-  return const_cast<nsStaticAtom*>(
-    &sAtoms[static_cast<CSSPseudoElementTypeBase>(aType)]);
+  size_t index = kAtomIndex_PseudoElements + static_cast<size_t>(aType);
+  return nsGkAtoms::GetAtomByIndex(index);
 }
 
 /* static */ already_AddRefed<nsAtom>
 nsCSSPseudoElements::GetPseudoAtom(const nsAString& aPseudoElement)
 {
   if (DOMStringIsNull(aPseudoElement) || aPseudoElement.IsEmpty() ||
       aPseudoElement.First() != char16_t(':')) {
     return nullptr;
@@ -170,8 +144,26 @@ nsCSSPseudoElements::PseudoTypeAsString(
       return NS_LITERAL_STRING("::after");
     default:
       MOZ_ASSERT(aPseudoType == CSSPseudoElementType::NotPseudo,
                  "Unexpected pseudo type");
       return EmptyString();
   }
 }
 
+#ifdef DEBUG
+/* static */ void
+nsCSSPseudoElements::AssertAtoms()
+{
+  nsStaticAtom* base = GetAtomBase();
+#define CSS_PSEUDO_ELEMENT(name_, value_, flags_)                    \
+  {                                                                  \
+    RefPtr<nsAtom> atom = NS_Atomize(value_);                        \
+    size_t index = static_cast<size_t>(CSSPseudoElementType::name_); \
+    MOZ_ASSERT(atom == nsGkAtoms::PseudoElement_##name_,             \
+               "Static atom for " #name_ " has incorrect value");    \
+    MOZ_ASSERT(atom == &base[index],                                 \
+               "Static atom for " #name_ " not at expected index");  \
+  }
+#include "nsCSSPseudoElementList.h"
+#undef CSS_PSEUDO_ELEMENT
+}
+#endif
--- a/layout/style/nsCSSPseudoElements.h
+++ b/layout/style/nsCSSPseudoElements.h
@@ -4,18 +4,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/. */
 
 /* atom list for CSS pseudo-elements */
 
 #ifndef nsCSSPseudoElements_h___
 #define nsCSSPseudoElements_h___
 
-#include "nsAtom.h"
-#include "nsStaticAtom.h"
+#include "nsGkAtoms.h"
 #include "mozilla/CSSEnabledState.h"
 #include "mozilla/Compiler.h"
 
 // Is this pseudo-element a CSS2 pseudo-element that can be specified
 // with the single colon syntax (in addition to the double-colon syntax,
 // which can be used for all pseudo-elements)?
 //
 // Note: We also rely on this for IsEagerlyCascadedInServo.
@@ -51,27 +50,16 @@
 // Can we use the ChromeOnly document.createElement(..., { pseudo: "::foo" })
 // API for creating pseudo-implementing native anonymous content in JS with this
 // pseudo-element?
 #define CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC           (1<<6)
 // Does this pseudo-element act like an item for containers (such as flex and
 // grid containers) and thus needs parent display-based style fixup?
 #define CSS_PSEUDO_ELEMENT_IS_FLEX_OR_GRID_ITEM        (1<<7)
 
-// Trivial subclass of nsStaticAtom so that function signatures can require an
-// atom from this atom list.
-class nsICSSPseudoElement : public nsStaticAtom
-{
-public:
-  constexpr nsICSSPseudoElement(const char16_t* aStr, uint32_t aLength,
-                                uint32_t aStringOffset)
-    : nsStaticAtom(aStr, aLength, aStringOffset)
-  {}
-};
-
 namespace mozilla {
 
 // The total count of CSSPseudoElement is less than 256,
 // so use uint8_t as its underlying type.
 typedef uint8_t CSSPseudoElementTypeBase;
 enum class CSSPseudoElementType : CSSPseudoElementTypeBase {
   // If the actual pseudo-elements stop being first here, change
   // GetPseudoType.
@@ -85,68 +73,49 @@ 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
-
-  enum class Atoms {
-    #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
-      NS_STATIC_ATOM_ENUM(name_)
-    #include "nsCSSPseudoElementList.h"
-    #undef CSS_PSEUDO_ELEMENT
-    AtomsCount
-  };
-
-  const nsICSSPseudoElement mAtoms[static_cast<size_t>(Atoms::AtomsCount)];
-};
-
-} // namespace detail
-
 } // namespace mozilla
 
 class nsCSSPseudoElements
 {
   typedef mozilla::CSSPseudoElementType Type;
   typedef mozilla::CSSEnabledState EnabledState;
 
 public:
-  static void RegisterStaticAtoms();
-
   static bool IsPseudoElement(nsAtom *aAtom);
 
   static bool IsCSS2PseudoElement(nsAtom *aAtom);
 
   // 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);
   }
 
-private:
-  static const nsStaticAtom* const sAtoms;
-  static constexpr size_t sAtomsLen =
-    static_cast<size_t>(mozilla::detail::CSSPseudoElementAtoms::Atoms::AtomsCount);
+public:
+#ifdef DEBUG
+  static void AssertAtoms();
+#endif
 
-public:
-  #define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \
-    NS_STATIC_ATOM_DECL_PTR(nsICSSPseudoElement, name_);
+  // Alias nsCSSPseudoElements::foo() to alias nsGkAtoms::foo.
+  // XXX Once nsGkAtoms::foo become constexpr variables, these can too.
+  // See bug 1449787.
+  #define CSS_PSEUDO_ELEMENT(name_, value_, flags_)       \
+    static constexpr nsICSSPseudoElement* const& name_()  \
+    {                                                     \
+      return nsGkAtoms::PseudoElement_##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/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -136,21 +136,21 @@ DocumentNeedsRestyle(
   // ch / ex units).
   if (presContext->HasPendingMediaQueryUpdates()) {
     // So gotta flush.
     return true;
   }
 
   // If the pseudo-element is animating, make sure to flush.
   if (aElement->MayHaveAnimations() && aPseudo) {
-    if (aPseudo == nsCSSPseudoElements::before) {
+    if (aPseudo == nsCSSPseudoElements::before()) {
       if (EffectSet::GetEffectSet(aElement, CSSPseudoElementType::before)) {
         return true;
       }
-    } else if (aPseudo == nsCSSPseudoElements::after) {
+    } else if (aPseudo == nsCSSPseudoElements::after()) {
       if (EffectSet::GetEffectSet(aElement, CSSPseudoElementType::after)) {
         return true;
       }
     }
   }
 
   // For Servo, we need to process the restyle-hint-invalidations first, to
   // expand LaterSiblings hint, so that we can look whether ancestors need
@@ -574,19 +574,19 @@ nsComputedDOMStyle::DoGetComputedStyleNo
 
   // XXX the !aElement->IsHTMLElement(nsGkAtoms::area)
   // check is needed due to bug 135040 (to avoid using
   // mPrimaryFrame). Remove it once that's fixed.
   if (inDocWithShell &&
       aStyleType == eAll &&
       !aElement->IsHTMLElement(nsGkAtoms::area)) {
     nsIFrame* frame = nullptr;
-    if (aPseudo == nsCSSPseudoElements::before) {
+    if (aPseudo == nsCSSPseudoElements::before()) {
       frame = nsLayoutUtils::GetBeforeFrame(aElement);
-    } else if (aPseudo == nsCSSPseudoElements::after) {
+    } else if (aPseudo == nsCSSPseudoElements::after()) {
       frame = nsLayoutUtils::GetAfterFrame(aElement);
     } else if (!aPseudo) {
       frame = nsLayoutUtils::GetStyleFrame(aElement);
     }
     if (frame) {
       ComputedStyle* result = frame->Style();
       // Don't use the style if it was influenced by pseudo-elements, since then
       // it's not the primary style for this element / pseudo.
@@ -942,19 +942,19 @@ nsComputedDOMStyle::UpdateCurrentStyleSo
   // XXX the !mElement->IsHTMLElement(nsGkAtoms::area)
   // check is needed due to bug 135040 (to avoid using
   // mPrimaryFrame). Remove it once that's fixed.
   if (mStyleType == eAll && !mElement->IsHTMLElement(nsGkAtoms::area)) {
     mOuterFrame = nullptr;
 
     if (!mPseudo) {
       mOuterFrame = mElement->GetPrimaryFrame();
-    } else if (mPseudo == nsCSSPseudoElements::before ||
-               mPseudo == nsCSSPseudoElements::after) {
-      nsAtom* property = mPseudo == nsCSSPseudoElements::before
+    } else if (mPseudo == nsCSSPseudoElements::before() ||
+               mPseudo == nsCSSPseudoElements::after()) {
+      nsAtom* property = mPseudo == nsCSSPseudoElements::before()
                             ? nsGkAtoms::beforePseudoProperty
                             : nsGkAtoms::afterPseudoProperty;
 
       auto* pseudo = static_cast<Element*>(mElement->GetProperty(property));
       mOuterFrame = pseudo ? pseudo->GetPrimaryFrame() : nullptr;
     }
 
     mInnerFrame = mOuterFrame;
--- a/servo/components/style/gecko/pseudo_element_definition.mako.rs
+++ b/servo/components/style/gecko/pseudo_element_definition.mako.rs
@@ -3,19 +3,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /// Gecko's pseudo-element definition.
 #[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
 pub enum PseudoElement {
     % for pseudo in PSEUDOS:
         /// ${pseudo.value}
         % if pseudo.is_tree_pseudo_element():
-        ${pseudo.capitalized()}(ThinBoxedSlice<Atom>),
+        ${pseudo.capitalized_pseudo()}(ThinBoxedSlice<Atom>),
         % else:
-        ${pseudo.capitalized()},
+        ${pseudo.capitalized_pseudo()},
         % endif
     % endfor
 }
 
 /// Important: If you change this, you should also update Gecko's
 /// nsCSSPseudoElements::IsEagerlyCascadedInServo.
 <% EAGER_PSEUDOS = ["Before", "After", "FirstLine", "FirstLetter"] %>
 <% TREE_PSEUDOS = [pseudo for pseudo in PSEUDOS if pseudo.is_tree_pseudo_element()] %>
@@ -36,17 +36,17 @@ pub const PSEUDO_COUNT: usize = ${len(PS
 /// The list of eager pseudos.
 pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [
     % for eager_pseudo_name in EAGER_PSEUDOS:
     PseudoElement::${eager_pseudo_name},
     % endfor
 ];
 
 <%def name="pseudo_element_variant(pseudo, tree_arg='..')">\
-PseudoElement::${pseudo.capitalized()}${"({})".format(tree_arg) if pseudo.is_tree_pseudo_element() else ""}\
+PseudoElement::${pseudo.capitalized_pseudo()}${"({})".format(tree_arg) if pseudo.is_tree_pseudo_element() else ""}\
 </%def>
 
 impl PseudoElement {
     /// Get the pseudo-element as an atom.
     #[inline]
     pub fn atom(&self) -> Atom {
         match *self {
             % for pseudo in PSEUDOS:
@@ -115,68 +115,68 @@ impl PseudoElement {
                     if unsafe { structs::StaticPrefs_sVarCache_layout_css_xul_tree_pseudos_content_enabled } {
                         0
                     } else {
                         structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME
                     },
                 % elif pseudo.is_anon_box():
                     structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS,
                 % else:
-                    structs::SERVO_CSS_PSEUDO_ELEMENT_FLAGS_${pseudo.original_ident},
+                    structs::SERVO_CSS_PSEUDO_ELEMENT_FLAGS_${pseudo.pseudo_ident},
                 % endif
             % endfor
         }
     }
 
     /// Construct a pseudo-element from a `CSSPseudoElementType`.
     #[inline]
     pub fn from_pseudo_type(type_: CSSPseudoElementType) -> Option<Self> {
         match type_ {
             % for pseudo in PSEUDOS:
                 % if not pseudo.is_anon_box():
-                    CSSPseudoElementType::${pseudo.original_ident} => {
+                    CSSPseudoElementType::${pseudo.pseudo_ident} => {
                         Some(${pseudo_element_variant(pseudo)})
                     },
                 % endif
             % endfor
             _ => None,
         }
     }
 
     /// Construct a `CSSPseudoElementType` from a pseudo-element
     #[inline]
     pub fn pseudo_type(&self) -> CSSPseudoElementType {
         use gecko_bindings::structs::CSSPseudoElementType_InheritingAnonBox;
 
         match *self {
             % for pseudo in PSEUDOS:
                 % if not pseudo.is_anon_box():
-                    PseudoElement::${pseudo.capitalized()} => CSSPseudoElementType::${pseudo.original_ident},
+                    PseudoElement::${pseudo.capitalized_pseudo()} => CSSPseudoElementType::${pseudo.pseudo_ident},
                 % elif pseudo.is_tree_pseudo_element():
-                    PseudoElement::${pseudo.capitalized()}(..) => CSSPseudoElementType::XULTree,
+                    PseudoElement::${pseudo.capitalized_pseudo()}(..) => CSSPseudoElementType::XULTree,
                 % elif pseudo.is_inheriting_anon_box():
-                    PseudoElement::${pseudo.capitalized()} => CSSPseudoElementType_InheritingAnonBox,
+                    PseudoElement::${pseudo.capitalized_pseudo()} => CSSPseudoElementType_InheritingAnonBox,
                 % else:
-                    PseudoElement::${pseudo.capitalized()} => CSSPseudoElementType::NonInheritingAnonBox,
+                    PseudoElement::${pseudo.capitalized_pseudo()} => CSSPseudoElementType::NonInheritingAnonBox,
                 % endif
             % endfor
         }
     }
 
     /// Get a PseudoInfo for a pseudo
     pub fn pseudo_info(&self) -> (*mut structs::nsAtom, CSSPseudoElementType) {
         (self.atom().as_ptr(), self.pseudo_type())
     }
 
     /// Get the argument list of a tree pseudo-element.
     #[inline]
     pub fn tree_pseudo_args(&self) -> Option<<&[Atom]> {
         match *self {
             % for pseudo in TREE_PSEUDOS:
-            PseudoElement::${pseudo.capitalized()}(ref args) => Some(args),
+            PseudoElement::${pseudo.capitalized_pseudo()}(ref args) => Some(args),
             % endfor
             _ => None,
         }
     }
 
     /// Construct a pseudo-element from an `Atom`.
     #[inline]
     pub fn from_atom(atom: &Atom) -> Option<Self> {
@@ -208,17 +208,17 @@ impl PseudoElement {
     }
 
     /// Construct a tree pseudo-element from atom and args.
     #[inline]
     pub fn from_tree_pseudo_atom(atom: &Atom, args: Box<[Atom]>) -> Option<Self> {
         % for pseudo in PSEUDOS:
             % if pseudo.is_tree_pseudo_element():
                 if atom == &atom!("${pseudo.value}") {
-                    return Some(PseudoElement::${pseudo.capitalized()}(args.into()));
+                    return Some(PseudoElement::${pseudo.capitalized_pseudo()}(args.into()));
                 }
             % endif
         % endfor
         None
     }
 
     /// Constructs a pseudo-element from a string of text.
     ///
--- a/servo/components/style/gecko/regen_atoms.py
+++ b/servo/components/style/gecko/regen_atoms.py
@@ -23,55 +23,44 @@ PRELUDE = """
 /* Autogenerated file created by components/style/gecko/binding_tools/regen_atoms.py, DO NOT EDIT DIRECTLY */
 """[1:]   # NOQA: E501
 
 
 def gnu_symbolify(source, ident):
     return "_ZN{}{}{}{}E".format(len(source.CLASS), source.CLASS, len(ident), ident)
 
 
-def msvc64_symbolify(source, ident):
-    return "?{}@{}@@2PEAV{}@@EA".format(ident, source.CLASS, source.TYPE)
+def msvc64_symbolify(source, ident, ty):
+    return "?{}@{}@@2PEAV{}@@EA".format(ident, source.CLASS, ty)
 
 
-def msvc32_symbolify(source, ident):
+def msvc32_symbolify(source, ident, ty):
     # Prepend "\x01" to avoid LLVM prefixing the mangled name with "_".
     # See https://github.com/rust-lang/rust/issues/36097
-    return "\\x01?{}@{}@@2PAV{}@@A".format(ident, source.CLASS, source.TYPE)
+    return "\\x01?{}@{}@@2PAV{}@@A".format(ident, source.CLASS, ty)
 
 
 class GkAtomSource:
     PATTERN = re.compile('^(GK_ATOM)\(([^,]*),[^"]*"([^"]*)",\s*([^)]*)\)',
                          re.MULTILINE)
     FILE = "include/nsGkAtomList.h"
     CLASS = "nsGkAtoms"
     TYPE = "nsStaticAtom"
 
 
-class CSSPseudoElementsAtomSource:
-    PATTERN = re.compile('^(CSS_PSEUDO_ELEMENT)\(([^,]*),[^"]*"([^"]*)",()',
-                         re.MULTILINE)
-    FILE = "include/nsCSSPseudoElementList.h"
-    CLASS = "nsCSSPseudoElements"
-    # NB: nsICSSPseudoElement is effectively the same as a nsStaticAtom, but we need
-    # this for MSVC name mangling.
-    TYPE = "nsICSSPseudoElement"
-
-
 class CSSAnonBoxesAtomSource:
     PATTERN = re.compile('^(CSS_ANON_BOX|CSS_NON_INHERITING_ANON_BOX|CSS_WRAPPER_ANON_BOX)\(([^,]*),[^"]*"([^"]*)"()\)',  # NOQA: E501
                          re.MULTILINE)
     FILE = "include/nsCSSAnonBoxList.h"
     CLASS = "nsCSSAnonBoxes"
     TYPE = "nsICSSAnonBoxPseudo"
 
 
 SOURCES = [
     GkAtomSource,
-    CSSPseudoElementsAtomSource,
     CSSAnonBoxesAtomSource,
 ]
 
 
 def map_atom(ident):
     if ident in {"box", "loop", "match", "mod", "ref",
                  "self", "type", "use", "where", "in"}:
         return ident + "_"
@@ -81,36 +70,47 @@ def map_atom(ident):
 class Atom:
     def __init__(self, source, macro_name, ident, value, ty):
         self.ident = "{}_{}".format(source.CLASS, ident)
         self.original_ident = ident
         self.value = value
         self.source = source
         self.macro = macro_name
         self.ty = ty
+        if self.is_pseudo():
+            if self.is_non_anon_box_pseudo():
+                self.pseudo_ident = (ident.split("_", 1))[1]
+            else:
+                self.pseudo_ident = ident
         if self.is_anon_box():
             assert self.is_inheriting_anon_box() or self.is_non_inheriting_anon_box()
 
     def cpp_class(self):
         return self.source.CLASS
 
     def gnu_symbol(self):
         return gnu_symbolify(self.source, self.original_ident)
 
     def msvc32_symbol(self):
-        return msvc32_symbolify(self.source, self.original_ident)
+        return msvc32_symbolify(self.source, self.original_ident, self.ty)
 
     def msvc64_symbol(self):
-        return msvc64_symbolify(self.source, self.original_ident)
+        return msvc64_symbolify(self.source, self.original_ident, self.ty)
 
     def type(self):
         return self.ty
 
-    def capitalized(self):
-        return self.original_ident[0].upper() + self.original_ident[1:]
+    def capitalized_pseudo(self):
+        return self.pseudo_ident[0].upper() + self.pseudo_ident[1:]
+
+    def is_pseudo(self):
+        return self.is_non_anon_box_pseudo() or self.is_anon_box()
+
+    def is_non_anon_box_pseudo(self):
+        return self.type() == "nsICSSPseudoElement"
 
     def is_anon_box(self):
         return self.type() == "nsICSSAnonBoxPseudo"
 
     def is_non_inheriting_anon_box(self):
         return self.macro == "CSS_NON_INHERITING_ANON_BOX"
 
     def is_inheriting_anon_box(self):
--- a/xpcom/ds/Atom.py
+++ b/xpcom/ds/Atom.py
@@ -3,8 +3,13 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
 class Atom():
     def __init__(self, ident, string, ty="nsStaticAtom"):
         self.ident = ident
         self.string = string
         self.ty = ty
+
+
+class PseudoElementAtom(Atom):
+    def __init__(self, ident, string):
+        Atom.__init__(self, ident, string, ty="nsICSSPseudoElement")
--- a/xpcom/ds/StaticAtoms.py
+++ b/xpcom/ds/StaticAtoms.py
@@ -1,19 +1,19 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-from Atom import Atom
+from Atom import Atom, PseudoElementAtom
 from HTMLAtoms import HTML_PARSER_ATOMS
 import sys
 
 # Static atom definitions, used to generate nsGkAtomList.h.
 #
-# Each atom is defined by a call to Atom.
+# Each atom is defined by a call to Atom or PseudoElementAtom.
 #
 # The first argument is the atom's identifier.
 # The second argument is the atom's string value.
 #
 # It is not possible to conditionally define static atoms with #ifdef etc.
 STATIC_ATOMS = [
     # --------------------------------------------------------------------------
     # Generic atoms
@@ -2232,16 +2232,43 @@ STATIC_ATOMS = [
     Atom("DirectoryService_WindowsProgramFiles", "ProgF"),
     Atom("DirectoryService_Programs", "Progs"),
     Atom("DirectoryService_Favorites", "Favs"),
     Atom("DirectoryService_Appdata", "AppData"),
     Atom("DirectoryService_LocalAppdata", "LocalAppData"),
     Atom("DirectoryService_LocalAppdataLow", "LocalAppDataLow"),
     Atom("DirectoryService_LowIntegrityTempBase", "LowTmpDBase"),
     Atom("DirectoryService_WinCookiesDirectory", "CookD"),
+
+    # CSS pseudo-elements -- these must appear in the same order as
+    # in nsCSSPseudoElementList.h
+    PseudoElementAtom("PseudoElement_after", ":after"),
+    PseudoElementAtom("PseudoElement_before", ":before"),
+    PseudoElementAtom("PseudoElement_backdrop", ":backdrop"),
+    PseudoElementAtom("PseudoElement_cue", ":cue"),
+    PseudoElementAtom("PseudoElement_firstLetter", ":first-letter"),
+    PseudoElementAtom("PseudoElement_firstLine", ":first-line"),
+    PseudoElementAtom("PseudoElement_selection", ":selection"),
+    PseudoElementAtom("PseudoElement_mozFocusInner", ":-moz-focus-inner"),
+    PseudoElementAtom("PseudoElement_mozFocusOuter", ":-moz-focus-outer"),
+    PseudoElementAtom("PseudoElement_mozListBullet", ":-moz-list-bullet"),
+    PseudoElementAtom("PseudoElement_mozListNumber", ":-moz-list-number"),
+    PseudoElementAtom("PseudoElement_mozMathAnonymous", ":-moz-math-anonymous"),
+    PseudoElementAtom("PseudoElement_mozNumberWrapper", ":-moz-number-wrapper"),
+    PseudoElementAtom("PseudoElement_mozNumberText", ":-moz-number-text"),
+    PseudoElementAtom("PseudoElement_mozNumberSpinBox", ":-moz-number-spin-box"),
+    PseudoElementAtom("PseudoElement_mozNumberSpinUp", ":-moz-number-spin-up"),
+    PseudoElementAtom("PseudoElement_mozNumberSpinDown", ":-moz-number-spin-down"),
+    PseudoElementAtom("PseudoElement_mozProgressBar", ":-moz-progress-bar"),
+    PseudoElementAtom("PseudoElement_mozRangeTrack", ":-moz-range-track"),
+    PseudoElementAtom("PseudoElement_mozRangeProgress", ":-moz-range-progress"),
+    PseudoElementAtom("PseudoElement_mozRangeThumb", ":-moz-range-thumb"),
+    PseudoElementAtom("PseudoElement_mozMeterBar", ":-moz-meter-bar"),
+    PseudoElementAtom("PseudoElement_placeholder", ":placeholder"),
+    PseudoElementAtom("PseudoElement_mozColorSwatch", ":-moz-color-swatch"),
 ] + HTML_PARSER_ATOMS
 
 
 def verify():
     idents = set()
     strings = set()
     failed = False
     for atom in STATIC_ATOMS:
@@ -2260,10 +2287,25 @@ def verify():
 def generate_nsgkatomlist_h(output, *ignore):
     verify()
     output.write("/* THIS FILE IS AUTOGENERATED BY StaticAtoms.py.  DO NOT EDIT */\n\n"
                  "// GK_ATOM(identifier, string, gecko_type)\n" +
                  "".join(["GK_ATOM(%s, \"%s\", %s)\n" % (a.ident, a.string, a.ty)
                           for a in STATIC_ATOMS]))
 
 
+def generate_nsgkatomconsts_h(output, *ignore):
+    pseudo_index = None
+    pseudo_count = 0
+    for i, atom in enumerate(STATIC_ATOMS):
+        if atom.ty is "nsICSSPseudoElement":
+            if pseudo_index is None:
+                pseudo_index = i
+            pseudo_count += 1
+    output.write("/* THIS IS AN AUTOGENERATED FILE.  DO NOT EDIT */\n\n"
+                 "namespace mozilla {\n"
+                 "  constexpr uint32_t kAtomIndex_PseudoElements = %d;\n"
+                 "  constexpr uint32_t kAtomCount_PseudoElements = %d;\n"
+                 "}\n" % (pseudo_index, pseudo_count))
+
+
 if __name__ == '__main__':
     generate_nsgkatomlist_h(sys.stdout)
--- a/xpcom/ds/moz.build
+++ b/xpcom/ds/moz.build
@@ -34,16 +34,17 @@ if CONFIG['OS_ARCH'] == 'WINNT':
     EXPORTS += ['nsWindowsRegKey.h']
     SOURCES += [
         'nsWindowsRegKey.cpp'
     ]
 
 XPIDL_MODULE = 'xpcom_ds'
 
 EXPORTS += [
+    '!nsGkAtomConsts.h',
     '!nsGkAtomList.h',
     'nsArray.h',
     'nsArrayEnumerator.h',
     'nsArrayUtils.h',
     'nsAtom.h',
     'nsBaseHashtable.h',
     'nsCharSeparatedTokenizer.h',
     'nsCheapSets.h',
@@ -136,9 +137,13 @@ EXTRA_COMPONENTS += [
 LOCAL_INCLUDES += [
     '../io',
 ]
 
 GENERATED_FILES += ['nsGkAtomList.h']
 GENERATED_FILES['nsGkAtomList.h'].script = 'StaticAtoms.py:generate_nsgkatomlist_h'
 GENERATED_FILES['nsGkAtomList.h'].inputs = ['Atom.py', 'HTMLAtoms.py']
 
+GENERATED_FILES += ['nsGkAtomConsts.h']
+GENERATED_FILES['nsGkAtomConsts.h'].script = 'StaticAtoms.py:generate_nsgkatomconsts_h'
+GENERATED_FILES['nsGkAtomConsts.h'].inputs = ['Atom.py', 'HTMLAtoms.py']
+
 FINAL_LIBRARY = 'xul'
--- a/xpcom/ds/nsGkAtoms.h
+++ b/xpcom/ds/nsGkAtoms.h
@@ -5,16 +5,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsGkAtoms_h___
 #define nsGkAtoms_h___
 
 #include "nsAtom.h"
 #include "nsStaticAtom.h"
 
+// Trivial subclasses of nsStaticAtom so that function signatures can require
+// an atom from a specific atom list.
+#define DEFINE_STATIC_ATOM_SUBCLASS(name_)                                    \
+  class name_ : public nsStaticAtom                                           \
+  {                                                                           \
+  public:                                                                     \
+    constexpr name_(const char16_t* aStr, uint32_t aLength, uint32_t aOffset) \
+      : nsStaticAtom(aStr, aLength, aOffset) {}                               \
+  };
+
+DEFINE_STATIC_ATOM_SUBCLASS(nsICSSPseudoElement)
+
+#undef DEFINE_STATIC_ATOM_SUBCLASS
+
 namespace mozilla {
 namespace detail {
 
 struct GkAtoms
 {
   #define GK_ATOM(name_, value_, type_) NS_STATIC_ATOM_DECL_STRING(name_, value_)
   #include "nsGkAtomList.h"
   #undef GK_ATOM
@@ -37,14 +51,20 @@ class nsGkAtoms
 private:
   static const nsStaticAtom* const sAtoms;
   static constexpr size_t sAtomsLen =
     static_cast<size_t>(mozilla::detail::GkAtoms::Atoms::AtomsCount);
 
 public:
   static void RegisterStaticAtoms();
 
+  static nsStaticAtom* GetAtomByIndex(size_t aIndex)
+  {
+    MOZ_ASSERT(aIndex < sAtomsLen);
+    return const_cast<nsStaticAtom*>(&sAtoms[aIndex]);
+  }
+
   #define GK_ATOM(name_, value_, type_) NS_STATIC_ATOM_DECL_PTR(type_, name_)
   #include "nsGkAtomList.h"
   #undef GK_ATOM
 };
 
 #endif /* nsGkAtoms_h___ */