Merge inbound to mozilla-central r=merge a=merge
authorarthur.iakab <aiakab@mozilla.com>
Wed, 03 Jan 2018 11:49:44 +0200
changeset 715271 ac93fdadf1022211eec62258ad22b42cb37a6d14
parent 715261 66a4f3cefc640b94866c98c2dd5f4741d313ba44 (current diff)
parent 715270 ca70091cdc76eead763e92eda4dcb939a277f62d (diff)
child 715272 012bb9c322ecda93be32f43582492b37e407396d
child 715277 f00f889b97adbea9b9214d912e66194fca66c178
child 715283 5d1b8569a1383ec6ca11a1978f5222a7d15e3083
child 715286 c60b31f691665a058cf26a64f85d8d6db9c5351c
child 715288 5e660bb48526dff3053545b85c764140414a1fd6
child 715289 79191c7a53bc7688f3d3c53ca7120256430e00fb
child 715290 8d7e14cc55c618084979d653bd6ff599a47e77ca
child 715295 c5775459d6155087687af9db401abce865d7a287
child 715321 a8cea025311880c5f50bb1a44ba2fed2ba93e5dc
child 715322 83f48c5476bc9719774b5a6bd2c7fe18faf8875e
child 715329 570da206a931f00fc0b42d86aba34fe945a3b1e0
child 715330 d47b3a0774f76ad0983741b1862186ab49e20d37
child 715333 a196b83c871408284cfa2778b9f7c5f091dc4850
child 715335 9592f0eb2d34bfc3d3d1b2a4531524f6b09d8616
child 715337 8d6949ab317f71601f4c282b979ec72c82bb8ca0
child 715345 74c81f452381153f20a7d33fb21885faae4b312c
child 715350 cac912efcd6fdc2b10e5463ed80367b1ae48fe73
child 715351 eaee31db3cbba80f073280dff57c22d0aea9a540
child 715358 0a223ea1523efbf3af8f141f2d2ae81eb27e836d
child 715359 d67deb203c67021bef3a9a73547ea320dae4d04d
child 715360 a11519959cbe5be50954de9cc39ac3099f53ed2b
child 715361 48eb42b61c8a50debab3ad27813fc833fbd52bd7
child 715367 0f26775a035a539ec0298c3223cde01e84dcf94e
child 715369 d221039e2509428a1bfad1ece1fea797dcf19d51
child 715370 4564b7fa9837ccb8e2e9c99f706a4fa4cc95faa0
child 715375 41e4dd8f635d59d3e5211b627ab125642035583b
child 715438 17a50e850061dfcc7981855417a16189ad966c97
child 715440 03d3d4a998649fb282e34cb3fad462860adf31fc
child 715455 6c53729ae4b07c67d9e3de99c12e1165a85ffaac
child 715467 d7d644761b0982a4f16317720ef8ac7262d1e04d
child 715507 0ce268bf70cbb1ebd8a43c4e999725cb81b09c82
child 715523 3c447614bf9c7a26ccd32563aeee51c9fc7eaeee
child 715554 f599d517d5845852eee8efd39ee44d37599f6100
child 715719 762520fce34f425b192dd6a18307a5a5252bb558
child 715749 e0620bbeeba5df5c1d3d9a38acb627ae17d7cf1c
child 715750 48aee2515f06b191830c356d43e8fcb943d69396
child 715761 43bfc07ed2fade466aecd333bc4bb874c257938d
child 715767 e8c8d7cad0725222bcbf92eb3c7e021e3d584536
child 715775 2c33aea07cbf1fcdd92c3ac725d3e6726f1c4799
child 715776 493cbcdfeebb8e155dd681a6af9942d2d5b833cf
child 715777 daa2750c3a54b09cf894b990d9cf66fd3a4ac621
child 715793 9fe886c7a794a318278f204f1912034d4d7f0674
child 715796 36952756950a4c172c359c67938185c3e4c7859e
child 715822 71a865006ca99ce0e891283bceb68a9c7f3b8b47
child 715823 447f6e454994a69a5dfcb6b3fd662e0709d3e047
child 715824 b088599866276c9620a2fb1af9e323506cc3445b
child 715826 858ea6b4daf22e36419cb583e277cbc33d887a2e
child 715994 bf924d8c6c1b939ff88fb152a1f35d0a1aa75265
child 715995 2f39bd78f65c93707dd532deb4cf6e5ec0aa353a
child 715996 8ad4df006bda7b2c36d4f8910d413fab365536f1
child 715997 1ecd586631888e05512de0326207ed423b8345ca
child 716019 4da60a9f6cb65f55ffd165a807664cd0999a45b5
child 716352 c1b75d855123745fd73b79af752075da1f2bf0f1
child 716354 588e3036ddc6185bf8d0e01488531db47be95014
child 716361 31c83f38c0dd4f498dc7496e33b122e7a8014d6c
child 716425 d9cc65c7e0f4c6784e3d75b89a77bd76267ec9b4
child 716459 500ffce719b16955cd2fc54112d01f7c573bc13f
child 716470 a06d9ecafafa10dba17d53d8705392f33ef93310
child 716471 e88123aebad87eb135ac68475d05e6fa5f61f331
child 716852 6444a0275f78cfc6f3af684f2f21f04fe2cd7afa
child 717016 e3d1e6b46f823dd9151e762e3a7c066083af214a
child 717156 d70ac6a3f5ff4a03747dd705ac00b4dbc9c136da
child 717197 a2b4d681ee3dbec82a523b39aff6d9e2e69d42f5
child 717376 7d9b9ff9a21f7a3866a5ba6252b9a22266a2d3a4
child 717377 b5d215bbe2c5b4d7a7a57aa9780b127f14694a66
child 717382 5e3f74ad3f3b34c54fb6a1e582b509e0c7520b24
child 717386 addca06954087e82e78bf19856357918050ac641
child 717391 84efa04f44d7998bc355f44894d354d26a46c3ac
child 717886 59ada760cfb15dae4ed94cfa0e90c3565d35d046
child 717966 a9ca740946f49c78133f05f6fe01db4145ae4931
child 717968 ae386a6bae06dedce191a12f6ee79cd48961bfd9
child 717975 c352c0b0e67bf3e248215e3924ee17db496e44ec
child 718605 c4172270d94af3eac4d084b51efa24debc78fa55
child 719279 fceda356bf812d80d81f4e35cbc3f74ad6151d77
child 719305 093890315d2264ca835bc7ed04eae281260f9c47
child 719951 f3d405368678b8a7afdaf8a839a19ced0f4ca985
child 720433 2b05d8ee9ef062b1e574b6710561f65c7b91c6ae
child 720434 9b779b22647f071f281f8aa7422eecd28acd7642
child 720549 58952698f3a0547b2273eabbb3603929583f8a1c
child 720985 df4f2a48ecbfda679a715c6e07ffd013a0beaad5
child 720986 e2c0b843c64820131ce6192b5628925c3536403a
child 722125 19cc6db7c56e4e056b3c56ecdc4cc52a71a19572
child 749568 75a88805da07464023e3d1a6a83cc8695309457a
child 750741 a6970f3833e00df43c06728524d41cd51c10dee9
push id94114
push userbmo:jeremy.lempereur@gmail.com
push dateWed, 03 Jan 2018 11:59:20 +0000
reviewersmerge, merge
milestone59.0a1
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