nsresult
HTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                            nsIContent* aBindingParent,
                            bool aCompileEventHandlers)
{
  Link::ResetLinkState(false, Link::ElementHasHref());

  nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                 aBindingParent,
                                                 aCompileEventHandlers);
  NS_ENSURE_SUCCESS(rv, rv);

  // Link must be inert in ShadowRoot.
  if (aDocument && !GetContainingShadow()) {
    aDocument->RegisterPendingLinkUpdate(this);
  }

  if (IsInComposedDoc()) {
    UpdatePreconnect();
    if (HasDNSPrefetchRel()) {
      TryDNSPrefetch();
    }
  }

  void (HTMLLinkElement::*update)() = &HTMLLinkElement::UpdateStyleSheetInternal;
  nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, update));

  void (HTMLLinkElement::*updateImport)() = &HTMLLinkElement::UpdateImport;
  nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, updateImport));

  CreateAndDispatchEvent(aDocument, NS_LITERAL_STRING("DOMLinkAdded"));

  return rv;
}
nsresult
HTMLAnchorElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
                                const nsAttrValue* aValue,
                                const nsAttrValue* aOldValue,
                                nsIPrincipal* aSubjectPrincipal,
                                bool aNotify)
{
  if (aNamespaceID == kNameSpaceID_None) {
    if (aName == nsGkAtoms::href) {
      Link::ResetLinkState(aNotify, !!aValue);
      if (aValue && IsInComposedDoc()) {
        TryDNSPrefetch();
      }
    }
  }

  return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName,
                                            aValue, aOldValue, aSubjectPrincipal, aNotify);
}
nsresult
HTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                              nsIContent* aBindingParent,
                              bool aCompileEventHandlers)
{
  Link::ResetLinkState(false, Link::ElementHasHref());

  nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                 aBindingParent,
                                                 aCompileEventHandlers);
  NS_ENSURE_SUCCESS(rv, rv);

  // Prefetch links
  nsIDocument* doc = GetComposedDoc();
  if (doc) {
    doc->RegisterPendingLinkUpdate(this);
    TryDNSPrefetch();
  }

  return rv;
}
nsresult
HTMLLinkElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                              const nsAttrValue* aValue, bool aNotify)
{
  // It's safe to call ResetLinkState here because our new attr value has
  // already been set or unset.  ResetLinkState needs the updated attribute
  // value because notifying the document that content states have changed will
  // call IntrinsicState, which will try to get updated information about the
  // visitedness from Link.
  if (aName == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
    bool hasHref = aValue;
    Link::ResetLinkState(!!aNotify, hasHref);
    if (IsInUncomposedDoc()) {
      CreateAndDispatchEvent(OwnerDoc(), NS_LITERAL_STRING("DOMLinkChanged"));
    }
  }

  if (aValue) {
    if (aNameSpaceID == kNameSpaceID_None &&
        (aName == nsGkAtoms::href ||
         aName == nsGkAtoms::rel ||
         aName == nsGkAtoms::title ||
         aName == nsGkAtoms::media ||
         aName == nsGkAtoms::type)) {
      bool dropSheet = false;
      if (aName == nsGkAtoms::rel) {
        nsAutoString value;
        aValue->ToString(value);
        uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(value,
                                                                NodePrincipal());
        if (GetSheet()) {
          dropSheet = !(linkTypes & nsStyleLinkElement::eSTYLESHEET);
        } else if (linkTypes & eHTMLIMPORT) {
          UpdateImport();
        } else if ((linkTypes & ePRECONNECT) && IsInComposedDoc()) {
          UpdatePreconnect();
        }
      }

      if (aName == nsGkAtoms::href) {
        UpdateImport();
        if (IsInComposedDoc()) {
          UpdatePreconnect();
        }
      }

      if ((aName == nsGkAtoms::rel || aName == nsGkAtoms::href) &&
          HasDNSPrefetchRel() && IsInComposedDoc()) {
        TryDNSPrefetch();
      }

      UpdateStyleSheetInternal(nullptr, nullptr,
                               dropSheet ||
                               (aName == nsGkAtoms::title ||
                                aName == nsGkAtoms::media ||
                                aName == nsGkAtoms::type));
    }
  } else {
    // Since removing href or rel makes us no longer link to a
    // stylesheet, force updates for those too.
    if (aNameSpaceID == kNameSpaceID_None) {
      if (aName == nsGkAtoms::href ||
          aName == nsGkAtoms::rel ||
          aName == nsGkAtoms::title ||
          aName == nsGkAtoms::media ||
          aName == nsGkAtoms::type) {
        UpdateStyleSheetInternal(nullptr, nullptr, true);
      }
      if (aName == nsGkAtoms::href ||
          aName == nsGkAtoms::rel) {
        UpdateImport();
      }
    }
  }

  return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
                                            aNotify);
}