inline static bool NodeAffectsDirAutoAncestor(nsINode* aTextNode)
{
  Element* parent = aTextNode->GetElementParent();
  return (parent &&
          !DoesNotParticipateInAutoDirection(parent) &&
          parent->NodeOrAncestorHasDirAuto());
}
/**
 * Set the directionality of a node with dir=auto as defined in
 * http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#the-directionality
 *
 * @return the text node containing the character that determined the direction
 */
static nsINode*
WalkDescendantsSetDirectionFromText(Element* aElement, bool aNotify = true)
{
  MOZ_ASSERT(aElement, "aElement is null");
  if (DoesNotParticipateInAutoDirection(aElement)) {
    return nullptr;
  }

  nsIContent* child = aElement->GetFirstChild();
  while (child) {
    if (child->IsElement() &&
        DoesNotAffectDirectionOfAncestors(child->AsElement())) {
      child = child->GetNextNonChildNode(aElement);
      continue;
    }

    if (child->NodeType() == nsIDOMNode::TEXT_NODE) {
      Directionality textNodeDir = GetDirectionFromText(child->GetText());
      if (textNodeDir != eDir_NotSet) {
        // We found a descendant text node with strong directional characters.
        // Set the directionality of aElement to the corresponding value.
        aElement->SetDirectionality(textNodeDir, aNotify);
        return child;
      }
    }
    child = child->GetNextNode(aElement);
  }

  // We walked all the descendants without finding a text node with strong
  // directional characters. Set the directionality to LTR
  aElement->SetDirectionality(eDir_LTR, aNotify);
  return nullptr;
}
/**
 * Returns true if aElement is one of the element whose text content should not
 * affect the direction of ancestors with dir=auto (though it may affect its own
 * direction, e.g. <bdi>)
 */
static bool
DoesNotAffectDirectionOfAncestors(const Element* aElement)
{
  return (DoesNotParticipateInAutoDirection(aElement) ||
          aElement->IsHTML(nsGkAtoms::bdi) ||
          aElement->HasFixedDir());
}
/**
 * Set the directionality of a node with dir=auto as defined in
 * http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#the-directionality
 *
 * @param[in] aStartAfterNode as an optimization, a caller may pass in a node
 *            from which to begin walking the descendants of aElement, if it is
 *            known that all text nodes before this node do not contain any
 *            strong directional characters
 * @return the text node containing the character that determined the direction
 */
static nsINode*
WalkDescendantsSetDirectionFromText(Element* aElement, bool aNotify = true,
                                       nsINode* aStartAfterNode = nullptr)
{
  MOZ_ASSERT(aElement, "aElement is null");

  nsIContent* child;
  if (aStartAfterNode &&
      nsContentUtils::ContentIsDescendantOf(aStartAfterNode, aElement)) {
#ifdef DEBUG
    child = aElement->GetFirstChild();
    while (child && child != aStartAfterNode) {
      if (child->NodeType() == nsIDOMNode::TEXT_NODE) {
        MOZ_ASSERT(GetDirectionFromText(child->GetText()) == eDir_NotSet,
                   "Strong directional characters before aStartAfterNode");
      }
      child = child->GetNextNode(aElement);
    }
#endif
    child = aStartAfterNode->GetNextNode(aElement);
  } else {
    child = aElement->GetFirstChild();
  }

  while (child) {
    if (child->IsElement() &&
        (DoesNotParticipateInAutoDirection(child->AsElement()) ||
         child->NodeInfo()->Equals(nsGkAtoms::bdi) ||
         child->HasFixedDir())) {
      child = child->GetNextNonChildNode(aElement);
      continue;
    }

    if (child->NodeType() == nsIDOMNode::TEXT_NODE) {
      Directionality textNodeDir = GetDirectionFromText(child->GetText());
      if (textNodeDir != eDir_NotSet) {
        // We found a descendant text node with strong directional characters.
        // Set the directionality of aElement to the corresponding value.
        aElement->SetDirectionality(textNodeDir, aNotify);
        return child;
      }
    }
    child = child->GetNextNode(aElement);
  }

  // We walked all the descendants without finding a text node with strong
  // directional characters. Set the directionality to LTR
  aElement->SetDirectionality(eDir_LTR, aNotify);
  return nullptr;
}
void
SetDirOnBind(mozilla::dom::Element* aElement, nsIContent* aParent)
{
  // Set the AncestorHasDirAuto flag, unless this element shouldn't affect
  // ancestors that have dir=auto
  if (!DoesNotParticipateInAutoDirection(aElement) &&
      !aElement->IsHTML(nsGkAtoms::bdi) &&
      aParent && aParent->NodeOrAncestorHasDirAuto()) {
    aElement->SetAncestorHasDirAuto();

    nsIContent* child = aElement->GetFirstChild();
    if (child) {
      // If we are binding an element to the tree that already has descendants,
      // and the parent has NodeHasDirAuto or NodeAncestorHasDirAuto, we need
      // to set NodeAncestorHasDirAuto on all the element's descendants, except
      // for nodes that don't affect the direction of their ancestors.
      do {
        if (child->IsElement() &&
            DoesNotAffectDirectionOfAncestors(child->AsElement())) {
          child = child->GetNextNonChildNode(aElement);
          continue;
        }

        child->SetAncestorHasDirAuto();
        child = child->GetNextNode(aElement);
      } while (child);

      // We may also need to reset the direction of an ancestor with dir=auto
      WalkAncestorsResetAutoDirection(aElement, true);
    }
  }

  if (!aElement->HasDirAuto()) {
    // if the element doesn't have dir=auto, set its own directionality from
    // the dir attribute or by inheriting from its ancestors.
    RecomputeDirectionality(aElement, false);
  }
}
void SetAncestorDirectionIfAuto(nsINode* aTextNode, Directionality aDir,
                                bool aNotify = true)
{
  MOZ_ASSERT(aTextNode->NodeType() == nsIDOMNode::TEXT_NODE,
             "Must be a text node");

  Element* parent = aTextNode->GetElementParent();
  while (parent && parent->NodeOrAncestorHasDirAuto()) {
    if (DoesNotParticipateInAutoDirection(parent) || parent->HasFixedDir()) {
      break;
    }

    if (parent->HasDirAuto()) {
      bool resetDirection = false;

      if (!parent->HasDirAutoSet()) {
        // Fast path if parent's direction is not yet set by any descendant
        resetDirection = true;
      } else {
        // If parent's direction is already set, we need to know if
        // aTextNode is before or after the text node that had set it.
        // We will walk parent's descendants in tree order starting from
        // aTextNode to optimize for the most common case where text nodes are
        // being appended to tree.
        nsINode* directionWasSetByTextNode =
          static_cast<nsINode*>(parent->GetProperty(nsGkAtoms::dirAutoSetBy));
        if (!directionWasSetByTextNode) {
          resetDirection = true;
        } else if (directionWasSetByTextNode != aTextNode) {
          nsIContent* child = aTextNode->GetNextNode(parent);
          while (child) {
            if (child->IsElement() &&
                (DoesNotParticipateInAutoDirection(child->AsElement()) ||
                 child->NodeInfo()->Equals(nsGkAtoms::bdi) ||
                 child->HasFixedDir())) {
              child = child->GetNextNonChildNode(parent);
              continue;
            }

            if (child == directionWasSetByTextNode) {
              // we found the node that set the element's direction after our
              // text node, so we need to reset the direction
              resetDirection = true;
              nsTextNodeDirectionalityMap::RemoveElementFromMap(
                directionWasSetByTextNode, parent
              );
              break;
            }

            child = child->GetNextNode(parent);
          }
        }
      }

      if (resetDirection) {
        parent->SetDirectionality(aDir, aNotify);
        nsTextNodeDirectionalityMap::AddEntryToMap(aTextNode, parent);
        SetDirectionalityOnDescendants(parent, aDir, aNotify);
      }

      // Since we found an element with dir=auto, we can stop walking the
      // parent chain: none of its ancestors will have their direction set by
      // any of its descendants.
      return;
    }
    parent = parent->GetElementParent();
  }
}