static Directionality
GetDirectionFromText(const nsTextFragment* aFrag,
                     uint32_t* aFirstStrong = nullptr)
{
  if (aFrag->Is2b()) {
    return GetDirectionFromText(aFrag->Get2b(), aFrag->GetLength(),
                                   aFirstStrong);
  }

  return GetDirectionFromText(aFrag->Get1b(), aFrag->GetLength(),
                                 aFirstStrong);
}
/**
 * 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;
}
/**
 * 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;
}
void
SetDirectionalityFromValue(Element* aElement, const nsAString& value,
                           bool aNotify)
{
  Directionality dir = GetDirectionFromText(PromiseFlatString(value).get(),
                                            value.Length());
  if (dir == eDir_NotSet) {
    dir = eDir_LTR;
  }

  aElement->SetDirectionality(dir, aNotify);
}
void
ResetDirectionSetByTextNode(nsTextNode* aTextNode)
{
  if (!NodeAffectsDirAutoAncestor(aTextNode)) {
    return;
  }

  Directionality dir = GetDirectionFromText(aTextNode->GetText());
  if (dir != eDir_NotSet && aTextNode->HasTextNodeDirectionalityMap()) {
    nsTextNodeDirectionalityMap::ResetTextNodeDirection(aTextNode);
  }
}
void
SetDirectionFromNewTextNode(nsTextNode* aTextNode)
{
  if (!NodeAffectsDirAutoAncestor(aTextNode)) {
    return;
  }

  Directionality dir = GetDirectionFromText(aTextNode->GetText());
  if (dir != eDir_NotSet) {
    SetAncestorDirectionIfAuto(aTextNode, dir);
  }
}
void
SetDirectionFromChangedTextNode(nsIContent* aTextNode, uint32_t aOffset,
                                const PRUnichar* aBuffer, uint32_t aLength,
                                bool aNotify)
{
  if (!NodeAffectsDirAutoAncestor(aTextNode)) {
    return;
  }

  uint32_t firstStrong;
  Directionality oldDir = GetDirectionFromText(aTextNode->GetText(),
                                               &firstStrong);
  if (aOffset > firstStrong) {
    return;
  }

  Directionality newDir = GetDirectionFromText(aBuffer, aLength);
  if (newDir == eDir_NotSet) {
    if (oldDir != eDir_NotSet && aTextNode->HasTextNodeDirectionalityMap()) {
      // This node used to have a strong directional character but no
      // longer does. ResetTextNodeDirection() will re-resolve the
      // directionality of any elements whose directionality was
      // determined by this node.
      nsTextNodeDirectionalityMap::ResetTextNodeDirection(aTextNode);
    }
  } else {
    // This node has a strong directional character. If it has a
    // TextNodeDirectionalityMap property, it already determines the
    // directionality of some element(s), so call UpdateTextNodeDirection to
    // reresolve their directionality. Otherwise call
    // SetAncestorDirectionIfAuto to find ancestor elements which should
    // have their directionality determined by this node.
    if (aTextNode->HasTextNodeDirectionalityMap()) {
      nsTextNodeDirectionalityMap::UpdateTextNodeDirection(aTextNode, newDir);
    } else {
      SetAncestorDirectionIfAuto(aTextNode, newDir, aNotify);
    }
  }
}