void
nsDOMAttributeMap::DropAttribute(int32_t aNamespaceID, nsIAtom* aLocalName)
{
  nsAttrKey attr(aNamespaceID, aLocalName);
  Attr *node = mAttributeCache.GetWeak(attr);
  if (node) {
    // Break link to map
    node->SetMap(nullptr);

    // Remove from cache
    mAttributeCache.Remove(attr);
  }
}
already_AddRefed<Attr>
nsDOMAttributeMap::SetNamedItemInternal(Attr& aAttr,
                                        bool aWithNS,
                                        ErrorResult& aError)
{
  NS_ENSURE_TRUE(mContent, nullptr);

  // XXX should check same-origin between mContent and aAttr however
  // nsContentUtils::CheckSameOrigin can't deal with attributenodes yet

  // Check that attribute is not owned by somebody else
  nsDOMAttributeMap* owner = aAttr.GetMap();
  if (owner) {
    if (owner != this) {
      aError.Throw(NS_ERROR_DOM_INUSE_ATTRIBUTE_ERR);
      return nullptr;
    }

    // setting a preexisting attribute is a no-op, just return the same
    // node.
    nsRefPtr<Attr> attribute = &aAttr;
    return attribute.forget();
  }

  nsresult rv;
  if (!mContent->HasSameOwnerDoc(&aAttr)) {
    nsCOMPtr<nsINode> adoptedNode =
      mContent->OwnerDoc()->AdoptNode(aAttr, aError);
    if (aError.Failed()) {
      return nullptr;
    }

    NS_ASSERTION(adoptedNode == &aAttr, "Uh, adopt node changed nodes?");
  }

  // Get nodeinfo and preexisting attribute (if it exists)
  nsAutoString name;
  nsCOMPtr<nsINodeInfo> ni;

  nsRefPtr<Attr> attr;
  // SetNamedItemNS()
  if (aWithNS) {
    // Return existing attribute, if present
    ni = aAttr.NodeInfo();

    if (mContent->HasAttr(ni->NamespaceID(), ni->NameAtom())) {
      attr = RemoveAttribute(ni);
    }
  } else { // SetNamedItem()
    aAttr.GetName(name);

    // get node-info of old attribute
    ni = mContent->GetExistingAttrNameFromQName(name);
    if (ni) {
      attr = RemoveAttribute(ni);
    }
    else {
      if (mContent->IsInHTMLDocument() &&
          mContent->IsHTML()) {
        nsContentUtils::ASCIIToLower(name);
      }

      rv = mContent->NodeInfo()->NodeInfoManager()->
        GetNodeInfo(name, nullptr, kNameSpaceID_None,
                    nsIDOMNode::ATTRIBUTE_NODE, getter_AddRefs(ni));
      if (NS_FAILED(rv)) {
        aError.Throw(rv);
        return nullptr;
      }
      // value is already empty
    }
  }

  nsAutoString value;
  aAttr.GetValue(value);

  // Add the new attribute to the attribute map before updating
  // its value in the element. @see bug 364413.
  nsAttrKey attrkey(ni->NamespaceID(), ni->NameAtom());
  mAttributeCache.Put(attrkey, &aAttr);
  aAttr.SetMap(this);

  rv = mContent->SetAttr(ni->NamespaceID(), ni->NameAtom(),
                         ni->GetPrefixAtom(), value, true);
  if (NS_FAILED(rv)) {
    aError.Throw(rv);
    DropAttribute(ni->NamespaceID(), ni->NameAtom());
  }

  return attr.forget();
}