Merge inbound to mozilla-central r=merge a=merge
authorarthur.iakab <aiakab@mozilla.com>
Wed, 03 Jan 2018 11:49:44 +0200
changeset 449334 ac93fdadf1022211eec62258ad22b42cb37a6d14
parent 449324 66a4f3cefc640b94866c98c2dd5f4741d313ba44 (current diff)
parent 449333 ca70091cdc76eead763e92eda4dcb939a277f62d (diff)
child 449339 41e4dd8f635d59d3e5211b627ab125642035583b
child 449367 c5775459d6155087687af9db401abce865d7a287
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone59.0a1
first release with
nightly linux32
ac93fdadf102 / 59.0a1 / 20180103100101 / files
nightly linux64
ac93fdadf102 / 59.0a1 / 20180103100101 / files
nightly mac
ac93fdadf102 / 59.0a1 / 20180103100101 / files
nightly win32
ac93fdadf102 / 59.0a1 / 20180103100101 / files
nightly win64
ac93fdadf102 / 59.0a1 / 20180103100101 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central r=merge a=merge
--- a/editor/composer/nsComposerCommands.cpp
+++ b/editor/composer/nsComposerCommands.cpp
@@ -201,17 +201,17 @@ nsStyleUpdatingCommand::GetCurrentState(
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   bool firstOfSelectionHasProp = false;
   bool anyOfSelectionHasProp = false;
   bool allOfSelectionHasProp = false;
 
-  nsresult rv = aHTMLEditor->GetInlineProperty(mTagName, EmptyString(),
+  nsresult rv = aHTMLEditor->GetInlineProperty(mTagName, nullptr,
                                                EmptyString(),
                                                &firstOfSelectionHasProp,
                                                &anyOfSelectionHasProp,
                                                &allOfSelectionHasProp);
 
   aParams->SetBooleanValue(STATE_ENABLED, NS_SUCCEEDED(rv));
   aParams->SetBooleanValue(STATE_ALL, allOfSelectionHasProp);
   aParams->SetBooleanValue(STATE_ANY, anyOfSelectionHasProp);
@@ -716,35 +716,33 @@ nsFontFaceStateCommand::SetState(mozilla
                                  nsString& newState)
 {
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   if (newState.EqualsLiteral("tt")) {
     // The old "teletype" attribute
-    nsresult rv = aHTMLEditor->SetInlineProperty(nsGkAtoms::tt, EmptyString(),
+    nsresult rv = aHTMLEditor->SetInlineProperty(nsGkAtoms::tt, nullptr,
                                                  EmptyString());
     NS_ENSURE_SUCCESS(rv, rv);
     // Clear existing font face
-    return aHTMLEditor->RemoveInlineProperty(nsGkAtoms::font,
-                                             NS_LITERAL_STRING("face"));
+    return aHTMLEditor->RemoveInlineProperty(nsGkAtoms::font, nsGkAtoms::face);
   }
 
   // Remove any existing TT nodes
-  nsresult rv = aHTMLEditor->RemoveInlineProperty(nsGkAtoms::tt, EmptyString());
+  nsresult rv = aHTMLEditor->RemoveInlineProperty(nsGkAtoms::tt, nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (newState.IsEmpty() || newState.EqualsLiteral("normal")) {
-    return aHTMLEditor->RemoveInlineProperty(nsGkAtoms::font,
-                                             NS_LITERAL_STRING("face"));
+    return aHTMLEditor->RemoveInlineProperty(nsGkAtoms::font, nsGkAtoms::face);
   }
 
-  return aHTMLEditor->SetInlineProperty(nsGkAtoms::font,
-                                        NS_LITERAL_STRING("face"), newState);
+  return aHTMLEditor->SetInlineProperty(nsGkAtoms::font, nsGkAtoms::face,
+                                        newState);
 }
 
 nsFontSizeStateCommand::nsFontSizeStateCommand()
   : nsMultiStateCommand()
 {
 }
 
 nsresult
@@ -754,17 +752,17 @@ nsFontSizeStateCommand::GetCurrentState(
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsAutoString outStateString;
   bool firstHas, anyHas, allHas;
   nsresult rv = aHTMLEditor->GetInlinePropertyWithAttrValue(
                                nsGkAtoms::font,
-                               NS_LITERAL_STRING("size"),
+                               nsGkAtoms::size,
                                EmptyString(),
                                &firstHas, &anyHas, &allHas,
                                outStateString);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString tOutStateString;
   LossyCopyUTF16toASCII(outStateString, tOutStateString);
   aParams->SetBooleanValue(STATE_MIXED, anyHas && !allHas);
@@ -791,28 +789,28 @@ nsFontSizeStateCommand::SetState(mozilla
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   if (!newState.IsEmpty() &&
       !newState.EqualsLiteral("normal") &&
       !newState.EqualsLiteral("medium")) {
     return aHTMLEditor->SetInlineProperty(nsGkAtoms::font,
-                                          NS_LITERAL_STRING("size"), newState);
+                                          nsGkAtoms::size, newState);
   }
 
   // remove any existing font size, big or small
   nsresult rv = aHTMLEditor->RemoveInlineProperty(nsGkAtoms::font,
-                                                  NS_LITERAL_STRING("size"));
+                                                  nsGkAtoms::size);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = aHTMLEditor->RemoveInlineProperty(nsGkAtoms::big, EmptyString());
+  rv = aHTMLEditor->RemoveInlineProperty(nsGkAtoms::big, nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return aHTMLEditor->RemoveInlineProperty(nsGkAtoms::small, EmptyString());
+  return aHTMLEditor->RemoveInlineProperty(nsGkAtoms::small, nullptr);
 }
 
 nsFontColorStateCommand::nsFontColorStateCommand()
 : nsMultiStateCommand()
 {
 }
 
 nsresult
@@ -840,21 +838,21 @@ nsFontColorStateCommand::SetState(mozill
                                   nsString& newState)
 {
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   if (newState.IsEmpty() || newState.EqualsLiteral("normal")) {
     return aHTMLEditor->RemoveInlineProperty(nsGkAtoms::font,
-                                             NS_LITERAL_STRING("color"));
+                                             nsGkAtoms::color);
   }
 
-  return aHTMLEditor->SetInlineProperty(nsGkAtoms::font,
-                                        NS_LITERAL_STRING("color"), newState);
+  return aHTMLEditor->SetInlineProperty(nsGkAtoms::font, nsGkAtoms::color,
+                                        newState);
 }
 
 nsHighlightColorStateCommand::nsHighlightColorStateCommand()
 : nsMultiStateCommand()
 {
 }
 
 nsresult
@@ -882,21 +880,21 @@ nsHighlightColorStateCommand::SetState(m
                                        nsString& newState)
 {
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   if (newState.IsEmpty() || newState.EqualsLiteral("normal")) {
     return aHTMLEditor->RemoveInlineProperty(nsGkAtoms::font,
-                                             NS_LITERAL_STRING("bgcolor"));
+                                             nsGkAtoms::bgcolor);
   }
 
   return aHTMLEditor->SetInlineProperty(nsGkAtoms::font,
-                                        NS_LITERAL_STRING("bgcolor"),
+                                        nsGkAtoms::bgcolor,
                                         newState);
 }
 
 NS_IMETHODIMP
 nsHighlightColorStateCommand::IsCommandEnabled(const char * aCommandName,
                                                nsISupports *refCon,
                                                bool *outCmdEnabled)
 {
@@ -1594,17 +1592,17 @@ RemoveOneProperty(mozilla::HTMLEditor* a
                   const nsAString& aProp)
 {
   MOZ_ASSERT(aHTMLEditor);
 
   /// XXX Hack alert! Look in nsIEditProperty.h for this
   RefPtr<nsAtom> styleAtom = NS_Atomize(aProp);
   NS_ENSURE_TRUE(styleAtom, NS_ERROR_OUT_OF_MEMORY);
 
-  return aHTMLEditor->RemoveInlineProperty(styleAtom, EmptyString());
+  return aHTMLEditor->RemoveInlineProperty(styleAtom, nullptr);
 }
 
 
 // the name of the attribute here should be the contents of the appropriate
 // tag, e.g. 'b' for bold, 'i' for italics.
 nsresult
 RemoveTextProperty(mozilla::HTMLEditor* aHTMLEditor,
                    const nsAString& aProp)
@@ -1625,11 +1623,10 @@ SetTextProperty(mozilla::HTMLEditor* aHT
                 const nsAString& aProp)
 {
   MOZ_ASSERT(aHTMLEditor);
 
   /// XXX Hack alert! Look in nsIEditProperty.h for this
   RefPtr<nsAtom> styleAtom = NS_Atomize(aProp);
   NS_ENSURE_TRUE(styleAtom, NS_ERROR_OUT_OF_MEMORY);
 
-  return aHTMLEditor->SetInlineProperty(styleAtom,
-                                        EmptyString(), EmptyString());
+  return aHTMLEditor->SetInlineProperty(styleAtom, nullptr, EmptyString());
 }
--- a/editor/libeditor/CSSEditUtils.cpp
+++ b/editor/libeditor/CSSEditUtils.cpp
@@ -313,25 +313,16 @@ CSSEditUtils::~CSSEditUtils()
 {
 }
 
 // Answers true if we have some CSS equivalence for the HTML style defined
 // by aProperty and/or aAttribute for the node aNode
 bool
 CSSEditUtils::IsCSSEditableProperty(nsINode* aNode,
                                     nsAtom* aProperty,
-                                    const nsAString* aAttribute)
-{
-  RefPtr<nsAtom> attribute = aAttribute ? NS_Atomize(*aAttribute) : nullptr;
-  return IsCSSEditableProperty(aNode, aProperty, attribute);
-}
-
-bool
-CSSEditUtils::IsCSSEditableProperty(nsINode* aNode,
-                                    nsAtom* aProperty,
                                     nsAtom* aAttribute)
 {
   MOZ_ASSERT(aNode);
 
   nsINode* node = aNode;
   // we need an element node here
   if (node->NodeType() == nsIDOMNode::TEXT_NODE) {
     node = node->GetParentNode();
@@ -592,17 +583,17 @@ CSSEditUtils::RemoveCSSInlineStyle(nsINo
 
   return mHTMLEditor->RemoveContainer(element);
 }
 
 // Answers true if the property can be removed by setting a "none" CSS value
 // on a node
 bool
 CSSEditUtils::IsCSSInvertible(nsAtom& aProperty,
-                              const nsAString* aAttribute)
+                              nsAtom* aAttribute)
 {
   return nsGkAtoms::b == &aProperty;
 }
 
 // Get the default browser background color if we need it for GetCSSBackgroundColorState
 void
 CSSEditUtils::GetDefaultBackgroundColor(nsAString& aColor)
 {
@@ -1046,17 +1037,17 @@ CSSEditUtils::GetCSSEquivalentToHTMLInli
 // The value of aStyleType controls the styles we retrieve: specified or
 // computed. The return value aIsSet is true if the CSS styles are set.
 //
 // The nsIContent variant returns aIsSet instead of using an out parameter, and
 // does not modify aValue.
 bool
 CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsINode* aNode,
                                                   nsAtom* aProperty,
-                                                  const nsAString* aAttribute,
+                                                  nsAtom* aAttribute,
                                                   const nsAString& aValue,
                                                   StyleType aStyleType)
 {
   // Use aValue as only an in param, not in-out
   nsAutoString value(aValue);
   return IsCSSEquivalentToHTMLInlineStyleSet(aNode, aProperty, aAttribute,
                                              value, aStyleType);
 }
--- a/editor/libeditor/CSSEditUtils.h
+++ b/editor/libeditor/CSSEditUtils.h
@@ -78,24 +78,22 @@ public:
   };
 
   /**
    * Answers true if the given combination element_name/attribute_name
    * has a CSS equivalence in this implementation.
    *
    * @param aNode          [IN] A DOM node.
    * @param aProperty      [IN] An atom containing a HTML tag name.
-   * @param aAttribute     [IN] A string containing the name of a HTML
+   * @param aAttribute     [IN] An atom containing a HTML
    *                            attribute carried by the element above.
    * @return               A boolean saying if the tag/attribute has a CSS
    *                       equiv.
    */
   bool IsCSSEditableProperty(nsINode* aNode, nsAtom* aProperty,
-                             const nsAString* aAttribute);
-  bool IsCSSEditableProperty(nsINode* aNode, nsAtom* aProperty,
                              nsAtom* aAttribute);
 
   /**
    * Adds/remove a CSS declaration to the STYLE atrribute carried by a given
    * element.
    *
    * @param aElement       [IN] A DOM element.
    * @param aProperty      [IN] An atom containing the CSS property to set.
@@ -161,17 +159,17 @@ public:
    * on a node.
    *
    * @param aProperty     [IN] An atom containing a CSS property.
    * @param aAttribute    [IN] Pointer to an attribute name or null if this
    *                           information is irrelevant.
    * @return              A boolean saying if the property can be remove by
    *                      setting a "none" value.
    */
-  bool IsCSSInvertible(nsAtom& aProperty, const nsAString* aAttribute);
+  bool IsCSSInvertible(nsAtom& aProperty, nsAtom* aAttribute);
 
   /**
    * Get the default browser background color if we need it for
    * GetCSSBackgroundColorState().
    *
    * @param aColor         [OUT] The default color as it is defined in prefs.
    */
   void GetDefaultBackgroundColor(nsAString& aColor);
@@ -218,17 +216,17 @@ public:
   bool IsCSSEquivalentToHTMLInlineStyleSet(nsINode* aContent,
                                            nsAtom* aProperty,
                                            nsAtom* aAttribute,
                                            nsAString& aValue,
                                            StyleType aStyleType);
 
   bool IsCSSEquivalentToHTMLInlineStyleSet(nsINode* aContent,
                                            nsAtom* aProperty,
-                                           const nsAString* aAttribute,
+                                           nsAtom* aAttribute,
                                            const nsAString& aValue,
                                            StyleType aStyleType);
 
   bool IsCSSEquivalentToHTMLInlineStyleSet(nsINode* aContent,
                                            nsAtom* aProperty,
                                            const nsAString* aAttribute,
                                            nsAString& aValue,
                                            StyleType aStyleType);
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -209,35 +209,35 @@ HTMLEditRules::InitFields()
   mRangeItem = new RangeItem();
 
   InitStyleCacheArray(mCachedStyles);
 }
 
 void
 HTMLEditRules::InitStyleCacheArray(StyleCache aStyleCache[SIZE_STYLE_TABLE])
 {
-  aStyleCache[0] = StyleCache(nsGkAtoms::b, EmptyString());
-  aStyleCache[1] = StyleCache(nsGkAtoms::i, EmptyString());
-  aStyleCache[2] = StyleCache(nsGkAtoms::u, EmptyString());
-  aStyleCache[3] = StyleCache(nsGkAtoms::font, NS_LITERAL_STRING("face"));
-  aStyleCache[4] = StyleCache(nsGkAtoms::font, NS_LITERAL_STRING("size"));
-  aStyleCache[5] = StyleCache(nsGkAtoms::font, NS_LITERAL_STRING("color"));
-  aStyleCache[6] = StyleCache(nsGkAtoms::tt, EmptyString());
-  aStyleCache[7] = StyleCache(nsGkAtoms::em, EmptyString());
-  aStyleCache[8] = StyleCache(nsGkAtoms::strong, EmptyString());
-  aStyleCache[9] = StyleCache(nsGkAtoms::dfn, EmptyString());
-  aStyleCache[10] = StyleCache(nsGkAtoms::code, EmptyString());
-  aStyleCache[11] = StyleCache(nsGkAtoms::samp, EmptyString());
-  aStyleCache[12] = StyleCache(nsGkAtoms::var, EmptyString());
-  aStyleCache[13] = StyleCache(nsGkAtoms::cite, EmptyString());
-  aStyleCache[14] = StyleCache(nsGkAtoms::abbr, EmptyString());
-  aStyleCache[15] = StyleCache(nsGkAtoms::acronym, EmptyString());
-  aStyleCache[16] = StyleCache(nsGkAtoms::backgroundColor, EmptyString());
-  aStyleCache[17] = StyleCache(nsGkAtoms::sub, EmptyString());
-  aStyleCache[18] = StyleCache(nsGkAtoms::sup, EmptyString());
+  aStyleCache[0] = StyleCache(nsGkAtoms::b, nullptr);
+  aStyleCache[1] = StyleCache(nsGkAtoms::i, nullptr);
+  aStyleCache[2] = StyleCache(nsGkAtoms::u, nullptr);
+  aStyleCache[3] = StyleCache(nsGkAtoms::font, nsGkAtoms::face);
+  aStyleCache[4] = StyleCache(nsGkAtoms::font, nsGkAtoms::size);
+  aStyleCache[5] = StyleCache(nsGkAtoms::font, nsGkAtoms::color);
+  aStyleCache[6] = StyleCache(nsGkAtoms::tt, nullptr);
+  aStyleCache[7] = StyleCache(nsGkAtoms::em, nullptr);
+  aStyleCache[8] = StyleCache(nsGkAtoms::strong, nullptr);
+  aStyleCache[9] = StyleCache(nsGkAtoms::dfn, nullptr);
+  aStyleCache[10] = StyleCache(nsGkAtoms::code, nullptr);
+  aStyleCache[11] = StyleCache(nsGkAtoms::samp, nullptr);
+  aStyleCache[12] = StyleCache(nsGkAtoms::var, nullptr);
+  aStyleCache[13] = StyleCache(nsGkAtoms::cite, nullptr);
+  aStyleCache[14] = StyleCache(nsGkAtoms::abbr, nullptr);
+  aStyleCache[15] = StyleCache(nsGkAtoms::acronym, nullptr);
+  aStyleCache[16] = StyleCache(nsGkAtoms::backgroundColor, nullptr);
+  aStyleCache[17] = StyleCache(nsGkAtoms::sub, nullptr);
+  aStyleCache[18] = StyleCache(nsGkAtoms::sup, nullptr);
 }
 
 HTMLEditRules::~HTMLEditRules()
 {
   // remove ourselves as a listener to edit actions
   // In some cases, we have already been removed by
   // ~HTMLEditor, in which case we will get a null pointer here
   // which we ignore.  But this allows us to add the ability to
@@ -4909,17 +4909,17 @@ HTMLEditRules::CreateStyleForInsertText(
   UniquePtr<PropItem> item =
     Move(mHTMLEditor->mTypeInState->TakeClearProperty());
   while (item && node != rootElement) {
     NS_ENSURE_STATE(mHTMLEditor);
     // XXX If we redesign ClearStyle(), we can use EditorDOMPoint in this
     //     method.
     nsresult rv =
       mHTMLEditor->ClearStyle(address_of(node), &offset,
-                              item->tag, &item->attr);
+                              item->tag, item->attr);
     NS_ENSURE_SUCCESS(rv, rv);
     item = Move(mHTMLEditor->mTypeInState->TakeClearProperty());
     weDidSomething = true;
   }
 
   // then process setting any styles
   int32_t relFontSize = mHTMLEditor->mTypeInState->TakeRelativeFontSize();
   item = Move(mHTMLEditor->mTypeInState->TakeSetProperty());
@@ -4964,17 +4964,17 @@ HTMLEditRules::CreateStyleForInsertText(
         rv = mHTMLEditor->RelativeFontChangeOnTextNode(dir, newNode, 0, -1);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
 
     while (item) {
       NS_ENSURE_STATE(mHTMLEditor);
       rv = mHTMLEditor->SetInlinePropertyOnNode(*node->AsContent(),
-                                                *item->tag, &item->attr,
+                                                *item->tag, item->attr,
                                                 item->value);
       NS_ENSURE_SUCCESS(rv, rv);
       item = mHTMLEditor->mTypeInState->TakeSetProperty();
     }
   }
   if (weDidSomething) {
     return aSelection.Collapse(node, offset);
   }
@@ -7777,26 +7777,26 @@ HTMLEditRules::GetInlineStyles(nsINode* 
     if (typeInSet) {
       continue;
     }
 
     bool isSet = false;
     nsAutoString outValue;
     // Don't use CSS for <font size>, we don't support it usefully (bug 780035)
     if (!useCSS || (aStyleCache[j].tag == nsGkAtoms::font &&
-                    aStyleCache[j].attr.EqualsLiteral("size"))) {
+                    aStyleCache[j].attr == nsGkAtoms::size)) {
       NS_ENSURE_STATE(mHTMLEditor);
       isSet = mHTMLEditor->IsTextPropertySetByContent(aNode, aStyleCache[j].tag,
-                                                      &(aStyleCache[j].attr),
+                                                      aStyleCache[j].attr,
                                                       nullptr,
                                                       &outValue);
     } else {
       NS_ENSURE_STATE(mHTMLEditor);
       isSet = mHTMLEditor->mCSSEditUtils->IsCSSEquivalentToHTMLInlineStyleSet(
-                aNode, aStyleCache[j].tag, &(aStyleCache[j].attr), outValue,
+                aNode, aStyleCache[j].tag, aStyleCache[j].attr, outValue,
                 CSSEditUtils::eComputed);
     }
     if (isSet) {
       aStyleCache[j].mPresent = true;
       aStyleCache[j].value.Assign(outValue);
     }
   }
   return NS_OK;
@@ -7845,25 +7845,25 @@ HTMLEditRules::ReapplyCachedStyles()
       bool bFirst, bAny, bAll;
       bFirst = bAny = bAll = false;
 
       nsAutoString curValue;
       if (useCSS) {
         // check computed style first in css case
         NS_ENSURE_STATE(mHTMLEditor);
         bAny = mHTMLEditor->mCSSEditUtils->IsCSSEquivalentToHTMLInlineStyleSet(
-          selNode, mCachedStyles[i].tag, &(mCachedStyles[i].attr), curValue,
+          selNode, mCachedStyles[i].tag, mCachedStyles[i].attr, curValue,
           CSSEditUtils::eComputed);
       }
       if (!bAny) {
         // then check typeinstate and html style
         NS_ENSURE_STATE(mHTMLEditor);
         nsresult rv =
           mHTMLEditor->GetInlinePropertyBase(*mCachedStyles[i].tag,
-                                             &(mCachedStyles[i].attr),
+                                             mCachedStyles[i].attr,
                                              &(mCachedStyles[i].value),
                                              &bFirst, &bAny, &bAll,
                                              &curValue);
         NS_ENSURE_SUCCESS(rv, rv);
       }
       // This style has disappeared through deletion.  Let's add the styles to
       // mTypeInState when same style isn't applied to the node already.
       if ((!bAny || IsStyleCachePreservingAction(mTheAction)) &&
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -46,26 +46,26 @@ struct StyleCache final : public PropIte
   StyleCache()
     : PropItem()
     , mPresent(false)
   {
     MOZ_COUNT_CTOR(StyleCache);
   }
 
   StyleCache(nsAtom* aTag,
-             const nsAString& aAttr,
+             nsAtom* aAttr,
              const nsAString& aValue)
     : PropItem(aTag, aAttr, aValue)
     , mPresent(false)
   {
     MOZ_COUNT_CTOR(StyleCache);
   }
 
   StyleCache(nsAtom* aTag,
-             const nsAString& aAttr)
+             nsAtom* aAttr)
     : PropItem(aTag, aAttr, EmptyString())
     , mPresent(false)
   {
     MOZ_COUNT_CTOR(StyleCache);
   }
 
   ~StyleCache()
   {
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -17,19 +17,17 @@
 #include "HTMLEditorEventListener.h"
 #include "HTMLEditRules.h"
 #include "HTMLEditUtils.h"
 #include "HTMLURIRefObject.h"
 #include "StyleSheetTransactions.h"
 #include "TextEditUtils.h"
 #include "TypeInState.h"
 
-#include "nsIDOMMozNamedAttrMap.h"
 #include "nsIDOMDocument.h"
-#include "nsIDOMAttr.h"
 #include "nsIDocumentInlines.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMMouseEvent.h"
 #include "nsISelectionController.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsILinkHandler.h"
 #include "nsIInlineSpellChecker.h"
 
@@ -2608,37 +2606,31 @@ HTMLEditor::InsertLinkAroundSelection(ns
   if (href.IsEmpty()) {
     return NS_OK;
   }
 
   nsresult rv;
   AutoPlaceholderBatch beginBatching(this);
 
   // Set all attributes found on the supplied anchor element
-  nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
-  aAnchorElement->GetAttributes(getter_AddRefs(attrMap));
+  RefPtr<nsDOMAttributeMap> attrMap = anchor->Attributes();
   NS_ENSURE_TRUE(attrMap, NS_ERROR_FAILURE);
 
-  uint32_t count;
-  attrMap->GetLength(&count);
-  nsAutoString name, value;
+  uint32_t count = attrMap->Length();
+  nsAutoString value;
 
   for (uint32_t i = 0; i < count; ++i) {
-    nsCOMPtr<nsIDOMAttr> attribute;
-    rv = attrMap->Item(i, getter_AddRefs(attribute));
-    NS_ENSURE_SUCCESS(rv, rv);
+    RefPtr<Attr> attribute = attrMap->Item(i);
 
     if (attribute) {
       // We must clear the string buffers
-      //   because GetName, GetValue appends to previous string!
-      name.Truncate();
+      //   because GetValue appends to previous string!
       value.Truncate();
 
-      rv = attribute->GetName(name);
-      NS_ENSURE_SUCCESS(rv, rv);
+      nsAtom* name = attribute->NodeInfo()->NameAtom();
 
       rv = attribute->GetValue(value);
       NS_ENSURE_SUCCESS(rv, rv);
 
       rv = SetInlineProperty(nsGkAtoms::a, name, value);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
@@ -3514,31 +3506,31 @@ HTMLEditor::SelectAll()
   return errorResult.StealNSResult();
 }
 
 // this will NOT find aAttribute unless aAttribute has a non-null value
 // so singleton attributes like <Table border> will not be matched!
 bool
 HTMLEditor::IsTextPropertySetByContent(nsINode* aNode,
                                        nsAtom* aProperty,
-                                       const nsAString* aAttribute,
+                                       nsAtom* aAttribute,
                                        const nsAString* aValue,
                                        nsAString* outValue)
 {
   MOZ_ASSERT(aNode && aProperty);
 
   while (aNode) {
     if (aNode->IsElement()) {
       Element* element = aNode->AsElement();
       if (aProperty == element->NodeInfo()->NameAtom()) {
-        if (!aAttribute || aAttribute->IsEmpty()) {
+        if (!aAttribute) {
           return true;
         }
         nsAutoString value;
-        element->GetAttribute(*aAttribute, value);
+        element->GetAttr(kNameSpaceID_None, aAttribute, value);
         if (outValue) {
           *outValue = value;
         }
         if (!value.IsEmpty()) {
           if (!aValue) {
             return true;
           }
           if (aValue->Equals(value, nsCaseInsensitiveStringComparator())) {
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -233,33 +233,33 @@ public:
   nsresult GetAbsolutelyPositionedSelectionContainer(nsINode** aContainer);
   Element* GetPositionedElement() const
   {
     return mAbsolutelyPositionedObject;
   }
   nsresult GetElementZIndex(Element* aElement, int32_t* aZindex);
 
   nsresult SetInlineProperty(nsAtom* aProperty,
-                             const nsAString& aAttribute,
+                             nsAtom* aAttribute,
                              const nsAString& aValue);
   nsresult GetInlineProperty(nsAtom* aProperty,
-                             const nsAString& aAttribute,
+                             nsAtom* aAttribute,
                              const nsAString& aValue,
                              bool* aFirst,
                              bool* aAny,
                              bool* aAll);
   nsresult GetInlinePropertyWithAttrValue(nsAtom* aProperty,
-                                          const nsAString& aAttr,
+                                          nsAtom* aAttr,
                                           const nsAString& aValue,
                                           bool* aFirst,
                                           bool* aAny,
                                           bool* aAll,
                                           nsAString& outValue);
   nsresult RemoveInlineProperty(nsAtom* aProperty,
-                                const nsAString& aAttribute);
+                                nsAtom* aAttribute);
 protected:
   virtual ~HTMLEditor();
 
   using EditorBase::IsBlockNode;
   virtual bool IsBlockNode(nsINode *aNode) override;
 
 public:
   // XXX Why don't we move following methods above for grouping by the origins?
@@ -620,17 +620,17 @@ protected:
    * @param outValue   [OUT] the value of the attribute, if aIsSet is true
    * @return           true if <aProperty aAttribute=aValue> effects
    *                   aNode.
    *
    * The nsIContent variant returns aIsSet instead of using an out parameter.
    */
   bool IsTextPropertySetByContent(nsINode* aNode,
                                   nsAtom* aProperty,
-                                  const nsAString* aAttribute,
+                                  nsAtom* aAttribute,
                                   const nsAString* aValue,
                                   nsAString* outValue = nullptr);
 
   // Methods for handling plaintext quotations
   NS_IMETHOD PasteAsPlaintextQuotation(int32_t aSelectionType);
 
   /**
    * Insert a string as quoted text, replacing the selected text (if any).
@@ -757,44 +757,42 @@ protected:
 
   /**
    * Helper routines for inline style.
    */
   nsresult SetInlinePropertyOnTextNode(Text& aData,
                                        int32_t aStartOffset,
                                        int32_t aEndOffset,
                                        nsAtom& aProperty,
-                                       const nsAString* aAttribute,
+                                       nsAtom* aAttribute,
                                        const nsAString& aValue);
   nsresult SetInlinePropertyOnNode(nsIContent& aNode,
                                    nsAtom& aProperty,
-                                   const nsAString* aAttribute,
+                                   nsAtom* aAttribute,
                                    const nsAString& aValue);
 
   nsresult PromoteInlineRange(nsRange& aRange);
   nsresult PromoteRangeIfStartsOrEndsInNamedAnchor(nsRange& aRange);
   nsresult SplitStyleAboveRange(nsRange* aRange,
                                 nsAtom* aProperty,
-                                const nsAString* aAttribute);
+                                nsAtom* aAttribute);
   nsresult SplitStyleAbovePoint(nsCOMPtr<nsINode>* aNode, int32_t* aOffset,
                                 nsAtom* aProperty,
-                                const nsAString* aAttribute,
+                                nsAtom* aAttribute,
                                 nsIContent** aOutLeftNode = nullptr,
                                 nsIContent** aOutRightNode = nullptr);
   nsresult RemoveStyleInside(nsIContent& aNode,
                              nsAtom* aProperty,
-                             const nsAString* aAttribute,
+                             nsAtom* aAttribute,
                              const bool aChildrenOnly = false);
-  nsresult RemoveInlinePropertyImpl(nsAtom* aProperty,
-                                    const nsAString* aAttribute);
 
   bool NodeIsProperty(nsINode& aNode);
   bool IsAtFrontOfNode(nsINode& aNode, int32_t aOffset);
   bool IsAtEndOfNode(nsINode& aNode, int32_t aOffset);
-  bool IsOnlyAttribute(const Element* aElement, const nsAString& aAttribute);
+  bool IsOnlyAttribute(const Element* aElement, nsAtom* aAttribute);
 
   nsresult RemoveBlockContainer(nsIContent& aNode);
 
   nsIContent* GetPriorHTMLSibling(nsINode* aNode);
 
   nsIContent* GetNextHTMLSibling(nsINode* aNode);
 
   /**
@@ -872,17 +870,17 @@ protected:
   bool IsLastEditableChild(nsINode* aNode);
   nsIContent* GetFirstEditableChild(nsINode& aNode);
   nsIContent* GetLastEditableChild(nsINode& aNode);
 
   nsIContent* GetFirstEditableLeaf(nsINode& aNode);
   nsIContent* GetLastEditableLeaf(nsINode& aNode);
 
   nsresult GetInlinePropertyBase(nsAtom& aProperty,
-                                 const nsAString* aAttribute,
+                                 nsAtom* aAttribute,
                                  const nsAString* aValue,
                                  bool* aFirst,
                                  bool* aAny,
                                  bool* aAll,
                                  nsAString* outValue);
   bool HasStyleOrIdOrClass(Element* aElement);
   nsresult RemoveElementIfNoStyleOrIdOrClass(Element& aElement);
 
@@ -910,17 +908,17 @@ protected:
                                    nsIDOMDocument* aSourceDoc,
                                    nsIDOMNode* aDestNode,
                                    int32_t aDestOffset,
                                    bool aDeleteSelection,
                                    bool aTrustedInput,
                                    bool aClearStyle = true);
 
   nsresult ClearStyle(nsCOMPtr<nsINode>* aNode, int32_t* aOffset,
-                      nsAtom* aProperty, const nsAString* aAttribute);
+                      nsAtom* aProperty, nsAtom* aAttribute);
 
   void SetElementPosition(Element& aElement, int32_t aX, int32_t aY);
 
   /**
    * Reset a selected cell or collapsed selection (the caret) after table
    * editing.
    *
    * @param aTable      A table in the document.
@@ -1132,21 +1130,21 @@ public:
   friend class HTMLEditorEventListener;
   friend class HTMLEditRules;
   friend class TextEditRules;
   friend class WSRunObject;
 
 private:
   bool IsSimpleModifiableNode(nsIContent* aContent,
                               nsAtom* aProperty,
-                              const nsAString* aAttribute,
+                              nsAtom* aAttribute,
                               const nsAString* aValue);
   nsresult SetInlinePropertyOnNodeImpl(nsIContent& aNode,
                                        nsAtom& aProperty,
-                                       const nsAString* aAttribute,
+                                       nsAtom* aAttribute,
                                        const nsAString& aValue);
   typedef enum { eInserted, eAppended } InsertedOrAppended;
   void DoContentInserted(nsIDocument* aDocument, nsIContent* aContainer,
                          nsIContent* aChild,
                          InsertedOrAppended aInsertedOrAppended);
   already_AddRefed<Element> GetElementOrParentByTagName(
                               const nsAString& aTagName, nsINode* aNode);
   already_AddRefed<Element> CreateElementWithDefaults(
--- a/editor/libeditor/HTMLStyleEditor.cpp
+++ b/editor/libeditor/HTMLStyleEditor.cpp
@@ -55,22 +55,23 @@ IsEmptyTextNode(HTMLEditor* aThis, nsINo
 }
 
 NS_IMETHODIMP
 HTMLEditor::SetInlineProperty(const nsAString& aProperty,
                               const nsAString& aAttribute,
                               const nsAString& aValue)
 {
   RefPtr<nsAtom> property = NS_Atomize(aProperty);
-  return SetInlineProperty(property, aAttribute, aValue);
+  RefPtr<nsAtom> attribute = NS_Atomize(aAttribute);
+  return SetInlineProperty(property, attribute, aValue);
 }
 
 nsresult
 HTMLEditor::SetInlineProperty(nsAtom* aProperty,
-                              const nsAString& aAttribute,
+                              nsAtom* aAttribute,
                               const nsAString& aValue)
 {
   NS_ENSURE_TRUE(aProperty, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
   RefPtr<TextEditRules> rules(mRules);
   CommitComposition();
 
   RefPtr<Selection> selection = GetSelection();
@@ -105,17 +106,17 @@ HTMLEditor::SetInlineProperty(nsAtom* aP
 
       // Check for easy case: both range endpoints in same text node
       nsCOMPtr<nsINode> startNode = range->GetStartContainer();
       nsCOMPtr<nsINode> endNode = range->GetEndContainer();
       if (startNode && startNode == endNode && startNode->GetAsText()) {
         rv = SetInlinePropertyOnTextNode(*startNode->GetAsText(),
                                          range->StartOffset(),
                                          range->EndOffset(),
-                                         *aProperty, &aAttribute, aValue);
+                                         *aProperty, aAttribute, aValue);
         NS_ENSURE_SUCCESS(rv, rv);
         continue;
       }
 
       // Not the easy case.  Range not contained in single text node.  There
       // are up to three phases here.  There are all the nodes reported by the
       // subtree iterator to be processed.  And there are potentially a
       // starting textnode and an ending textnode which are only partially
@@ -146,87 +147,84 @@ HTMLEditor::SetInlineProperty(nsAtom* aP
       }
       // First check the start parent of the range to see if it needs to be
       // separately handled (it does if it's a text node, due to how the
       // subtree iterator works - it will not have reported it).
       if (startNode && startNode->GetAsText() && IsEditable(startNode)) {
         rv = SetInlinePropertyOnTextNode(*startNode->GetAsText(),
                                          range->StartOffset(),
                                          startNode->Length(), *aProperty,
-                                         &aAttribute, aValue);
+                                         aAttribute, aValue);
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
       // Then loop through the list, set the property on each node
       for (auto& node : arrayOfNodes) {
-        rv = SetInlinePropertyOnNode(*node, *aProperty, &aAttribute, aValue);
+        rv = SetInlinePropertyOnNode(*node, *aProperty, aAttribute, aValue);
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
       // Last check the end parent of the range to see if it needs to be
       // separately handled (it does if it's a text node, due to how the
       // subtree iterator works - it will not have reported it).
       if (endNode && endNode->GetAsText() && IsEditable(endNode)) {
         rv = SetInlinePropertyOnTextNode(*endNode->GetAsText(), 0,
                                           range->EndOffset(), *aProperty,
-                                          &aAttribute, aValue);
+                                          aAttribute, aValue);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
   }
   if (!cancel) {
     // Post-process
     return rules->DidDoAction(selection, &ruleInfo, rv);
   }
   return NS_OK;
 }
 
 // Helper function for SetInlinePropertyOn*: is aNode a simple old <b>, <font>,
 // <span style="">, etc. that we can reuse instead of creating a new one?
 bool
 HTMLEditor::IsSimpleModifiableNode(nsIContent* aContent,
                                    nsAtom* aProperty,
-                                   const nsAString* aAttribute,
+                                   nsAtom* aAttribute,
                                    const nsAString* aValue)
 {
   // aContent can be null, in which case we'll return false in a few lines
   MOZ_ASSERT(aProperty);
   MOZ_ASSERT_IF(aAttribute, aValue);
 
   nsCOMPtr<dom::Element> element = do_QueryInterface(aContent);
   if (!element) {
     return false;
   }
 
   // First check for <b>, <i>, etc.
   if (element->IsHTMLElement(aProperty) && !element->GetAttrCount() &&
-      (!aAttribute || aAttribute->IsEmpty())) {
+      !aAttribute) {
     return true;
   }
 
   // Special cases for various equivalencies: <strong>, <em>, <s>
   if (!element->GetAttrCount() &&
       ((aProperty == nsGkAtoms::b &&
         element->IsHTMLElement(nsGkAtoms::strong)) ||
        (aProperty == nsGkAtoms::i &&
         element->IsHTMLElement(nsGkAtoms::em)) ||
        (aProperty == nsGkAtoms::strike &&
         element->IsHTMLElement(nsGkAtoms::s)))) {
     return true;
   }
 
   // Now look for things like <font>
-  if (aAttribute && !aAttribute->IsEmpty()) {
-    RefPtr<nsAtom> atom = NS_Atomize(*aAttribute);
-    MOZ_ASSERT(atom);
-
+  if (aAttribute) {
     nsString attrValue;
     if (element->IsHTMLElement(aProperty) &&
-        IsOnlyAttribute(element, *aAttribute) &&
-        element->GetAttr(kNameSpaceID_None, atom, attrValue) &&
+        IsOnlyAttribute(element, aAttribute) &&
+        element->GetAttr(kNameSpaceID_None, aAttribute, attrValue) &&
         attrValue.Equals(*aValue, nsCaseInsensitiveStringComparator())) {
       // This is not quite correct, because it excludes cases like
       // <font face=000> being the same as <font face=#000000>.
       // Property-specific handling is needed (bug 760211).
       return true;
     }
   }
 
@@ -254,17 +252,17 @@ HTMLEditor::IsSimpleModifiableNode(nsICo
   return mCSSEditUtils->ElementsSameStyle(newSpan, element);
 }
 
 nsresult
 HTMLEditor::SetInlinePropertyOnTextNode(Text& aText,
                                         int32_t aStartOffset,
                                         int32_t aEndOffset,
                                         nsAtom& aProperty,
-                                        const nsAString* aAttribute,
+                                        nsAtom* aAttribute,
                                         const nsAString& aValue)
 {
   if (!aText.GetParentNode() ||
       !CanContainTag(*aText.GetParentNode(), aProperty)) {
     return NS_OK;
   }
 
   // Don't need to do anything if no characters actually selected
@@ -328,21 +326,19 @@ HTMLEditor::SetInlinePropertyOnTextNode(
   // Reparent the node inside inline node with appropriate {attribute,value}
   return SetInlinePropertyOnNode(*textNodeForTheRange,
                                  aProperty, aAttribute, aValue);
 }
 
 nsresult
 HTMLEditor::SetInlinePropertyOnNodeImpl(nsIContent& aNode,
                                         nsAtom& aProperty,
-                                        const nsAString* aAttribute,
+                                        nsAtom* aAttribute,
                                         const nsAString& aValue)
 {
-  RefPtr<nsAtom> attrAtom = aAttribute ? NS_Atomize(*aAttribute) : nullptr;
-
   // If this is an element that can't be contained in a span, we have to
   // recurse to its children.
   if (!TagCanContain(*nsGkAtoms::span, aNode)) {
     if (aNode.HasChildren()) {
       nsTArray<OwningNonNull<nsIContent>> arrayOfNodes;
 
       // Populate the list.
       for (nsCOMPtr<nsIContent> child = aNode.GetFirstChild();
@@ -391,56 +387,55 @@ HTMLEditor::SetInlinePropertyOnNodeImpl(
                                         aAttribute, &aValue)) {
     return NS_OK;
   }
 
   bool useCSS = (IsCSSEnabled() &&
                  mCSSEditUtils->IsCSSEditableProperty(&aNode, &aProperty,
                                                       aAttribute)) ||
                 // bgcolor is always done using CSS
-                attrAtom == nsGkAtoms::bgcolor;
+                aAttribute == nsGkAtoms::bgcolor;
 
   if (useCSS) {
     nsCOMPtr<dom::Element> tmp;
     // We only add style="" to <span>s with no attributes (bug 746515).  If we
     // don't have one, we need to make one.
     if (aNode.IsHTMLElement(nsGkAtoms::span) &&
         !aNode.AsElement()->GetAttrCount()) {
       tmp = aNode.AsElement();
     } else {
       tmp = InsertContainerAbove(&aNode, nsGkAtoms::span);
       NS_ENSURE_STATE(tmp);
     }
 
     // Add the CSS styles corresponding to the HTML style request
     mCSSEditUtils->SetCSSEquivalentToHTMLStyle(tmp,
-                                               &aProperty, attrAtom,
+                                               &aProperty, aAttribute,
                                                &aValue, false);
     return NS_OK;
   }
 
   // is it already the right kind of node, but with wrong attribute?
   if (aNode.IsHTMLElement(&aProperty)) {
     // Just set the attribute on it.
-    nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(&aNode);
-    return SetAttribute(elem, *aAttribute, aValue);
+    return SetAttribute(aNode.AsElement(), aAttribute, aValue);
   }
 
   // ok, chuck it in its very own container
-  nsCOMPtr<Element> tmp = InsertContainerAbove(&aNode, &aProperty, attrAtom,
-                                               &aValue);
+  RefPtr<Element> tmp = InsertContainerAbove(&aNode, &aProperty, aAttribute,
+                                             &aValue);
   NS_ENSURE_STATE(tmp);
 
   return NS_OK;
 }
 
 nsresult
 HTMLEditor::SetInlinePropertyOnNode(nsIContent& aNode,
                                     nsAtom& aProperty,
-                                    const nsAString* aAttribute,
+                                    nsAtom* aAttribute,
                                     const nsAString& aValue)
 {
   nsCOMPtr<nsIContent> previousSibling = aNode.GetPreviousSibling(),
                        nextSibling = aNode.GetNextSibling();
   NS_ENSURE_STATE(aNode.GetParentNode());
   OwningNonNull<nsINode> parent = *aNode.GetParentNode();
 
   nsresult rv = RemoveStyleInside(aNode, &aProperty, aAttribute);
@@ -474,17 +469,17 @@ HTMLEditor::SetInlinePropertyOnNode(nsIC
   }
 
   return NS_OK;
 }
 
 nsresult
 HTMLEditor::SplitStyleAboveRange(nsRange* inRange,
                                  nsAtom* aProperty,
-                                 const nsAString* aAttribute)
+                                 nsAtom* aAttribute)
 {
   NS_ENSURE_TRUE(inRange, NS_ERROR_NULL_POINTER);
 
   nsCOMPtr<nsINode> startNode = inRange->GetStartContainer();
   int32_t startOffset = inRange->StartOffset();
   nsCOMPtr<nsINode> endNode = inRange->GetEndContainer();
   int32_t endOffset = inRange->EndOffset();
 
@@ -513,17 +508,17 @@ HTMLEditor::SplitStyleAboveRange(nsRange
   return NS_OK;
 }
 
 nsresult
 HTMLEditor::SplitStyleAbovePoint(nsCOMPtr<nsINode>* aNode,
                                  int32_t* aOffset,
                                  // null here means we split all properties
                                  nsAtom* aProperty,
-                                 const nsAString* aAttribute,
+                                 nsAtom* aAttribute,
                                  nsIContent** aOutLeftNode,
                                  nsIContent** aOutRightNode)
 {
   NS_ENSURE_TRUE(aNode && *aNode && aOffset, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE((*aNode)->IsContent(), NS_OK);
 
   if (aOutLeftNode) {
     *aOutLeftNode = nullptr;
@@ -581,17 +576,17 @@ HTMLEditor::SplitStyleAbovePoint(nsCOMPt
 
   return NS_OK;
 }
 
 nsresult
 HTMLEditor::ClearStyle(nsCOMPtr<nsINode>* aNode,
                        int32_t* aOffset,
                        nsAtom* aProperty,
-                       const nsAString* aAttribute)
+                       nsAtom* aAttribute)
 {
   nsCOMPtr<nsIContent> leftNode, rightNode;
   nsresult rv = SplitStyleAbovePoint(aNode, aOffset, aProperty,
                                      aAttribute, getter_AddRefs(leftNode),
                                      getter_AddRefs(rightNode));
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (leftNode) {
@@ -677,17 +672,17 @@ HTMLEditor::NodeIsProperty(nsINode& aNod
 {
   return IsContainer(&aNode) && IsEditable(&aNode) && !IsBlockNode(&aNode) &&
          !aNode.IsHTMLElement(nsGkAtoms::a);
 }
 
 nsresult
 HTMLEditor::RemoveStyleInside(nsIContent& aNode,
                               nsAtom* aProperty,
-                              const nsAString* aAttribute,
+                              nsAtom* aAttribute,
                               const bool aChildrenOnly /* = false */)
 {
   if (aNode.GetAsText()) {
     return NS_OK;
   }
 
   // first process the children
   RefPtr<nsIContent> child = aNode.GetFirstChild();
@@ -706,17 +701,17 @@ HTMLEditor::RemoveStyleInside(nsIContent
        // but check for link (<a href=...)
        (aProperty == nsGkAtoms::href && HTMLEditUtils::IsLink(&aNode)) ||
        // and for named anchors
        (aProperty == nsGkAtoms::name && HTMLEditUtils::IsNamedAnchor(&aNode)) ||
        // or node is any prop and we asked for that
        (!aProperty && NodeIsProperty(aNode)))) {
     // if we weren't passed an attribute, then we want to
     // remove any matching inlinestyles entirely
-    if (!aAttribute || aAttribute->IsEmpty()) {
+    if (!aAttribute) {
       bool hasStyleAttr =
         aNode.IsElement() &&
         aNode.AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::style);
       bool hasClassAttr =
         aNode.IsElement() &&
         aNode.AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::_class);
       if (aProperty && (hasStyleAttr || hasClassAttr)) {
         // aNode carries inline styles or a class attribute so we can't
@@ -732,96 +727,95 @@ HTMLEditor::RemoveStyleInside(nsIContent
         rv =
           CloneAttribute(nsGkAtoms::_class, spanNode, aNode.AsElement());
         NS_ENSURE_SUCCESS(rv, rv);
       }
       nsresult rv = RemoveContainer(&aNode);
       NS_ENSURE_SUCCESS(rv, rv);
     } else if (aNode.IsElement()) {
       // otherwise we just want to eliminate the attribute
-      RefPtr<nsAtom> attribute = NS_Atomize(*aAttribute);
-      if (aNode.AsElement()->HasAttr(kNameSpaceID_None, attribute)) {
+      if (aNode.AsElement()->HasAttr(kNameSpaceID_None, aAttribute)) {
         // if this matching attribute is the ONLY one on the node,
         // then remove the whole node.  Otherwise just nix the attribute.
-        if (IsOnlyAttribute(aNode.AsElement(), *aAttribute)) {
+        if (IsOnlyAttribute(aNode.AsElement(), aAttribute)) {
           nsresult rv = RemoveContainer(&aNode);
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return rv;
           }
         } else {
-          nsresult rv = RemoveAttribute(aNode.AsElement(), attribute);
+          nsresult rv = RemoveAttribute(aNode.AsElement(), aAttribute);
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return rv;
           }
         }
       }
     }
   }
 
   if (!aChildrenOnly &&
       mCSSEditUtils->IsCSSEditableProperty(&aNode, aProperty, aAttribute)) {
     // the HTML style defined by aProperty/aAttribute has a CSS equivalence in
     // this implementation for the node aNode; let's check if it carries those
     // css styles
     if (aNode.IsElement()) {
-      RefPtr<nsAtom> attribute =
-        aAttribute ? NS_Atomize(*aAttribute) : nullptr;
       bool hasAttribute =
         mCSSEditUtils->HaveCSSEquivalentStyles(
-                         aNode, aProperty, attribute, CSSEditUtils::eSpecified);
+                         aNode, aProperty, aAttribute,
+                         CSSEditUtils::eSpecified);
       if (hasAttribute) {
         // yes, tmp has the corresponding css declarations in its style
         // attribute
         // let's remove them
         mCSSEditUtils->RemoveCSSEquivalentToHTMLStyle(aNode.AsElement(),
                                                       aProperty,
-                                                      attribute,
+                                                      aAttribute,
                                                       nullptr,
                                                       false);
         // remove the node if it is a span or font, if its style attribute is
         // empty or absent, and if it does not have a class nor an id
         RemoveElementIfNoStyleOrIdOrClass(*aNode.AsElement());
       }
     }
   }
 
   // Or node is big or small and we are setting font size
   if (aChildrenOnly) {
     return NS_OK;
   }
   if (aProperty == nsGkAtoms::font &&
       (aNode.IsHTMLElement(nsGkAtoms::big) ||
        aNode.IsHTMLElement(nsGkAtoms::small)) &&
-      aAttribute && aAttribute->LowerCaseEqualsLiteral("size")) {
+      aAttribute == nsGkAtoms::size) {
     // if we are setting font size, remove any nested bigs and smalls
     return RemoveContainer(&aNode);
   }
   return NS_OK;
 }
 
 bool
 HTMLEditor::IsOnlyAttribute(const Element* aElement,
-                            const nsAString& aAttribute)
+                            nsAtom* aAttribute)
 {
   MOZ_ASSERT(aElement);
 
   uint32_t attrCount = aElement->GetAttrCount();
   for (uint32_t i = 0; i < attrCount; ++i) {
     const nsAttrName* name = aElement->GetAttrNameAt(i);
     if (!name->NamespaceEquals(kNameSpaceID_None)) {
       return false;
     }
 
-    nsAutoString attrString;
-    name->LocalName()->ToString(attrString);
     // if it's the attribute we know about, or a special _moz attribute,
     // keep looking
-    if (!attrString.Equals(aAttribute, nsCaseInsensitiveStringComparator()) &&
-        !StringBeginsWith(attrString, NS_LITERAL_STRING("_moz"))) {
-      return false;
+    if (name->LocalName() != aAttribute) {
+      nsAutoString attrString;
+      name->LocalName()->ToString(attrString);
+      if (!StringBeginsWith(attrString, NS_LITERAL_STRING("_moz"))) {
+        return false;
+      }
     }
   }
   // if we made it through all of them without finding a real attribute
   // other than aAttribute, then return true
   return true;
 }
 
 nsresult
@@ -939,17 +933,17 @@ HTMLEditor::IsAtEndOfNode(nsINode& aNode
     return true;
   }
   return false;
 }
 
 
 nsresult
 HTMLEditor::GetInlinePropertyBase(nsAtom& aProperty,
-                                  const nsAString* aAttribute,
+                                  nsAtom* aAttribute,
                                   const nsAString* aValue,
                                   bool* aFirst,
                                   bool* aAny,
                                   bool* aAll,
                                   nsAString* outValue)
 {
   *aAny = false;
   *aAll = true;
@@ -968,18 +962,17 @@ HTMLEditor::GetInlinePropertyBase(nsAtom
     bool firstNodeInRange = true;
 
     if (isCollapsed) {
       nsCOMPtr<nsINode> collapsedNode = range->GetStartContainer();
       NS_ENSURE_TRUE(collapsedNode, NS_ERROR_FAILURE);
       bool isSet, theSetting;
       nsString tOutString;
       if (aAttribute) {
-        nsString tString(*aAttribute);
-        mTypeInState->GetTypingState(isSet, theSetting, &aProperty, tString,
+        mTypeInState->GetTypingState(isSet, theSetting, &aProperty, aAttribute,
                                      &tOutString);
         if (outValue) {
           outValue->Assign(tOutString);
         }
       } else {
         mTypeInState->GetTypingState(isSet, theSetting, &aProperty);
       }
       if (isSet) {
@@ -1104,119 +1097,109 @@ NS_IMETHODIMP
 HTMLEditor::GetInlineProperty(const nsAString& aProperty,
                               const nsAString& aAttribute,
                               const nsAString& aValue,
                               bool* aFirst,
                               bool* aAny,
                               bool* aAll)
 {
   RefPtr<nsAtom> property = NS_Atomize(aProperty);
-  return GetInlineProperty(property, aAttribute, aValue, aFirst, aAny, aAll);
+  RefPtr<nsAtom> attribute = NS_Atomize(aAttribute);
+  return GetInlineProperty(property, attribute, aValue, aFirst, aAny, aAll);
 }
 
 nsresult
 HTMLEditor::GetInlineProperty(nsAtom* aProperty,
-                              const nsAString& aAttribute,
+                              nsAtom* aAttribute,
                               const nsAString& aValue,
                               bool* aFirst,
                               bool* aAny,
                               bool* aAll)
 {
   NS_ENSURE_TRUE(aProperty && aFirst && aAny && aAll, NS_ERROR_NULL_POINTER);
-  const nsAString *att = nullptr;
-  if (!aAttribute.IsEmpty())
-    att = &aAttribute;
   const nsAString *val = nullptr;
   if (!aValue.IsEmpty())
     val = &aValue;
-  return GetInlinePropertyBase(*aProperty, att, val, aFirst, aAny, aAll, nullptr);
+  return GetInlinePropertyBase(*aProperty, aAttribute, val, aFirst, aAny, aAll,
+                               nullptr);
 }
 
 NS_IMETHODIMP
 HTMLEditor::GetInlinePropertyWithAttrValue(const nsAString& aProperty,
                                            const nsAString& aAttribute,
                                            const nsAString& aValue,
                                            bool* aFirst,
                                            bool* aAny,
                                            bool* aAll,
                                            nsAString& outValue)
 {
   RefPtr<nsAtom> property = NS_Atomize(aProperty);
-  return GetInlinePropertyWithAttrValue(property, aAttribute, aValue, aFirst,
+  RefPtr<nsAtom> attribute = NS_Atomize(aAttribute);
+  return GetInlinePropertyWithAttrValue(property, attribute, aValue, aFirst,
                                         aAny, aAll, outValue);
 }
 
 nsresult
 HTMLEditor::GetInlinePropertyWithAttrValue(nsAtom* aProperty,
-                                           const nsAString& aAttribute,
+                                           nsAtom* aAttribute,
                                            const nsAString& aValue,
                                            bool* aFirst,
                                            bool* aAny,
                                            bool* aAll,
                                            nsAString& outValue)
 {
   NS_ENSURE_TRUE(aProperty && aFirst && aAny && aAll, NS_ERROR_NULL_POINTER);
-  const nsAString *att = nullptr;
-  if (!aAttribute.IsEmpty())
-    att = &aAttribute;
   const nsAString *val = nullptr;
   if (!aValue.IsEmpty())
     val = &aValue;
-  return GetInlinePropertyBase(*aProperty, att, val, aFirst, aAny, aAll, &outValue);
+  return GetInlinePropertyBase(*aProperty, aAttribute, val, aFirst, aAny, aAll, &outValue);
 }
 
 NS_IMETHODIMP
 HTMLEditor::RemoveAllInlineProperties()
 {
   AutoPlaceholderBatch batchIt(this);
   AutoRules beginRulesSniffing(this, EditAction::resetTextProperties,
                                nsIEditor::eNext);
 
-  nsresult rv = RemoveInlinePropertyImpl(nullptr, nullptr);
+  nsresult rv = RemoveInlineProperty(nullptr, nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLEditor::RemoveInlineProperty(const nsAString& aProperty,
                                  const nsAString& aAttribute)
 {
   RefPtr<nsAtom> property = NS_Atomize(aProperty);
-  return RemoveInlineProperty(property, aAttribute);
+  RefPtr<nsAtom> attribute = NS_Atomize(aAttribute);
+  return RemoveInlineProperty(property, attribute);
 }
 
 nsresult
 HTMLEditor::RemoveInlineProperty(nsAtom* aProperty,
-                                 const nsAString& aAttribute)
+                                 nsAtom* aAttribute)
 {
-  return RemoveInlinePropertyImpl(aProperty, &aAttribute);
-}
-
-nsresult
-HTMLEditor::RemoveInlinePropertyImpl(nsAtom* aProperty,
-                                     const nsAString* aAttribute)
-{
-  MOZ_ASSERT_IF(aProperty, aAttribute);
   NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
   CommitComposition();
 
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   if (selection->Collapsed()) {
     // Manipulating text attributes on a collapsed selection only sets state
     // for the next text insertion
 
     // For links, aProperty uses "href", use "a" instead
     if (aProperty == nsGkAtoms::href || aProperty == nsGkAtoms::name) {
       aProperty = nsGkAtoms::a;
     }
 
     if (aProperty) {
-      mTypeInState->ClearProp(aProperty, *aAttribute);
+      mTypeInState->ClearProp(aProperty, aAttribute);
     } else {
       mTypeInState->ClearAllProps();
     }
     return NS_OK;
   }
 
   AutoPlaceholderBatch batchIt(this);
   AutoRules beginRulesSniffing(this, EditAction::removeTextProperty,
@@ -1365,17 +1348,17 @@ HTMLEditor::RelativeFontChange(FontSize 
       selectedNode = *selectedNode->GetParentNode();
     }
     if (!CanContainTag(selectedNode, atom)) {
       return NS_OK;
     }
 
     // Manipulating text attributes on a collapsed selection only sets state
     // for the next text insertion
-    mTypeInState->SetProp(&atom, EmptyString(), EmptyString());
+    mTypeInState->SetProp(&atom, nullptr, EmptyString());
     return NS_OK;
   }
 
   // Wrap with txn batching, rules sniffing, and selection preservation code
   AutoPlaceholderBatch batchIt(this);
   AutoRules beginRulesSniffing(this, EditAction::setTextProperty,
                                nsIEditor::eNext);
   AutoSelectionRestorer selectionRestorer(selection, this);
@@ -1652,19 +1635,18 @@ HTMLEditor::GetFontFaceState(bool* aMixe
                              nsAString& outFace)
 {
   NS_ENSURE_TRUE(aMixed, NS_ERROR_FAILURE);
   *aMixed = true;
   outFace.Truncate();
 
   bool first, any, all;
 
-  NS_NAMED_LITERAL_STRING(attr, "face");
   nsresult rv =
-    GetInlinePropertyBase(*nsGkAtoms::font, &attr.AsString(), nullptr, &first,
+    GetInlinePropertyBase(*nsGkAtoms::font, nsGkAtoms::face, nullptr, &first,
                           &any, &all, &outFace);
   NS_ENSURE_SUCCESS(rv, rv);
   if (any && !all) {
     return NS_OK; // mixed
   }
   if (all) {
     *aMixed = false;
     return NS_OK;
@@ -1693,21 +1675,20 @@ HTMLEditor::GetFontFaceState(bool* aMixe
 NS_IMETHODIMP
 HTMLEditor::GetFontColorState(bool* aMixed,
                               nsAString& aOutColor)
 {
   NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER);
   *aMixed = true;
   aOutColor.Truncate();
 
-  NS_NAMED_LITERAL_STRING(colorStr, "color");
   bool first, any, all;
 
   nsresult rv =
-    GetInlinePropertyBase(*nsGkAtoms::font, &colorStr.AsString(), nullptr,
+    GetInlinePropertyBase(*nsGkAtoms::font, nsGkAtoms::color, nullptr,
                           &first, &any, &all, &aOutColor);
   NS_ENSURE_SUCCESS(rv, rv);
   if (any && !all) {
     return NS_OK; // mixed
   }
   if (all) {
     *aMixed = false;
     return NS_OK;
--- a/editor/libeditor/TypeInState.cpp
+++ b/editor/libeditor/TypeInState.cpp
@@ -134,17 +134,17 @@ TypeInState::Reset()
     delete mSetArray[i];
   }
   mSetArray.Clear();
 }
 
 
 void
 TypeInState::SetProp(nsAtom* aProp,
-                     const nsAString& aAttr,
+                     nsAtom* aAttr,
                      const nsAString& aValue)
 {
   // special case for big/small, these nest
   if (nsGkAtoms::big == aProp) {
     mRelativeFontSize++;
     return;
   }
   if (nsGkAtoms::small == aProp) {
@@ -166,22 +166,22 @@ TypeInState::SetProp(nsAtom* aProp,
   RemovePropFromClearedList(aProp, aAttr);
 }
 
 
 void
 TypeInState::ClearAllProps()
 {
   // null prop means "all" props
-  ClearProp(nullptr, EmptyString());
+  ClearProp(nullptr, nullptr);
 }
 
 void
 TypeInState::ClearProp(nsAtom* aProp,
-                       const nsAString& aAttr)
+                       nsAtom* aAttr)
 {
   // if it's already cleared we are done
   if (IsPropCleared(aProp, aAttr)) {
     return;
   }
 
   // make a new propitem
   PropItem* item = new PropItem(aProp, aAttr, EmptyString());
@@ -239,42 +239,34 @@ TypeInState::TakeRelativeFontSize()
   int32_t relSize = mRelativeFontSize;
   mRelativeFontSize = 0;
   return relSize;
 }
 
 void
 TypeInState::GetTypingState(bool& isSet,
                             bool& theSetting,
-                            nsAtom* aProp)
-{
-  GetTypingState(isSet, theSetting, aProp, EmptyString(), nullptr);
-}
-
-void
-TypeInState::GetTypingState(bool& isSet,
-                            bool& theSetting,
                             nsAtom* aProp,
-                            const nsString& aAttr,
+                            nsAtom* aAttr,
                             nsString* aValue)
 {
   if (IsPropSet(aProp, aAttr, aValue)) {
     isSet = true;
     theSetting = true;
   } else if (IsPropCleared(aProp, aAttr)) {
     isSet = true;
     theSetting = false;
   } else {
     isSet = false;
   }
 }
 
 void
 TypeInState::RemovePropFromSetList(nsAtom* aProp,
-                                   const nsAString& aAttr)
+                                   nsAtom* aAttr)
 {
   int32_t index;
   if (!aProp) {
     // clear _all_ props
     for (size_t i = 0, n = mSetArray.Length(); i < n; i++) {
       delete mSetArray[i];
     }
     mSetArray.Clear();
@@ -282,37 +274,37 @@ TypeInState::RemovePropFromSetList(nsAto
   } else if (FindPropInList(aProp, aAttr, nullptr, mSetArray, index)) {
     delete mSetArray[index];
     mSetArray.RemoveElementAt(index);
   }
 }
 
 void
 TypeInState::RemovePropFromClearedList(nsAtom* aProp,
-                                       const nsAString& aAttr)
+                                       nsAtom* aAttr)
 {
   int32_t index;
   if (FindPropInList(aProp, aAttr, nullptr, mClearedArray, index)) {
     delete mClearedArray[index];
     mClearedArray.RemoveElementAt(index);
   }
 }
 
 bool
 TypeInState::IsPropSet(nsAtom* aProp,
-                       const nsAString& aAttr,
+                       nsAtom* aAttr,
                        nsAString* outValue)
 {
   int32_t i;
   return IsPropSet(aProp, aAttr, outValue, i);
 }
 
 bool
 TypeInState::IsPropSet(nsAtom* aProp,
-                       const nsAString& aAttr,
+                       nsAtom* aAttr,
                        nsAString* outValue,
                        int32_t& outIndex)
 {
   // linear search.  list should be short.
   size_t count = mSetArray.Length();
   for (size_t i = 0; i < count; i++) {
     PropItem *item = mSetArray[i];
     if (item->tag == aProp && item->attr == aAttr) {
@@ -324,42 +316,42 @@ TypeInState::IsPropSet(nsAtom* aProp,
     }
   }
   return false;
 }
 
 
 bool
 TypeInState::IsPropCleared(nsAtom* aProp,
-                           const nsAString& aAttr)
+                           nsAtom* aAttr)
 {
   int32_t i;
   return IsPropCleared(aProp, aAttr, i);
 }
 
 
 bool
 TypeInState::IsPropCleared(nsAtom* aProp,
-                           const nsAString& aAttr,
+                           nsAtom* aAttr,
                            int32_t& outIndex)
 {
   if (FindPropInList(aProp, aAttr, nullptr, mClearedArray, outIndex)) {
     return true;
   }
-  if (FindPropInList(0, EmptyString(), nullptr, mClearedArray, outIndex)) {
+  if (FindPropInList(nullptr, nullptr, nullptr, mClearedArray, outIndex)) {
     // special case for all props cleared
     outIndex = -1;
     return true;
   }
   return false;
 }
 
 bool
 TypeInState::FindPropInList(nsAtom* aProp,
-                            const nsAString& aAttr,
+                            nsAtom* aAttr,
                             nsAString* outValue,
                             nsTArray<PropItem*>& aList,
                             int32_t& outIndex)
 {
   // linear search.  list should be short.
   size_t count = aList.Length();
   for (size_t i = 0; i < count; i++) {
     PropItem *item = aList[i];
@@ -375,22 +367,23 @@ TypeInState::FindPropInList(nsAtom* aPro
 }
 
 /********************************************************************
  * mozilla::PropItem: helper struct for mozilla::TypeInState
  *******************************************************************/
 
 PropItem::PropItem()
   : tag(nullptr)
+  , attr(nullptr)
 {
   MOZ_COUNT_CTOR(PropItem);
 }
 
 PropItem::PropItem(nsAtom* aTag,
-                   const nsAString& aAttr,
+                   nsAtom* aAttr,
                    const nsAString &aValue)
   : tag(aTag)
   , attr(aAttr)
   , value(aValue)
 {
   MOZ_COUNT_CTOR(PropItem);
 }
 
--- a/editor/libeditor/TypeInState.h
+++ b/editor/libeditor/TypeInState.h
@@ -28,21 +28,21 @@ namespace mozilla {
 class HTMLEditRules;
 namespace dom {
 class Selection;
 } // namespace dom
 
 struct PropItem
 {
   nsAtom* tag;
-  nsString attr;
+  nsAtom* attr;
   nsString value;
 
   PropItem();
-  PropItem(nsAtom* aTag, const nsAString& aAttr, const nsAString& aValue);
+  PropItem(nsAtom* aTag, nsAtom* aAttr, const nsAString& aValue);
   ~PropItem();
 };
 
 class TypeInState final : public nsISelectionListener
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(TypeInState)
@@ -50,20 +50,20 @@ public:
   TypeInState();
   void Reset();
 
   nsresult UpdateSelState(dom::Selection* aSelection);
 
   // nsISelectionListener
   NS_DECL_NSISELECTIONLISTENER
 
-  void SetProp(nsAtom* aProp, const nsAString& aAttr, const nsAString& aValue);
+  void SetProp(nsAtom* aProp, nsAtom* aAttr, const nsAString& aValue);
 
   void ClearAllProps();
-  void ClearProp(nsAtom* aProp, const nsAString& aAttr);
+  void ClearProp(nsAtom* aProp, nsAtom* aAttr);
 
   /**
    * TakeClearProperty() hands back next property item on the clear list.
    * Caller assumes ownership of PropItem and must delete it.
    */
   UniquePtr<PropItem> TakeClearProperty();
 
   /**
@@ -73,34 +73,33 @@ public:
   UniquePtr<PropItem> TakeSetProperty();
 
   /**
    * TakeRelativeFontSize() hands back relative font value, which is then
    * cleared out.
    */
   int32_t TakeRelativeFontSize();
 
-  void GetTypingState(bool& isSet, bool& theSetting, nsAtom* aProp);
   void GetTypingState(bool& isSet, bool& theSetting, nsAtom* aProp,
-                      const nsString& aAttr, nsString* outValue);
+                      nsAtom* aAttr = nullptr, nsString* outValue = nullptr);
 
-  static bool FindPropInList(nsAtom* aProp, const nsAString& aAttr,
+  static bool FindPropInList(nsAtom* aProp, nsAtom* aAttr,
                              nsAString* outValue, nsTArray<PropItem*>& aList,
                              int32_t& outIndex);
 
 protected:
   virtual ~TypeInState();
 
-  void RemovePropFromSetList(nsAtom* aProp, const nsAString& aAttr);
-  void RemovePropFromClearedList(nsAtom* aProp, const nsAString& aAttr);
-  bool IsPropSet(nsAtom* aProp, const nsAString& aAttr, nsAString* outValue);
-  bool IsPropSet(nsAtom* aProp, const nsAString& aAttr, nsAString* outValue,
+  void RemovePropFromSetList(nsAtom* aProp, nsAtom* aAttr);
+  void RemovePropFromClearedList(nsAtom* aProp, nsAtom* aAttr);
+  bool IsPropSet(nsAtom* aProp, nsAtom* aAttr, nsAString* outValue);
+  bool IsPropSet(nsAtom* aProp, nsAtom* aAttr, nsAString* outValue,
                  int32_t& outIndex);
-  bool IsPropCleared(nsAtom* aProp, const nsAString& aAttr);
-  bool IsPropCleared(nsAtom* aProp, const nsAString& aAttr, int32_t& outIndex);
+  bool IsPropCleared(nsAtom* aProp, nsAtom* aAttr);
+  bool IsPropCleared(nsAtom* aProp, nsAtom* aAttr, int32_t& outIndex);
 
   nsTArray<PropItem*> mSetArray;
   nsTArray<PropItem*> mClearedArray;
   int32_t mRelativeFontSize;
   nsCOMPtr<nsIDOMNode> mLastSelectionContainer;
   int32_t mLastSelectionOffset;
 
   friend class HTMLEditRules;
--- a/testing/marionette/CONTRIBUTING.md
+++ b/testing/marionette/CONTRIBUTING.md
@@ -43,48 +43,45 @@ Writing code
 Because there are many moving parts involved remote controlling
 a web browser, it can be challenging to a new contributor to know
 where to start.  Please don’t hesitate to [ask questions]!
 
 The canonical source code repository is [mozilla-central].  Bugs are
 filed in the [`Testing :: Marionette`] component on Bugzilla.  We also
 have a curated set of [good first bugs] you may consider attempting first.
 
-The purpose of this guide _is not_ to make sure you have a basic
-development environment set up.  For that there is plentiful
-documentation, such as the [Developer Guide] to get you rolling.
-Once you do, we can get started working up your first patch!
-Remember to [reach out to us] at any point if you have questions.
+We have collected a lot of good advice for working on Marionette
+code in <doc/CodeStyle.md>, which we highly recommend you read.
+There is more development documentation archived under <doc/index.rst>
+that you may peruse at your convenience.
 
 [ask questions]: #communication
 [reach out to us]: #communication
 [mozilla-central]: https://searchfox.org/mozilla-central/source/testing/marionette/
 [good first bugs]: https://www.joshmatthews.net/bugsahoy/?automation=1&js=1
-[Developer Guide]: https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide
 
 
 Building
 --------
 
-As Marionette is built in to Firefox and ships with official Firefox
-releases, it is included in a normal Firefox build.  To get your
-development environment set up you can run this command on any
-system and follow the on-screen instructions:
+Marionette is built in to Firefox and ships in the official
+Firefox binary.  As Marionette is written in [XPCOM] flavoured
+JavaScript, you may choose to rely on so called [artifact builds],
+which will download pre-compiled Firefox blobs to your computer.
+This means you don’t have to compile Firefox locally, but does
+come at the cost of having a good internet connection.  To enable
+[artifact builds] you may choose ‘Firefox for Desktop Artifact
+Mode’ when bootstrapping.
+
+Once you have a clone of [mozilla-unified], you can set up your
+development environment by running this command and following the
+on-screen instructions:
 
 	% ./mach bootstrap
 
-As Marionette is written in [XPCOM] flavoured JavaScript, you may
-choose to rely on so called [artifact builds], which will download
-pre-compiled Firefox blobs to your computer.  This means you don’t
-have to compile Firefox locally, but does come at the cost of having
-a good internet connection.  To enable [artifact builds] you may
-add this line to the _mozconfig_ file in your top source directory:
-
-	ac_add_options --enable-artifact-builds
-
 To perform a regular build, simply do:
 
 	% ./mach build
 
 You can clean out the objdir using this command:
 
 	% ./mach clobber
 
@@ -98,75 +95,76 @@ builds when this happens you may optiona
 
 If you compile Firefox frequently you will also want to enable
 [ccache] and [sccache] if you develop on a macOS or Linux system:
 
 	mk_add_options 'export RUSTC_WRAPPER=sccache'
 	mk_add_options 'export CCACHE_CPP2=yes'
 	ac_add_options --with-ccache
 
+[mozilla-unified]: https://hg.mozilla.org/mozilla-unified/
 [artifact builds]: https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Build_Instructions/Artifact_builds
 [ccache]: https://ccache.samba.org/
 [sccache]: https://github.com/mozilla/sccache
 
 
 Running tests
 -------------
 
 We verify and test Marionette in a couple of different ways.
 Marionette has a set of [xpcshell] unit tests located in
 _testing/marionette/test_*.js_.  These can be run this way:
 
-	% ./mach xpcshell-test testing/marionette/test_error.js
+	% ./mach test testing/marionette/test_*.js
 
 Because tests are run in parallell and xpcshell itself is quite
 chatty, it can sometimes be useful to run the tests sequentially:
 
-	% ./mach xpcshell-test --sequential testing/marionette/test_error.js
+	% ./mach test --sequential testing/marionette/test_error.js
 
 These unit tests run as part of the _X_ jobs on Treeherder.
 
 We also have a set of functional tests that make use of the Marionette
 Python client.  These start a Firefox process and tests the Marionette
 protocol input and output.  The following command will run all tests:
 
-	% ./mach marionette test
+	% ./mach test mn
 
 But you can also run individual tests:
 
-	% ./mach marionette test testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py 
+	% ./mach test testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py
 
 When working on Marionette code it is often useful to surface the
 stdout from Firefox:
 
-	% ./mach marionette test --gecko-log -
+	% ./mach test --gecko-log - TEST
 
 It is common to use this in conjunction with an option to increase
 the Marionette log level:
 
-	% ./mach marionette test --gecko-log - -vv
+	% ./mach test --gecko-log - -vv TEST
 
 A single `-v` enables debug logging, and a double `-vv` enables
 trace logging.
 
 As these are functional integration tests and pop up Firefox windows
 sporadically, a helpful tip is to surpress the window whilst you
 are running them by using Firefox’ [headless mode]:
 
-	% ./mach marionette test --headless
+	% ./mach test -z TEST
 
-It is equivalent to setting the `MOZ_HEADLESS` output variable.
-In addition to `MOZ_HEADLESS` there is also `MOZ_HEADLESS_WIDTH` and
-`MOZ_HEADLESS_HEIGHT` for controlling the dimensions of the no-op
-virtual display.  This is similar to using xvfb(1) which you may
-know from the X windowing system, but has the additional benefit
-of also working on macOS and Windows.
+`-z` is an alias for `--headless` and equivalent to setting the
+`MOZ_HEADLESS` output variable.  In addition to `MOZ_HEADLESS`
+there is also `MOZ_HEADLESS_WIDTH` and `MOZ_HEADLESS_HEIGHT` for
+controlling the dimensions of the no-op virtual display.  This is
+similar to using xvfb(1) which you may know from the X windowing system,
+but has the additional benefit of also working on macOS and Windows.
 
 We have a separate page documenting how to write good Python tests in
-[[doc/PythonTests.md]].  These tests will run as part of the _Mn_
+<doc/PythonTests.md>.  These tests will run as part of the _Mn_
 job on Treeherder.
 
 In addition to these two test types that specifically test the
 Marionette protocol, Marionette is used as the backend for the
 [geckodriver] WebDriver implementation.  It is served by a WPT test
 suite which effectively tests conformance to the W3C specification.
 
 This is a good try syntax to use when testing Marionette changes:
@@ -186,16 +184,17 @@ sending them to [MozReview].
 
 Once you have contributed a couple of patches, we are happy to
 sponsor you in [becoming a Mozilla committer].  When you have been
 granted commit access level 1 you will have permission to use the
 [Firefox CI] to trigger your own “try runs” to test your changes.
 
 [MozReview]: http://mozilla-version-control-tools.readthedocs.io/en/latest/mozreview.html
 [becoming a Mozilla committer]: https://www.mozilla.org/en-US/about/governance/policies/commit/
+[Firefox CI]: https://treeherder.mozilla.org/
 
 
 Communication
 =============
 
 The mailing list for Marionette discussion is
 tools-marionette@lists.mozilla.org ([subscribe], [archive]).
 
--- a/testing/marionette/README.md
+++ b/testing/marionette/README.md
@@ -36,20 +36,22 @@ Clients
 
 Clients may be implemented in any language that is capable of writing
 and receiving data over TCP socket.  A [reference client] is provided.
 Clients may be implemented both synchronously and asynchronously,
 although the latter is impossible in protocol levels 2 and earlier
 due to the lack of message sequencing.
 
 
-Bugs
-====
+Contributing
+============
 
-Bugs are tracked in the `Testing :: Marionette` component.
+If you want to help improve Marionette, we have more information in
+<CONTRIBUTING.md>.  You will find additional development documentation at
+<https://firefox-source-docs.mozilla.org/testing/marionette/marionette>.
 
 
 Communication
 =============
 
 The mailing list for discussion is tools-marionette@lists.mozilla.org
 ([subscribe], [archive]).  If you prefer real-time chat, there
 is often someone in the #ateam IRC channel on irc.mozilla.org.
new file mode 100644
--- /dev/null
+++ b/testing/marionette/doc/CodeStyle.md
@@ -0,0 +1,262 @@
+Style guide for source code
+===========================
+
+Like other projects, we also have some guidelines to keep to the code.
+For the overall Marionette project, a few rough rules are:
+
+  * Make your code readable and sensible, and don’t try to be
+    clever.  Prefer simple and easy solutions over more convoluted
+    and foreign syntax.
+
+  * Fixing style violations whilst working on a real change as a
+    preparatory clean-up step is good, but otherwise avoid useless
+    code churn for the sake of conforming to the style guide.
+
+  * Code is mutable and not written in stone.  Nothing that
+    is checked in is sacred and we encourage change to make
+    testing/marionette a pleasant ecosystem to work in.
+
+
+JavaScript
+----------
+
+Marionette is written in [XPCOM] flavoured JavaScript and ships
+as part of Firefox.  We have access to all the latest ECMAScript
+features currently in development, usually before it ships in the
+wild and we try to make use of new features when appropriate,
+especially when they move us off legacy internal replacements
+(such as Promise.jsm and Task.jsm).
+
+One of the peculiarities of working on JavaScript code that ships as
+part of a runtime platform is, that unlike in a regular web document,
+we share a single global state with the rest of Firefox.  This means
+we have to be responsible and not leak resources unnecessarily.
+
+JS code in Gecko is organised into _modules_ carrying _.js_ or _.jsm_
+file extensions.  Depending on the area of Gecko you’re working on,
+you may find they have different techniques for exporting symbols,
+varying indentation and code style, as well as varying linting
+requirements.
+
+To export symbols to other Marionette modules, remember to assign
+your exported symbols to the shared global `this`:
+
+	this.EXPORTED_SYMBOLS = ["PollPromise", "TimedPromise"];
+
+When importing symbols in Marionette code, try to be specific about
+what you need:
+
+	const {TimedPromise} = Cu.import("chrome://marionette/content/sync.js", {});
+
+The [linter] will complain if you import a named symbol that is
+not in use.  If however you _need_ to import every symbol, you can:
+
+	const wait = {};
+	Cu.import("chrome://marionette/content/sync.js", wait);
+
+	wait.sleep(42);
+	await wait.TimedPromise(…);
+
+We prefer object assignment shorthands when redefining names,
+for example when you use functionality from the `Components` global:
+
+	const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+When using symbols by their own name, the assignment name can be
+omitted:
+
+	const {TYPE_ONE_SHOT, TYPE_REPEATING_SLACK} = Ci.nsITimer;
+
+In addition to the default [Mozilla eslint rules], we have [our
+own specialisations] that are stricter and enforce more security.
+A few notable examples are that we disallow fallthrough `case`
+statements unless they are explicitly grouped together:
+
+	switch (x) {
+	  case "foo":
+	    doSomething();
+
+	  case "bar":  // <-- disallowed!
+	    doSomethingElse();
+	    break;
+
+	  case "baz":
+	  case "bah":  // <-- allowed (-:
+	    doCrazyThings();
+	}
+
+We disallow the use of `var`, for which we always prefer `let` and
+`const` as replacements.  Do be aware that `const` does not mean
+that the variable is immutable: just that it cannot be reassigned.
+We require all lines to end with semicolons, disallow construction
+of plain `new Object()`, require variable names to be camel-cased,
+and complain about unused variables.
+
+For purely aesthetic reasons we indent our code with two spaces,
+which includes switch-statement `case`s, and limit the maximum
+line length to 78 columns.  When you need to wrap a statement to
+the next line, the second line is indented with four spaces, like this:
+
+	throw new TypeError(
+	    "Expected an element or WindowProxy, " +
+	    pprint`got: ${el}`);
+
+This is not normally something you have to think to deeply about as
+it is enforced by the [linter].  The linter also has an automatic
+mode that fixes and formats certain classes of style violations.
+
+If you find yourself struggling to fit a long statement on one line,
+this is usually an indication that it is too long and should be
+split into multiple lines.  This is also a helpful tip to make the
+code easier to read.  Assigning transitive values to descriptive
+variable names can serve as self-documentation:
+
+	let location = event.target.documentURI || event.target.location.href;
+	log.debug(`Received DOM event ${event.type} for ${location}`);
+
+On the topic of variable naming the opinions are as many as programmers
+writing code, but it is often helpful to keep the input and output
+arguments to functions descriptive (longer), and let transitive
+internal values to be described more succinctly:
+
+	/** Prettifies instance of Error and its stacktrace to a string. */
+	function stringify(error) {
+	  try {
+	    let s = error.toString();
+	    if ("stack" in error) {
+	      s += "\n" + error.stack;
+	    }
+	    return s;
+	  } catch (e) {
+	    return "<unprintable error>";
+	  }
+	}
+
+When we can, we try to extract the relevant object properties in
+the arguments to an event handler or a function:
+
+	const responseListener = ({name, target, json, data}) => { … };
+
+Instead of:
+
+	const responseListener = msg => {
+	  let name = msg.name;
+	  let target = msg.target;
+	  let json = msg.json;
+	  let data = msg.data;
+	  …
+	};
+
+All source files should have `"use strict";` as the first directive
+so that the file is parsed in [strict mode].
+
+Every source code file that ships as part of the Firefox bundle
+must also have a [copying header], such as this:
+
+	/* 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/. */
+
+New xpcshell test files _should not_ have a license header as all
+new Mozilla tests should be in the [public domain] so that they can
+easily be shared with other browser vendors.  We want to re-license
+existing tests covered by the [MPL] so that they can be shared.
+We very much welcome your help in doing version control archeology
+to make this happen!
+
+The practical details of working on the Marionette code is outlined
+in [CONTRIBUTING.md], but generally you do not have to re-build
+Firefox when changing code.  Any change to testing/marionette/*.js
+will be picked up on restarting Firefox.  The only notable exception
+is testing/marionette/components/marionette.js, which does require
+a re-build.
+
+[XPCOM]: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM
+[strict mode]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
+[our own specialisations]: https://searchfox.org/mozilla-central/source/testing/marionette/.eslintrc.js
+[linter]: #linting
+[copying header]: https://www.mozilla.org/en-US/MPL/headers/
+[public domain]: https://creativecommons.org/publicdomain/zero/1.0/
+[MPL]: https://www.mozilla.org/en-US/MPL/2.0/
+[CONTRIBUTING.md]: ../CONTRIBUTING.md
+
+
+Python
+------
+
+TODO
+
+
+Documentation
+-------------
+
+We keep our documentation in-tree under [testing/marionette/doc]
+and [testing/geckodriver/doc].  Updates and minor changes to
+documentation should ideally not be scrutinised to the same degree
+as code changes to encourage frequent updates so that the documentation
+does not go stale.  To that end, documentation changes with `r=me`
+from module peers are permitted.
+
+Use fmt(1) or an equivalent editor specific mechanism (such as Meta-Q
+in Emacs) to format paragraphs at a maximum width of 75 columns
+with a goal of roughly 65.  This is equivalent to `fmt -w 75 -g 65`,
+which happens to be the default on BSD and macOS.
+
+We endeavour to document all _public APIs_ of the Marionette component.
+These include public functions—or command implementations—on
+the `GeckoDriver` class, as well as all exported symbols from
+other modules.  Documentation for non-exported symbols is not required.
+
+The API documentation can be regenerated to [testing/marionette/doc/api]
+so:
+
+	% ./mach marionette doc
+
+You can also serve the documentation in an HTTP server if you work
+remotely on another terminal:
+
+	% ./mach marionette doc --http 0.0.0.0:6060
+	serving at 0.0.0.0:6060
+
+Normally there is no need to regenerate the API documentation after you
+make a change.  ato’s [mnapigen] script runs regularly in a cron job.
+
+[Mozilla eslint rules]: ../../../.eslintrc.js
+[mnapigen]: https://git.sny.no/rc/tree/bin/mnapigen
+[testing/geckodriver/doc]: ../../geckodriver/doc
+[testing/marionette/doc/api]: api/
+[testing/marionette/doc]: .
+
+
+Linting
+-------
+
+Marionette consists mostly of JavaScript (server) and Python (client,
+harness, test runner) code.  We lint our code with [mozlint],
+which harmonises the output from [eslint] and [flake8].
+
+To run the linter with a sensible output:
+
+	% ./mach lint -funix testing/marionette
+
+For certain classes of style violations the eslint linter has
+an automatic mode for fixing and formatting your code.  This is
+particularly useful to keep to whitespace and indentation rules:
+
+	% ./mach eslint --fix testing/marionette
+
+The linter is also run as a try job (shorthand `ES`) which means
+any style violations will automatically block a patch from landing
+(if using Autoland) or cause your changeset to be backed out (if
+landing directly on mozilla-inbound).
+
+If you use git(1) you can [enable automatic linting] before you push
+to a remote through a pre-push (or pre-commit) hook.  This will
+run the linters on the changed files before a push and abort if
+there are any problems.  This is convenient for avoiding a try run
+failing due to a stupid linting issue.
+
+[mozlint]: https://firefox-source-docs.mozilla.org/tools/lint/usage.html
+[eslint]: https://eslint.org/
+[flake8]: http://flake8.pycqa.org/en/latest/
+[enable automatic linting]: https://firefox-source-docs.mozilla.org/tools/lint/usage.html#using-a-vcs-hook
--- a/testing/marionette/doc/index.rst
+++ b/testing/marionette/doc/index.rst
@@ -19,43 +19,46 @@ properties and attributes of the DOM.
 
 
 For users
 =========
 
 .. toctree::
    :maxdepth: 1
 
+   `Marionette README`_
    Intro.md
    Protocol.md
 
-
 See also:
 
-* Documentation for `Marionette Python client`_., which is used
+* Documentation for `Marionette Python client`_, which is used
   in-tree to write many kinds of Marionette-based tests.
 * Documentation for `Firefox Puppeteer`_, which is used to in-tree
   to write Firefox UI tests.
 
+.. _Marionette README: https://searchfox.org/mozilla-central/source/testing/marionette/README.md
 .. _Marionette Python client: http://marionette-client.readthedocs.io
 .. _Firefox Puppeteer: http://firefox-puppeteer.readthedocs.io
 
 
 For developers
 ==============
 
 .. toctree::
    :maxdepth: 1
 
-   ../CONTRIBUTING.md
+   `Contributing to Marionette`_
    NewContributors.md
    Debugging.md
    PythonTests.md
    SeleniumAtoms.md
 
+.. _Contributing to Marionette: https://searchfox.org/mozilla-central/source/testing/marionette/CONTRIBUTING.md
+
 
 Bugs
 ====
 
 Bugs are tracked in the `Testing :: Marionette` component.
 
 
 Communication