LCOV - code coverage report
Current view: top level - dom/base - DirectionalityUtils.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 66 387 17.1 %
Date: 2017-07-14 16:53:18 Functions: 11 44 25.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : /*
       8             :   Implementation description from https://etherpad.mozilla.org/dir-auto
       9             : 
      10             :   Static case
      11             :   ===========
      12             :   When we see a new content node with @dir=auto from the parser, we set the
      13             :   NodeHasDirAuto flag on the node.  We won't have enough information to
      14             :   decide the directionality of the node at this point.
      15             : 
      16             :   When we bind a new content node to the document, if its parent has either of
      17             :   the NodeAncestorHasDirAuto or NodeHasDirAuto flags, we set the
      18             :   NodeAncestorHasDirAuto flag on the node.
      19             : 
      20             :   When a new input with @type=text/search/tel/url/email and @dir=auto is added
      21             :   from the parser, we resolve the directionality based on its @value.
      22             : 
      23             :   When a new text node with non-neutral content is appended to a textarea
      24             :   element with NodeHasDirAuto, if the directionality of the textarea element
      25             :   is still unresolved, it is resolved based on the value of the text node.
      26             :   Elements with unresolved directionality behave as LTR.
      27             : 
      28             :   When a new text node with non-neutral content is appended to an element that
      29             :   is not a textarea but has either of the NodeAncestorHasDirAuto or
      30             :   NodeHasDirAuto flags, we walk up the parent chain while the
      31             :   NodeAncestorHasDirAuto flag is present, and when we reach an element with
      32             :   NodeHasDirAuto and no resolved directionality, we resolve the directionality
      33             :   based on the contents of the text node and cease walking the parent chain.
      34             :   Note that we should ignore elements with NodeHasDirAuto with resolved
      35             :   directionality, so that the second text node in this example tree doesn't
      36             :   affect the directionality of the div:
      37             : 
      38             :   <div dir=auto>
      39             :     <span>foo</span>
      40             :     <span>بار</span>
      41             :   </div>
      42             : 
      43             :   The parent chain walk will be aborted if we hit a script or style element, or
      44             :   if we hit an element with @dir=ltr or @dir=rtl.
      45             : 
      46             :   I will call this algorithm "upward propagation".
      47             : 
      48             :   Each text node should maintain a list of elements which have their
      49             :   directionality determined by the first strong character of that text node.
      50             :   This is useful to make dynamic changes more efficient.  One way to implement
      51             :   this is to have a per-document hash table mapping a text node to a set of
      52             :   elements.  I'll call this data structure TextNodeDirectionalityMap. The
      53             :   algorithm for appending a new text node above needs to update this data
      54             :   structure.
      55             : 
      56             :   *IMPLEMENTATION NOTE*
      57             :   In practice, the implementation uses two per-node properties:
      58             : 
      59             :   dirAutoSetBy, which is set on a node with auto-directionality, and points to
      60             :   the textnode that contains the strong character which determines the
      61             :   directionality of the node.
      62             : 
      63             :   textNodeDirectionalityMap, which is set on a text node and points to a hash
      64             :   table listing the nodes whose directionality is determined by the text node.
      65             : 
      66             :   Handling dynamic changes
      67             :   ========================
      68             : 
      69             :   We need to handle the following cases:
      70             : 
      71             :   1. When the value of an input element with @type=text/search/tel/url/email is
      72             :   changed, if it has NodeHasDirAuto, we update the resolved directionality.
      73             : 
      74             :   2. When the dir attribute is changed from something else (including the case
      75             :   where it doesn't exist) to auto on a textarea or an input element with
      76             :   @type=text/search/tel/url/email, we set the NodeHasDirAuto flag and resolve
      77             :   the directionality based on the value of the element.
      78             : 
      79             :   3. When the dir attribute is changed from something else (including the case
      80             :   where it doesn't exist) to auto on any element except case 1 above and the bdi
      81             :   element, we run the following algorithm:
      82             :   * We set the NodeHasDirAuto flag.
      83             :   * If the element doesn't have the NodeAncestorHasDirAuto flag, we set the
      84             :   NodeAncestorHasDirAuto flag on all of its child nodes.  (Note that if the
      85             :   element does have NodeAncestorHasDirAuto, all of its children should
      86             :   already have this flag too.  We can assert this in debug builds.)
      87             :   * To resolve the directionality of the element, we run the algorithm explained
      88             :   in http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#the-dir-attribute
      89             :   (I'll call this the "downward propagation algorithm".) by walking the child
      90             :   subtree in tree order.  Note that an element with @dir=auto should not affect
      91             :   other elements in its document with @dir=auto.  So there is no need to walk up
      92             :   the parent chain in this case.  TextNodeDirectionalityMap needs to be updated
      93             :   as appropriate.
      94             : 
      95             :   3a. When the dir attribute is set to any valid value on an element that didn't
      96             :   have a valid dir attribute before, this means that any descendant of that
      97             :   element will not affect the directionality of any of its ancestors. So we need
      98             :   to check whether any text node descendants of the element are listed in
      99             :   TextNodeDirectionalityMap, and whether the elements whose direction they set
     100             :   are ancestors of the element. If so, we need to rerun the downward propagation
     101             :   algorithm for those ancestors.
     102             : 
     103             :   4.  When the dir attribute is changed from auto to something else (including
     104             :   the case where it gets removed) on a textarea or an input element with
     105             :   @type=text/search/tel/url/email, we unset the NodeHasDirAuto flag and
     106             :   resolve the directionality based on the directionality of the value of the @dir
     107             :   attribute on element itself or its parent element.
     108             : 
     109             :   5. When the dir attribute is changed from auto to something else (including the
     110             :   case where it gets removed) on any element except case 4 above and the bdi
     111             :   element, we run the following algorithm:
     112             :   * We unset the NodeHasDirAuto flag.
     113             :   * If the element does not have the NodeAncestorHasDirAuto flag, we unset
     114             :   the NodeAncestorHasDirAuto flag on all of its child nodes, except those
     115             :   who are a descendant of another element with NodeHasDirAuto.  (Note that if
     116             :   the element has the NodeAncestorHasDirAuto flag, all of its child nodes
     117             :   should still retain the same flag.)
     118             :   * We resolve the directionality of the element based on the value of the @dir
     119             :   attribute on the element itself or its parent element.
     120             :   TextNodeDirectionalityMap needs to be updated as appropriate.
     121             : 
     122             :   5a. When the dir attribute is removed or set to an invalid value on any
     123             :   element (except a bdi element) with the NodeAncestorHasDirAuto flag which
     124             :   previously had a valid dir attribute, it might have a text node descendant that
     125             :   did not previously affect the directionality of any of its ancestors but should
     126             :   now begin to affect them.
     127             :   We run the following algorithm:
     128             :   * Walk up the parent chain from the element.
     129             :   * For any element that appears in the TextNodeDirectionalityMap, remove the
     130             :     element from the map and rerun the downward propagation algorithm
     131             :     (see section 3).
     132             :   * If we reach an element without either of the NodeHasDirAuto or
     133             :     NodeAncestorHasDirAuto flags, abort the parent chain walk.
     134             : 
     135             :   6. When an element with @dir=auto is added to the document, we should handle it
     136             :   similar to the case 2/3 above.
     137             : 
     138             :   7. When an element with NodeHasDirAuto or NodeAncestorHasDirAuto is
     139             :   removed from the document, we should handle it similar to the case 4/5 above,
     140             :   except that we don't need to handle anything in the child subtree.  We should
     141             :   also remove all of the occurrences of that node and its descendants from
     142             :   TextNodeDirectionalityMap. (This is the conceptual description of what needs to
     143             :   happen but in the implementation UnbindFromTree is going to be called on all of
     144             :   the descendants so we don't need to descend into the child subtree).
     145             : 
     146             :   8. When the contents of a text node is changed either from script or by the
     147             :   user, we need to run the following algorithm:
     148             :   * If the change has happened after the first character with strong
     149             :   directionality in the text node, do nothing.
     150             :   * If the text node is a child of a bdi, script or style element, do nothing.
     151             :   * If the text node belongs to a textarea with NodeHasDirAuto, we need to
     152             :   update the directionality of the textarea.
     153             :   * Grab a list of elements affected by this text node from
     154             :   TextNodeDirectionalityMap and re-resolve the directionality of each one of them
     155             :   based on the new contents of the text node.
     156             :   * If the text node does not exist in TextNodeDirectionalityMap, and it has the
     157             :   NodeAncestorHasDirAuto flag set, this could potentially be a text node
     158             :   which is going to start affecting the directionality of its parent @dir=auto
     159             :   elements. In this case, we need to fall back to the (potentially expensive)
     160             :   "upward propagation algorithm".  The TextNodeDirectionalityMap data structure
     161             :   needs to be update during this algorithm.
     162             :   * If the new contents of the text node do not have any strong characters, and
     163             :   the old contents used to, and the text node used to exist in
     164             :   TextNodeDirectionalityMap and it has the NodeAncestorHasDirAuto flag set,
     165             :   the elements associated with this text node inside TextNodeDirectionalityMap
     166             :   will now get their directionality from another text node.  In this case, for
     167             :   each element in the list retrieved from TextNodeDirectionalityMap, run the
     168             :   downward propagation algorithm (section 3), and remove the text node from
     169             :   TextNodeDirectionalityMap.
     170             : 
     171             :   9. When a new text node is injected into a document, we need to run the
     172             :   following algorithm:
     173             :   * If the contents of the text node do not have any characters with strong
     174             :   direction, do nothing.
     175             :   * If the text node is a child of a bdi, script or style element, do nothing.
     176             :   * If the text node is appended to a textarea element with NodeHasDirAuto, we
     177             :   need to update the directionality of the textarea.
     178             :   * If the text node has NodeAncestorHasDirAuto, we need to run the "upward
     179             :   propagation algorithm".  The TextNodeDirectionalityMap data structure needs to
     180             :   be update during this algorithm.
     181             : 
     182             :   10. When a text node is removed from a document, we need to run the following
     183             :   algorithm:
     184             :   * If the contents of the text node do not have any characters with strong
     185             :   direction, do nothing.
     186             :   * If the text node is a child of a bdi, script or style element, do nothing.
     187             :   * If the text node is removed from a textarea element with NodeHasDirAuto,
     188             :   set the directionality to "ltr". (This is what the spec currently says, but I'm
     189             :   filing a spec bug to get it fixed -- the directionality should depend on the
     190             :   parent element here.)
     191             :   * If the text node has NodeAncestorHasDirAuto, we need to look at the list
     192             :   of elements being affected by this text node from TextNodeDirectionalityMap,
     193             :   run the "downward propagation algorithm" (section 3) for each one of them,
     194             :   while updating TextNodeDirectionalityMap along the way.
     195             : 
     196             :   11. If the value of the @dir attribute on a bdi element is changed to an
     197             :   invalid value (or if it's removed), determine the new directionality similar
     198             :   to the case 3 above.
     199             : 
     200             :   == Implemention Notes ==
     201             :   When a new node gets bound to the tree, the BindToTree function gets called.
     202             :   The reverse case is UnbindFromTree.
     203             :   When the contents of a text node change, nsGenericDOMDataNode::SetTextInternal
     204             :   gets called.
     205             :   */
     206             : 
     207             : #include "mozilla/dom/DirectionalityUtils.h"
     208             : 
     209             : #include "nsINode.h"
     210             : #include "nsIContent.h"
     211             : #include "nsIDocument.h"
     212             : #include "mozilla/AutoRestore.h"
     213             : #include "mozilla/DebugOnly.h"
     214             : #include "mozilla/dom/Element.h"
     215             : #include "nsIDOMHTMLDocument.h"
     216             : #include "nsUnicodeProperties.h"
     217             : #include "nsTextFragment.h"
     218             : #include "nsAttrValue.h"
     219             : #include "nsTextNode.h"
     220             : #include "nsCheapSets.h"
     221             : 
     222             : namespace mozilla {
     223             : 
     224             : using mozilla::dom::Element;
     225             : 
     226             : /**
     227             :  * Returns true if aElement is one of the elements whose text content should not
     228             :  * affect its own direction, nor the direction of ancestors with dir=auto.
     229             :  *
     230             :  * Note that this does not include <bdi>, whose content does affect its own
     231             :  * direction when it has dir=auto (which it has by default), so one needs to
     232             :  * test for it separately, e.g. with DoesNotAffectDirectionOfAncestors.
     233             :  * It *does* include textarea, because even if a textarea has dir=auto, it has
     234             :  * unicode-bidi: plaintext and is handled automatically in bidi resolution.
     235             :  */
     236             : static bool
     237         662 : DoesNotParticipateInAutoDirection(const Element* aElement)
     238             : {
     239         662 :   mozilla::dom::NodeInfo* nodeInfo = aElement->NodeInfo();
     240         880 :   return (!aElement->IsHTMLElement() ||
     241         430 :           nodeInfo->Equals(nsGkAtoms::script) ||
     242         424 :           nodeInfo->Equals(nsGkAtoms::style) ||
     243        1083 :           nodeInfo->Equals(nsGkAtoms::textarea) ||
     244         871 :           aElement->IsInAnonymousSubtree());
     245             : }
     246             : 
     247             : static inline bool
     248           0 : IsBdiWithoutDirAuto(const Element* aElement)
     249             : {
     250             :   // We are testing for bdi elements without explicit dir="auto", so we can't
     251             :   // use the HasDirAuto() flag, since that will return true for bdi element with
     252             :   // no dir attribute or an invalid dir attribute
     253           0 :   return (aElement->IsHTMLElement(nsGkAtoms::bdi) &&
     254           0 :           (!aElement->HasValidDir() || aElement->HasFixedDir()));
     255             : }
     256             : 
     257             : /**
     258             :  * Returns true if aElement is one of the element whose text content should not
     259             :  * affect the direction of ancestors with dir=auto (though it may affect its own
     260             :  * direction, e.g. <bdi>)
     261             :  */
     262             : static bool
     263           0 : DoesNotAffectDirectionOfAncestors(const Element* aElement)
     264             : {
     265           0 :   return (DoesNotParticipateInAutoDirection(aElement) ||
     266           0 :           IsBdiWithoutDirAuto(aElement) ||
     267           0 :           aElement->HasFixedDir());
     268             : }
     269             : 
     270             : /**
     271             :  * Returns the directionality of a Unicode character
     272             :  */
     273             : static Directionality
     274           1 : GetDirectionFromChar(uint32_t ch)
     275             : {
     276           1 :   switch(mozilla::unicode::GetBidiCat(ch)) {
     277             :     case eCharType_RightToLeft:
     278             :     case eCharType_RightToLeftArabic:
     279           0 :       return eDir_RTL;
     280             : 
     281             :     case eCharType_LeftToRight:
     282           1 :       return eDir_LTR;
     283             : 
     284             :     default:
     285           0 :       return eDir_NotSet;
     286             :   }
     287             : }
     288             : 
     289             : inline static bool
     290         855 : NodeAffectsDirAutoAncestor(nsINode* aTextNode)
     291             : {
     292         855 :   Element* parent = aTextNode->GetParentElement();
     293         532 :   return (parent &&
     294         603 :           !DoesNotParticipateInAutoDirection(parent) &&
     295         926 :           parent->NodeOrAncestorHasDirAuto() &&
     296         855 :           !aTextNode->IsInAnonymousSubtree());
     297             : }
     298             : 
     299             : Directionality
     300           1 : GetDirectionFromText(const char16_t* aText, const uint32_t aLength,
     301             :                      uint32_t* aFirstStrong)
     302             : {
     303           1 :   const char16_t* start = aText;
     304           1 :   const char16_t* end = aText + aLength;
     305             : 
     306           1 :   while (start < end) {
     307           1 :     uint32_t current = start - aText;
     308           1 :     uint32_t ch = *start++;
     309             : 
     310           1 :     if (NS_IS_HIGH_SURROGATE(ch) &&
     311           0 :         start < end &&
     312           0 :         NS_IS_LOW_SURROGATE(*start)) {
     313           0 :       ch = SURROGATE_TO_UCS4(ch, *start++);
     314           0 :       current++;
     315             :     }
     316             : 
     317             :     // Just ignore lone surrogates
     318           1 :     if (!IS_SURROGATE(ch)) {
     319           1 :       Directionality dir = GetDirectionFromChar(ch);
     320           1 :       if (dir != eDir_NotSet) {
     321           1 :         if (aFirstStrong) {
     322           0 :           *aFirstStrong = current;
     323             :         }
     324           1 :         return dir;
     325             :       }
     326             :     }
     327             :   }
     328             : 
     329           0 :   if (aFirstStrong) {
     330           0 :     *aFirstStrong = UINT32_MAX;
     331             :   }
     332           0 :   return eDir_NotSet;
     333             : }
     334             : 
     335             : static Directionality
     336           0 : GetDirectionFromText(const char* aText, const uint32_t aLength,
     337             :                         uint32_t* aFirstStrong = nullptr)
     338             : {
     339           0 :   const char* start = aText;
     340           0 :   const char* end = aText + aLength;
     341             : 
     342           0 :   while (start < end) {
     343           0 :     uint32_t current = start - aText;
     344           0 :     unsigned char ch = (unsigned char)*start++;
     345             : 
     346           0 :     Directionality dir = GetDirectionFromChar(ch);
     347           0 :     if (dir != eDir_NotSet) {
     348           0 :       if (aFirstStrong) {
     349           0 :         *aFirstStrong = current;
     350             :       }
     351           0 :       return dir;
     352             :     }
     353             :   }
     354             : 
     355           0 :   if (aFirstStrong) {
     356           0 :     *aFirstStrong = UINT32_MAX;
     357             :   }
     358           0 :   return eDir_NotSet;
     359             : }
     360             : 
     361             : static Directionality
     362           0 : GetDirectionFromText(const nsTextFragment* aFrag,
     363             :                      uint32_t* aFirstStrong = nullptr)
     364             : {
     365           0 :   if (aFrag->Is2b()) {
     366           0 :     return GetDirectionFromText(aFrag->Get2b(), aFrag->GetLength(),
     367           0 :                                    aFirstStrong);
     368             :   }
     369             : 
     370           0 :   return GetDirectionFromText(aFrag->Get1b(), aFrag->GetLength(),
     371           0 :                                  aFirstStrong);
     372             : }
     373             : 
     374             : /**
     375             :  * Set the directionality of a node with dir=auto as defined in
     376             :  * http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#the-directionality
     377             :  *
     378             :  * @param[in] changedNode If we call this method because the content of a text
     379             :  *            node is about to change, pass in the changed node, so that we
     380             :  *            know not to return it
     381             :  * @return the text node containing the character that determined the direction
     382             :  */
     383             : static nsTextNode*
     384           0 : WalkDescendantsSetDirectionFromText(Element* aElement, bool aNotify = true,
     385             :                                     nsINode* aChangedNode = nullptr)
     386             : {
     387           0 :   MOZ_ASSERT(aElement, "Must have an element");
     388           0 :   MOZ_ASSERT(aElement->HasDirAuto(), "Element must have dir=auto");
     389             : 
     390           0 :   if (DoesNotParticipateInAutoDirection(aElement)) {
     391           0 :     return nullptr;
     392             :   }
     393             : 
     394           0 :   nsIContent* child = aElement->GetFirstChild();
     395           0 :   while (child) {
     396           0 :     if (child->IsElement() &&
     397           0 :         DoesNotAffectDirectionOfAncestors(child->AsElement())) {
     398           0 :       child = child->GetNextNonChildNode(aElement);
     399           0 :       continue;
     400             :     }
     401             : 
     402           0 :     if (child->NodeType() == nsIDOMNode::TEXT_NODE &&
     403             :         child != aChangedNode) {
     404           0 :       Directionality textNodeDir = GetDirectionFromText(child->GetText());
     405           0 :       if (textNodeDir != eDir_NotSet) {
     406             :         // We found a descendant text node with strong directional characters.
     407             :         // Set the directionality of aElement to the corresponding value.
     408           0 :         aElement->SetDirectionality(textNodeDir, aNotify);
     409           0 :         return static_cast<nsTextNode*>(child);
     410             :       }
     411             :     }
     412           0 :     child = child->GetNextNode(aElement);
     413             :   }
     414             : 
     415             :   // We walked all the descendants without finding a text node with strong
     416             :   // directional characters. Set the directionality to LTR
     417           0 :   aElement->SetDirectionality(eDir_LTR, aNotify);
     418           0 :   return nullptr;
     419             : }
     420             : 
     421             : class nsTextNodeDirectionalityMap
     422             : {
     423             :   static void
     424           0 :   nsTextNodeDirectionalityMapDtor(void *aObject, nsIAtom* aPropertyName,
     425             :                                   void *aPropertyValue, void* aData)
     426             :   {
     427           0 :     nsINode* textNode = static_cast<nsINode * >(aObject);
     428           0 :     textNode->ClearHasTextNodeDirectionalityMap();
     429             : 
     430             :     nsTextNodeDirectionalityMap* map =
     431           0 :       reinterpret_cast<nsTextNodeDirectionalityMap * >(aPropertyValue);
     432           0 :     map->EnsureMapIsClear(textNode);
     433           0 :     delete map;
     434           0 :   }
     435             : 
     436             : public:
     437           0 :   explicit nsTextNodeDirectionalityMap(nsINode* aTextNode)
     438           0 :     : mElementToBeRemoved(nullptr)
     439             :   {
     440           0 :     MOZ_ASSERT(aTextNode, "Null text node");
     441           0 :     MOZ_COUNT_CTOR(nsTextNodeDirectionalityMap);
     442             :     aTextNode->SetProperty(nsGkAtoms::textNodeDirectionalityMap, this,
     443           0 :                            nsTextNodeDirectionalityMapDtor);
     444           0 :     aTextNode->SetHasTextNodeDirectionalityMap();
     445           0 :   }
     446             : 
     447           0 :   ~nsTextNodeDirectionalityMap()
     448           0 :   {
     449           0 :     MOZ_COUNT_DTOR(nsTextNodeDirectionalityMap);
     450           0 :   }
     451             : 
     452             :   static void
     453           0 :   nsTextNodeDirectionalityMapPropertyDestructor(void* aObject,
     454             :                                                 nsIAtom* aProperty,
     455             :                                                 void* aPropertyValue,
     456             :                                                 void* aData)
     457             :   {
     458             :     nsTextNode* textNode =
     459           0 :       static_cast<nsTextNode*>(aPropertyValue);
     460           0 :     nsTextNodeDirectionalityMap* map = GetDirectionalityMap(textNode);
     461           0 :     if (map) {
     462           0 :       map->RemoveEntryForProperty(static_cast<Element*>(aObject));
     463             :     }
     464           0 :     NS_RELEASE(textNode);
     465           0 :   }
     466             : 
     467           0 :   void AddEntry(nsTextNode* aTextNode, Element* aElement)
     468             :   {
     469           0 :     if (!mElements.Contains(aElement)) {
     470           0 :       mElements.Put(aElement);
     471           0 :       NS_ADDREF(aTextNode);
     472           0 :       aElement->SetProperty(nsGkAtoms::dirAutoSetBy, aTextNode,
     473           0 :                             nsTextNodeDirectionalityMapPropertyDestructor);
     474           0 :       aElement->SetHasDirAutoSet();
     475             :     }
     476           0 :   }
     477             : 
     478           0 :   void RemoveEntry(nsTextNode* aTextNode, Element* aElement)
     479             :   {
     480           0 :     NS_ASSERTION(mElements.Contains(aElement),
     481             :                  "element already removed from map");
     482             : 
     483           0 :     mElements.Remove(aElement);
     484           0 :     aElement->ClearHasDirAutoSet();
     485           0 :     aElement->DeleteProperty(nsGkAtoms::dirAutoSetBy);
     486           0 :   }
     487             : 
     488           0 :   void RemoveEntryForProperty(Element* aElement)
     489             :   {
     490           0 :     if (mElementToBeRemoved != aElement) {
     491           0 :       mElements.Remove(aElement);
     492             :     }
     493           0 :     aElement->ClearHasDirAutoSet();
     494           0 :   }
     495             : 
     496             : private:
     497             :   nsCheapSet<nsPtrHashKey<Element>> mElements;
     498             :   // Only used for comparison.
     499             :   Element* mElementToBeRemoved;
     500             : 
     501           0 :   static nsTextNodeDirectionalityMap* GetDirectionalityMap(nsINode* aTextNode)
     502             :   {
     503           0 :     MOZ_ASSERT(aTextNode->NodeType() == nsIDOMNode::TEXT_NODE,
     504             :                "Must be a text node");
     505           0 :     nsTextNodeDirectionalityMap* map = nullptr;
     506             : 
     507           0 :     if (aTextNode->HasTextNodeDirectionalityMap()) {
     508             :       map = static_cast<nsTextNodeDirectionalityMap * >
     509           0 :         (aTextNode->GetProperty(nsGkAtoms::textNodeDirectionalityMap));
     510             :     }
     511             : 
     512           0 :     return map;
     513             :   }
     514             : 
     515           0 :   static nsCheapSetOperator SetNodeDirection(nsPtrHashKey<Element>* aEntry, void* aDir)
     516             :   {
     517           0 :     MOZ_ASSERT(aEntry->GetKey()->IsElement(), "Must be an Element");
     518           0 :     aEntry->GetKey()->SetDirectionality(*reinterpret_cast<Directionality*>(aDir),
     519           0 :                                         true);
     520           0 :     return OpNext;
     521             :   }
     522             : 
     523           0 :   struct nsTextNodeDirectionalityMapAndElement
     524             :   {
     525             :     nsTextNodeDirectionalityMap* mMap;
     526             :     nsCOMPtr<nsINode> mNode;
     527             :   };
     528             : 
     529           0 :   static nsCheapSetOperator ResetNodeDirection(nsPtrHashKey<Element>* aEntry, void* aData)
     530             :   {
     531           0 :     MOZ_ASSERT(aEntry->GetKey()->IsElement(), "Must be an Element");
     532             :     // run the downward propagation algorithm
     533             :     // and remove the text node from the map
     534             :     nsTextNodeDirectionalityMapAndElement* data =
     535           0 :       static_cast<nsTextNodeDirectionalityMapAndElement*>(aData);
     536           0 :     nsINode* oldTextNode = data->mNode;
     537           0 :     Element* rootNode = aEntry->GetKey();
     538           0 :     nsTextNode* newTextNode = nullptr;
     539           0 :     if (rootNode->GetParentNode() && rootNode->HasDirAuto()) {
     540             :       newTextNode = WalkDescendantsSetDirectionFromText(rootNode, true,
     541           0 :                                                         oldTextNode);
     542             :     }
     543             : 
     544           0 :     AutoRestore<Element*> restore(data->mMap->mElementToBeRemoved);
     545           0 :     data->mMap->mElementToBeRemoved = rootNode;
     546           0 :     if (newTextNode) {
     547             :       nsINode* oldDirAutoSetBy =
     548           0 :         static_cast<nsTextNode*>(rootNode->GetProperty(nsGkAtoms::dirAutoSetBy));
     549           0 :       if (oldDirAutoSetBy == newTextNode) {
     550             :         // We're already registered.
     551           0 :         return OpNext;
     552             :       }
     553           0 :       nsTextNodeDirectionalityMap::AddEntryToMap(newTextNode, rootNode);
     554             :     } else {
     555           0 :       rootNode->ClearHasDirAutoSet();
     556           0 :       rootNode->DeleteProperty(nsGkAtoms::dirAutoSetBy);
     557             :     }
     558           0 :     return OpRemove;
     559             :   }
     560             : 
     561           0 :   static nsCheapSetOperator ClearEntry(nsPtrHashKey<Element>* aEntry, void* aData)
     562             :   {
     563           0 :     Element* rootNode = aEntry->GetKey();
     564           0 :     rootNode->ClearHasDirAutoSet();
     565           0 :     rootNode->DeleteProperty(nsGkAtoms::dirAutoSetBy);
     566           0 :     return OpRemove;
     567             :   }
     568             : 
     569             : public:
     570           0 :   uint32_t UpdateAutoDirection(Directionality aDir)
     571             :   {
     572           0 :     return mElements.EnumerateEntries(SetNodeDirection, &aDir);
     573             :   }
     574             : 
     575           0 :   void ResetAutoDirection(nsINode* aTextNode)
     576             :   {
     577           0 :     nsTextNodeDirectionalityMapAndElement data = { this, aTextNode };
     578           0 :     mElements.EnumerateEntries(ResetNodeDirection, &data);
     579           0 :   }
     580             : 
     581           0 :   void EnsureMapIsClear(nsINode* aTextNode)
     582             :   {
     583           0 :     AutoRestore<Element*> restore(mElementToBeRemoved);
     584             :     DebugOnly<uint32_t> clearedEntries =
     585           0 :       mElements.EnumerateEntries(ClearEntry, aTextNode);
     586           0 :     MOZ_ASSERT(clearedEntries == 0, "Map should be empty already");
     587           0 :   }
     588             : 
     589           0 :   static void RemoveElementFromMap(nsTextNode* aTextNode, Element* aElement)
     590             :   {
     591           0 :     if (aTextNode->HasTextNodeDirectionalityMap()) {
     592           0 :       GetDirectionalityMap(aTextNode)->RemoveEntry(aTextNode, aElement);
     593             :     }
     594           0 :   }
     595             : 
     596           0 :   static void AddEntryToMap(nsTextNode* aTextNode, Element* aElement)
     597             :   {
     598           0 :     nsTextNodeDirectionalityMap* map = GetDirectionalityMap(aTextNode);
     599           0 :     if (!map) {
     600           0 :       map = new nsTextNodeDirectionalityMap(aTextNode);
     601             :     }
     602             : 
     603           0 :     map->AddEntry(aTextNode, aElement);
     604           0 :   }
     605             : 
     606           0 :   static uint32_t UpdateTextNodeDirection(nsINode* aTextNode,
     607             :                                           Directionality aDir)
     608             :   {
     609           0 :     MOZ_ASSERT(aTextNode->HasTextNodeDirectionalityMap(),
     610             :                "Map missing in UpdateTextNodeDirection");
     611           0 :     return GetDirectionalityMap(aTextNode)->UpdateAutoDirection(aDir);
     612             :   }
     613             : 
     614           0 :   static void ResetTextNodeDirection(nsTextNode* aTextNode,
     615             :                                      nsTextNode* aChangedTextNode)
     616             :   {
     617           0 :     MOZ_ASSERT(aTextNode->HasTextNodeDirectionalityMap(),
     618             :                "Map missing in ResetTextNodeDirection");
     619           0 :     RefPtr<nsTextNode> textNode = aTextNode;
     620           0 :     GetDirectionalityMap(textNode)->ResetAutoDirection(aChangedTextNode);
     621           0 :   }
     622             : 
     623         366 :   static void EnsureMapIsClearFor(nsINode* aTextNode)
     624             :   {
     625         366 :     if (aTextNode->HasTextNodeDirectionalityMap()) {
     626           0 :       GetDirectionalityMap(aTextNode)->EnsureMapIsClear(aTextNode);
     627             :     }
     628         366 :   }
     629             : };
     630             : 
     631             : Directionality
     632         140 : RecomputeDirectionality(Element* aElement, bool aNotify)
     633             : {
     634         140 :   MOZ_ASSERT(!aElement->HasDirAuto(),
     635             :              "RecomputeDirectionality called with dir=auto");
     636             : 
     637         140 :   Directionality dir = eDir_LTR;
     638             : 
     639         140 :   if (aElement->HasValidDir()) {
     640           0 :     dir = aElement->GetDirectionality();
     641             :   } else {
     642         140 :     Element* parent = aElement->GetParentElement();
     643         140 :     if (parent) {
     644             :       // If the element doesn't have an explicit dir attribute with a valid
     645             :       // value, the directionality is the same as the parent element (but
     646             :       // don't propagate the parent directionality if it isn't set yet).
     647         128 :       Directionality parentDir = parent->GetDirectionality();
     648         128 :       if (parentDir != eDir_NotSet) {
     649          42 :         dir = parentDir;
     650             :       }
     651             :     } else {
     652             :       // If there is no parent element and no dir attribute, the directionality
     653             :       // is LTR.
     654          12 :       dir = eDir_LTR;
     655             :     }
     656             : 
     657         140 :     aElement->SetDirectionality(dir, aNotify);
     658             :   }
     659         140 :   return dir;
     660             : }
     661             : 
     662             : void
     663           0 : SetDirectionalityOnDescendants(Element* aElement, Directionality aDir,
     664             :                                bool aNotify)
     665             : {
     666           0 :   for (nsIContent* child = aElement->GetFirstChild(); child; ) {
     667           0 :     if (!child->IsElement()) {
     668           0 :       child = child->GetNextNode(aElement);
     669           0 :       continue;
     670             :     }
     671             : 
     672           0 :     Element* element = child->AsElement();
     673           0 :     if (element->HasValidDir() || element->HasDirAuto()) {
     674           0 :       child = child->GetNextNonChildNode(aElement);
     675           0 :       continue;
     676             :     }
     677           0 :     element->SetDirectionality(aDir, aNotify);
     678           0 :     child = child->GetNextNode(aElement);
     679             :   }
     680           0 : }
     681             : 
     682             : /**
     683             :  * Walk the parent chain of a text node whose dir attribute has been removed and
     684             :  * reset the direction of any of its ancestors which have dir=auto and whose
     685             :  * directionality is determined by a text node descendant.
     686             :  */
     687             : void
     688           0 : WalkAncestorsResetAutoDirection(Element* aElement, bool aNotify)
     689             : {
     690             :   nsTextNode* setByNode;
     691           0 :   Element* parent = aElement->GetParentElement();
     692             : 
     693           0 :   while (parent && parent->NodeOrAncestorHasDirAuto()) {
     694           0 :     if (parent->HasDirAutoSet()) {
     695             :       // If the parent has the DirAutoSet flag, its direction is determined by
     696             :       // some text node descendant.
     697             :       // Remove it from the map and reset its direction by the downward
     698             :       // propagation algorithm
     699             :       setByNode =
     700           0 :         static_cast<nsTextNode*>(parent->GetProperty(nsGkAtoms::dirAutoSetBy));
     701           0 :       if (setByNode) {
     702           0 :         nsTextNodeDirectionalityMap::RemoveElementFromMap(setByNode, parent);
     703             :       }
     704             :     }
     705           0 :     if (parent->HasDirAuto()) {
     706           0 :       setByNode = WalkDescendantsSetDirectionFromText(parent, aNotify);
     707           0 :       if (setByNode) {
     708           0 :         nsTextNodeDirectionalityMap::AddEntryToMap(setByNode, parent);
     709             :       }
     710           0 :       break;
     711             :     }
     712           0 :     parent = parent->GetParentElement();
     713             :   }
     714           0 : }
     715             : 
     716             : void
     717           0 : WalkDescendantsResetAutoDirection(Element* aElement)
     718             : {
     719           0 :   nsIContent* child = aElement->GetFirstChild();
     720           0 :   while (child) {
     721           0 :     if (child->IsElement() && child->AsElement()->HasDirAuto()) {
     722           0 :       child = child->GetNextNonChildNode(aElement);
     723           0 :       continue;
     724             :     }
     725             : 
     726           0 :     if (child->NodeType() == nsIDOMNode::TEXT_NODE &&
     727           0 :         child->HasTextNodeDirectionalityMap()) {
     728           0 :       nsTextNodeDirectionalityMap::ResetTextNodeDirection(static_cast<nsTextNode*>(child), nullptr);
     729             :       // Don't call nsTextNodeDirectionalityMap::EnsureMapIsClearFor(child)
     730             :       // since ResetTextNodeDirection may have kept elements in child's
     731             :       // DirectionalityMap.
     732             :     }
     733           0 :     child = child->GetNextNode(aElement);
     734             :   }
     735           0 : }
     736             : 
     737             : void
     738           0 : WalkDescendantsSetDirAuto(Element* aElement, bool aNotify)
     739             : {
     740             :   // Only test for DoesNotParticipateInAutoDirection -- in other words, if
     741             :   // aElement is a <bdi> which is having its dir attribute set to auto (or
     742             :   // removed or set to an invalid value, which are equivalent to dir=auto for
     743             :   // <bdi>, we *do* want to set AncestorHasDirAuto on its descendants, unlike
     744             :   // in SetDirOnBind where we don't propagate AncestorHasDirAuto to a <bdi>
     745             :   // being bound to an existing node with dir=auto.
     746           0 :   if (!DoesNotParticipateInAutoDirection(aElement)) {
     747             : 
     748             :     bool setAncestorDirAutoFlag =
     749             : #ifdef DEBUG
     750           0 :       true;
     751             : #else
     752             :       !aElement->AncestorHasDirAuto();
     753             : #endif
     754             : 
     755           0 :     if (setAncestorDirAutoFlag) {
     756           0 :       nsIContent* child = aElement->GetFirstChild();
     757           0 :       while (child) {
     758           0 :         if (child->IsElement() &&
     759           0 :             DoesNotAffectDirectionOfAncestors(child->AsElement())) {
     760           0 :           child = child->GetNextNonChildNode(aElement);
     761           0 :           continue;
     762             :         }
     763             : 
     764           0 :         MOZ_ASSERT(!aElement->AncestorHasDirAuto() ||
     765             :                    child->AncestorHasDirAuto(),
     766             :                    "AncestorHasDirAuto set on node but not its children");
     767           0 :         child->SetAncestorHasDirAuto();
     768           0 :         child = child->GetNextNode(aElement);
     769             :       }
     770             :     }
     771             :   }
     772             : 
     773           0 :   nsTextNode* textNode = WalkDescendantsSetDirectionFromText(aElement, aNotify);
     774           0 :   if (textNode) {
     775           0 :     nsTextNodeDirectionalityMap::AddEntryToMap(textNode, aElement);
     776             :   }
     777           0 : }
     778             : 
     779             : void
     780           0 : WalkDescendantsClearAncestorDirAuto(Element* aElement)
     781             : {
     782           0 :   nsIContent* child = aElement->GetFirstChild();
     783           0 :   while (child) {
     784           0 :     if (child->IsElement() && child->AsElement()->HasDirAuto()) {
     785           0 :       child = child->GetNextNonChildNode(aElement);
     786           0 :       continue;
     787             :     }
     788             : 
     789           0 :     child->ClearAncestorHasDirAuto();
     790           0 :     child = child->GetNextNode(aElement);
     791             :   }
     792           0 : }
     793             : 
     794           0 : void SetAncestorDirectionIfAuto(nsTextNode* aTextNode, Directionality aDir,
     795             :                                 bool aNotify = true)
     796             : {
     797           0 :   MOZ_ASSERT(aTextNode->NodeType() == nsIDOMNode::TEXT_NODE,
     798             :              "Must be a text node");
     799             : 
     800           0 :   Element* parent = aTextNode->GetParentElement();
     801           0 :   while (parent && parent->NodeOrAncestorHasDirAuto()) {
     802           0 :     if (DoesNotParticipateInAutoDirection(parent) || parent->HasFixedDir()) {
     803           0 :       break;
     804             :     }
     805             : 
     806           0 :     if (parent->HasDirAuto()) {
     807           0 :       bool resetDirection = false;
     808             :       nsTextNode* directionWasSetByTextNode =
     809           0 :         static_cast<nsTextNode*>(parent->GetProperty(nsGkAtoms::dirAutoSetBy));
     810             : 
     811           0 :       if (!parent->HasDirAutoSet()) {
     812             :         // Fast path if parent's direction is not yet set by any descendant
     813           0 :         MOZ_ASSERT(!directionWasSetByTextNode,
     814             :                    "dirAutoSetBy property should be null");
     815           0 :         resetDirection = true;
     816             :       } else {
     817             :         // If parent's direction is already set, we need to know if
     818             :         // aTextNode is before or after the text node that had set it.
     819             :         // We will walk parent's descendants in tree order starting from
     820             :         // aTextNode to optimize for the most common case where text nodes are
     821             :         // being appended to tree.
     822           0 :         if (!directionWasSetByTextNode) {
     823           0 :           resetDirection = true;
     824           0 :         } else if (directionWasSetByTextNode != aTextNode) {
     825           0 :           nsIContent* child = aTextNode->GetNextNode(parent);
     826           0 :           while (child) {
     827           0 :             if (child->IsElement() &&
     828           0 :                 DoesNotAffectDirectionOfAncestors(child->AsElement())) {
     829           0 :               child = child->GetNextNonChildNode(parent);
     830           0 :               continue;
     831             :             }
     832             : 
     833           0 :             if (child == directionWasSetByTextNode) {
     834             :               // we found the node that set the element's direction after our
     835             :               // text node, so we need to reset the direction
     836           0 :               resetDirection = true;
     837           0 :               break;
     838             :             }
     839             : 
     840           0 :             child = child->GetNextNode(parent);
     841             :           }
     842             :         }
     843             :       }
     844             : 
     845           0 :       if (resetDirection) {
     846           0 :         if (directionWasSetByTextNode) {
     847             :           nsTextNodeDirectionalityMap::RemoveElementFromMap(
     848             :             directionWasSetByTextNode, parent
     849           0 :           );
     850             :         }
     851           0 :         parent->SetDirectionality(aDir, aNotify);
     852           0 :         nsTextNodeDirectionalityMap::AddEntryToMap(aTextNode, parent);
     853           0 :         SetDirectionalityOnDescendants(parent, aDir, aNotify);
     854             :       }
     855             : 
     856             :       // Since we found an element with dir=auto, we can stop walking the
     857             :       // parent chain: none of its ancestors will have their direction set by
     858             :       // any of its descendants.
     859           0 :       return;
     860             :     }
     861           0 :     parent = parent->GetParentElement();
     862             :   }
     863             : }
     864             : 
     865             : bool
     866         336 : TextNodeWillChangeDirection(nsIContent* aTextNode, Directionality* aOldDir,
     867             :                             uint32_t aOffset)
     868             : {
     869         336 :   if (!NodeAffectsDirAutoAncestor(aTextNode)) {
     870         336 :     nsTextNodeDirectionalityMap::EnsureMapIsClearFor(aTextNode);
     871         336 :     return false;
     872             :   }
     873             : 
     874             :   uint32_t firstStrong;
     875           0 :   *aOldDir = GetDirectionFromText(aTextNode->GetText(), &firstStrong);
     876           0 :   return (aOffset <= firstStrong);
     877             : }
     878             : 
     879             : void
     880           0 : TextNodeChangedDirection(nsTextNode* aTextNode, Directionality aOldDir,
     881             :                          bool aNotify)
     882             : {
     883           0 :   Directionality newDir = GetDirectionFromText(aTextNode->GetText());
     884           0 :   if (newDir == eDir_NotSet) {
     885           0 :     if (aOldDir != eDir_NotSet && aTextNode->HasTextNodeDirectionalityMap()) {
     886             :       // This node used to have a strong directional character but no
     887             :       // longer does. ResetTextNodeDirection() will re-resolve the
     888             :       // directionality of any elements whose directionality was
     889             :       // determined by this node.
     890           0 :       nsTextNodeDirectionalityMap::ResetTextNodeDirection(aTextNode, aTextNode);
     891             :     }
     892             :   } else {
     893             :     // This node has a strong directional character. If it has a
     894             :     // TextNodeDirectionalityMap property, it already determines the
     895             :     // directionality of some element(s), so call UpdateTextNodeDirection to
     896             :     // reresolve their directionality. If it has no map, or if
     897             :     // UpdateTextNodeDirection returns zero, indicating that the map is
     898             :     // empty, call SetAncestorDirectionIfAuto to find ancestor elements
     899             :     // which should have their directionality determined by this node.
     900           0 :     if (aTextNode->HasTextNodeDirectionalityMap() &&
     901           0 :         nsTextNodeDirectionalityMap::UpdateTextNodeDirection(aTextNode,
     902             :                                                              newDir)) {
     903           0 :       return;
     904             :     }
     905           0 :     SetAncestorDirectionIfAuto(aTextNode, newDir, aNotify);
     906             :   }
     907             : }
     908             : 
     909             : void
     910         489 : SetDirectionFromNewTextNode(nsTextNode* aTextNode)
     911             : {
     912         489 :   if (!NodeAffectsDirAutoAncestor(aTextNode)) {
     913         489 :     return;
     914             :   }
     915             : 
     916           0 :   Element* parent = aTextNode->GetParentElement();
     917           0 :   if (parent && parent->NodeOrAncestorHasDirAuto()) {
     918           0 :     aTextNode->SetAncestorHasDirAuto();
     919             :   }
     920             : 
     921           0 :   Directionality dir = GetDirectionFromText(aTextNode->GetText());
     922           0 :   if (dir != eDir_NotSet) {
     923           0 :     SetAncestorDirectionIfAuto(aTextNode, dir);
     924             :   }
     925             : }
     926             : 
     927             : void
     928          30 : ResetDirectionSetByTextNode(nsTextNode* aTextNode)
     929             : {
     930          30 :   if (!NodeAffectsDirAutoAncestor(aTextNode)) {
     931          30 :     nsTextNodeDirectionalityMap::EnsureMapIsClearFor(aTextNode);
     932          30 :     return;
     933             :   }
     934             : 
     935           0 :   Directionality dir = GetDirectionFromText(aTextNode->GetText());
     936           0 :   if (dir != eDir_NotSet && aTextNode->HasTextNodeDirectionalityMap()) {
     937           0 :     nsTextNodeDirectionalityMap::ResetTextNodeDirection(aTextNode, aTextNode);
     938             :   }
     939             : }
     940             : 
     941             : void
     942           0 : SetDirectionalityFromValue(Element* aElement, const nsAString& value,
     943             :                            bool aNotify)
     944             : {
     945           0 :   Directionality dir = GetDirectionFromText(PromiseFlatString(value).get(),
     946           0 :                                             value.Length());
     947           0 :   if (dir == eDir_NotSet) {
     948           0 :     dir = eDir_LTR;
     949             :   }
     950             : 
     951           0 :   aElement->SetDirectionality(dir, aNotify);
     952           0 : }
     953             : 
     954             : void
     955           0 : OnSetDirAttr(Element* aElement, const nsAttrValue* aNewValue,
     956             :              bool hadValidDir, bool hadDirAuto, bool aNotify)
     957             : {
     958           0 :   if (aElement->IsHTMLElement(nsGkAtoms::input)) {
     959           0 :     return;
     960             :   }
     961             : 
     962           0 :   if (aElement->AncestorHasDirAuto()) {
     963           0 :     if (!hadValidDir) {
     964             :       // The element is a descendant of an element with dir = auto, is
     965             :       // having its dir attribute set, and previously didn't have a valid dir
     966             :       // attribute.
     967             :       // Check whether any of its text node descendants determine the
     968             :       // direction of any of its ancestors, and redetermine their direction
     969           0 :       WalkDescendantsResetAutoDirection(aElement);
     970           0 :     } else if (!aElement->HasValidDir()) {
     971             :       // The element is a descendant of an element with dir = auto and is
     972             :       // having its dir attribute removed or set to an invalid value.
     973             :       // Reset the direction of any of its ancestors whose direction is
     974             :       // determined by a text node descendant
     975           0 :       WalkAncestorsResetAutoDirection(aElement, aNotify);
     976             :     }
     977           0 :   } else if (hadDirAuto && !aElement->HasDirAuto()) {
     978             :     // The element isn't a descendant of an element with dir = auto, and is
     979             :     // having its dir attribute set to something other than auto.
     980             :     // Walk the descendant tree and clear the AncestorHasDirAuto flag.
     981             :     //
     982             :     // N.B: For elements other than <bdi> it would be enough to test that the
     983             :     //      current value of dir was "auto" in BeforeSetAttr to know that we
     984             :     //      were unsetting dir="auto". For <bdi> things are more complicated,
     985             :     //      since it behaves like dir="auto" whenever the dir attribute is
     986             :     //      empty or invalid, so we would have to check whether the old value
     987             :     //      was not either "ltr" or "rtl", and the new value was either "ltr"
     988             :     //      or "rtl". Element::HasDirAuto() encapsulates all that, so doing it
     989             :     //      here is simpler.
     990           0 :     WalkDescendantsClearAncestorDirAuto(aElement);
     991             :   }
     992             : 
     993           0 :   if (aElement->HasDirAuto()) {
     994           0 :     WalkDescendantsSetDirAuto(aElement, aNotify);
     995             :   } else {
     996           0 :     if (aElement->HasDirAutoSet()) {
     997             :       nsTextNode* setByNode =
     998           0 :         static_cast<nsTextNode*>(aElement->GetProperty(nsGkAtoms::dirAutoSetBy));
     999           0 :       nsTextNodeDirectionalityMap::RemoveElementFromMap(setByNode, aElement);
    1000             :     }
    1001           0 :     SetDirectionalityOnDescendants(aElement,
    1002           0 :                                    RecomputeDirectionality(aElement, aNotify),
    1003           0 :                                    aNotify);
    1004             :   }
    1005             : }
    1006             : 
    1007             : void
    1008         130 : SetDirOnBind(mozilla::dom::Element* aElement, nsIContent* aParent)
    1009             : {
    1010             :   // Set the AncestorHasDirAuto flag, unless this element shouldn't affect
    1011             :   // ancestors that have dir=auto
    1012         345 :   if (!DoesNotParticipateInAutoDirection(aElement) &&
    1013         170 :       !aElement->IsHTMLElement(nsGkAtoms::bdi) &&
    1014         208 :       aParent && aParent->NodeOrAncestorHasDirAuto()) {
    1015           0 :     aElement->SetAncestorHasDirAuto();
    1016             : 
    1017           0 :     nsIContent* child = aElement->GetFirstChild();
    1018           0 :     if (child) {
    1019             :       // If we are binding an element to the tree that already has descendants,
    1020             :       // and the parent has NodeHasDirAuto or NodeAncestorHasDirAuto, we need
    1021             :       // to set NodeAncestorHasDirAuto on all the element's descendants, except
    1022             :       // for nodes that don't affect the direction of their ancestors.
    1023           0 :       do {
    1024           0 :         if (child->IsElement() &&
    1025           0 :             DoesNotAffectDirectionOfAncestors(child->AsElement())) {
    1026           0 :           child = child->GetNextNonChildNode(aElement);
    1027           0 :           continue;
    1028             :         }
    1029             : 
    1030           0 :         child->SetAncestorHasDirAuto();
    1031           0 :         child = child->GetNextNode(aElement);
    1032           0 :       } while (child);
    1033             : 
    1034             :       // We may also need to reset the direction of an ancestor with dir=auto
    1035           0 :       WalkAncestorsResetAutoDirection(aElement, true);
    1036             :     }
    1037             :   }
    1038             : 
    1039         130 :   if (!aElement->HasDirAuto()) {
    1040             :     // if the element doesn't have dir=auto, set its own directionality from
    1041             :     // the dir attribute or by inheriting from its ancestors.
    1042         130 :     RecomputeDirectionality(aElement, false);
    1043             :   }
    1044         130 : }
    1045             : 
    1046          10 : void ResetDir(mozilla::dom::Element* aElement)
    1047             : {
    1048          10 :   if (aElement->HasDirAutoSet()) {
    1049             :     nsTextNode* setByNode =
    1050           0 :       static_cast<nsTextNode*>(aElement->GetProperty(nsGkAtoms::dirAutoSetBy));
    1051           0 :     nsTextNodeDirectionalityMap::RemoveElementFromMap(setByNode, aElement);
    1052             :   }
    1053             : 
    1054          10 :   if (!aElement->HasDirAuto()) {
    1055          10 :     RecomputeDirectionality(aElement, false);
    1056             :   }
    1057          10 : }
    1058             : 
    1059             : } // end namespace mozilla
    1060             : 

Generated by: LCOV version 1.13