tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

RestyleManager.cpp (158674B)


      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 #include "mozilla/RestyleManager.h"
      8 
      9 #include "ActiveLayerTracker.h"
     10 #include "ScrollSnap.h"
     11 #include "StickyScrollContainer.h"
     12 #include "mozilla/AnimationUtils.h"
     13 #include "mozilla/Assertions.h"
     14 #include "mozilla/ComputedStyle.h"
     15 #include "mozilla/ComputedStyleInlines.h"
     16 #include "mozilla/DocumentStyleRootIterator.h"
     17 #include "mozilla/EffectSet.h"
     18 #include "mozilla/GeckoBindings.h"
     19 #include "mozilla/IntegerRange.h"
     20 #include "mozilla/LayerAnimationInfo.h"
     21 #include "mozilla/PresShell.h"
     22 #include "mozilla/PresShellInlines.h"
     23 #include "mozilla/ProfilerLabels.h"
     24 #include "mozilla/SVGIntegrationUtils.h"
     25 #include "mozilla/SVGObserverUtils.h"
     26 #include "mozilla/SVGTextFrame.h"
     27 #include "mozilla/SVGUtils.h"
     28 #include "mozilla/ScrollContainerFrame.h"
     29 #include "mozilla/ServoBindings.h"
     30 #include "mozilla/ServoStyleSetInlines.h"
     31 #include "mozilla/StaticPrefs_layout.h"
     32 #include "mozilla/ViewportFrame.h"
     33 #include "mozilla/dom/ChildIterator.h"
     34 #include "mozilla/dom/DocumentInlines.h"
     35 #include "mozilla/dom/ElementInlines.h"
     36 #include "mozilla/dom/HTMLBodyElement.h"
     37 #include "mozilla/dom/HTMLInputElement.h"
     38 #include "mozilla/layers/AnimationInfo.h"
     39 #include "mozilla/layout/ScrollAnchorContainer.h"
     40 #include "nsAnimationManager.h"
     41 #include "nsBlockFrame.h"
     42 #include "nsCSSFrameConstructor.h"
     43 #include "nsCSSRendering.h"
     44 #include "nsContentUtils.h"
     45 #include "nsDocShell.h"
     46 #include "nsIFrame.h"
     47 #include "nsIFrameInlines.h"
     48 #include "nsImageFrame.h"
     49 #include "nsPlaceholderFrame.h"
     50 #include "nsPrintfCString.h"
     51 #include "nsRefreshDriver.h"
     52 #include "nsStyleChangeList.h"
     53 #include "nsStyleUtil.h"
     54 #include "nsTableWrapperFrame.h"
     55 #include "nsTransitionManager.h"
     56 
     57 #ifdef ACCESSIBILITY
     58 #  include "nsAccessibilityService.h"
     59 #endif
     60 
     61 using mozilla::layers::AnimationInfo;
     62 using mozilla::layout::ScrollAnchorContainer;
     63 
     64 using namespace mozilla::dom;
     65 using namespace mozilla::layers;
     66 
     67 namespace mozilla {
     68 
     69 RestyleManager::RestyleManager(nsPresContext* aPresContext)
     70    : mPresContext(aPresContext),
     71      mRestyleGeneration(1),
     72      mUndisplayedRestyleGeneration(1),
     73      mInStyleRefresh(false),
     74      mAnimationGeneration(0) {
     75  MOZ_ASSERT(mPresContext);
     76 }
     77 
     78 void RestyleManager::ContentInserted(nsIContent* aChild) {
     79  MOZ_ASSERT(aChild->GetParentNode());
     80  if (aChild->IsElement()) {
     81    StyleSet()->MaybeInvalidateForElementInsertion(*aChild->AsElement());
     82  }
     83  RestyleForInsertOrChange(aChild);
     84 }
     85 
     86 void RestyleManager::ContentAppended(nsIContent* aFirstNewContent) {
     87  MOZ_ASSERT(aFirstNewContent->GetParentNode());
     88 
     89 #ifdef DEBUG
     90  for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
     91    NS_ASSERTION(cur->IsRootOfNativeAnonymousSubtree() ==
     92                     aFirstNewContent->IsRootOfNativeAnonymousSubtree(),
     93                 "anonymous nodes should not be in child lists");
     94  }
     95 #endif
     96 
     97  // We get called explicitly with NAC by editor and view transitions code, but
     98  // in those cases we don't need to do any invalidation.
     99  if (MOZ_UNLIKELY(aFirstNewContent->IsRootOfNativeAnonymousSubtree())) {
    100    return;
    101  }
    102 
    103  StyleSet()->MaybeInvalidateForElementAppend(*aFirstNewContent);
    104 
    105  auto* container = aFirstNewContent->GetParentNode();
    106  const auto selectorFlags = container->GetSelectorFlags() &
    107                             NodeSelectorFlags::AllSimpleRestyleFlagsForAppend;
    108  if (!selectorFlags) {
    109    return;
    110  }
    111 
    112  // The container cannot be a document.
    113  MOZ_ASSERT(container->IsElement() || container->IsShadowRoot());
    114 
    115  if (selectorFlags & NodeSelectorFlags::HasEmptySelector) {
    116    // see whether we need to restyle the container
    117    bool wasEmpty = true;  // :empty or :-moz-only-whitespace
    118    for (nsIContent* cur = container->GetFirstChild(); cur != aFirstNewContent;
    119         cur = cur->GetNextSibling()) {
    120      // We don't know whether we're testing :empty or :-moz-only-whitespace,
    121      // so be conservative and assume :-moz-only-whitespace (i.e., make
    122      // IsSignificantChild less likely to be true, and thus make us more
    123      // likely to restyle).
    124      if (nsStyleUtil::IsSignificantChild(cur, false)) {
    125        wasEmpty = false;
    126        break;
    127      }
    128    }
    129    if (wasEmpty && container->IsElement()) {
    130      RestyleForEmptyChange(container->AsElement());
    131      return;
    132    }
    133  }
    134 
    135  if (selectorFlags & NodeSelectorFlags::HasSlowSelector) {
    136    RestyleWholeContainer(container, selectorFlags);
    137    // Restyling the container is the most we can do here, so we're done.
    138    return;
    139  }
    140 
    141  if (selectorFlags & NodeSelectorFlags::HasEdgeChildSelector) {
    142    // restyle the last element child before this node
    143    for (nsIContent* cur = aFirstNewContent->GetPreviousSibling(); cur;
    144         cur = cur->GetPreviousSibling()) {
    145      if (cur->IsElement()) {
    146        auto* element = cur->AsElement();
    147        PostRestyleEvent(element, RestyleHint::RestyleSubtree(),
    148                         nsChangeHint(0));
    149        StyleSet()->MaybeInvalidateRelativeSelectorForNthEdgeDependency(
    150            *element, StyleRelativeSelectorNthEdgeInvalidateFor::Last);
    151        break;
    152      }
    153    }
    154  }
    155 }
    156 
    157 void RestyleManager::RestylePreviousSiblings(nsIContent* aStartingSibling) {
    158  for (nsIContent* sibling = aStartingSibling; sibling;
    159       sibling = sibling->GetPreviousSibling()) {
    160    if (auto* element = Element::FromNode(sibling)) {
    161      PostRestyleEvent(element, RestyleHint::RestyleSubtree(), nsChangeHint(0));
    162    }
    163  }
    164 }
    165 
    166 void RestyleManager::RestyleSiblingsStartingWith(nsIContent* aStartingSibling) {
    167  for (nsIContent* sibling = aStartingSibling; sibling;
    168       sibling = sibling->GetNextSibling()) {
    169    if (auto* element = Element::FromNode(sibling)) {
    170      PostRestyleEvent(element, RestyleHint::RestyleSubtree(), nsChangeHint(0));
    171    }
    172  }
    173 }
    174 
    175 void RestyleManager::RestyleWholeContainer(nsINode* aContainer,
    176                                           NodeSelectorFlags aSelectorFlags) {
    177  if (!mRestyledAsWholeContainer.EnsureInserted(aContainer)) {
    178    return;
    179  }
    180  if (auto* containerElement = Element::FromNode(aContainer)) {
    181    PostRestyleEvent(containerElement, RestyleHint::RestyleSubtree(),
    182                     nsChangeHint(0));
    183    if (aSelectorFlags & NodeSelectorFlags::HasSlowSelectorNthAll) {
    184      StyleSet()->MaybeInvalidateRelativeSelectorForNthDependencyFromSibling(
    185          containerElement->GetFirstElementChild(),
    186          /* aForceRestyleSiblings = */ false);
    187    }
    188  } else {
    189    RestyleSiblingsStartingWith(aContainer->GetFirstChild());
    190  }
    191 }
    192 
    193 void RestyleManager::RestyleForEmptyChange(Element* aContainer) {
    194  PostRestyleEvent(aContainer, RestyleHint::RestyleSubtree(), nsChangeHint(0));
    195  StyleSet()->MaybeInvalidateRelativeSelectorForEmptyDependency(*aContainer);
    196 
    197  // In some cases (:empty + E, :empty ~ E), a change in the content of
    198  // an element requires restyling its parent's siblings.
    199  nsIContent* grandparent = aContainer->GetParent();
    200  if (!grandparent || !(grandparent->GetSelectorFlags() &
    201                        NodeSelectorFlags::HasSlowSelectorLaterSiblings)) {
    202    return;
    203  }
    204  RestyleSiblingsStartingWith(aContainer->GetNextSibling());
    205 }
    206 
    207 void RestyleManager::MaybeRestyleForEdgeChildChange(nsINode* aContainer,
    208                                                    nsIContent* aChangedChild) {
    209  MOZ_ASSERT(aContainer->GetSelectorFlags() &
    210             NodeSelectorFlags::HasEdgeChildSelector);
    211  MOZ_ASSERT(aChangedChild->GetParent() == aContainer);
    212  // restyle the previously-first element child if it is after this node
    213  bool passedChild = false;
    214  for (nsIContent* content = aContainer->GetFirstChild(); content;
    215       content = content->GetNextSibling()) {
    216    if (content == aChangedChild) {
    217      passedChild = true;
    218      continue;
    219    }
    220    if (content->IsElement()) {
    221      if (passedChild) {
    222        auto* element = content->AsElement();
    223        PostRestyleEvent(element, RestyleHint::RestyleSubtree(),
    224                         nsChangeHint(0));
    225        StyleSet()->MaybeInvalidateRelativeSelectorForNthEdgeDependency(
    226            *element, StyleRelativeSelectorNthEdgeInvalidateFor::First);
    227      }
    228      break;
    229    }
    230  }
    231  // restyle the previously-last element child if it is before this node
    232  passedChild = false;
    233  for (nsIContent* content = aContainer->GetLastChild(); content;
    234       content = content->GetPreviousSibling()) {
    235    if (content == aChangedChild) {
    236      passedChild = true;
    237      continue;
    238    }
    239    if (content->IsElement()) {
    240      if (passedChild) {
    241        auto* element = content->AsElement();
    242        PostRestyleEvent(element, RestyleHint::RestyleSubtree(),
    243                         nsChangeHint(0));
    244        StyleSet()->MaybeInvalidateRelativeSelectorForNthEdgeDependency(
    245            *element, StyleRelativeSelectorNthEdgeInvalidateFor::Last);
    246      }
    247      break;
    248    }
    249  }
    250 }
    251 
    252 template <typename CharT>
    253 bool WhitespaceOnly(const CharT* aBuffer, size_t aUpTo) {
    254  for (auto index : IntegerRange(aUpTo)) {
    255    if (!dom::IsSpaceCharacter(aBuffer[index])) {
    256      return false;
    257    }
    258  }
    259  return true;
    260 }
    261 
    262 template <typename CharT>
    263 bool WhitespaceOnlyChangedOnAppend(const CharT* aBuffer, size_t aOldLength,
    264                                   size_t aNewLength) {
    265  MOZ_ASSERT(aOldLength <= aNewLength);
    266  if (!WhitespaceOnly(aBuffer, aOldLength)) {
    267    // The old text was already not whitespace-only.
    268    return false;
    269  }
    270 
    271  return !WhitespaceOnly(aBuffer + aOldLength, aNewLength - aOldLength);
    272 }
    273 
    274 static bool HasAnySignificantSibling(Element* aContainer, nsIContent* aChild) {
    275  MOZ_ASSERT(aChild->GetParent() == aContainer);
    276  for (nsIContent* child = aContainer->GetFirstChild(); child;
    277       child = child->GetNextSibling()) {
    278    if (child == aChild) {
    279      continue;
    280    }
    281    // We don't know whether we're testing :empty or :-moz-only-whitespace,
    282    // so be conservative and assume :-moz-only-whitespace (i.e., make
    283    // IsSignificantChild less likely to be true, and thus make us more
    284    // likely to restyle).
    285    if (nsStyleUtil::IsSignificantChild(child, false)) {
    286      return true;
    287    }
    288  }
    289 
    290  return false;
    291 }
    292 
    293 void RestyleManager::CharacterDataChanged(
    294    nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
    295  nsINode* parent = aContent->GetParentNode();
    296  MOZ_ASSERT(parent, "How were we notified of a stray node?");
    297 
    298  const auto slowSelectorFlags =
    299      parent->GetSelectorFlags() & NodeSelectorFlags::AllSimpleRestyleFlags;
    300  if (!(slowSelectorFlags & (NodeSelectorFlags::HasEmptySelector |
    301                             NodeSelectorFlags::HasEdgeChildSelector))) {
    302    // Nothing to do, no other slow selector can change as a result of this.
    303    return;
    304  }
    305 
    306  if (!aContent->IsText()) {
    307    // Doesn't matter to styling (could be a processing instruction or a
    308    // comment), it can't change whether any selectors match or don't.
    309    return;
    310  }
    311 
    312  if (MOZ_UNLIKELY(!parent->IsElement())) {
    313    MOZ_ASSERT(parent->IsShadowRoot());
    314    return;
    315  }
    316 
    317  if (MOZ_UNLIKELY(aContent->IsRootOfNativeAnonymousSubtree())) {
    318    // This is an anonymous node and thus isn't in child lists, so isn't taken
    319    // into account for selector matching the relevant selectors here.
    320    return;
    321  }
    322 
    323  // Handle appends specially since they're common and we can know both the old
    324  // and the new text exactly.
    325  //
    326  // TODO(emilio): This could be made much more general if :-moz-only-whitespace
    327  // / :-moz-first-node and :-moz-last-node didn't exist. In that case we only
    328  // need to know whether we went from empty to non-empty, and that's trivial to
    329  // know, with CharacterDataChangeInfo...
    330  if (!aInfo.mAppend) {
    331    // FIXME(emilio): This restyles unnecessarily if the text node is the only
    332    // child of the parent element. Fortunately, it's uncommon to have such
    333    // nodes and this not being an append.
    334    //
    335    // See the testcase in bug 1427625 for a test-case that triggers this.
    336    RestyleForInsertOrChange(aContent);
    337    return;
    338  }
    339 
    340  const CharacterDataBuffer* text = &aContent->AsText()->DataBuffer();
    341 
    342  const size_t oldLength = aInfo.mChangeStart;
    343  const size_t newLength = text->GetLength();
    344 
    345  const bool emptyChanged = !oldLength && newLength;
    346 
    347  const bool whitespaceOnlyChanged =
    348      text->Is2b()
    349          ? WhitespaceOnlyChangedOnAppend(text->Get2b(), oldLength, newLength)
    350          : WhitespaceOnlyChangedOnAppend(text->Get1b(), oldLength, newLength);
    351 
    352  if (!emptyChanged && !whitespaceOnlyChanged) {
    353    return;
    354  }
    355 
    356  if (slowSelectorFlags & NodeSelectorFlags::HasEmptySelector) {
    357    if (!HasAnySignificantSibling(parent->AsElement(), aContent)) {
    358      // We used to be empty, restyle the parent.
    359      RestyleForEmptyChange(parent->AsElement());
    360      return;
    361    }
    362  }
    363 
    364  if (slowSelectorFlags & NodeSelectorFlags::HasEdgeChildSelector) {
    365    MaybeRestyleForEdgeChildChange(parent, aContent);
    366  }
    367 }
    368 
    369 // Restyling for a ContentInserted or CharacterDataChanged notification.
    370 // This could be used for ContentRemoved as well if we got the
    371 // notification before the removal happened (and sometimes
    372 // CharacterDataChanged is more like a removal than an addition).
    373 // The comments are written and variables are named in terms of it being
    374 // a ContentInserted notification.
    375 void RestyleManager::RestyleForInsertOrChange(nsIContent* aChild) {
    376  nsINode* container = aChild->GetParentNode();
    377  MOZ_ASSERT(container);
    378 
    379  const auto selectorFlags =
    380      container->GetSelectorFlags() & NodeSelectorFlags::AllSimpleRestyleFlags;
    381  if (!selectorFlags) {
    382    return;
    383  }
    384 
    385  NS_ASSERTION(!aChild->IsRootOfNativeAnonymousSubtree(),
    386               "anonymous nodes should not be in child lists");
    387 
    388  // The container cannot be a document.
    389  MOZ_ASSERT(container->IsElement() || container->IsShadowRoot());
    390 
    391  if (selectorFlags & NodeSelectorFlags::HasEmptySelector &&
    392      container->IsElement()) {
    393    // See whether we need to restyle the container due to :empty /
    394    // :-moz-only-whitespace.
    395    const bool wasEmpty =
    396        !HasAnySignificantSibling(container->AsElement(), aChild);
    397    if (wasEmpty) {
    398      // FIXME(emilio): When coming from CharacterDataChanged this can restyle
    399      // unnecessarily. Also can restyle unnecessarily if aChild is not
    400      // significant anyway, though that's more unlikely.
    401      RestyleForEmptyChange(container->AsElement());
    402      return;
    403    }
    404  }
    405 
    406  if (selectorFlags & NodeSelectorFlags::HasSlowSelector) {
    407    RestyleWholeContainer(container, selectorFlags);
    408    // Restyling the container is the most we can do here, so we're done.
    409    return;
    410  }
    411 
    412  if (selectorFlags & NodeSelectorFlags::HasSlowSelectorLaterSiblings) {
    413    // Restyle all later siblings.
    414    if (selectorFlags & NodeSelectorFlags::HasSlowSelectorNthAll) {
    415      StyleSet()->MaybeInvalidateRelativeSelectorForNthDependencyFromSibling(
    416          aChild->GetNextElementSibling(), /* aForceRestyleSiblings = */ true);
    417    } else {
    418      RestyleSiblingsStartingWith(aChild->GetNextSibling());
    419    }
    420  }
    421 
    422  if (selectorFlags & NodeSelectorFlags::HasEdgeChildSelector) {
    423    MaybeRestyleForEdgeChildChange(container, aChild);
    424  }
    425 }
    426 
    427 void RestyleManager::ContentWillBeRemoved(nsIContent* aOldChild) {
    428  auto* container = aOldChild->GetParentNode();
    429  MOZ_ASSERT(container);
    430 
    431  // Computed style data isn't useful for detached nodes, and we'll need to
    432  // recompute it anyway if we ever insert the nodes back into a document.
    433  if (auto* element = Element::FromNode(aOldChild)) {
    434    RestyleManager::ClearServoDataFromSubtree(element);
    435    // If this element is undisplayed or may have undisplayed descendants, we
    436    // need to invalidate the cache, since there's the unlikely event of those
    437    // elements getting destroyed and their addresses reused in a way that we
    438    // look up the cache with their address for a different element before it's
    439    // invalidated.
    440    IncrementUndisplayedRestyleGeneration();
    441  }
    442 
    443  // This is called with anonymous nodes explicitly by editor and view
    444  // transitions code, which manage anon content manually.
    445  // See similar code in ContentAppended.
    446  if (MOZ_UNLIKELY(aOldChild->IsRootOfNativeAnonymousSubtree())) {
    447    MOZ_ASSERT(!aOldChild->GetNextSibling(), "NAC doesn't have siblings");
    448    MOZ_ASSERT(aOldChild->GetProperty(nsGkAtoms::restylableAnonymousNode),
    449               "anonymous nodes should not be in child lists (bug 439258)");
    450    return;
    451  }
    452 
    453  if (aOldChild->IsElement()) {
    454    StyleSet()->MaybeInvalidateForElementRemove(*aOldChild->AsElement());
    455  }
    456 
    457  const auto selectorFlags =
    458      container->GetSelectorFlags() & NodeSelectorFlags::AllSimpleRestyleFlags;
    459  if (!selectorFlags) {
    460    return;
    461  }
    462 
    463  // The container cannot be a document.
    464  const bool containerIsElement = container->IsElement();
    465  MOZ_ASSERT(containerIsElement || container->IsShadowRoot());
    466 
    467  if (selectorFlags & NodeSelectorFlags::HasEmptySelector &&
    468      containerIsElement) {
    469    // see whether we need to restyle the container
    470    bool isEmpty = true;  // :empty or :-moz-only-whitespace
    471    for (nsIContent* child = container->GetFirstChild(); child;
    472         child = child->GetNextSibling()) {
    473      // We don't know whether we're testing :empty or :-moz-only-whitespace,
    474      // so be conservative and assume :-moz-only-whitespace (i.e., make
    475      // IsSignificantChild less likely to be true, and thus make us more
    476      // likely to restyle).
    477      if (child != aOldChild && nsStyleUtil::IsSignificantChild(child, false)) {
    478        isEmpty = false;
    479        break;
    480      }
    481    }
    482    if (isEmpty && containerIsElement) {
    483      RestyleForEmptyChange(container->AsElement());
    484      return;
    485    }
    486  }
    487 
    488  // It is somewhat common to remove all nodes in a container from the
    489  // beginning. If we're doing that, going through the
    490  // HasSlowSelectorLaterSiblings code-path would be quadratic, so that's not
    491  // amazing. Instead, we take the slower path (which also restyles the
    492  // container) in that case. It restyles one more element, but it avoids the
    493  // quadratic behavior.
    494  const bool restyleWholeContainer =
    495      (selectorFlags & NodeSelectorFlags::HasSlowSelector) ||
    496      (selectorFlags & NodeSelectorFlags::HasSlowSelectorLaterSiblings &&
    497       !aOldChild->GetPreviousSibling());
    498 
    499  if (restyleWholeContainer) {
    500    RestyleWholeContainer(container, selectorFlags);
    501    // Restyling the container is the most we can do here, so we're done.
    502    return;
    503  }
    504 
    505  if (selectorFlags & NodeSelectorFlags::HasSlowSelectorLaterSiblings) {
    506    // Restyle all later siblings.
    507    if (selectorFlags & NodeSelectorFlags::HasSlowSelectorNthAll) {
    508      Element* nextSibling = aOldChild->GetNextElementSibling();
    509      StyleSet()->MaybeInvalidateRelativeSelectorForNthDependencyFromSibling(
    510          nextSibling, /* aForceRestyleSiblings = */ true);
    511    } else {
    512      RestyleSiblingsStartingWith(aOldChild->GetNextSibling());
    513    }
    514  }
    515 
    516  if (selectorFlags & NodeSelectorFlags::HasEdgeChildSelector) {
    517    const nsIContent* nextSibling = aOldChild->GetNextSibling();
    518    // restyle the now-first element child if it was after aOldChild
    519    bool reachedFollowingSibling = false;
    520    for (nsIContent* content = container->GetFirstChild(); content;
    521         content = content->GetNextSibling()) {
    522      if (content == aOldChild) {
    523        // aOldChild is getting removed, so we don't want to account for it for
    524        // the purposes of computing whether we're now the first / last child.
    525        continue;
    526      }
    527      if (content == nextSibling) {
    528        reachedFollowingSibling = true;
    529        // do NOT continue here; we might want to restyle this node
    530      }
    531      if (content->IsElement()) {
    532        if (reachedFollowingSibling) {
    533          auto* element = content->AsElement();
    534          PostRestyleEvent(element, RestyleHint::RestyleSubtree(),
    535                           nsChangeHint(0));
    536          StyleSet()->MaybeInvalidateRelativeSelectorForNthEdgeDependency(
    537              *element, StyleRelativeSelectorNthEdgeInvalidateFor::First);
    538        }
    539        break;
    540      }
    541    }
    542    // restyle the now-last element child if it was before aOldChild
    543    reachedFollowingSibling = !nextSibling;
    544    for (nsIContent* content = container->GetLastChild(); content;
    545         content = content->GetPreviousSibling()) {
    546      if (content == aOldChild) {
    547        // See above.
    548        continue;
    549      }
    550      if (content->IsElement()) {
    551        if (reachedFollowingSibling) {
    552          auto* element = content->AsElement();
    553          PostRestyleEvent(element, RestyleHint::RestyleSubtree(),
    554                           nsChangeHint(0));
    555          StyleSet()->MaybeInvalidateRelativeSelectorForNthEdgeDependency(
    556              *element, StyleRelativeSelectorNthEdgeInvalidateFor::Last);
    557        }
    558        break;
    559      }
    560      if (content == nextSibling) {
    561        reachedFollowingSibling = true;
    562      }
    563    }
    564  }
    565 }
    566 
    567 static bool StateChangeMayAffectFrame(const Element& aElement,
    568                                      const nsIFrame& aFrame,
    569                                      ElementState aStates) {
    570  const bool brokenChanged = aStates.HasState(ElementState::BROKEN);
    571  if (!brokenChanged) {
    572    return false;
    573  }
    574 
    575  if (aFrame.IsGeneratedContentFrame()) {
    576    // If it's other generated content, ignore state changes on it.
    577    return aElement.IsHTMLElement(nsGkAtoms::mozgeneratedcontentimage);
    578  }
    579 
    580  if (aElement.IsAnyOfHTMLElements(nsGkAtoms::object, nsGkAtoms::embed)) {
    581    // Broken affects object fallback behavior.
    582    return true;
    583  }
    584 
    585  const bool mightChange = [&] {
    586    if (aElement.IsHTMLElement(nsGkAtoms::img)) {
    587      return true;
    588    }
    589    const auto* input = HTMLInputElement::FromNode(aElement);
    590    return input && input->ControlType() == FormControlType::InputImage;
    591  }();
    592 
    593  if (!mightChange) {
    594    return false;
    595  }
    596 
    597  const bool needsImageFrame =
    598      nsImageFrame::ImageFrameTypeFor(aElement, *aFrame.Style()) !=
    599      nsImageFrame::ImageFrameType::None;
    600  return needsImageFrame != aFrame.IsImageFrameOrSubclass();
    601 }
    602 
    603 static bool RepaintForAppearance(nsIFrame& aFrame, const Element& aElement,
    604                                 ElementState aStateMask) {
    605  constexpr auto kThemingStates =
    606      ElementState::HOVER | ElementState::ACTIVE | ElementState::FOCUSRING |
    607      ElementState::DISABLED | ElementState::CHECKED |
    608      ElementState::INDETERMINATE | ElementState::READONLY |
    609      ElementState::FOCUS;
    610  if (!aStateMask.HasAtLeastOneOfStates(kThemingStates)) {
    611    return false;
    612  }
    613 
    614  if (aElement.IsAnyOfXULElements(nsGkAtoms::checkbox, nsGkAtoms::radio)) {
    615    // The checkbox inside these elements inherit hover state and so on, see
    616    // nsNativeTheme::GetContentState.
    617    // FIXME(emilio): Would be nice to not have these hard-coded.
    618    return true;
    619  }
    620  auto appearance = aFrame.StyleDisplay()->EffectiveAppearance();
    621  if (appearance == StyleAppearance::None) {
    622    return false;
    623  }
    624  nsPresContext* pc = aFrame.PresContext();
    625  return pc->Theme()->ThemeSupportsWidget(pc, &aFrame, appearance);
    626 }
    627 
    628 /**
    629 * Calculates the change hint and the restyle hint for a given content state
    630 * change.
    631 */
    632 static nsChangeHint ChangeForContentStateChange(const Element& aElement,
    633                                                ElementState aStateMask) {
    634  auto changeHint = nsChangeHint(0);
    635 
    636  // Any change to a content state that affects which frames we construct
    637  // must lead to a frame reconstruct here if we already have a frame.
    638  // Note that we never decide through non-CSS means to not create frames
    639  // based on content states, so if we already don't have a frame we don't
    640  // need to force a reframe -- if it's needed, the HasStateDependentStyle
    641  // call will handle things.
    642  if (nsIFrame* primaryFrame = aElement.GetPrimaryFrame()) {
    643    if (StateChangeMayAffectFrame(aElement, *primaryFrame, aStateMask)) {
    644      return nsChangeHint_ReconstructFrame;
    645    }
    646    if (RepaintForAppearance(*primaryFrame, aElement, aStateMask)) {
    647      changeHint |= nsChangeHint_RepaintFrame;
    648    }
    649    primaryFrame->ElementStateChanged(aStateMask);
    650  }
    651 
    652  if (aStateMask.HasState(ElementState::VISITED)) {
    653    // Exposing information to the page about whether the link is
    654    // visited or not isn't really something we can worry about here.
    655    // FIXME: We could probably do this a bit better.
    656    changeHint |= nsChangeHint_RepaintFrame;
    657  }
    658 
    659  // This changes the applicable text-transform in the editor root.
    660  if (aStateMask.HasState(ElementState::REVEALED)) {
    661    // This is the same change hint as tweaking text-transform.
    662    changeHint |= NS_STYLE_HINT_REFLOW;
    663  }
    664 
    665  return changeHint;
    666 }
    667 
    668 #ifdef DEBUG
    669 /* static */
    670 nsCString RestyleManager::ChangeHintToString(nsChangeHint aHint) {
    671  nsCString result;
    672  bool any = false;
    673  const char* names[] = {"RepaintFrame",
    674                         "NeedReflow",
    675                         "ClearAncestorIntrinsics",
    676                         "ClearDescendantIntrinsics",
    677                         "NeedDirtyReflow",
    678                         "UpdateCursor",
    679                         "UpdateEffects",
    680                         "UpdateOpacityLayer",
    681                         "UpdateTransformLayer",
    682                         "ReconstructFrame",
    683                         "UpdateOverflow",
    684                         "UpdateSubtreeOverflow",
    685                         "UpdatePostTransformOverflow",
    686                         "UpdateParentOverflow",
    687                         "ChildrenOnlyTransform",
    688                         "RecomputePosition",
    689                         "UpdateContainingBlock",
    690                         "SchedulePaint",
    691                         "NeutralChange",
    692                         "InvalidateRenderingObservers",
    693                         "ReflowChangesSizeOrPosition",
    694                         "UpdateComputedBSize",
    695                         "UpdateUsesOpacity",
    696                         "UpdateBackgroundPosition",
    697                         "AddOrRemoveTransform",
    698                         "ScrollbarChange",
    699                         "UpdateTableCellSpans",
    700                         "VisibilityChange"};
    701  static_assert(nsChangeHint_AllHints ==
    702                    static_cast<uint32_t>((1ull << std::size(names)) - 1),
    703                "Name list doesn't match change hints.");
    704  uint32_t hint = aHint & static_cast<uint32_t>((1ull << std::size(names)) - 1);
    705  uint32_t rest =
    706      aHint & ~static_cast<uint32_t>((1ull << std::size(names)) - 1);
    707  if ((hint & NS_STYLE_HINT_REFLOW) == NS_STYLE_HINT_REFLOW) {
    708    result.AppendLiteral("NS_STYLE_HINT_REFLOW");
    709    hint = hint & ~NS_STYLE_HINT_REFLOW;
    710    any = true;
    711  } else if ((hint & nsChangeHint_AllReflowHints) ==
    712             nsChangeHint_AllReflowHints) {
    713    result.AppendLiteral("nsChangeHint_AllReflowHints");
    714    hint = hint & ~nsChangeHint_AllReflowHints;
    715    any = true;
    716  } else if ((hint & NS_STYLE_HINT_VISUAL) == NS_STYLE_HINT_VISUAL) {
    717    result.AppendLiteral("NS_STYLE_HINT_VISUAL");
    718    hint = hint & ~NS_STYLE_HINT_VISUAL;
    719    any = true;
    720  }
    721  for (uint32_t i = 0; i < std::size(names); i++) {
    722    if (hint & (1u << i)) {
    723      if (any) {
    724        result.AppendLiteral(" | ");
    725      }
    726      result.AppendPrintf("nsChangeHint_%s", names[i]);
    727      any = true;
    728    }
    729  }
    730  if (rest) {
    731    if (any) {
    732      result.AppendLiteral(" | ");
    733    }
    734    result.AppendPrintf("0x%0x", rest);
    735  } else {
    736    if (!any) {
    737      result.AppendLiteral("nsChangeHint(0)");
    738    }
    739  }
    740  return result;
    741 }
    742 #endif
    743 
    744 /**
    745 * Frame construction helpers follow.
    746 */
    747 #ifdef DEBUG
    748 static bool gInApplyRenderingChangeToTree = false;
    749 #endif
    750 
    751 /**
    752 * The change hint should be some combination of nsChangeHint_RepaintFrame,
    753 * nsChangeHint_UpdateOpacityLayer and nsChangeHint_SchedulePaint, nothing else.
    754 */
    755 static void InvalidateDescendants(nsIFrame*, nsChangeHint);
    756 
    757 static void StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint);
    758 
    759 /**
    760 * This helper function is used to find the correct SVG frame to target when we
    761 * encounter nsChangeHint_ChildrenOnlyTransform; needed since sometimes we end
    762 * up handling that hint while processing hints for one of the SVG frame's
    763 * ancestor frames.
    764 *
    765 * The reason that we sometimes end up trying to process the hint for an
    766 * ancestor of the SVG frame that the hint is intended for is due to the way we
    767 * process restyle events. ApplyRenderingChangeToTree adjusts the frame from
    768 * the restyled element's principle frame to one of its ancestor frames based
    769 * on what nsCSSRendering::FindBackground returns, since the background style
    770 * may have been propagated up to an ancestor frame. Processing hints using an
    771 * ancestor frame is fine in general, but nsChangeHint_ChildrenOnlyTransform is
    772 * a special case since it is intended to update a specific frame.
    773 */
    774 static nsIFrame* GetFrameForChildrenOnlyTransformHint(nsIFrame* aFrame) {
    775  if (aFrame->IsViewportFrame()) {
    776    // This happens if the root-<svg> is fixed positioned, in which case we
    777    // can't use aFrame->GetContent() to find the primary frame, since
    778    // GetContent() returns nullptr for ViewportFrame.
    779    aFrame = aFrame->PrincipalChildList().FirstChild();
    780  }
    781  // For a ScrollContainerFrame, this will get the SVG frame that has the
    782  // children-only transforms:
    783  aFrame = aFrame->GetContent()->GetPrimaryFrame();
    784  if (aFrame->IsSVGOuterSVGFrame()) {
    785    aFrame = aFrame->PrincipalChildList().FirstChild();
    786    MOZ_ASSERT(aFrame->IsSVGOuterSVGAnonChildFrame(),
    787               "Where is the SVGOuterSVGFrame's anon child??");
    788  }
    789  MOZ_ASSERT(aFrame->IsSVGContainerFrame(),
    790             "Children-only transforms only expected on SVG frames");
    791  return aFrame;
    792 }
    793 
    794 // This function tries to optimize a position style change by either
    795 // moving aFrame or ignoring the style change when it's safe to do so.
    796 // It returns true when that succeeds, otherwise it posts a reflow request
    797 // and returns false.
    798 static bool RecomputePosition(nsIFrame* aFrame) {
    799  // It's pointless to move around frames that have never been reflowed or
    800  // are dirty (i.e. they will be reflowed), or aren't affected by position
    801  // styles.
    802  if (aFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
    803                              NS_FRAME_SVG_LAYOUT)) {
    804    return true;
    805  }
    806 
    807  // Don't process position changes on table frames, since we already handle
    808  // the dynamic position change on the table wrapper frame, and the
    809  // reflow-based fallback code path also ignores positions on inner table
    810  // frames.
    811  if (aFrame->IsTableFrame()) {
    812    return true;
    813  }
    814 
    815  const nsStyleDisplay* display = aFrame->StyleDisplay();
    816  // Changes to the offsets of a non-positioned element can safely be ignored.
    817  if (display->mPosition == StylePositionProperty::Static) {
    818    return true;
    819  }
    820 
    821  if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
    822    // If the frame has an intrinsic block-size, we resolve its 'auto' margins
    823    // after doing layout, since we need to know the frame's block size. See
    824    // AbsoluteContainingBlock::ResolveAutoMarginsAfterLayout().
    825    //
    826    // Since the size of the frame doesn't change, we could modify the below
    827    // computation to compute the margin correctly without doing a full reflow,
    828    // however we decided to try doing a full reflow for now.
    829    if (aFrame->HasIntrinsicKeywordForBSize()) {
    830      WritingMode wm = aFrame->GetWritingMode();
    831      const auto* styleMargin = aFrame->StyleMargin();
    832      const auto anchorResolutionParams =
    833          AnchorPosResolutionParams::From(aFrame);
    834      if (styleMargin->HasBlockAxisAuto(wm, anchorResolutionParams)) {
    835        return false;
    836      }
    837    }
    838    // Flexbox and Grid layout supports CSS Align and the optimizations below
    839    // don't support that yet.
    840    nsIFrame* ph = aFrame->GetPlaceholderFrame();
    841    if (ph && ph->HasAnyStateBits(PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN)) {
    842      return false;
    843    }
    844  }
    845 
    846  // If we need to reposition any descendant that depends on our static
    847  // position, then we also can't take the optimized path.
    848  //
    849  // TODO(emilio): It may be worth trying to find them and try to call
    850  // RecomputePosition on them too instead of disabling the optimization...
    851  if (aFrame->DescendantMayDependOnItsStaticPosition()) {
    852    return false;
    853  }
    854 
    855  aFrame->SchedulePaint();
    856 
    857  auto postPendingScrollAnchorOrResnap = [](nsIFrame* frame) {
    858    if (frame->IsInScrollAnchorChain()) {
    859      ScrollAnchorContainer* container = ScrollAnchorContainer::FindFor(frame);
    860      frame->PresShell()->PostPendingScrollAnchorAdjustment(container);
    861    }
    862 
    863    // We need to trigger re-snapping to this content if we snapped to the
    864    // content on the last scroll operation.
    865    ScrollSnapUtils::PostPendingResnapIfNeededFor(frame);
    866  };
    867 
    868  // For relative positioning, we can simply update the frame rect
    869  if (display->IsRelativelyOrStickyPositionedStyle()) {
    870    if (aFrame->IsGridItem()) {
    871      // A grid item's CB is its grid area, not the parent frame content area
    872      // as is assumed below.
    873      return false;
    874    }
    875    // Move the frame
    876    if (display->mPosition == StylePositionProperty::Sticky) {
    877      // Update sticky positioning for an entire element at once, starting with
    878      // the first continuation or ib-split sibling.
    879      // It's rare that the frame we already have isn't already the first
    880      // continuation or ib-split sibling, but it can happen when styles differ
    881      // across continuations such as ::first-line or ::first-letter, and in
    882      // those cases we will generally (but maybe not always) do the work twice.
    883      nsIFrame* firstContinuation =
    884          nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
    885 
    886      StickyScrollContainer::ComputeStickyOffsets(firstContinuation);
    887      auto* ssc = StickyScrollContainer::GetOrCreateForFrame(firstContinuation);
    888      if (ssc) {
    889        ssc->PositionContinuations(firstContinuation);
    890      }
    891    } else {
    892      MOZ_ASSERT(display->IsRelativelyPositionedStyle(),
    893                 "Unexpected type of positioning");
    894      for (nsIFrame* cont = aFrame; cont;
    895           cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
    896        nsIFrame* cb = cont->GetContainingBlock();
    897        WritingMode wm = cb->GetWritingMode();
    898        const LogicalSize cbSize = cb->ContentSize();
    899        const LogicalMargin newLogicalOffsets =
    900            ReflowInput::ComputeRelativeOffsets(wm, cont, cbSize);
    901        const nsMargin newOffsets = newLogicalOffsets.GetPhysicalMargin(wm);
    902 
    903        // ReflowInput::ApplyRelativePositioning would work here, but
    904        // since we've already checked mPosition and aren't changing the frame's
    905        // normal position, go ahead and add the offsets directly.
    906        // First, we need to ensure that the normal position is stored though.
    907        bool hasProperty;
    908        nsPoint normalPosition = cont->GetNormalPosition(&hasProperty);
    909        if (!hasProperty) {
    910          cont->AddProperty(nsIFrame::NormalPositionProperty(), normalPosition);
    911        }
    912        cont->SetPosition(normalPosition +
    913                          nsPoint(newOffsets.left, newOffsets.top));
    914      }
    915    }
    916 
    917    postPendingScrollAnchorOrResnap(aFrame);
    918    return true;
    919  }
    920 
    921  // For the absolute positioning case, set up a fake HTML reflow input for
    922  // the frame, and then get the offsets and size from it. If the frame's size
    923  // doesn't need to change, we can simply update the frame position. Otherwise
    924  // we fall back to a reflow.
    925  UniquePtr<gfxContext> rc =
    926      aFrame->PresShell()->CreateReferenceRenderingContext();
    927 
    928  // Construct a bogus parent reflow input so that there's a usable reflow input
    929  // for the containing block.
    930  nsIFrame* parentFrame = aFrame->GetParent();
    931  WritingMode parentWM = parentFrame->GetWritingMode();
    932  WritingMode frameWM = aFrame->GetWritingMode();
    933  LogicalSize parentSize = parentFrame->GetLogicalSize();
    934 
    935  nsFrameState savedState = parentFrame->GetStateBits();
    936  ReflowInput parentReflowInput(aFrame->PresContext(), parentFrame, rc.get(),
    937                                parentSize);
    938  parentFrame->RemoveStateBits(~nsFrameState(0));
    939  parentFrame->AddStateBits(savedState);
    940 
    941  // The bogus parent state here was created with no parent state of its own,
    942  // and therefore it won't have an mCBReflowInput set up.
    943  // But we may need one (for InitCBReflowInput in a child state), so let's
    944  // try to create one here for the cases where it will be needed.
    945  Maybe<ReflowInput> cbReflowInput;
    946  nsIFrame* cbFrame = parentFrame->GetContainingBlock();
    947  if (cbFrame && (aFrame->GetContainingBlock() != parentFrame ||
    948                  parentFrame->IsTableFrame())) {
    949    const auto cbWM = cbFrame->GetWritingMode();
    950    LogicalSize cbSize = cbFrame->GetLogicalSize();
    951    cbReflowInput.emplace(cbFrame->PresContext(), cbFrame, rc.get(), cbSize);
    952    cbReflowInput->SetComputedLogicalMargin(
    953        cbWM, cbFrame->GetLogicalUsedMargin(cbWM));
    954    cbReflowInput->SetComputedLogicalPadding(
    955        cbWM, cbFrame->GetLogicalUsedPadding(cbWM));
    956    cbReflowInput->SetComputedLogicalBorderPadding(
    957        cbWM, cbFrame->GetLogicalUsedBorderAndPadding(cbWM));
    958    parentReflowInput.mCBReflowInput = cbReflowInput.ptr();
    959  }
    960 
    961  NS_WARNING_ASSERTION(parentSize.ISize(parentWM) != NS_UNCONSTRAINEDSIZE &&
    962                           parentSize.BSize(parentWM) != NS_UNCONSTRAINEDSIZE,
    963                       "parentSize should be valid");
    964  parentReflowInput.SetComputedISize(std::max(parentSize.ISize(parentWM), 0));
    965  parentReflowInput.SetComputedBSize(std::max(parentSize.BSize(parentWM), 0));
    966  parentReflowInput.SetComputedLogicalMargin(parentWM, LogicalMargin(parentWM));
    967 
    968  parentReflowInput.SetComputedLogicalPadding(
    969      parentWM, parentFrame->GetLogicalUsedPadding(parentWM));
    970  parentReflowInput.SetComputedLogicalBorderPadding(
    971      parentWM, parentFrame->GetLogicalUsedBorderAndPadding(parentWM));
    972  LogicalSize availSize = parentSize.ConvertTo(frameWM, parentWM);
    973  availSize.BSize(frameWM) = NS_UNCONSTRAINEDSIZE;
    974 
    975  ViewportFrame* viewport = do_QueryFrame(parentFrame);
    976  nsSize cbSize =
    977      viewport
    978          ? viewport->GetContainingBlockAdjustedForScrollbars(parentReflowInput)
    979                .Size()
    980          : aFrame->GetContainingBlock()->GetSize();
    981  const nsMargin& parentBorder =
    982      parentReflowInput.mStyleBorder->GetComputedBorder();
    983  cbSize -= nsSize(parentBorder.LeftRight(), parentBorder.TopBottom());
    984  LogicalSize lcbSize(frameWM, cbSize);
    985  ReflowInput reflowInput(aFrame->PresContext(), parentReflowInput, aFrame,
    986                          availSize, Some(lcbSize));
    987  nscoord computedISize = reflowInput.ComputedISize();
    988  nscoord computedBSize = reflowInput.ComputedBSize();
    989  const auto frameBP = reflowInput.ComputedLogicalBorderPadding(frameWM);
    990  computedISize += frameBP.IStartEnd(frameWM);
    991  if (computedBSize != NS_UNCONSTRAINEDSIZE) {
    992    computedBSize += frameBP.BStartEnd(frameWM);
    993  }
    994  LogicalSize logicalSize = aFrame->GetLogicalSize(frameWM);
    995  nsSize size = aFrame->GetSize();
    996  // The RecomputePosition hint is not used if any offset changed between auto
    997  // and non-auto. If computedSize.height == NS_UNCONSTRAINEDSIZE then the new
    998  // element height will be its intrinsic height, and since 'top' and 'bottom''s
    999  // auto-ness hasn't changed, the old height must also be its intrinsic
   1000  // height, which we can assume hasn't changed (or reflow would have
   1001  // been triggered).
   1002  if (computedISize == logicalSize.ISize(frameWM) &&
   1003      (computedBSize == NS_UNCONSTRAINEDSIZE ||
   1004       computedBSize == logicalSize.BSize(frameWM))) {
   1005    // If we're solving for 'left' or 'top', then compute it here, in order to
   1006    // match the reflow code path.
   1007    //
   1008    // TODO(emilio): It'd be nice if this did logical math instead, but it seems
   1009    // to me the math should work out on vertical writing modes as well. See Bug
   1010    // 1675861 for some hints.
   1011    const nsMargin offset = reflowInput.ComputedPhysicalOffsets();
   1012    const nsMargin margin = reflowInput.ComputedPhysicalMargin();
   1013 
   1014    nscoord left = offset.left;
   1015    if (left == NS_AUTOOFFSET) {
   1016      left =
   1017          cbSize.width - offset.right - margin.right - size.width - margin.left;
   1018    }
   1019 
   1020    nscoord top = offset.top;
   1021    if (top == NS_AUTOOFFSET) {
   1022      top = cbSize.height - offset.bottom - margin.bottom - size.height -
   1023            margin.top;
   1024    }
   1025 
   1026    // Move the frame
   1027    nsPoint pos(parentBorder.left + left + margin.left,
   1028                parentBorder.top + top + margin.top);
   1029    aFrame->SetPosition(pos);
   1030 
   1031    postPendingScrollAnchorOrResnap(aFrame);
   1032    return true;
   1033  }
   1034 
   1035  // Fall back to a reflow
   1036  return false;
   1037 }
   1038 
   1039 /**
   1040 * Return true if aFrame's subtree has placeholders for out-of-flow content
   1041 * that would be affected due to the change to
   1042 * `aPossiblyChangingContainingBlock` (and thus would need to get reframed).
   1043 *
   1044 * In particular, this function returns true if there are placeholders whose OOF
   1045 * frames may need to be reparented (via reframing) as a result of whatever
   1046 * change actually happened.
   1047 *
   1048 * The `aIs{Abs,Fixed}PosContainingBlock` params represent whether
   1049 * `aPossiblyChangingContainingBlock` is a containing block for abs pos / fixed
   1050 * pos stuff, respectively, for the _new_ style that the frame already has, not
   1051 * the old one.
   1052 */
   1053 static bool ContainingBlockChangeAffectsDescendants(
   1054    nsIFrame* aPossiblyChangingContainingBlock, nsIFrame* aFrame,
   1055    bool aIsAbsPosContainingBlock, bool aIsFixedPosContainingBlock) {
   1056  // All fixed-pos containing blocks should also be abs-pos containing blocks.
   1057  MOZ_ASSERT_IF(aIsFixedPosContainingBlock, aIsAbsPosContainingBlock);
   1058 
   1059  for (const auto& childList : aFrame->ChildLists()) {
   1060    for (nsIFrame* f : childList.mList) {
   1061      if (f->IsPlaceholderFrame()) {
   1062        nsIFrame* outOfFlow = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
   1063        // If SVG text frames could appear here, they could confuse us since
   1064        // they ignore their position style ... but they can't.
   1065        NS_ASSERTION(!outOfFlow->IsInSVGTextSubtree(),
   1066                     "SVG text frames can't be out of flow");
   1067        // Top-layer frames don't change containing block based on direct
   1068        // ancestors.
   1069        auto* display = outOfFlow->StyleDisplay();
   1070        if (display->IsAbsolutelyPositionedStyle() &&
   1071            display->mTopLayer == StyleTopLayer::None) {
   1072          const bool isContainingBlock =
   1073              aIsFixedPosContainingBlock ||
   1074              (aIsAbsPosContainingBlock &&
   1075               display->mPosition == StylePositionProperty::Absolute);
   1076          // NOTE(emilio): aPossiblyChangingContainingBlock is guaranteed to be
   1077          // a first continuation, see the assertion in the caller.
   1078          nsIFrame* parent = outOfFlow->GetParent()->FirstContinuation();
   1079          if (isContainingBlock) {
   1080            // If we are becoming a containing block, we only need to reframe if
   1081            // this oof's current containing block is an ancestor of the new
   1082            // frame.
   1083            if (parent != aPossiblyChangingContainingBlock &&
   1084                nsLayoutUtils::IsProperAncestorFrame(
   1085                    parent, aPossiblyChangingContainingBlock)) {
   1086              return true;
   1087            }
   1088          } else {
   1089            // If we are not a containing block anymore, we only need to reframe
   1090            // if we are the current containing block of the oof frame.
   1091            if (parent == aPossiblyChangingContainingBlock) {
   1092              return true;
   1093            }
   1094          }
   1095        }
   1096      }
   1097      // NOTE:  It's tempting to check f->IsAbsPosContainingBlock() or
   1098      // f->IsFixedPosContainingBlock() here.  However, that would only
   1099      // be testing the *new* style of the frame, which might exclude
   1100      // descendants that currently have this frame as an abs-pos
   1101      // containing block.  Taking the codepath where we don't reframe
   1102      // could lead to an unsafe call to
   1103      // cont->MarkAsNotAbsoluteContainingBlock() before we've reframed
   1104      // the descendant and taken it off the absolute list.
   1105      if (ContainingBlockChangeAffectsDescendants(
   1106              aPossiblyChangingContainingBlock, f, aIsAbsPosContainingBlock,
   1107              aIsFixedPosContainingBlock)) {
   1108        return true;
   1109      }
   1110    }
   1111  }
   1112  return false;
   1113 }
   1114 
   1115 // Returns the frame that would serve as the containing block for aFrame's
   1116 // positioned descendants, if aFrame had styles to make it a CB for such
   1117 // descendants. (Typically this is just aFrame itself, or its insertion frame).
   1118 //
   1119 // Returns nullptr if this frame can't be easily determined.
   1120 static nsIFrame* ContainingBlockForFrame(nsIFrame* aFrame) {
   1121  if (aFrame->IsFieldSetFrame()) {
   1122    // FIXME: This should be easily implementable.
   1123    return nullptr;
   1124  }
   1125  nsIFrame* insertionFrame = aFrame->GetContentInsertionFrame();
   1126  if (insertionFrame == aFrame) {
   1127    return insertionFrame;
   1128  }
   1129  // Generally frames with a different insertion frame are hard to deal with,
   1130  // but scrollframes are easy because the containing block is just the
   1131  // insertion frame.
   1132  if (aFrame->IsScrollContainerFrame()) {
   1133    return insertionFrame;
   1134  }
   1135  // Table cell frames are also easy because the containing block is
   1136  // the frame itself.
   1137  if (aFrame->IsTableCellFrame()) {
   1138    return aFrame;
   1139  }
   1140  return nullptr;
   1141 }
   1142 
   1143 static bool NeedToReframeToUpdateContainingBlock(nsIFrame* aFrame,
   1144                                                 nsIFrame* aMaybeChangingCB) {
   1145  // NOTE: This looks at the new style.
   1146  const bool isFixedContainingBlock = aFrame->IsFixedPosContainingBlock();
   1147  MOZ_ASSERT_IF(isFixedContainingBlock, aFrame->IsAbsPosContainingBlock());
   1148 
   1149  const bool isAbsPosContainingBlock =
   1150      isFixedContainingBlock || aFrame->IsAbsPosContainingBlock();
   1151 
   1152  for (nsIFrame* f = aFrame; f;
   1153       f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
   1154    if (ContainingBlockChangeAffectsDescendants(aMaybeChangingCB, f,
   1155                                                isAbsPosContainingBlock,
   1156                                                isFixedContainingBlock)) {
   1157      return true;
   1158    }
   1159  }
   1160  return false;
   1161 }
   1162 
   1163 static void DoApplyRenderingChangeToTree(nsIFrame* aFrame,
   1164                                         nsChangeHint aChange) {
   1165  MOZ_ASSERT(gInApplyRenderingChangeToTree,
   1166             "should only be called within ApplyRenderingChangeToTree");
   1167 
   1168  for (; aFrame;
   1169       aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) {
   1170    // Invalidate and sync views on all descendant frames, following
   1171    // placeholders. We don't need to update transforms in
   1172    // InvalidateDescendants, because there can't be any
   1173    // out-of-flows or popups that need to be transformed; all out-of-flow
   1174    // descendants of the transformed element must also be descendants of the
   1175    // transformed frame.
   1176    InvalidateDescendants(
   1177        aFrame, nsChangeHint(aChange & (nsChangeHint_RepaintFrame |
   1178                                        nsChangeHint_UpdateOpacityLayer |
   1179                                        nsChangeHint_SchedulePaint)));
   1180    // This must be set to true if the rendering change needs to
   1181    // invalidate content.  If it's false, a composite-only paint
   1182    // (empty transaction) will be scheduled.
   1183    bool needInvalidatingPaint = false;
   1184 
   1185    // if frame has view, will already be invalidated
   1186    if (aChange & nsChangeHint_RepaintFrame) {
   1187      // Note that this whole block will be skipped when painting is suppressed
   1188      // (due to our caller ApplyRendingChangeToTree() discarding the
   1189      // nsChangeHint_RepaintFrame hint).  If you add handling for any other
   1190      // hints within this block, be sure that they too should be ignored when
   1191      // painting is suppressed.
   1192      needInvalidatingPaint = true;
   1193      aFrame->InvalidateFrameSubtree();
   1194      if ((aChange & nsChangeHint_UpdateEffects) &&
   1195          aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
   1196        // Need to update our overflow rects:
   1197        SVGUtils::ScheduleReflowSVG(aFrame);
   1198      }
   1199 
   1200      ActiveLayerTracker::NotifyNeedsRepaint(aFrame);
   1201    }
   1202    if (aChange & nsChangeHint_UpdateOpacityLayer) {
   1203      // FIXME/bug 796697: we can get away with empty transactions for
   1204      // opacity updates in many cases.
   1205      needInvalidatingPaint = true;
   1206 
   1207      ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_opacity);
   1208      if (SVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
   1209        // SVG effects paints the opacity without using
   1210        // nsDisplayOpacity. We need to invalidate manually.
   1211        aFrame->InvalidateFrameSubtree();
   1212      }
   1213    }
   1214    if ((aChange & nsChangeHint_UpdateTransformLayer) &&
   1215        aFrame->IsTransformed()) {
   1216      // Note: All the transform-like properties should map to the same
   1217      // layer activity index, so does the restyle count. Therefore, using
   1218      // eCSSProperty_transform should be fine.
   1219      ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_transform);
   1220      needInvalidatingPaint = true;
   1221    }
   1222    if (aChange & nsChangeHint_ChildrenOnlyTransform) {
   1223      needInvalidatingPaint = true;
   1224      nsIFrame* childFrame = GetFrameForChildrenOnlyTransformHint(aFrame)
   1225                                 ->PrincipalChildList()
   1226                                 .FirstChild();
   1227      for (; childFrame; childFrame = childFrame->GetNextSibling()) {
   1228        // Note: All the transform-like properties should map to the same
   1229        // layer activity index, so does the restyle count. Therefore, using
   1230        // eCSSProperty_transform should be fine.
   1231        ActiveLayerTracker::NotifyRestyle(childFrame, eCSSProperty_transform);
   1232      }
   1233    }
   1234    if (aChange & nsChangeHint_SchedulePaint) {
   1235      needInvalidatingPaint = true;
   1236    }
   1237    aFrame->SchedulePaint(needInvalidatingPaint
   1238                              ? nsIFrame::PAINT_DEFAULT
   1239                              : nsIFrame::PAINT_COMPOSITE_ONLY);
   1240  }
   1241 }
   1242 
   1243 static void InvalidateDescendants(nsIFrame* aFrame, nsChangeHint aChange) {
   1244  MOZ_ASSERT(gInApplyRenderingChangeToTree,
   1245             "should only be called within ApplyRenderingChangeToTree");
   1246 
   1247  NS_ASSERTION(nsChangeHint_size_t(aChange) ==
   1248                   (aChange & (nsChangeHint_RepaintFrame |
   1249                               nsChangeHint_UpdateOpacityLayer |
   1250                               nsChangeHint_SchedulePaint)),
   1251               "Invalid change flag");
   1252 
   1253  for (const auto& [list, listID] : aFrame->ChildLists()) {
   1254    for (nsIFrame* child : list) {
   1255      if (!child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
   1256        // only do frames that don't have placeholders
   1257        if (child->IsPlaceholderFrame()) {
   1258          // do the out-of-flow frame and its continuations
   1259          nsIFrame* outOfFlowFrame =
   1260              nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
   1261          DoApplyRenderingChangeToTree(outOfFlowFrame, aChange);
   1262        } else {  // regular frame
   1263          InvalidateDescendants(child, aChange);
   1264        }
   1265      }
   1266    }
   1267  }
   1268 }
   1269 
   1270 static void ApplyRenderingChangeToTree(PresShell* aPresShell, nsIFrame* aFrame,
   1271                                       nsChangeHint aChange) {
   1272  // We check StyleDisplay()->HasTransformStyle() in addition to checking
   1273  // IsTransformed() since we can get here for some frames that don't support
   1274  // CSS transforms, and table frames, which are their own odd-ball, since the
   1275  // transform is handled by their wrapper, which _also_ gets a separate hint.
   1276  NS_ASSERTION(!(aChange & nsChangeHint_UpdateTransformLayer) ||
   1277                   aFrame->IsTransformed() ||
   1278                   aFrame->StyleDisplay()->HasTransformStyle(),
   1279               "Unexpected UpdateTransformLayer hint");
   1280 
   1281  if (aPresShell->IsPaintingSuppressed()) {
   1282    // Don't allow synchronous rendering changes when painting is turned off.
   1283    aChange &= ~nsChangeHint_RepaintFrame;
   1284    if (!aChange) {
   1285      return;
   1286    }
   1287  }
   1288 
   1289 // Trigger rendering updates by damaging this frame and any
   1290 // continuations of this frame.
   1291 #ifdef DEBUG
   1292  gInApplyRenderingChangeToTree = true;
   1293 #endif
   1294  if (aChange & nsChangeHint_RepaintFrame) {
   1295    // If the frame is the primary frame of either the body element or
   1296    // the html element, we propagate the repaint change hint to the
   1297    // viewport. This is necessary for background and scrollbar colors
   1298    // propagation.
   1299    if (aFrame->ShouldPropagateRepaintsToRoot()) {
   1300      nsIFrame* rootFrame = aPresShell->GetRootFrame();
   1301      MOZ_ASSERT(rootFrame, "No root frame?");
   1302      DoApplyRenderingChangeToTree(rootFrame, nsChangeHint_RepaintFrame);
   1303      aChange &= ~nsChangeHint_RepaintFrame;
   1304      if (!aChange) {
   1305        return;
   1306      }
   1307    }
   1308  }
   1309  DoApplyRenderingChangeToTree(aFrame, aChange);
   1310 #ifdef DEBUG
   1311  gInApplyRenderingChangeToTree = false;
   1312 #endif
   1313 }
   1314 
   1315 static void AddSubtreeToOverflowTracker(
   1316    nsIFrame* aFrame, OverflowChangedTracker& aOverflowChangedTracker) {
   1317  if (aFrame->FrameMaintainsOverflow()) {
   1318    aOverflowChangedTracker.AddFrame(aFrame,
   1319                                     OverflowChangedTracker::CHILDREN_CHANGED);
   1320  }
   1321  for (const auto& childList : aFrame->ChildLists()) {
   1322    for (nsIFrame* child : childList.mList) {
   1323      AddSubtreeToOverflowTracker(child, aOverflowChangedTracker);
   1324    }
   1325  }
   1326 }
   1327 
   1328 static void StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint) {
   1329  IntrinsicDirty dirtyType;
   1330  if (aHint & nsChangeHint_ClearDescendantIntrinsics) {
   1331    NS_ASSERTION(aHint & nsChangeHint_ClearAncestorIntrinsics,
   1332                 "Please read the comments in nsChangeHint.h");
   1333    NS_ASSERTION(aHint & nsChangeHint_NeedDirtyReflow,
   1334                 "ClearDescendantIntrinsics requires NeedDirtyReflow");
   1335    dirtyType = IntrinsicDirty::FrameAncestorsAndDescendants;
   1336  } else if ((aHint & nsChangeHint_UpdateComputedBSize) &&
   1337             aFrame->HasAnyStateBits(
   1338                 NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
   1339    dirtyType = IntrinsicDirty::FrameAncestorsAndDescendants;
   1340  } else if (aHint & nsChangeHint_ClearAncestorIntrinsics) {
   1341    dirtyType = IntrinsicDirty::FrameAndAncestors;
   1342  } else {
   1343    dirtyType = IntrinsicDirty::None;
   1344  }
   1345 
   1346  if (aHint & nsChangeHint_UpdateComputedBSize) {
   1347    aFrame->SetHasBSizeChange(true);
   1348  }
   1349 
   1350  nsFrameState dirtyBits;
   1351  if (aFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
   1352    dirtyBits = nsFrameState(0);
   1353  } else if ((aHint & nsChangeHint_NeedDirtyReflow) ||
   1354             dirtyType == IntrinsicDirty::FrameAncestorsAndDescendants) {
   1355    dirtyBits = NS_FRAME_IS_DIRTY;
   1356  } else {
   1357    dirtyBits = NS_FRAME_HAS_DIRTY_CHILDREN;
   1358  }
   1359 
   1360  // If we're not going to clear any intrinsic sizes on the frames, and
   1361  // there are no dirty bits to set, then there's nothing to do.
   1362  if (dirtyType == IntrinsicDirty::None && !dirtyBits) {
   1363    return;
   1364  }
   1365 
   1366  ReflowRootHandling rootHandling;
   1367  if (aHint & nsChangeHint_ReflowChangesSizeOrPosition) {
   1368    rootHandling = ReflowRootHandling::PositionOrSizeChange;
   1369  } else {
   1370    rootHandling = ReflowRootHandling::NoPositionOrSizeChange;
   1371  }
   1372 
   1373  do {
   1374    aFrame->PresShell()->FrameNeedsReflow(aFrame, dirtyType, dirtyBits,
   1375                                          rootHandling);
   1376    aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
   1377  } while (aFrame);
   1378 }
   1379 
   1380 // Get the next sibling which might have a frame.  This only considers siblings
   1381 // that stylo post-traversal looks at, so only elements and text.  In
   1382 // particular, it ignores comments.
   1383 static nsIContent* NextSiblingWhichMayHaveFrame(nsIContent* aContent) {
   1384  for (nsIContent* next = aContent->GetNextSibling(); next;
   1385       next = next->GetNextSibling()) {
   1386    if (next->IsElement() || next->IsText()) {
   1387      return next;
   1388    }
   1389  }
   1390 
   1391  return nullptr;
   1392 }
   1393 
   1394 // If |aFrame| is dirty or has dirty children, then we can skip updating
   1395 // overflows since that will happen when it's reflowed.
   1396 static inline bool CanSkipOverflowUpdates(const nsIFrame* aFrame) {
   1397  return aFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY |
   1398                                 NS_FRAME_HAS_DIRTY_CHILDREN);
   1399 }
   1400 
   1401 static inline void TryToDealWithScrollbarChange(nsChangeHint& aHint,
   1402                                                nsIContent* aContent,
   1403                                                nsIFrame* aFrame,
   1404                                                nsPresContext* aPc) {
   1405  if (!(aHint & nsChangeHint_ScrollbarChange)) {
   1406    return;
   1407  }
   1408  aHint &= ~nsChangeHint_ScrollbarChange;
   1409  if (aHint & nsChangeHint_ReconstructFrame) {
   1410    return;
   1411  }
   1412 
   1413  MOZ_ASSERT(aFrame, "If we're not reframing, we ought to have a frame");
   1414 
   1415  const bool isRoot = aContent->IsInUncomposedDoc() && !aContent->GetParent();
   1416 
   1417  // Only bother with this if we're the root or the body element, since:
   1418  //  (a) It'd be *expensive* to reframe these particular nodes.  They're
   1419  //      at the root, so reframing would mean rebuilding the world.
   1420  //  (b) It's often *unnecessary* to reframe for "overflow" changes on
   1421  //      these particular nodes.  In general, the only reason we reframe
   1422  //      for "overflow" changes is so we can construct (or destroy) a
   1423  //      scrollframe & scrollbars -- and the html/body nodes often don't
   1424  //      need their own scrollframe/scrollbars because they coopt the ones
   1425  //      on the viewport (which always exist). So depending on whether
   1426  //      that's happening, we can skip the reframe for these nodes.
   1427  if (isRoot || aContent->IsHTMLElement(nsGkAtoms::body)) {
   1428    // If the restyled element provided/provides the scrollbar styles for
   1429    // the viewport before and/or after this restyle, AND it's not coopting
   1430    // that responsibility from some other element (which would need
   1431    // reconstruction to make its own scrollframe now), THEN: we don't need
   1432    // to reconstruct - we can just reflow, because no scrollframe is being
   1433    // added/removed.
   1434    Element* prevOverride = aPc->GetViewportScrollStylesOverrideElement();
   1435    Element* newOverride = aPc->UpdateViewportScrollStylesOverride();
   1436 
   1437    const auto ProvidesScrollbarStyles = [&](nsIContent* aOverride) {
   1438      if (aOverride) {
   1439        return aOverride == aContent;
   1440      }
   1441      return isRoot;
   1442    };
   1443 
   1444    if (ProvidesScrollbarStyles(prevOverride) ||
   1445        ProvidesScrollbarStyles(newOverride)) {
   1446      // If we get here, the restyled element provided the scrollbar styles
   1447      // for viewport before this restyle, OR it will provide them after.
   1448      if (!prevOverride || !newOverride || prevOverride == newOverride) {
   1449        // If we get here, the restyled element is NOT replacing (or being
   1450        // replaced by) some other element as the viewport's
   1451        // scrollbar-styles provider. (If it were, we'd potentially need to
   1452        // reframe to create a dedicated scrollframe for whichever element
   1453        // is being booted from providing viewport scrollbar styles.)
   1454        //
   1455        // Under these conditions, we're OK to assume that this "overflow"
   1456        // change only impacts the root viewport's scrollframe, which
   1457        // already exists, so we can simply reflow instead of reframing.
   1458        if (ScrollContainerFrame* sf = do_QueryFrame(aFrame)) {
   1459          sf->MarkScrollbarsDirtyForReflow();
   1460        } else if (ScrollContainerFrame* sf =
   1461                       aPc->PresShell()->GetRootScrollContainerFrame()) {
   1462          sf->MarkScrollbarsDirtyForReflow();
   1463        }
   1464        aHint |= nsChangeHint_ReflowHintsForScrollbarChange;
   1465      } else {
   1466        // If we changed the override element, we need to reconstruct as the old
   1467        // override element might start / stop being scrollable.
   1468        aHint |= nsChangeHint_ReconstructFrame;
   1469      }
   1470      return;
   1471    }
   1472  }
   1473 
   1474  const bool scrollable = aFrame->StyleDisplay()->IsScrollableOverflow();
   1475  if (ScrollContainerFrame* sf = do_QueryFrame(aFrame)) {
   1476    if (scrollable && sf->HasAllNeededScrollbars()) {
   1477      sf->MarkScrollbarsDirtyForReflow();
   1478      // Once we've created scrollbars for a frame, don't bother reconstructing
   1479      // it just to remove them if we still need a scroll frame.
   1480      aHint |= nsChangeHint_ReflowHintsForScrollbarChange;
   1481      return;
   1482    }
   1483  } else if (aFrame->IsTextInputFrame()) {
   1484    // input / textarea for the most part don't honor overflow themselves, the
   1485    // editor root will deal with the change if needed.
   1486    // However the textarea intrinsic size relies on GetDesiredScrollbarSizes(),
   1487    // so we need to reflow the textarea itself, not just the inner control.
   1488    aHint |= nsChangeHint_ReflowHintsForScrollbarChange;
   1489    return;
   1490  } else if (!scrollable) {
   1491    // Something changed, but we don't have nor will have a scroll frame,
   1492    // there's nothing to do here.
   1493    return;
   1494  }
   1495 
   1496  // Oh well, we couldn't optimize it out, just reconstruct frames for the
   1497  // subtree.
   1498  aHint |= nsChangeHint_ReconstructFrame;
   1499 }
   1500 
   1501 static void TryToHandleContainingBlockChange(nsChangeHint& aHint,
   1502                                             nsIFrame* aFrame) {
   1503  if (!(aHint & nsChangeHint_UpdateContainingBlock)) {
   1504    return;
   1505  }
   1506  if (aHint & nsChangeHint_ReconstructFrame) {
   1507    return;
   1508  }
   1509  MOZ_ASSERT(aFrame, "If we're not reframing, we ought to have a frame");
   1510  nsIFrame* containingBlock = ContainingBlockForFrame(aFrame);
   1511  if (!containingBlock ||
   1512      NeedToReframeToUpdateContainingBlock(aFrame, containingBlock)) {
   1513    // The frame has positioned children that need to be reparented, or it can't
   1514    // easily be converted to/from being an abs-pos container correctly.
   1515    aHint |= nsChangeHint_ReconstructFrame;
   1516    return;
   1517  }
   1518  const bool isCb = aFrame->IsAbsPosContainingBlock();
   1519 
   1520  // The absolute container should be containingBlock.
   1521  for (nsIFrame* cont = containingBlock; cont;
   1522       cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
   1523    // Normally frame construction would set state bits as needed,
   1524    // but we're not going to reconstruct the frame so we need to set
   1525    // them. It's because we need to set this state on each affected frame
   1526    // that we can't coalesce nsChangeHint_UpdateContainingBlock hints up
   1527    // to ancestors (i.e. it can't be an change hint that is handled for
   1528    // descendants).
   1529    if (isCb) {
   1530      if (!cont->IsAbsoluteContainer() &&
   1531          cont->HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) {
   1532        cont->MarkAsAbsoluteContainingBlock();
   1533      }
   1534    } else if (cont->IsAbsoluteContainer()) {
   1535      if (cont->HasAbsolutelyPositionedChildren()) {
   1536        // If |cont| still has absolutely positioned children,
   1537        // we can't call MarkAsNotAbsoluteContainingBlock.  This
   1538        // will remove a frame list that still has children in
   1539        // it that we need to keep track of.
   1540        // The optimization of removing it isn't particularly
   1541        // important, although it does mean we skip some tests.
   1542        NS_WARNING("skipping removal of absolute containing block");
   1543      } else {
   1544        cont->MarkAsNotAbsoluteContainingBlock();
   1545      }
   1546    }
   1547  }
   1548 }
   1549 
   1550 void RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList) {
   1551  NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
   1552               "Someone forgot a script blocker");
   1553 
   1554  // See bug 1378219 comment 9:
   1555  // Recursive calls here are a bit worrying, but apparently do happen in the
   1556  // wild (although not currently in any of our automated tests). Try to get a
   1557  // stack from Nightly/Dev channel to figure out what's going on and whether
   1558  // it's OK.
   1559  MOZ_DIAGNOSTIC_ASSERT(!mDestroyedFrames, "ProcessRestyledFrames recursion");
   1560 
   1561  if (aChangeList.IsEmpty()) {
   1562    return;
   1563  }
   1564 
   1565  // If mDestroyedFrames is null, we want to create a new hashtable here
   1566  // and destroy it on exit; but if it is already non-null (because we're in
   1567  // a recursive call), we will continue to use the existing table to
   1568  // accumulate destroyed frames, and NOT clear mDestroyedFrames on exit.
   1569  // We use a MaybeClearDestroyedFrames helper to conditionally reset the
   1570  // mDestroyedFrames pointer when this method returns.
   1571  typedef decltype(mDestroyedFrames) DestroyedFramesT;
   1572  class MOZ_RAII MaybeClearDestroyedFrames {
   1573   private:
   1574    DestroyedFramesT& mDestroyedFramesRef;  // ref to caller's mDestroyedFrames
   1575    const bool mResetOnDestruction;
   1576 
   1577   public:
   1578    explicit MaybeClearDestroyedFrames(DestroyedFramesT& aTarget)
   1579        : mDestroyedFramesRef(aTarget),
   1580          mResetOnDestruction(!aTarget)  // reset only if target starts out null
   1581    {}
   1582    ~MaybeClearDestroyedFrames() {
   1583      if (mResetOnDestruction) {
   1584        mDestroyedFramesRef.reset(nullptr);
   1585      }
   1586    }
   1587  };
   1588 
   1589  MaybeClearDestroyedFrames maybeClear(mDestroyedFrames);
   1590  if (!mDestroyedFrames) {
   1591    mDestroyedFrames = MakeUnique<nsTHashSet<const nsIFrame*>>();
   1592  }
   1593 
   1594  AUTO_PROFILER_LABEL("RestyleManager::ProcessRestyledFrames", LAYOUT);
   1595 
   1596  nsPresContext* presContext = PresContext();
   1597  nsCSSFrameConstructor* frameConstructor = presContext->FrameConstructor();
   1598 
   1599  bool didUpdateCursor = false;
   1600 
   1601  for (size_t i = 0; i < aChangeList.Length(); ++i) {
   1602    // Collect and coalesce adjacent siblings for lazy frame construction.
   1603    // Eventually it would be even better to make RecreateFramesForContent
   1604    // accept a range and coalesce all adjacent reconstructs (bug 1344139).
   1605    size_t lazyRangeStart = i;
   1606    while (i < aChangeList.Length() && aChangeList[i].mContent &&
   1607           aChangeList[i].mContent->HasFlag(NODE_NEEDS_FRAME) &&
   1608           (i == lazyRangeStart ||
   1609            NextSiblingWhichMayHaveFrame(aChangeList[i - 1].mContent) ==
   1610                aChangeList[i].mContent)) {
   1611      MOZ_ASSERT(aChangeList[i].mHint & nsChangeHint_ReconstructFrame);
   1612      MOZ_ASSERT(!aChangeList[i].mFrame);
   1613      ++i;
   1614    }
   1615    if (i != lazyRangeStart) {
   1616      nsIContent* start = aChangeList[lazyRangeStart].mContent;
   1617      nsIContent* end =
   1618          NextSiblingWhichMayHaveFrame(aChangeList[i - 1].mContent);
   1619      if (!end) {
   1620        frameConstructor->ContentAppended(
   1621            start, nsCSSFrameConstructor::InsertionKind::Sync);
   1622      } else {
   1623        frameConstructor->ContentRangeInserted(
   1624            start, end, nsCSSFrameConstructor::InsertionKind::Sync);
   1625      }
   1626    }
   1627    for (size_t j = lazyRangeStart; j < i; ++j) {
   1628      MOZ_ASSERT(!aChangeList[j].mContent->GetPrimaryFrame() ||
   1629                 !aChangeList[j].mContent->HasFlag(NODE_NEEDS_FRAME));
   1630    }
   1631    if (i == aChangeList.Length()) {
   1632      break;
   1633    }
   1634 
   1635    const nsStyleChangeData& data = aChangeList[i];
   1636    nsIFrame* frame = data.mFrame;
   1637    nsIContent* content = data.mContent;
   1638    nsChangeHint hint = data.mHint;
   1639    bool didReflowThisFrame = false;
   1640 
   1641    NS_ASSERTION(!(hint & nsChangeHint_AllReflowHints) ||
   1642                     (hint & nsChangeHint_NeedReflow),
   1643                 "Reflow hint bits set without actually asking for a reflow");
   1644 
   1645    // skip any frame that has been destroyed due to a ripple effect
   1646    if (frame && mDestroyedFrames->Contains(frame)) {
   1647      continue;
   1648    }
   1649 
   1650    if (frame && frame->GetContent() != content) {
   1651      // XXXbz this is due to image maps messing with the primary frame of
   1652      // <area>s.  See bug 135040.  Remove this block once that's fixed.
   1653      frame = nullptr;
   1654      if (!(hint & nsChangeHint_ReconstructFrame)) {
   1655        continue;
   1656      }
   1657    }
   1658 
   1659    TryToDealWithScrollbarChange(hint, content, frame, presContext);
   1660    TryToHandleContainingBlockChange(hint, frame);
   1661 
   1662    if (hint & nsChangeHint_ReconstructFrame) {
   1663      // If we ever start passing true here, be careful of restyles
   1664      // that involve a reframe and animations.  In particular, if the
   1665      // restyle we're processing here is an animation restyle, but
   1666      // the style resolution we will do for the frame construction
   1667      // happens async when we're not in an animation restyle already,
   1668      // problems could arise.
   1669      // We could also have problems with triggering of CSS transitions
   1670      // on elements whose frames are reconstructed, since we depend on
   1671      // the reconstruction happening synchronously.
   1672      frameConstructor->RecreateFramesForContent(
   1673          content, nsCSSFrameConstructor::InsertionKind::Sync);
   1674      continue;
   1675    }
   1676 
   1677    MOZ_ASSERT(frame, "This shouldn't happen");
   1678    if (hint & nsChangeHint_AddOrRemoveTransform) {
   1679      for (nsIFrame* cont = frame; cont;
   1680           cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
   1681        if (cont->StyleDisplay()->HasTransform(cont)) {
   1682          cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
   1683        }
   1684        // Don't remove NS_FRAME_MAY_BE_TRANSFORMED since it may still be
   1685        // transformed by other means. It's OK to have the bit even if it's
   1686        // not needed.
   1687      }
   1688      // When dropping a running transform animation we will first add an
   1689      // nsChangeHint_UpdateTransformLayer hint as part of the animation-only
   1690      // restyle. During the subsequent regular restyle, if the animation was
   1691      // the only reason the element had any transform applied, we will add
   1692      // nsChangeHint_AddOrRemoveTransform as part of the regular restyle.
   1693      //
   1694      // With the Gecko backend, these two change hints are processed
   1695      // after each restyle but when using the Servo backend they accumulate
   1696      // and are processed together after we have already removed the
   1697      // transform as part of the regular restyle. Since we don't actually
   1698      // need the nsChangeHint_UpdateTransformLayer hint if we already have
   1699      // a nsChangeHint_AddOrRemoveTransform hint, and since we
   1700      // will fail an assertion in ApplyRenderingChangeToTree if we try
   1701      // specify nsChangeHint_UpdateTransformLayer but don't have any
   1702      // transform style, we just drop the unneeded hint here.
   1703      hint &= ~nsChangeHint_UpdateTransformLayer;
   1704    }
   1705 
   1706    if (!frame->FrameMaintainsOverflow()) {
   1707      // frame does not maintain overflow rects, so avoid calling
   1708      // FinishAndStoreOverflow on it:
   1709      hint &=
   1710          ~(nsChangeHint_UpdateOverflow | nsChangeHint_ChildrenOnlyTransform |
   1711            nsChangeHint_UpdatePostTransformOverflow |
   1712            nsChangeHint_UpdateParentOverflow |
   1713            nsChangeHint_UpdateSubtreeOverflow);
   1714    }
   1715 
   1716    if (!frame->HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED)) {
   1717      // Frame can not be transformed, and thus a change in transform will
   1718      // have no effect and we should not use either
   1719      // nsChangeHint_UpdatePostTransformOverflow or
   1720      // nsChangeHint_UpdateTransformLayerhint.
   1721      hint &= ~(nsChangeHint_UpdatePostTransformOverflow |
   1722                nsChangeHint_UpdateTransformLayer);
   1723    }
   1724 
   1725    if ((hint & nsChangeHint_UpdateEffects) &&
   1726        frame == nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame)) {
   1727      SVGObserverUtils::UpdateEffects(frame);
   1728    }
   1729    if ((hint & nsChangeHint_InvalidateRenderingObservers) ||
   1730        ((hint & nsChangeHint_UpdateOpacityLayer) &&
   1731         frame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT))) {
   1732      SVGObserverUtils::InvalidateRenderingObservers(frame);
   1733      frame->SchedulePaint();
   1734    }
   1735    if (hint & nsChangeHint_NeedReflow) {
   1736      StyleChangeReflow(frame, hint);
   1737      didReflowThisFrame = true;
   1738    }
   1739 
   1740    // Here we need to propagate repaint frame change hint instead of update
   1741    // opacity layer change hint when we do opacity optimization for SVG.
   1742    // We can't do it in nsStyleEffects::CalcDifference() just like we do
   1743    // for the optimization for 0.99 over opacity values since we have no way
   1744    // to call SVGUtils::CanOptimizeOpacity() there.
   1745    if ((hint & nsChangeHint_UpdateOpacityLayer) &&
   1746        SVGUtils::CanOptimizeOpacity(frame)) {
   1747      hint &= ~nsChangeHint_UpdateOpacityLayer;
   1748      hint |= nsChangeHint_RepaintFrame;
   1749    }
   1750 
   1751    if ((hint & nsChangeHint_UpdateUsesOpacity) && frame->IsTablePart()) {
   1752      NS_ASSERTION(hint & nsChangeHint_UpdateOpacityLayer,
   1753                   "should only return UpdateUsesOpacity hint "
   1754                   "when also returning UpdateOpacityLayer hint");
   1755      // When an internal table part (including cells) changes between
   1756      // having opacity 1 and non-1, it changes whether its
   1757      // backgrounds (and those of table parts inside of it) are
   1758      // painted as part of the table's nsDisplayTableBorderBackground
   1759      // display item, or part of its own display item.  That requires
   1760      // invalidation, so change UpdateOpacityLayer to RepaintFrame.
   1761      hint &= ~nsChangeHint_UpdateOpacityLayer;
   1762      hint |= nsChangeHint_RepaintFrame;
   1763    }
   1764 
   1765    // Opacity disables preserve-3d, so if we toggle it, then we also need
   1766    // to update the overflow areas of all potentially affected frames.
   1767    if ((hint & nsChangeHint_UpdateUsesOpacity) &&
   1768        frame->StyleDisplay()->mTransformStyle ==
   1769            StyleTransformStyle::Preserve3d) {
   1770      hint |= nsChangeHint_UpdateSubtreeOverflow;
   1771    }
   1772 
   1773    if (hint & nsChangeHint_UpdateBackgroundPosition) {
   1774      // For most frame types, DLBI can detect background position changes,
   1775      // so we only need to schedule a paint.
   1776      hint |= nsChangeHint_SchedulePaint;
   1777      if (frame->IsTablePart() || frame->IsMathMLFrame()) {
   1778        // Table parts and MathML frames don't build display items for their
   1779        // backgrounds, so DLBI can't detect background-position changes for
   1780        // these frames. Repaint the whole frame.
   1781        hint |= nsChangeHint_RepaintFrame;
   1782      }
   1783    }
   1784 
   1785    if (hint &
   1786        (nsChangeHint_RepaintFrame | nsChangeHint_UpdateOpacityLayer |
   1787         nsChangeHint_UpdateTransformLayer |
   1788         nsChangeHint_ChildrenOnlyTransform | nsChangeHint_SchedulePaint)) {
   1789      ApplyRenderingChangeToTree(presContext->PresShell(), frame, hint);
   1790    }
   1791 
   1792    if (hint & (nsChangeHint_UpdateTransformLayer |
   1793                nsChangeHint_AddOrRemoveTransform)) {
   1794      // We need to trigger re-snapping to this content if we snapped to the
   1795      // content on the last scroll operation.
   1796      ScrollSnapUtils::PostPendingResnapIfNeededFor(frame);
   1797    }
   1798 
   1799    if ((hint & nsChangeHint_RecomputePosition) && !didReflowThisFrame) {
   1800      // It is possible for this to fall back to a reflow
   1801      if (!RecomputePosition(frame)) {
   1802        StyleChangeReflow(frame, nsChangeHint_NeedReflow |
   1803                                     nsChangeHint_ReflowChangesSizeOrPosition);
   1804        didReflowThisFrame = true;
   1805      }
   1806    }
   1807    NS_ASSERTION(!(hint & nsChangeHint_ChildrenOnlyTransform) ||
   1808                     (hint & nsChangeHint_UpdateOverflow),
   1809                 "nsChangeHint_UpdateOverflow should be passed too");
   1810    if (!didReflowThisFrame &&
   1811        (hint & (nsChangeHint_UpdateOverflow |
   1812                 nsChangeHint_UpdatePostTransformOverflow |
   1813                 nsChangeHint_UpdateParentOverflow |
   1814                 nsChangeHint_UpdateSubtreeOverflow))) {
   1815      if (hint & nsChangeHint_UpdateSubtreeOverflow) {
   1816        for (nsIFrame* cont = frame; cont;
   1817             cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
   1818          AddSubtreeToOverflowTracker(cont, mOverflowChangedTracker);
   1819        }
   1820        // The work we just did in AddSubtreeToOverflowTracker
   1821        // subsumes some of the other hints:
   1822        hint &= ~(nsChangeHint_UpdateOverflow |
   1823                  nsChangeHint_UpdatePostTransformOverflow);
   1824      }
   1825      if (hint & nsChangeHint_ChildrenOnlyTransform) {
   1826        // We need to update overflows. The correct frame(s) to update depends
   1827        // on whether the ChangeHint came from an outer or an inner svg.
   1828        nsIFrame* hintFrame = GetFrameForChildrenOnlyTransformHint(frame);
   1829        NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame),
   1830                     "SVG frames should not have continuations "
   1831                     "or ib-split siblings");
   1832        NS_ASSERTION(
   1833            !nsLayoutUtils::GetNextContinuationOrIBSplitSibling(hintFrame),
   1834            "SVG frames should not have continuations "
   1835            "or ib-split siblings");
   1836        if (hintFrame->IsSVGOuterSVGAnonChildFrame()) {
   1837          // The children only transform of an outer svg frame is applied to
   1838          // the outer svg's anonymous child frame (instead of to the
   1839          // anonymous child's children).
   1840 
   1841          if (!CanSkipOverflowUpdates(hintFrame)) {
   1842            mOverflowChangedTracker.AddFrame(
   1843                hintFrame, OverflowChangedTracker::CHILDREN_CHANGED);
   1844          }
   1845        } else {
   1846          // The children only transform is applied to the child frames of an
   1847          // inner svg frame, so update the child overflows.
   1848          nsIFrame* childFrame = hintFrame->PrincipalChildList().FirstChild();
   1849          for (; childFrame; childFrame = childFrame->GetNextSibling()) {
   1850            MOZ_ASSERT(childFrame->IsSVGFrame(),
   1851                       "Not expecting non-SVG children");
   1852            if (!CanSkipOverflowUpdates(childFrame)) {
   1853              mOverflowChangedTracker.AddFrame(
   1854                  childFrame, OverflowChangedTracker::CHILDREN_CHANGED);
   1855            }
   1856            NS_ASSERTION(
   1857                !nsLayoutUtils::GetNextContinuationOrIBSplitSibling(childFrame),
   1858                "SVG frames should not have continuations "
   1859                "or ib-split siblings");
   1860            NS_ASSERTION(
   1861                childFrame->GetParent() == hintFrame,
   1862                "SVG child frame not expected to have different parent");
   1863          }
   1864        }
   1865      }
   1866      if (!CanSkipOverflowUpdates(frame)) {
   1867        if (hint & (nsChangeHint_UpdateOverflow |
   1868                    nsChangeHint_UpdatePostTransformOverflow)) {
   1869          OverflowChangedTracker::ChangeKind changeKind;
   1870          // If we have both nsChangeHint_UpdateOverflow and
   1871          // nsChangeHint_UpdatePostTransformOverflow,
   1872          // CHILDREN_CHANGED is selected as it is
   1873          // strictly stronger.
   1874          if (hint & nsChangeHint_UpdateOverflow) {
   1875            changeKind = OverflowChangedTracker::CHILDREN_CHANGED;
   1876          } else {
   1877            changeKind = OverflowChangedTracker::TRANSFORM_CHANGED;
   1878          }
   1879          for (nsIFrame* cont = frame; cont;
   1880               cont =
   1881                   nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
   1882            mOverflowChangedTracker.AddFrame(cont, changeKind);
   1883          }
   1884        }
   1885        // UpdateParentOverflow hints need to be processed in addition
   1886        // to the above, since if the processing of the above hints
   1887        // yields no change, the update will not propagate to the
   1888        // parent.
   1889        if (hint & nsChangeHint_UpdateParentOverflow) {
   1890          MOZ_ASSERT(frame->GetParent(),
   1891                     "shouldn't get style hints for the root frame");
   1892          for (nsIFrame* cont = frame; cont;
   1893               cont =
   1894                   nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
   1895            mOverflowChangedTracker.AddFrame(
   1896                cont->GetParent(), OverflowChangedTracker::CHILDREN_CHANGED);
   1897          }
   1898        }
   1899      }
   1900    }
   1901    if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) {
   1902      presContext->PresShell()->SynthesizeMouseMove(false);
   1903      didUpdateCursor = true;
   1904    }
   1905    if (hint & nsChangeHint_UpdateTableCellSpans) {
   1906      frameConstructor->UpdateTableCellSpans(content);
   1907    }
   1908    if (hint & nsChangeHint_VisibilityChange) {
   1909      frame->UpdateVisibleDescendantsState();
   1910    }
   1911  }
   1912 
   1913  aChangeList.Clear();
   1914  FlushOverflowChangedTracker();
   1915 }
   1916 
   1917 /* static */
   1918 uint64_t RestyleManager::GetAnimationGenerationForFrame(nsIFrame* aStyleFrame) {
   1919  EffectSet* effectSet = EffectSet::GetForStyleFrame(aStyleFrame);
   1920  return effectSet ? effectSet->GetAnimationGeneration() : 0;
   1921 }
   1922 
   1923 /* static */
   1924 void RestyleManager::AddLayerChangesForAnimation(
   1925    nsIFrame* aStyleFrame, nsIFrame* aPrimaryFrame, Element* aElement,
   1926    nsChangeHint aHintForThisFrame, nsStyleChangeList& aChangeListToProcess) {
   1927  MOZ_ASSERT(aElement);
   1928  MOZ_ASSERT(!!aStyleFrame == !!aPrimaryFrame);
   1929  if (!aStyleFrame) {
   1930    return;
   1931  }
   1932 
   1933  uint64_t frameGeneration =
   1934      RestyleManager::GetAnimationGenerationForFrame(aStyleFrame);
   1935 
   1936  Maybe<nsCSSPropertyIDSet> effectiveAnimationProperties;
   1937 
   1938  nsChangeHint hint = nsChangeHint(0);
   1939  auto maybeApplyChangeHint = [&](const Maybe<uint64_t>& aGeneration,
   1940                                  DisplayItemType aDisplayItemType) -> bool {
   1941    if (aGeneration && frameGeneration != *aGeneration) {
   1942      // If we have a transform layer but don't have any transform style, we
   1943      // probably just removed the transform but haven't destroyed the layer
   1944      // yet. In this case we will typically add the appropriate change hint
   1945      // (nsChangeHint_UpdateContainingBlock) when we compare styles so in
   1946      // theory we could skip adding any change hint here.
   1947      //
   1948      // However, sometimes when we compare styles we'll get no change. For
   1949      // example, if the transform style was 'none' when we sent the transform
   1950      // animation to the compositor and the current transform style is now
   1951      // 'none' we'll think nothing changed but actually we still need to
   1952      // trigger an update to clear whatever style the transform animation set
   1953      // on the compositor. To handle this case we simply set all the change
   1954      // hints relevant to removing transform style (since we don't know exactly
   1955      // what changes happened while the animation was running on the
   1956      // compositor).
   1957      //
   1958      // Note that we *don't* add nsChangeHint_UpdateTransformLayer since if we
   1959      // did, ApplyRenderingChangeToTree would complain that we're updating a
   1960      // transform layer without a transform.
   1961      if (aDisplayItemType == DisplayItemType::TYPE_TRANSFORM &&
   1962          !aStyleFrame->StyleDisplay()->HasTransformStyle()) {
   1963        // Add all the hints for a removing a transform if they are not already
   1964        // set for this frame.
   1965        if (!(NS_IsHintSubset(nsChangeHint_ComprehensiveAddOrRemoveTransform,
   1966                              aHintForThisFrame))) {
   1967          hint |= nsChangeHint_ComprehensiveAddOrRemoveTransform;
   1968        }
   1969        return true;
   1970      }
   1971      hint |= LayerAnimationInfo::GetChangeHintFor(aDisplayItemType);
   1972    }
   1973 
   1974    // We consider it's the first paint for the frame if we have an animation
   1975    // for the property but have no layer, for the case of WebRender,  no
   1976    // corresponding animation info.
   1977    // Note that in case of animations which has properties preventing running
   1978    // on the compositor, e.g., width or height, corresponding layer is not
   1979    // created at all, but even in such cases, we normally set valid change
   1980    // hint for such animations in each tick, i.e. restyles in each tick. As
   1981    // a result, we usually do restyles for such animations in every tick on
   1982    // the main-thread.  The only animations which will be affected by this
   1983    // explicit change hint are animations that have opacity/transform but did
   1984    // not have those properies just before. e.g, setting transform by
   1985    // setKeyframes or changing target element from other target which prevents
   1986    // running on the compositor, etc.
   1987    if (!aGeneration) {
   1988      nsChangeHint hintForDisplayItem =
   1989          LayerAnimationInfo::GetChangeHintFor(aDisplayItemType);
   1990      // We don't need to apply the corresponding change hint if we already have
   1991      // it.
   1992      if (NS_IsHintSubset(hintForDisplayItem, aHintForThisFrame)) {
   1993        return true;
   1994      }
   1995 
   1996      if (!effectiveAnimationProperties) {
   1997        effectiveAnimationProperties.emplace(
   1998            nsLayoutUtils::GetAnimationPropertiesForCompositor(aStyleFrame));
   1999      }
   2000      const nsCSSPropertyIDSet& propertiesForDisplayItem =
   2001          LayerAnimationInfo::GetCSSPropertiesFor(aDisplayItemType);
   2002      if (effectiveAnimationProperties->Intersects(propertiesForDisplayItem)) {
   2003        hint |= hintForDisplayItem;
   2004      }
   2005    }
   2006    return true;
   2007  };
   2008 
   2009  AnimationInfo::EnumerateGenerationOnFrame(
   2010      aStyleFrame, aElement, LayerAnimationInfo::sDisplayItemTypes,
   2011      maybeApplyChangeHint);
   2012 
   2013  if (hint) {
   2014    // We apply the hint to the primary frame, not the style frame. Transform
   2015    // and opacity hints apply to the table wrapper box, not the table box.
   2016    aChangeListToProcess.AppendChange(aPrimaryFrame, aElement, hint);
   2017  }
   2018 }
   2019 
   2020 RestyleManager::AnimationsWithDestroyedFrame::AnimationsWithDestroyedFrame(
   2021    RestyleManager* aRestyleManager)
   2022    : mRestyleManager(aRestyleManager),
   2023      mRestorePointer(mRestyleManager->mAnimationsWithDestroyedFrame) {
   2024  MOZ_ASSERT(!mRestyleManager->mAnimationsWithDestroyedFrame,
   2025             "shouldn't construct recursively");
   2026  mRestyleManager->mAnimationsWithDestroyedFrame = this;
   2027 }
   2028 
   2029 void RestyleManager::AnimationsWithDestroyedFrame::Put(
   2030    nsIContent* aContent, ComputedStyle* aComputedStyle) {
   2031  MOZ_ASSERT(aContent);
   2032  PseudoStyleType pseudoType = aComputedStyle->GetPseudoType();
   2033  nsIContent* target = aContent;
   2034  if (pseudoType == PseudoStyleType::NotPseudo ||
   2035      !AnimationUtils::StoresAnimationsInParent(pseudoType)) {
   2036    pseudoType = PseudoStyleType::NotPseudo;
   2037  } else {
   2038    target = aContent->GetParent();
   2039  }
   2040  mContents.AppendElement(std::make_pair(target->AsElement(), pseudoType));
   2041 }
   2042 
   2043 void RestyleManager::AnimationsWithDestroyedFrame::
   2044    StopAnimationsForElementsWithoutFrames() {
   2045  nsPresContext* context = mRestyleManager->PresContext();
   2046  nsAnimationManager* animationManager = context->AnimationManager();
   2047  nsTransitionManager* transitionManager = context->TransitionManager();
   2048  const Document* doc = context->Document();
   2049  for (auto& [element, pseudoType] : mContents) {
   2050    PseudoStyleRequest request(pseudoType);
   2051    if (pseudoType == PseudoStyleType::NotPseudo) {
   2052      if (element->GetPrimaryFrame()) {
   2053        continue;
   2054      }
   2055      // The contents of view transition pseudos are put together with
   2056      // NotPseudo.
   2057      const auto type = element->GetPseudoElementType();
   2058      if (PseudoStyle::IsViewTransitionPseudoElement(type)) {
   2059        request = {type,
   2060                   element->HasName()
   2061                       ? element->GetParsedAttr(nsGkAtoms::name)->GetAtomValue()
   2062                       : nullptr};
   2063        // View transition pseudo-elements use the document element to look up
   2064        // their animations.
   2065        element = doc->GetRootElement();
   2066        MOZ_ASSERT(element);
   2067      }
   2068    } else if (auto* pseudo = element->GetPseudoElement(request);
   2069               pseudo && pseudo->GetPrimaryFrame()) {
   2070      continue;
   2071    }
   2072 
   2073    animationManager->StopAnimationsForElement(element, request);
   2074    transitionManager->StopAnimationsForElement(element, request);
   2075 
   2076    // All other animations should keep running but not running on the
   2077    // *compositor* at this point.
   2078    if (EffectSet* effectSet = EffectSet::Get(element, request)) {
   2079      for (KeyframeEffect* effect : *effectSet) {
   2080        effect->ResetIsRunningOnCompositor();
   2081      }
   2082    }
   2083  }
   2084 }
   2085 
   2086 // When using handled hints by an ancestor, we need to make sure that our
   2087 // ancestor in the DOM tree is actually our ancestor in the flat tree.
   2088 // Otherwise, we can't guarantee that e.g. a repaint from an ancestor in the DOM
   2089 // will really end up repainting us.
   2090 static bool CanUseHandledHintsFromAncestors(const nsIFrame* aFrame) {
   2091  if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
   2092    // An out of flow can be parented in other part of the tree.
   2093    return false;
   2094  }
   2095  if (aFrame->IsColumnSpanInMulticolSubtree()) {
   2096    // Any column-spanner's parent frame is not its DOM parent's primary frame.
   2097    return false;
   2098  }
   2099  if (aFrame->IsTableCaption()) {
   2100    // This one is more subtle. captions are in-flow children of the table
   2101    // frame. But they are parented to the table wrapper. So hints handled for
   2102    // the inner table might not be applicable to us.
   2103    return false;
   2104  }
   2105  return true;
   2106 }
   2107 
   2108 #ifdef DEBUG
   2109 static bool IsAnonBox(const nsIFrame* aFrame) {
   2110  return aFrame->Style()->IsAnonBox();
   2111 }
   2112 
   2113 static const nsIFrame* FirstContinuationOrPartOfIBSplit(
   2114    const nsIFrame* aFrame) {
   2115  if (!aFrame) {
   2116    return nullptr;
   2117  }
   2118 
   2119  return nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
   2120 }
   2121 
   2122 static const nsIFrame* ExpectedOwnerForChild(const nsIFrame* aFrame) {
   2123  const nsIFrame* parent = aFrame->GetParent();
   2124  if (aFrame->IsTableFrame()) {
   2125    MOZ_ASSERT(parent->IsTableWrapperFrame());
   2126    parent = parent->GetParent();
   2127  }
   2128 
   2129  if (IsAnonBox(aFrame) && !aFrame->IsTextFrame()) {
   2130    if (parent->IsLineFrame()) {
   2131      parent = parent->GetParent();
   2132    }
   2133    return parent->IsViewportFrame() ? nullptr
   2134                                     : FirstContinuationOrPartOfIBSplit(parent);
   2135  }
   2136 
   2137  if (aFrame->IsLineFrame()) {
   2138    // A ::first-line always ends up here via its block, which is therefore the
   2139    // right expected owner.  That block can be an
   2140    // anonymous box.  For example, we could have a ::first-line on a columnated
   2141    // block; the blockframe is the column-content anonymous box in that case.
   2142    // So we don't want to end up in the code below, which steps out of anon
   2143    // boxes.  Just return the parent of the line frame, which is the block.
   2144    return parent;
   2145  }
   2146 
   2147  if (aFrame->IsLetterFrame()) {
   2148    // Ditto for ::first-letter. A first-letter always arrives here via its
   2149    // direct parent, except when it's parented to a ::first-line.
   2150    if (parent->IsLineFrame()) {
   2151      parent = parent->GetParent();
   2152    }
   2153    return FirstContinuationOrPartOfIBSplit(parent);
   2154  }
   2155 
   2156  if (parent->IsLetterFrame()) {
   2157    // Things never have ::first-letter as their expected parent.  Go
   2158    // on up to the ::first-letter's parent.
   2159    parent = parent->GetParent();
   2160  }
   2161 
   2162  parent = FirstContinuationOrPartOfIBSplit(parent);
   2163 
   2164  // We've handled already anon boxes, so now we're looking at
   2165  // a frame of a DOM element or pseudo. Hop through anon and line-boxes
   2166  // generated by our DOM parent, and go find the owner frame for it.
   2167  while (parent && (IsAnonBox(parent) || parent->IsLineFrame())) {
   2168    if (const nsTableWrapperFrame* wrapperFrame = do_QueryFrame(parent)) {
   2169      const nsTableFrame* tableFrame = wrapperFrame->InnerTableFrame();
   2170      // Handle :-moz-table and :-moz-inline-table.
   2171      parent = IsAnonBox(tableFrame) ? parent->GetParent() : tableFrame;
   2172    } else {
   2173      // We get the in-flow parent here so that we can handle the OOF anonymous
   2174      // boxed to get the correct parent.
   2175      parent = parent->GetInFlowParent();
   2176    }
   2177    parent = FirstContinuationOrPartOfIBSplit(parent);
   2178  }
   2179 
   2180  return parent;
   2181 }
   2182 
   2183 // FIXME(emilio, bug 1633685): We should ideally figure out how to properly
   2184 // restyle replicated fixed pos frames... We seem to assume everywhere that they
   2185 // can't get restyled at the moment...
   2186 static bool IsInReplicatedFixedPosTree(const nsIFrame* aFrame) {
   2187  if (!aFrame->PresContext()->IsPaginated()) {
   2188    return false;
   2189  }
   2190 
   2191  for (; aFrame; aFrame = aFrame->GetParent()) {
   2192    if (aFrame->StyleDisplay()->mPosition == StylePositionProperty::Fixed &&
   2193        !aFrame->FirstContinuation()->IsPrimaryFrame() &&
   2194        nsLayoutUtils::IsReallyFixedPos(aFrame)) {
   2195      return true;
   2196    }
   2197  }
   2198 
   2199  return true;
   2200 }
   2201 
   2202 void ServoRestyleState::AssertOwner(const ServoRestyleState& aParent) const {
   2203  MOZ_ASSERT(mOwner);
   2204  MOZ_ASSERT(CanUseHandledHintsFromAncestors(mOwner));
   2205  // We allow aParent.mOwner to be null, for cases when we're not starting at
   2206  // the root of the tree.  We also allow aParent.mOwner to be somewhere up our
   2207  // expected owner chain not our immediate owner, which allows us creating long
   2208  // chains of ServoRestyleStates in some cases where it's just not worth it.
   2209  if (aParent.mOwner) {
   2210    const nsIFrame* owner = ExpectedOwnerForChild(mOwner);
   2211    if (owner != aParent.mOwner && !IsInReplicatedFixedPosTree(mOwner)) {
   2212      MOZ_ASSERT(IsAnonBox(owner),
   2213                 "Should only have expected owner weirdness when anon boxes "
   2214                 "are involved");
   2215      bool found = false;
   2216      for (; owner; owner = ExpectedOwnerForChild(owner)) {
   2217        if (owner == aParent.mOwner) {
   2218          found = true;
   2219          break;
   2220        }
   2221      }
   2222      MOZ_ASSERT(found, "Must have aParent.mOwner on our expected owner chain");
   2223    }
   2224  }
   2225 }
   2226 
   2227 nsChangeHint ServoRestyleState::ChangesHandledFor(
   2228    const nsIFrame* aFrame) const {
   2229  if (!mOwner) {
   2230    MOZ_ASSERT(!mChangesHandled);
   2231    return mChangesHandled;
   2232  }
   2233 
   2234  MOZ_ASSERT(mOwner == ExpectedOwnerForChild(aFrame) ||
   2235                 IsInReplicatedFixedPosTree(aFrame),
   2236             "Missed some frame in the hierarchy?");
   2237  return mChangesHandled;
   2238 }
   2239 #endif
   2240 
   2241 void ServoRestyleState::AddPendingWrapperRestyle(nsIFrame* aWrapperFrame) {
   2242  MOZ_ASSERT(aWrapperFrame->Style()->IsWrapperAnonBox(),
   2243             "All our wrappers are anon boxes, and why would we restyle "
   2244             "non-inheriting ones?");
   2245  MOZ_ASSERT(aWrapperFrame->Style()->IsInheritingAnonBox(),
   2246             "All our wrappers are anon boxes, and why would we restyle "
   2247             "non-inheriting ones?");
   2248  MOZ_ASSERT(
   2249      aWrapperFrame->Style()->GetPseudoType() != PseudoStyleType::cellContent,
   2250      "Someone should be using TableAwareParentFor");
   2251  MOZ_ASSERT(
   2252      aWrapperFrame->Style()->GetPseudoType() != PseudoStyleType::tableWrapper,
   2253      "Someone should be using TableAwareParentFor");
   2254  // Make sure we only add first continuations.
   2255  aWrapperFrame = aWrapperFrame->FirstContinuation();
   2256  nsIFrame* last = mPendingWrapperRestyles.SafeLastElement(nullptr);
   2257  if (last == aWrapperFrame) {
   2258    // Already queued up, nothing to do.
   2259    return;
   2260  }
   2261 
   2262  // Make sure to queue up parents before children.  But don't queue up
   2263  // ancestors of non-anonymous boxes here; those are handled when we traverse
   2264  // their non-anonymous kids.
   2265  if (aWrapperFrame->ParentIsWrapperAnonBox()) {
   2266    AddPendingWrapperRestyle(TableAwareParentFor(aWrapperFrame));
   2267  }
   2268 
   2269  // If the append fails, we'll fail to restyle properly, but that's probably
   2270  // better than crashing.
   2271  if (mPendingWrapperRestyles.AppendElement(aWrapperFrame, fallible)) {
   2272    aWrapperFrame->SetIsWrapperAnonBoxNeedingRestyle(true);
   2273  }
   2274 }
   2275 
   2276 void ServoRestyleState::ProcessWrapperRestyles(nsIFrame* aParentFrame) {
   2277  size_t i = mPendingWrapperRestyleOffset;
   2278  while (i < mPendingWrapperRestyles.Length()) {
   2279    i += ProcessMaybeNestedWrapperRestyle(aParentFrame, i);
   2280  }
   2281 
   2282  mPendingWrapperRestyles.TruncateLength(mPendingWrapperRestyleOffset);
   2283 }
   2284 
   2285 size_t ServoRestyleState::ProcessMaybeNestedWrapperRestyle(nsIFrame* aParent,
   2286                                                           size_t aIndex) {
   2287  // The frame at index aIndex is something we should restyle ourselves, but
   2288  // following frames may need separate ServoRestyleStates to restyle.
   2289  MOZ_ASSERT(aIndex < mPendingWrapperRestyles.Length());
   2290 
   2291  nsIFrame* cur = mPendingWrapperRestyles[aIndex];
   2292  MOZ_ASSERT(cur->Style()->IsWrapperAnonBox());
   2293 
   2294  // Where is cur supposed to inherit from?  From its parent frame, except in
   2295  // the case when cur is a table, in which case it should be its grandparent.
   2296  // Also, not in the case when the resulting frame would be a first-line; in
   2297  // that case we should be inheriting from the block, and the first-line will
   2298  // do its fixup later if needed.
   2299  //
   2300  // Note that after we do all that fixup the parent we get might still not be
   2301  // aParent; for example aParent could be a scrollframe, in which case we
   2302  // should inherit from the scrollcontent frame.  Or the parent might be some
   2303  // continuation of aParent.
   2304  //
   2305  // Try to assert as much as we can about the parent we actually end up using
   2306  // without triggering bogus asserts in all those various edge cases.
   2307  nsIFrame* parent = cur->GetParent();
   2308  if (cur->IsTableFrame()) {
   2309    MOZ_ASSERT(parent->IsTableWrapperFrame());
   2310    parent = parent->GetParent();
   2311  }
   2312  if (parent->IsLineFrame()) {
   2313    parent = parent->GetParent();
   2314  }
   2315  MOZ_ASSERT(FirstContinuationOrPartOfIBSplit(parent) == aParent ||
   2316             (parent->Style()->IsInheritingAnonBox() &&
   2317              parent->GetContent() == aParent->GetContent()));
   2318 
   2319  // Now "this" is a ServoRestyleState for aParent, so if parent is not a next
   2320  // continuation (possibly across ib splits) of aParent we need a new
   2321  // ServoRestyleState for the kid.
   2322  Maybe<ServoRestyleState> parentRestyleState;
   2323  nsIFrame* parentForRestyle =
   2324      nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent);
   2325  if (parentForRestyle != aParent) {
   2326    parentRestyleState.emplace(*parentForRestyle, *this, nsChangeHint_Empty,
   2327                               CanUseHandledHints::Yes);
   2328  }
   2329  ServoRestyleState& curRestyleState =
   2330      parentRestyleState ? *parentRestyleState : *this;
   2331 
   2332  // This frame may already have been restyled.  Even if it has, we can't just
   2333  // return, because the next frame may be a kid of it that does need restyling.
   2334  if (cur->IsWrapperAnonBoxNeedingRestyle()) {
   2335    parentForRestyle->UpdateStyleOfChildAnonBox(cur, curRestyleState);
   2336    cur->SetIsWrapperAnonBoxNeedingRestyle(false);
   2337  }
   2338 
   2339  size_t numProcessed = 1;
   2340 
   2341  // Note: no overflow possible here, since aIndex < length.
   2342  if (aIndex + 1 < mPendingWrapperRestyles.Length()) {
   2343    nsIFrame* next = mPendingWrapperRestyles[aIndex + 1];
   2344    if (TableAwareParentFor(next) == cur &&
   2345        next->IsWrapperAnonBoxNeedingRestyle()) {
   2346      // It might be nice if we could do better than nsChangeHint_Empty.  On
   2347      // the other hand, presumably our mChangesHandled already has the bits
   2348      // we really want here so in practice it doesn't matter.
   2349      ServoRestyleState childState(*cur, curRestyleState, nsChangeHint_Empty,
   2350                                   CanUseHandledHints::Yes,
   2351                                   /* aAssertWrapperRestyleLength = */ false);
   2352      numProcessed +=
   2353          childState.ProcessMaybeNestedWrapperRestyle(cur, aIndex + 1);
   2354    }
   2355  }
   2356 
   2357  return numProcessed;
   2358 }
   2359 
   2360 nsIFrame* ServoRestyleState::TableAwareParentFor(const nsIFrame* aChild) {
   2361  // We want to get the anon box parent for aChild. where aChild has
   2362  // ParentIsWrapperAnonBox().
   2363  //
   2364  // For the most part this is pretty straightforward, but there are two
   2365  // wrinkles.  First, if aChild is a table, then we really want the parent of
   2366  // its table wrapper.
   2367  if (aChild->IsTableFrame()) {
   2368    aChild = aChild->GetParent();
   2369    MOZ_ASSERT(aChild->IsTableWrapperFrame());
   2370  }
   2371 
   2372  nsIFrame* parent = aChild->GetParent();
   2373  // Now if parent is a cell-content frame, we actually want the cellframe.
   2374  if (parent->Style()->GetPseudoType() == PseudoStyleType::cellContent) {
   2375    return parent->GetParent();
   2376  }
   2377  if (const nsTableWrapperFrame* wrapper = do_QueryFrame(parent)) {
   2378    // Must be a caption.  In that case we want the table here.
   2379    MOZ_ASSERT(aChild->StyleDisplay()->mDisplay == StyleDisplay::TableCaption);
   2380    return wrapper->InnerTableFrame();
   2381  }
   2382  return parent;
   2383 }
   2384 
   2385 void RestyleManager::PostRestyleEvent(Element* aElement,
   2386                                      RestyleHint aRestyleHint,
   2387                                      nsChangeHint aMinChangeHint) {
   2388  MOZ_ASSERT(!(aMinChangeHint & nsChangeHint_NeutralChange),
   2389             "Didn't expect explicit change hints to be neutral!");
   2390  if (MOZ_UNLIKELY(IsDisconnected()) ||
   2391      MOZ_UNLIKELY(PresContext()->PresShell()->IsDestroying())) {
   2392    return;
   2393  }
   2394 
   2395  // We allow posting restyles from within change hint handling, but not from
   2396  // within the restyle algorithm itself.
   2397  MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
   2398 
   2399  if (!aRestyleHint && !aMinChangeHint) {
   2400    // FIXME(emilio): we should assert against this instead.
   2401    return;  // Nothing to do.
   2402  }
   2403 
   2404  // Assuming the restyle hints will invalidate cached style for
   2405  // getComputedStyle, since we don't know if any of the restyling that we do
   2406  // would affect undisplayed elements.
   2407  if (aRestyleHint) {
   2408    if (!(aRestyleHint & RestyleHint::ForAnimations())) {
   2409      mHaveNonAnimationRestyles = true;
   2410    }
   2411 
   2412    IncrementUndisplayedRestyleGeneration();
   2413  }
   2414 
   2415  // Processing change hints sometimes causes new change hints to be generated,
   2416  // and very occasionally, additional restyle hints. We collect the change
   2417  // hints manually to avoid re-traversing the DOM to find them.
   2418  if (mReentrantChanges && !aRestyleHint) {
   2419    mReentrantChanges->AppendElement(ReentrantChange{aElement, aMinChangeHint});
   2420    return;
   2421  }
   2422 
   2423  if (aRestyleHint || aMinChangeHint) {
   2424    Servo_NoteExplicitHints(aElement, aRestyleHint, aMinChangeHint);
   2425  }
   2426 }
   2427 
   2428 void RestyleManager::PostRestyleEventForAnimations(
   2429    Element* aElement, const PseudoStyleRequest& aPseudoRequest,
   2430    RestyleHint aRestyleHint) {
   2431  Element* elementToRestyle = aElement->GetPseudoElement(aPseudoRequest);
   2432  if (!elementToRestyle) {
   2433    // FIXME: Bug 1371107: When reframing happens,
   2434    // EffectCompositor::mElementsToRestyle still has unbound old pseudo
   2435    // element. We should drop it.
   2436    return;
   2437  }
   2438 
   2439  mPresContext->TriggeredAnimationRestyle();
   2440 
   2441  Servo_NoteExplicitHints(elementToRestyle, aRestyleHint, nsChangeHint(0));
   2442 }
   2443 
   2444 void RestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint,
   2445                                         RestyleHint aRestyleHint) {
   2446  // NOTE(emilio): The semantics of these methods are quite funny, in the sense
   2447  // that we're not supposed to need to rebuild the actual stylist data.
   2448  //
   2449  // That's handled as part of the MediumFeaturesChanged stuff, if needed.
   2450  //
   2451  // Clear the cached style data only if we are guaranteed to process the whole
   2452  // DOM tree again.
   2453  //
   2454  // FIXME(emilio): Decouple this, probably. This probably just wants to reset
   2455  // the "uses viewport units / uses rem" bits, and _maybe_ clear cached anon
   2456  // box styles and such... But it doesn't really always need to clear the
   2457  // initial style of the document and similar...
   2458  if (aRestyleHint.DefinitelyRecascadesAllSubtree()) {
   2459    StyleSet()->ClearCachedStyleData();
   2460  }
   2461 
   2462  DocumentStyleRootIterator iter(mPresContext->Document());
   2463  while (Element* root = iter.GetNextStyleRoot()) {
   2464    PostRestyleEvent(root, aRestyleHint, aExtraHint);
   2465  }
   2466 
   2467  // TODO(emilio, bz): Extensions can add/remove stylesheets that can affect
   2468  // non-inheriting anon boxes. It's not clear if we want to support that, but
   2469  // if we do, we need to re-selector-match them here.
   2470 }
   2471 
   2472 /* static */
   2473 void RestyleManager::ClearServoDataFromSubtree(Element* aElement,
   2474                                               IncludeRoot aIncludeRoot) {
   2475  if (aElement->HasServoData()) {
   2476    StyleChildrenIterator it(aElement);
   2477    for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
   2478      if (n->IsElement()) {
   2479        ClearServoDataFromSubtree(n->AsElement(), IncludeRoot::Yes);
   2480      }
   2481    }
   2482  }
   2483 
   2484  if (MOZ_LIKELY(aIncludeRoot == IncludeRoot::Yes)) {
   2485    aElement->ClearServoData();
   2486    MOZ_ASSERT(!aElement->HasAnyOfFlags(Element::kAllServoDescendantBits |
   2487                                        NODE_NEEDS_FRAME));
   2488    MOZ_ASSERT(aElement != aElement->OwnerDoc()->GetServoRestyleRoot());
   2489  }
   2490 }
   2491 
   2492 /* static */
   2493 void RestyleManager::ClearRestyleStateFromSubtree(Element* aElement) {
   2494  if (aElement->HasAnyOfFlags(Element::kAllServoDescendantBits)) {
   2495    StyleChildrenIterator it(aElement);
   2496    for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
   2497      if (n->IsElement()) {
   2498        ClearRestyleStateFromSubtree(n->AsElement());
   2499      }
   2500    }
   2501  }
   2502 
   2503  bool wasRestyled = false;
   2504  (void)Servo_TakeChangeHint(aElement, &wasRestyled);
   2505  aElement->UnsetFlags(Element::kAllServoDescendantBits);
   2506 }
   2507 
   2508 /**
   2509 * This struct takes care of encapsulating some common state that text nodes may
   2510 * need to track during the post-traversal.
   2511 *
   2512 * This is currently used to properly compute change hints when the parent
   2513 * element of this node is a display: contents node, and also to avoid computing
   2514 * the style for text children more than once per element.
   2515 */
   2516 struct RestyleManager::TextPostTraversalState {
   2517 public:
   2518  TextPostTraversalState(Element& aParentElement, ComputedStyle* aParentContext,
   2519                         bool aDisplayContentsParentStyleChanged,
   2520                         ServoRestyleState& aParentRestyleState)
   2521      : mParentElement(aParentElement),
   2522        mParentContext(aParentContext),
   2523        mParentRestyleState(aParentRestyleState),
   2524        mStyle(nullptr),
   2525        mShouldPostHints(aDisplayContentsParentStyleChanged),
   2526        mShouldComputeHints(aDisplayContentsParentStyleChanged),
   2527        mComputedHint(nsChangeHint_Empty) {}
   2528 
   2529  nsStyleChangeList& ChangeList() { return mParentRestyleState.ChangeList(); }
   2530 
   2531  ComputedStyle& ComputeStyle(nsIContent* aTextNode) {
   2532    if (!mStyle) {
   2533      mStyle = mParentRestyleState.StyleSet().ResolveStyleForText(
   2534          aTextNode, &ParentStyle());
   2535    }
   2536    MOZ_ASSERT(mStyle);
   2537    return *mStyle;
   2538  }
   2539 
   2540  void ComputeHintIfNeeded(nsIContent* aContent, nsIFrame* aTextFrame,
   2541                           ComputedStyle& aNewStyle) {
   2542    MOZ_ASSERT(aTextFrame);
   2543    MOZ_ASSERT(aNewStyle.GetPseudoType() == PseudoStyleType::mozText);
   2544 
   2545    if (MOZ_LIKELY(!mShouldPostHints)) {
   2546      return;
   2547    }
   2548 
   2549    ComputedStyle* oldStyle = aTextFrame->Style();
   2550    MOZ_ASSERT(oldStyle->GetPseudoType() == PseudoStyleType::mozText);
   2551 
   2552    // We rely on the fact that all the text children for the same element share
   2553    // style to avoid recomputing style differences for all of them.
   2554    //
   2555    // TODO(emilio): The above may not be true for ::first-{line,letter}, but
   2556    // we'll cross that bridge when we support those in stylo.
   2557    if (mShouldComputeHints) {
   2558      mShouldComputeHints = false;
   2559      uint32_t equalStructs;
   2560      mComputedHint = oldStyle->CalcStyleDifference(aNewStyle, &equalStructs);
   2561      mComputedHint = NS_RemoveSubsumedHints(
   2562          mComputedHint, mParentRestyleState.ChangesHandledFor(aTextFrame));
   2563    }
   2564 
   2565    if (mComputedHint) {
   2566      mParentRestyleState.ChangeList().AppendChange(aTextFrame, aContent,
   2567                                                    mComputedHint);
   2568    }
   2569  }
   2570 
   2571 private:
   2572  ComputedStyle& ParentStyle() {
   2573    if (!mParentContext) {
   2574      mLazilyResolvedParentContext =
   2575          ServoStyleSet::ResolveServoStyle(mParentElement);
   2576      mParentContext = mLazilyResolvedParentContext;
   2577    }
   2578    return *mParentContext;
   2579  }
   2580 
   2581  Element& mParentElement;
   2582  ComputedStyle* mParentContext;
   2583  RefPtr<ComputedStyle> mLazilyResolvedParentContext;
   2584  ServoRestyleState& mParentRestyleState;
   2585  RefPtr<ComputedStyle> mStyle;
   2586  bool mShouldPostHints;
   2587  bool mShouldComputeHints;
   2588  nsChangeHint mComputedHint;
   2589 };
   2590 
   2591 static void UpdateFirstLetterIfNeeded(nsIFrame* aFrame,
   2592                                      ServoRestyleState& aRestyleState) {
   2593  MOZ_ASSERT(
   2594      !aFrame->IsBlockFrameOrSubclass(),
   2595      "You're probably duplicating work with UpdatePseudoElementStyles!");
   2596  if (!aFrame->HasFirstLetterChild()) {
   2597    return;
   2598  }
   2599 
   2600  // We need to find the block the first-letter is associated with so we can
   2601  // find the right element for the first-letter's style resolution.  Might as
   2602  // well just delegate the whole thing to that block.
   2603  nsIFrame* block = aFrame->GetParent();
   2604  while (!block->IsBlockFrameOrSubclass()) {
   2605    block = block->GetParent();
   2606  }
   2607 
   2608  static_cast<nsBlockFrame*>(block->FirstContinuation())
   2609      ->UpdateFirstLetterStyle(aRestyleState);
   2610 }
   2611 
   2612 static void UpdateOneAdditionalComputedStyle(nsIFrame* aFrame, uint32_t aIndex,
   2613                                             ComputedStyle& aOldContext,
   2614                                             ServoRestyleState& aRestyleState) {
   2615  auto pseudoType = aOldContext.GetPseudoType();
   2616  MOZ_ASSERT(pseudoType != PseudoStyleType::NotPseudo);
   2617  MOZ_ASSERT(
   2618      !nsCSSPseudoElements::PseudoElementSupportsUserActionState(pseudoType));
   2619 
   2620  RefPtr<ComputedStyle> newStyle =
   2621      aRestyleState.StyleSet().ResolvePseudoElementStyle(
   2622          *aFrame->GetContent()->AsElement(), pseudoType, nullptr,
   2623          aFrame->Style());
   2624 
   2625  uint32_t equalStructs;  // Not used, actually.
   2626  nsChangeHint childHint =
   2627      aOldContext.CalcStyleDifference(*newStyle, &equalStructs);
   2628  if (CanUseHandledHintsFromAncestors(aFrame)) {
   2629    childHint = NS_RemoveSubsumedHints(childHint,
   2630                                       aRestyleState.ChangesHandledFor(aFrame));
   2631  }
   2632 
   2633  if (childHint) {
   2634    if (childHint & nsChangeHint_ReconstructFrame) {
   2635      // If we generate a reconstruct here, remove any non-reconstruct hints we
   2636      // may have already generated for this content.
   2637      aRestyleState.ChangeList().PopChangesForContent(aFrame->GetContent());
   2638    }
   2639    aRestyleState.ChangeList().AppendChange(aFrame, aFrame->GetContent(),
   2640                                            childHint);
   2641  }
   2642 
   2643  aFrame->SetAdditionalComputedStyle(aIndex, newStyle);
   2644 }
   2645 
   2646 static void UpdateAdditionalComputedStyles(nsIFrame* aFrame,
   2647                                           ServoRestyleState& aRestyleState) {
   2648  MOZ_ASSERT(aFrame);
   2649  MOZ_ASSERT(aFrame->GetContent() && aFrame->GetContent()->IsElement());
   2650 
   2651  // FIXME(emilio): Consider adding a bit or something to avoid the initial
   2652  // virtual call?
   2653  uint32_t index = 0;
   2654  while (auto* oldStyle = aFrame->GetAdditionalComputedStyle(index)) {
   2655    UpdateOneAdditionalComputedStyle(aFrame, index++, *oldStyle, aRestyleState);
   2656  }
   2657 }
   2658 
   2659 static void UpdateFramePseudoElementStyles(nsIFrame* aFrame,
   2660                                           ServoRestyleState& aRestyleState) {
   2661  if (nsBlockFrame* blockFrame = do_QueryFrame(aFrame)) {
   2662    blockFrame->UpdatePseudoElementStyles(aRestyleState);
   2663  } else {
   2664    UpdateFirstLetterIfNeeded(aFrame, aRestyleState);
   2665  }
   2666 }
   2667 
   2668 enum class ServoPostTraversalFlags : uint32_t {
   2669  Empty = 0,
   2670  // Whether parent was restyled.
   2671  ParentWasRestyled = 1 << 0,
   2672  // Skip sending accessibility notifications for all descendants.
   2673  SkipA11yNotifications = 1 << 1,
   2674  // Always send accessibility notifications if the element is shown.
   2675  // The SkipA11yNotifications flag above overrides this flag.
   2676  SendA11yNotificationsIfShown = 1 << 2,
   2677 };
   2678 
   2679 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ServoPostTraversalFlags)
   2680 
   2681 #ifdef ACCESSIBILITY
   2682 static bool IsVisibleForA11y(const ComputedStyle& aStyle) {
   2683  return aStyle.StyleVisibility()->IsVisible() && !aStyle.StyleUI()->IsInert();
   2684 }
   2685 
   2686 static bool IsSubtreeVisibleForA11y(const ComputedStyle& aStyle) {
   2687  return aStyle.StyleDisplay()->mContentVisibility !=
   2688         StyleContentVisibility::Hidden;
   2689 }
   2690 #endif
   2691 
   2692 // Send proper accessibility notifications and return post traversal
   2693 // flags for kids.
   2694 static ServoPostTraversalFlags SendA11yNotifications(
   2695    nsPresContext* aPresContext, Element* aElement,
   2696    const ComputedStyle& aOldStyle, const ComputedStyle& aNewStyle,
   2697    ServoPostTraversalFlags aFlags) {
   2698  using Flags = ServoPostTraversalFlags;
   2699  MOZ_ASSERT(!(aFlags & Flags::SkipA11yNotifications) ||
   2700                 !(aFlags & Flags::SendA11yNotificationsIfShown),
   2701             "The two a11y flags should never be set together");
   2702 
   2703 #ifdef ACCESSIBILITY
   2704  nsAccessibilityService* accService = GetAccService();
   2705  if (!accService) {
   2706    // If we don't have accessibility service, accessibility is not
   2707    // enabled. Just skip everything.
   2708    return Flags::Empty;
   2709  }
   2710 
   2711  if (aNewStyle.StyleUIReset()->mMozSubtreeHiddenOnlyVisually !=
   2712      aOldStyle.StyleUIReset()->mMozSubtreeHiddenOnlyVisually) {
   2713    if (aElement->GetParent() &&
   2714        aElement->GetParent()->IsXULElement(nsGkAtoms::tabpanels)) {
   2715      accService->NotifyOfTabPanelVisibilityChange(
   2716          aPresContext->PresShell(), aElement,
   2717          aNewStyle.StyleUIReset()->mMozSubtreeHiddenOnlyVisually);
   2718    }
   2719  }
   2720 
   2721  if (aFlags & Flags::SkipA11yNotifications) {
   2722    // Propagate the skipping flag to descendants.
   2723    return Flags::SkipA11yNotifications;
   2724  }
   2725 
   2726  bool needsNotify = false;
   2727  const bool isVisible = IsVisibleForA11y(aNewStyle);
   2728  const bool wasVisible = IsVisibleForA11y(aOldStyle);
   2729 
   2730  if (aFlags & Flags::SendA11yNotificationsIfShown) {
   2731    if (!isVisible) {
   2732      // Propagate the sending-if-shown flag to descendants.
   2733      return Flags::SendA11yNotificationsIfShown;
   2734    }
   2735    // We have asked accessibility service to remove the whole subtree
   2736    // of element which becomes invisible from the accessible tree, but
   2737    // this element is visible, so we need to add it back.
   2738    needsNotify = true;
   2739  } else {
   2740    // If we shouldn't skip in any case, we need to check whether our own
   2741    // visibility has changed.
   2742    // Also notify if the subtree visibility change due to content-visibility.
   2743    const bool isSubtreeVisible = IsSubtreeVisibleForA11y(aNewStyle);
   2744    const bool wasSubtreeVisible = IsSubtreeVisibleForA11y(aOldStyle);
   2745    needsNotify =
   2746        wasVisible != isVisible || wasSubtreeVisible != isSubtreeVisible;
   2747  }
   2748 
   2749  if (needsNotify) {
   2750    PresShell* presShell = aPresContext->PresShell();
   2751    if (isVisible) {
   2752      accService->ContentRangeInserted(presShell, aElement,
   2753                                       aElement->GetNextSibling());
   2754      // We are adding the subtree. Accessibility service would handle
   2755      // descendants, so we should just skip them from notifying.
   2756      return Flags::SkipA11yNotifications;
   2757    }
   2758    if (wasVisible) {
   2759      // Remove the subtree of this invisible element, and ask any shown
   2760      // descendant to add themselves back.
   2761      accService->ContentRemoved(presShell, aElement);
   2762      return Flags::SendA11yNotificationsIfShown;
   2763    }
   2764  }
   2765 #endif
   2766 
   2767  return Flags::Empty;
   2768 }
   2769 
   2770 static bool NeedsToReframeForConditionallyCreatedPseudoElement(
   2771    Element* aElement, ComputedStyle* aNewStyle, nsIFrame* aStyleFrame,
   2772    ServoRestyleState& aRestyleState) {
   2773  if (MOZ_UNLIKELY(aStyleFrame->IsLeaf())) {
   2774    return false;
   2775  }
   2776  const auto& disp = *aStyleFrame->StyleDisplay();
   2777  if (disp.IsListItem() && aStyleFrame->IsBlockFrameOrSubclass() &&
   2778      !nsLayoutUtils::GetMarkerPseudo(aElement)) {
   2779    RefPtr<ComputedStyle> pseudoStyle =
   2780        aRestyleState.StyleSet().ProbePseudoElementStyle(
   2781            *aElement, PseudoStyleType::marker, nullptr, aNewStyle);
   2782    if (pseudoStyle) {
   2783      return true;
   2784    }
   2785  }
   2786  if (disp.mTopLayer == StyleTopLayer::Auto &&
   2787      !aElement->IsInNativeAnonymousSubtree() &&
   2788      !nsLayoutUtils::GetBackdropPseudo(aElement)) {
   2789    RefPtr<ComputedStyle> pseudoStyle =
   2790        aRestyleState.StyleSet().ProbePseudoElementStyle(
   2791            *aElement, PseudoStyleType::backdrop, nullptr, aNewStyle);
   2792    if (pseudoStyle) {
   2793      return true;
   2794    }
   2795  }
   2796  return false;
   2797 }
   2798 
   2799 bool RestyleManager::ProcessPostTraversal(Element* aElement,
   2800                                          ServoRestyleState& aRestyleState,
   2801                                          ServoPostTraversalFlags aFlags) {
   2802  nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(aElement);
   2803  nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
   2804 
   2805  MOZ_DIAGNOSTIC_ASSERT(aElement->HasServoData(),
   2806                        "Element without Servo data on a post-traversal? How?");
   2807 
   2808  // NOTE(emilio): This is needed because for table frames the bit is set on the
   2809  // table wrapper (which is the primary frame), not on the table itself.
   2810  const bool isOutOfFlow =
   2811      primaryFrame && primaryFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
   2812 
   2813  const bool canUseHandledHints =
   2814      primaryFrame && CanUseHandledHintsFromAncestors(primaryFrame);
   2815 
   2816  // Grab the change hint from Servo.
   2817  bool wasRestyled = false;
   2818  nsChangeHint changeHint =
   2819      static_cast<nsChangeHint>(Servo_TakeChangeHint(aElement, &wasRestyled));
   2820 
   2821  RefPtr<ComputedStyle> upToDateStyleIfRestyled =
   2822      wasRestyled ? ServoStyleSet::ResolveServoStyle(*aElement) : nullptr;
   2823 
   2824  // We should really fix the weird primary frame mapping for image maps
   2825  // (bug 135040)...
   2826  if (styleFrame && styleFrame->GetContent() != aElement) {
   2827    MOZ_ASSERT(styleFrame->IsImageFrameOrSubclass());
   2828    styleFrame = nullptr;
   2829  }
   2830 
   2831  // Handle lazy frame construction by posting a reconstruct for any lazily-
   2832  // constructed roots.
   2833  if (aElement->HasFlag(NODE_NEEDS_FRAME)) {
   2834    changeHint |= nsChangeHint_ReconstructFrame;
   2835    MOZ_ASSERT(!styleFrame);
   2836  }
   2837 
   2838  if (styleFrame) {
   2839    MOZ_ASSERT(primaryFrame);
   2840 
   2841    nsIFrame* maybeAnonBoxChild;
   2842    if (isOutOfFlow) {
   2843      maybeAnonBoxChild = primaryFrame->GetPlaceholderFrame();
   2844    } else {
   2845      maybeAnonBoxChild = primaryFrame;
   2846    }
   2847 
   2848    // Do not subsume change hints for the column-spanner.
   2849    if (canUseHandledHints) {
   2850      changeHint = NS_RemoveSubsumedHints(
   2851          changeHint, aRestyleState.ChangesHandledFor(styleFrame));
   2852    }
   2853 
   2854    // If the parent wasn't restyled, the styles of our anon box parents won't
   2855    // change either.
   2856    if ((aFlags & ServoPostTraversalFlags::ParentWasRestyled) &&
   2857        maybeAnonBoxChild->ParentIsWrapperAnonBox()) {
   2858      aRestyleState.AddPendingWrapperRestyle(
   2859          ServoRestyleState::TableAwareParentFor(maybeAnonBoxChild));
   2860    }
   2861 
   2862    // If we don't have a ::marker or ::backdrop pseudo-element, but need it,
   2863    // then reconstruct the frame.  (The opposite situation implies 'display'
   2864    // changes so doesn't need to be handled explicitly here.)
   2865    if (wasRestyled && !(changeHint & nsChangeHint_ReconstructFrame) &&
   2866        NeedsToReframeForConditionallyCreatedPseudoElement(
   2867            aElement, upToDateStyleIfRestyled, styleFrame, aRestyleState)) {
   2868      changeHint |= nsChangeHint_ReconstructFrame;
   2869    }
   2870  }
   2871 
   2872  // Although we shouldn't generate non-ReconstructFrame hints for elements with
   2873  // no frames, we can still get them here if they were explicitly posted by
   2874  // PostRestyleEvent, such as a RepaintFrame hint when a :link changes to be
   2875  // :visited.  Skip processing these hints if there is no frame.
   2876  if ((styleFrame || (changeHint & nsChangeHint_ReconstructFrame)) &&
   2877      changeHint) {
   2878    aRestyleState.ChangeList().AppendChange(styleFrame, aElement, changeHint);
   2879  }
   2880 
   2881  // If our change hint is reconstruct, we delegate to the frame constructor,
   2882  // which consumes the new style and expects the old style to be on the frame.
   2883  //
   2884  // XXXbholley: We should teach the frame constructor how to clear the dirty
   2885  // descendants bit to avoid the traversal here.
   2886  if (changeHint & nsChangeHint_ReconstructFrame) {
   2887    if (wasRestyled &&
   2888        StaticPrefs::layout_css_scroll_anchoring_suppressions_enabled()) {
   2889      const bool wasAbsPos =
   2890          styleFrame &&
   2891          styleFrame->StyleDisplay()->IsAbsolutelyPositionedStyle();
   2892      auto* newDisp = upToDateStyleIfRestyled->StyleDisplay();
   2893      // https://drafts.csswg.org/css-scroll-anchoring/#suppression-triggers
   2894      //
   2895      // We need to do the position check here rather than in
   2896      // DidSetComputedStyle because changing position reframes.
   2897      //
   2898      // We suppress adjustments whenever we change from being display: none to
   2899      // be an abspos.
   2900      //
   2901      // Similarly, for other changes from abspos to non-abspos styles.
   2902      //
   2903      // TODO(emilio): I _think_ chrome won't suppress adjustments whenever
   2904      // `display` changes. But that causes some infinite loops in cases like
   2905      // bug 1568778.
   2906      if (wasAbsPos != newDisp->IsAbsolutelyPositionedStyle()) {
   2907        aRestyleState.AddPendingScrollAnchorSuppression(aElement);
   2908      }
   2909    }
   2910    ClearRestyleStateFromSubtree(aElement);
   2911    return true;
   2912  }
   2913 
   2914  // TODO(emilio): We could avoid some refcount traffic here, specially in the
   2915  // ComputedStyle case, which uses atomic refcounting.
   2916  //
   2917  // Hold the ComputedStyle alive, because it could become a dangling pointer
   2918  // during the replacement. In practice it's not a huge deal, but better not
   2919  // playing with dangling pointers if not needed.
   2920  //
   2921  // NOTE(emilio): We could keep around the old computed style for display:
   2922  // contents elements too, but we don't really need it right now.
   2923  RefPtr<ComputedStyle> oldOrDisplayContentsStyle =
   2924      styleFrame ? styleFrame->Style() : nullptr;
   2925 
   2926  MOZ_ASSERT(!(styleFrame && Servo_Element_IsDisplayContents(aElement)),
   2927             "display: contents node has a frame, yet we didn't reframe it"
   2928             " above?");
   2929  const bool isDisplayContents = !styleFrame && aElement->HasServoData() &&
   2930                                 Servo_Element_IsDisplayContents(aElement);
   2931  if (isDisplayContents) {
   2932    oldOrDisplayContentsStyle = ServoStyleSet::ResolveServoStyle(*aElement);
   2933  }
   2934 
   2935  Maybe<ServoRestyleState> thisFrameRestyleState;
   2936  if (styleFrame) {
   2937    thisFrameRestyleState.emplace(
   2938        *styleFrame, aRestyleState, changeHint,
   2939        ServoRestyleState::CanUseHandledHints(canUseHandledHints));
   2940  }
   2941 
   2942  // We can't really assume as used changes from display: contents elements (or
   2943  // other elements without frames).
   2944  ServoRestyleState& childrenRestyleState =
   2945      thisFrameRestyleState ? *thisFrameRestyleState : aRestyleState;
   2946 
   2947  ComputedStyle* upToDateStyle =
   2948      wasRestyled ? upToDateStyleIfRestyled : oldOrDisplayContentsStyle;
   2949 
   2950  ServoPostTraversalFlags childrenFlags =
   2951      wasRestyled ? ServoPostTraversalFlags::ParentWasRestyled
   2952                  : ServoPostTraversalFlags::Empty;
   2953 
   2954  if (wasRestyled && oldOrDisplayContentsStyle) {
   2955    MOZ_ASSERT(styleFrame || isDisplayContents);
   2956 
   2957    // We want to walk all the continuations here, even the ones with different
   2958    // styles.  In practice, the only reason we get continuations with different
   2959    // styles here is ::first-line (::first-letter never affects element
   2960    // styles).  But in that case, newStyle is the right context for the
   2961    // _later_ continuations anyway (the ones not affected by ::first-line), not
   2962    // the earlier ones, so there is no point stopping right at the point when
   2963    // we'd actually be setting the right ComputedStyle.
   2964    //
   2965    // This does mean that we may be setting the wrong ComputedStyle on our
   2966    // initial continuations; ::first-line fixes that up after the fact.
   2967    for (nsIFrame* f = styleFrame; f; f = f->GetNextContinuation()) {
   2968      MOZ_ASSERT_IF(f != styleFrame, !f->GetAdditionalComputedStyle(0));
   2969      f->SetComputedStyle(upToDateStyle);
   2970    }
   2971 
   2972    if (styleFrame) {
   2973      UpdateAdditionalComputedStyles(styleFrame, aRestyleState);
   2974    }
   2975 
   2976    if (!aElement->GetParent()) {
   2977      // This is the root.  Update styles on the viewport as needed.
   2978      ViewportFrame* viewport =
   2979          do_QueryFrame(mPresContext->PresShell()->GetRootFrame());
   2980      if (viewport) {
   2981        // NB: The root restyle state, not the one for our children!
   2982        viewport->UpdateStyle(aRestyleState);
   2983      }
   2984    }
   2985 
   2986    // Some changes to animations don't affect the computed style and yet still
   2987    // require the layer to be updated. For example, pausing an animation via
   2988    // the Web Animations API won't affect an element's style but still
   2989    // requires to update the animation on the layer.
   2990    //
   2991    // We can sometimes reach this when the animated style is being removed.
   2992    // Since AddLayerChangesForAnimation checks if |styleFrame| has a transform
   2993    // style or not, we need to call it *after* setting |newStyle| to
   2994    // |styleFrame| to ensure the animated transform has been removed first.
   2995    AddLayerChangesForAnimation(styleFrame, primaryFrame, aElement, changeHint,
   2996                                aRestyleState.ChangeList());
   2997 
   2998    childrenFlags |= SendA11yNotifications(mPresContext, aElement,
   2999                                           *oldOrDisplayContentsStyle,
   3000                                           *upToDateStyle, aFlags);
   3001  }
   3002 
   3003  const bool traverseElementChildren =
   3004      aElement->HasAnyOfFlags(Element::kAllServoDescendantBits);
   3005  const bool traverseTextChildren =
   3006      wasRestyled || aElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES);
   3007  bool recreatedAnyContext = wasRestyled;
   3008  if (traverseElementChildren || traverseTextChildren) {
   3009    StyleChildrenIterator it(aElement);
   3010    TextPostTraversalState textState(*aElement, upToDateStyle,
   3011                                     isDisplayContents && wasRestyled,
   3012                                     childrenRestyleState);
   3013    for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
   3014      if (traverseElementChildren && n->IsElement()) {
   3015        recreatedAnyContext |= ProcessPostTraversal(
   3016            n->AsElement(), childrenRestyleState, childrenFlags);
   3017      } else if (traverseTextChildren && n->IsText()) {
   3018        recreatedAnyContext |= ProcessPostTraversalForText(
   3019            n, textState, childrenRestyleState, childrenFlags);
   3020      }
   3021    }
   3022  }
   3023 
   3024  // We want to update frame pseudo-element styles after we've traversed our
   3025  // kids, because some of those updates (::first-line/::first-letter) need to
   3026  // modify the styles of the kids, and the child traversal above would just
   3027  // clobber those modifications.
   3028  if (styleFrame) {
   3029    if (wasRestyled) {
   3030      // Make sure to update anon boxes and pseudo bits after updating text,
   3031      // otherwise ProcessPostTraversalForText could clobber first-letter
   3032      // styles, for example.
   3033      styleFrame->UpdateStyleOfOwnedAnonBoxes(childrenRestyleState);
   3034    }
   3035    // Process anon box wrapper frames before ::first-line bits, but _after_
   3036    // owned anon boxes, since the children wrapper anon boxes could be
   3037    // inheriting from our own owned anon boxes.
   3038    childrenRestyleState.ProcessWrapperRestyles(styleFrame);
   3039    if (wasRestyled) {
   3040      UpdateFramePseudoElementStyles(styleFrame, childrenRestyleState);
   3041    } else if (traverseElementChildren &&
   3042               styleFrame->IsBlockFrameOrSubclass()) {
   3043      // Even if we were not restyled, if we're a block with a first-line and
   3044      // one of our descendant elements which is on the first line was restyled,
   3045      // we need to update the styles of things on the first line, because
   3046      // they're wrong now.
   3047      //
   3048      // FIXME(bz) Could we do better here?  For example, could we keep track of
   3049      // frames that are "block with a ::first-line so we could avoid
   3050      // IsFrameOfType() and digging about for the first-line frame if not?
   3051      // Could we keep track of whether the element children we actually restyle
   3052      // are affected by first-line?  Something else?  Bug 1385443 tracks making
   3053      // this better.
   3054      nsIFrame* firstLineFrame =
   3055          static_cast<nsBlockFrame*>(styleFrame)->GetFirstLineFrame();
   3056      if (firstLineFrame) {
   3057        for (nsIFrame* kid : firstLineFrame->PrincipalChildList()) {
   3058          ReparentComputedStyleForFirstLine(kid);
   3059        }
   3060      }
   3061    }
   3062  }
   3063 
   3064  aElement->UnsetFlags(Element::kAllServoDescendantBits);
   3065  return recreatedAnyContext;
   3066 }
   3067 
   3068 bool RestyleManager::ProcessPostTraversalForText(
   3069    nsIContent* aTextNode, TextPostTraversalState& aPostTraversalState,
   3070    ServoRestyleState& aRestyleState, ServoPostTraversalFlags aFlags) {
   3071  // Handle lazy frame construction.
   3072  if (aTextNode->HasFlag(NODE_NEEDS_FRAME)) {
   3073    aPostTraversalState.ChangeList().AppendChange(
   3074        nullptr, aTextNode, nsChangeHint_ReconstructFrame);
   3075    return true;
   3076  }
   3077 
   3078  // Handle restyle.
   3079  nsIFrame* primaryFrame = aTextNode->GetPrimaryFrame();
   3080  if (!primaryFrame) {
   3081    return false;
   3082  }
   3083 
   3084  // If the parent wasn't restyled, the styles of our anon box parents won't
   3085  // change either.
   3086  if ((aFlags & ServoPostTraversalFlags::ParentWasRestyled) &&
   3087      primaryFrame->ParentIsWrapperAnonBox()) {
   3088    aRestyleState.AddPendingWrapperRestyle(
   3089        ServoRestyleState::TableAwareParentFor(primaryFrame));
   3090  }
   3091 
   3092  ComputedStyle& newStyle = aPostTraversalState.ComputeStyle(aTextNode);
   3093  aPostTraversalState.ComputeHintIfNeeded(aTextNode, primaryFrame, newStyle);
   3094 
   3095  // We want to walk all the continuations here, even the ones with different
   3096  // styles.  In practice, the only reasons we get continuations with different
   3097  // styles are ::first-line and ::first-letter.  But in those cases,
   3098  // newStyle is the right context for the _later_ continuations anyway (the
   3099  // ones not affected by ::first-line/::first-letter), not the earlier ones,
   3100  // so there is no point stopping right at the point when we'd actually be
   3101  // setting the right ComputedStyle.
   3102  //
   3103  // This does mean that we may be setting the wrong ComputedStyle on our
   3104  // initial continuations; ::first-line/::first-letter fix that up after the
   3105  // fact.
   3106  for (nsIFrame* f = primaryFrame; f; f = f->GetNextContinuation()) {
   3107    f->SetComputedStyle(&newStyle);
   3108  }
   3109 
   3110  return true;
   3111 }
   3112 
   3113 void RestyleManager::ClearSnapshots() {
   3114  for (auto iter = mSnapshots.Iter(); !iter.Done(); iter.Next()) {
   3115    iter.Key()->UnsetFlags(ELEMENT_HAS_SNAPSHOT | ELEMENT_HANDLED_SNAPSHOT);
   3116    iter.Remove();
   3117  }
   3118 }
   3119 
   3120 ServoElementSnapshot& RestyleManager::SnapshotFor(Element& aElement) {
   3121  MOZ_DIAGNOSTIC_ASSERT(!mInStyleRefresh);
   3122 
   3123  // NOTE(emilio): We can handle snapshots from a one-off restyle of those that
   3124  // we do to restyle stuff for reconstruction, for example.
   3125  //
   3126  // It seems to be the case that we always flush in between that happens and
   3127  // the next attribute change, so we can assert that we haven't handled the
   3128  // snapshot here yet. If this assertion didn't hold, we'd need to unset that
   3129  // flag from here too.
   3130  //
   3131  // Can't wait to make ProcessPendingRestyles the only entry-point for styling,
   3132  // so this becomes much easier to reason about. Today is not that day though.
   3133  MOZ_ASSERT(!aElement.HasFlag(ELEMENT_HANDLED_SNAPSHOT));
   3134 
   3135  ServoElementSnapshot* snapshot =
   3136      mSnapshots.GetOrInsertNew(&aElement, aElement);
   3137  aElement.SetFlags(ELEMENT_HAS_SNAPSHOT);
   3138 
   3139  // Now that we have a snapshot, make sure a restyle is triggered.
   3140  aElement.NoteDirtyForServo();
   3141  return *snapshot;
   3142 }
   3143 
   3144 void RestyleManager::DoProcessPendingRestyles(ServoTraversalFlags aFlags) {
   3145  nsPresContext* presContext = PresContext();
   3146  PresShell* presShell = presContext->PresShell();
   3147 
   3148  MOZ_ASSERT(presContext->Document(), "No document?  Pshaw!");
   3149  // FIXME(emilio): In the "flush animations" case, ideally, we should only
   3150  // recascade animation styles running on the compositor, so we shouldn't care
   3151  // about other styles, or new rules that apply to the page...
   3152  //
   3153  // However, that's not true as of right now, see bug 1388031 and bug 1388692.
   3154  MOZ_ASSERT((aFlags & ServoTraversalFlags::FlushThrottledAnimations) ||
   3155                 !presContext->HasPendingMediaQueryUpdates(),
   3156             "Someone forgot to update media queries?");
   3157  MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(), "Missing a script blocker!");
   3158  MOZ_RELEASE_ASSERT(!mInStyleRefresh, "Reentrant call?");
   3159 
   3160  if (MOZ_UNLIKELY(!presShell->DidInitialize())) {
   3161    // PresShell::FlushPendingNotifications doesn't early-return in the case
   3162    // where the PresShell hasn't yet been initialized (and therefore we haven't
   3163    // yet done the initial style traversal of the DOM tree). We should arguably
   3164    // fix up the callers and assert against this case, but we just detect and
   3165    // handle it for now.
   3166    return;
   3167  }
   3168 
   3169  // It'd be bad!
   3170  PresShell::AutoAssertNoFlush noReentrantFlush(*presShell);
   3171 
   3172  // Create a AnimationsWithDestroyedFrame during restyling process to
   3173  // stop animations and transitions on elements that have no frame at the end
   3174  // of the restyling process.
   3175  AnimationsWithDestroyedFrame animationsWithDestroyedFrame(this);
   3176 
   3177  ServoStyleSet* styleSet = StyleSet();
   3178  Document* doc = presContext->Document();
   3179 
   3180  if (!doc->GetServoRestyleRoot()) {
   3181    // This might post new restyles, so need to do it here. Don't do it if we're
   3182    // already going to restyle tho, so that we don't potentially reflow with
   3183    // dirty styling.
   3184    presContext->UpdateContainerQueryStylesAndAnchorPosLayout();
   3185    presContext->FinishedContainerQueryUpdate();
   3186  }
   3187 
   3188  // Perform the Servo traversal, and the post-traversal if required. We do this
   3189  // in a loop because certain rare paths in the frame constructor can trigger
   3190  // additional style invalidations.
   3191  //
   3192  // FIXME(emilio): Confirm whether that's still true now that XBL is gone.
   3193  mInStyleRefresh = true;
   3194  if (mHaveNonAnimationRestyles) {
   3195    ++mAnimationGeneration;
   3196  }
   3197 
   3198  if (mRestyleForCSSRuleChanges) {
   3199    aFlags |= ServoTraversalFlags::ForCSSRuleChanges;
   3200  }
   3201 
   3202  while (styleSet->StyleDocument(aFlags)) {
   3203    ClearSnapshots();
   3204    mRestyledAsWholeContainer.Clear();
   3205 
   3206    // Select scroll anchors for frames that have been scrolled. Do this
   3207    // before processing restyled frames so that anchor nodes are correctly
   3208    // marked when directly moving frames with RecomputePosition.
   3209    presContext->PresShell()->FlushPendingScrollAnchorSelections();
   3210 
   3211    nsStyleChangeList currentChanges;
   3212    bool anyStyleChanged = false;
   3213 
   3214    // Recreate styles , and queue up change hints (which also handle lazy frame
   3215    // construction).
   3216    nsTArray<RefPtr<Element>> anchorsToSuppress;
   3217 
   3218    // Unfortunately, the frame constructor and ProcessPostTraversal can
   3219    // generate new change hints while processing existing ones. We redirect
   3220    // those into a secondary queue and iterate until there's nothing left.
   3221    ReentrantChangeList newChanges;
   3222    mReentrantChanges = &newChanges;
   3223 
   3224    {
   3225      DocumentStyleRootIterator iter(doc->GetServoRestyleRoot());
   3226      while (Element* root = iter.GetNextStyleRoot()) {
   3227        nsTArray<nsIFrame*> wrappersToRestyle;
   3228        ServoRestyleState state(*styleSet, currentChanges, wrappersToRestyle,
   3229                                anchorsToSuppress);
   3230        ServoPostTraversalFlags flags = ServoPostTraversalFlags::Empty;
   3231        anyStyleChanged |= ProcessPostTraversal(root, state, flags);
   3232      }
   3233 
   3234      // We want to suppress adjustments the current (before-change) scroll
   3235      // anchor container now, and save a reference to the content node so that
   3236      // we can suppress them in the after-change scroll anchor .
   3237      for (Element* element : anchorsToSuppress) {
   3238        if (nsIFrame* frame = element->GetPrimaryFrame()) {
   3239          if (auto* container = ScrollAnchorContainer::FindFor(frame)) {
   3240            container->SuppressAdjustments();
   3241          }
   3242        }
   3243      }
   3244    }
   3245 
   3246    doc->ClearServoRestyleRoot();
   3247    ClearSnapshots();
   3248 
   3249    while (!currentChanges.IsEmpty()) {
   3250      ProcessRestyledFrames(currentChanges);
   3251      MOZ_ASSERT(currentChanges.IsEmpty());
   3252      for (ReentrantChange& change : newChanges) {
   3253        if (!(change.mHint & nsChangeHint_ReconstructFrame) &&
   3254            !change.mContent->GetPrimaryFrame()) {
   3255          // SVG Elements post change hints without ensuring that the primary
   3256          // frame will be there after that (see bug 1366142).
   3257          //
   3258          // Just ignore those, since we can't really process them.
   3259          continue;
   3260        }
   3261        currentChanges.AppendChange(change.mContent->GetPrimaryFrame(),
   3262                                    change.mContent, change.mHint);
   3263      }
   3264      newChanges.Clear();
   3265    }
   3266 
   3267    mReentrantChanges = nullptr;
   3268 
   3269    // Suppress adjustments in the after-change scroll anchors if needed, now
   3270    // that we're done reframing everything.
   3271    for (Element* element : anchorsToSuppress) {
   3272      if (nsIFrame* frame = element->GetPrimaryFrame()) {
   3273        if (auto* container = ScrollAnchorContainer::FindFor(frame)) {
   3274          container->SuppressAdjustments();
   3275        }
   3276      }
   3277    }
   3278 
   3279    if (anyStyleChanged) {
   3280      // Maybe no styles changed when:
   3281      //
   3282      //  * Only explicit change hints were posted in the first place.
   3283      //  * When an attribute or state change in the content happens not to need
   3284      //    a restyle after all.
   3285      //
   3286      // In any case, we don't need to increment the restyle generation in that
   3287      // case.
   3288      IncrementRestyleGeneration();
   3289    }
   3290 
   3291    // See https://bugzilla.mozilla.org/show_bug.cgi?id=1980206
   3292    presContext->PresShell()->MergeAnchorPosAnchorChanges();
   3293 
   3294    mInStyleRefresh = false;
   3295    presContext->UpdateContainerQueryStylesAndAnchorPosLayout();
   3296    mInStyleRefresh = true;
   3297  }
   3298 
   3299  doc->ClearServoRestyleRoot();
   3300  presContext->FinishedContainerQueryUpdate();
   3301  presContext->UpdateHiddenByContentVisibilityForAnimationsIfNeeded();
   3302  ClearSnapshots();
   3303  mRestyledAsWholeContainer.Clear();
   3304  styleSet->AssertTreeIsClean();
   3305 
   3306  mHaveNonAnimationRestyles = false;
   3307  mRestyleForCSSRuleChanges = false;
   3308  mInStyleRefresh = false;
   3309 
   3310  // Now that everything has settled, see if we have enough free rule nodes in
   3311  // the tree to warrant sweeping them.
   3312  styleSet->MaybeGCRuleTree();
   3313 
   3314  // Note: We are in the scope of |animationsWithDestroyedFrame|, so
   3315  //       |mAnimationsWithDestroyedFrame| is still valid.
   3316  MOZ_ASSERT(mAnimationsWithDestroyedFrame);
   3317  mAnimationsWithDestroyedFrame->StopAnimationsForElementsWithoutFrames();
   3318 }
   3319 
   3320 #ifdef DEBUG
   3321 static void VerifyFlatTree(const nsIContent& aContent) {
   3322  StyleChildrenIterator iter(&aContent);
   3323 
   3324  for (auto* content = iter.GetNextChild(); content;
   3325       content = iter.GetNextChild()) {
   3326    MOZ_ASSERT(content->GetFlattenedTreeParentNodeForStyle() == &aContent);
   3327    VerifyFlatTree(*content);
   3328  }
   3329 }
   3330 #endif
   3331 
   3332 void RestyleManager::ProcessPendingRestyles() {
   3333  AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Styles", LAYOUT);
   3334 #ifdef DEBUG
   3335  if (auto* root = mPresContext->Document()->GetRootElement()) {
   3336    VerifyFlatTree(*root);
   3337  }
   3338 #endif
   3339 
   3340  DoProcessPendingRestyles(ServoTraversalFlags::Empty);
   3341 }
   3342 
   3343 void RestyleManager::ProcessAllPendingAttributeAndStateInvalidations() {
   3344  if (mSnapshots.IsEmpty()) {
   3345    return;
   3346  }
   3347  for (const auto& key : mSnapshots.Keys()) {
   3348    // Servo data for the element might have been dropped. (e.g. by removing
   3349    // from its document)
   3350    if (key->HasFlag(ELEMENT_HAS_SNAPSHOT)) {
   3351      Servo_ProcessInvalidations(StyleSet()->RawData(), key, &mSnapshots);
   3352    }
   3353  }
   3354  ClearSnapshots();
   3355 }
   3356 
   3357 void RestyleManager::UpdateOnlyAnimationStyles() {
   3358  bool doCSS = PresContext()->EffectCompositor()->HasPendingStyleUpdates();
   3359  if (!doCSS) {
   3360    return;
   3361  }
   3362 
   3363  DoProcessPendingRestyles(ServoTraversalFlags::FlushThrottledAnimations);
   3364 }
   3365 
   3366 void RestyleManager::ElementStateChanged(Element* aElement,
   3367                                         ElementState aChangedBits) {
   3368  AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("ElementStateChanged",
   3369                                      LAYOUT_StyleComputation);
   3370 #ifdef NIGHTLY_BUILD
   3371  if (MOZ_UNLIKELY(mInStyleRefresh)) {
   3372    MOZ_CRASH_UNSAFE_PRINTF(
   3373        "Element state change during style refresh (%" PRIu64 ")",
   3374        aChangedBits.GetInternalValue());
   3375  }
   3376 #endif  // NIGHTLY_BUILD
   3377 
   3378  const ElementState kVisitedAndUnvisited =
   3379      ElementState::VISITED | ElementState::UNVISITED;
   3380 
   3381  // We'll restyle when the relevant visited query finishes, regardless of the
   3382  // style (see Link::VisitedQueryFinished). So there's no need to do anything
   3383  // as a result of this state change just yet.
   3384  //
   3385  // Note that this check checks for _both_ bits: This is only true when visited
   3386  // changes to unvisited or vice-versa, but not when we start or stop being a
   3387  // link itself.
   3388  if (aChangedBits.HasAllStates(kVisitedAndUnvisited)) {
   3389    aChangedBits &= ~kVisitedAndUnvisited;
   3390    if (aChangedBits.IsEmpty()) {
   3391      return;
   3392    }
   3393  }
   3394 
   3395  if (auto changeHint = ChangeForContentStateChange(*aElement, aChangedBits)) {
   3396    Servo_NoteExplicitHints(aElement, RestyleHint{0}, changeHint);
   3397  }
   3398 
   3399  // Don't bother taking a snapshot if no rules depend on these state bits.
   3400  //
   3401  // We always take a snapshot for the LTR/RTL event states, since Servo doesn't
   3402  // track those bits in the same way, and we know that :dir() rules are always
   3403  // present in UA style sheets.
   3404  if (!aChangedBits.HasAtLeastOneOfStates(ElementState::DIR_STATES) &&
   3405      !StyleSet()->HasStateDependency(*aElement, aChangedBits)) {
   3406    return;
   3407  }
   3408 
   3409  // Assuming we need to invalidate cached style in getComputedStyle for
   3410  // undisplayed elements, since we don't know if it is needed.
   3411  IncrementUndisplayedRestyleGeneration();
   3412 
   3413  const bool hasData = aElement->HasServoData();
   3414  if (!hasData &&
   3415      !(aElement->GetSelectorFlags() &
   3416        NodeSelectorFlags::RelativeSelectorSearchDirectionAncestorSibling)) {
   3417    return;
   3418  }
   3419 
   3420  ServoElementSnapshot& snapshot = SnapshotFor(*aElement);
   3421  ElementState previousState = aElement->StyleState() ^ aChangedBits;
   3422  snapshot.AddState(previousState);
   3423 
   3424  ServoStyleSet& styleSet = *StyleSet();
   3425  if (hasData) {
   3426    // If the element has no style data, the siblings don't need to be
   3427    // invalidated, we're inside a display: none subtree anyways. We still need
   3428    // to invalidate for :has() tho, because that might go upwards.
   3429    MaybeRestyleForNthOfState(styleSet, aElement, aChangedBits);
   3430  }
   3431  MaybeRestyleForRelativeSelectorState(styleSet, aElement, aChangedBits);
   3432 }
   3433 
   3434 void RestyleManager::CustomStatesWillChange(Element& aElement) {
   3435  MOZ_DIAGNOSTIC_ASSERT(!mInStyleRefresh);
   3436 
   3437  IncrementUndisplayedRestyleGeneration();
   3438 
   3439  // Relative selector invalidation travels ancestor and earlier sibling
   3440  // direction, so it's very possible that it invalidates a styled element.
   3441  if (!aElement.HasServoData() &&
   3442      !(aElement.GetSelectorFlags() &
   3443        NodeSelectorFlags::RelativeSelectorSearchDirectionAncestorSibling)) {
   3444    return;
   3445  }
   3446 
   3447  ServoElementSnapshot& snapshot = SnapshotFor(aElement);
   3448  snapshot.AddCustomStates(aElement);
   3449 }
   3450 
   3451 void RestyleManager::CustomStateChanged(Element& aElement, nsAtom* aState) {
   3452  ServoStyleSet& styleSet = *StyleSet();
   3453  MaybeRestyleForNthOfCustomState(styleSet, aElement, aState);
   3454  styleSet.MaybeInvalidateRelativeSelectorCustomStateDependency(
   3455      aElement, aState, Snapshots());
   3456 }
   3457 
   3458 void RestyleManager::MaybeRestyleForNthOfCustomState(ServoStyleSet& aStyleSet,
   3459                                                     Element& aChild,
   3460                                                     nsAtom* aState) {
   3461  const auto* parentNode = aChild.GetParentNode();
   3462  MOZ_ASSERT(parentNode);
   3463  const auto parentFlags = parentNode->GetSelectorFlags();
   3464  if (!(parentFlags & NodeSelectorFlags::HasSlowSelectorNthOf)) {
   3465    return;
   3466  }
   3467 
   3468  if (aStyleSet.HasNthOfCustomStateDependency(aChild, aState)) {
   3469    RestyleSiblingsForNthOf(&aChild, parentFlags);
   3470  }
   3471 }
   3472 
   3473 void RestyleManager::MaybeRestyleForNthOfState(ServoStyleSet& aStyleSet,
   3474                                               Element* aChild,
   3475                                               ElementState aChangedBits) {
   3476  const auto* parentNode = aChild->GetParentNode();
   3477  MOZ_ASSERT(parentNode);
   3478  const auto parentFlags = parentNode->GetSelectorFlags();
   3479  if (!(parentFlags & NodeSelectorFlags::HasSlowSelectorNthOf)) {
   3480    return;
   3481  }
   3482 
   3483  if (aStyleSet.HasNthOfStateDependency(*aChild, aChangedBits)) {
   3484    RestyleSiblingsForNthOf(aChild, parentFlags);
   3485  }
   3486 }
   3487 
   3488 static inline bool AttributeInfluencesOtherPseudoClassState(
   3489    const Element& aElement, const nsAtom* aAttribute) {
   3490  // We must record some state for :-moz-table-border-nonzero and
   3491  // :-moz-select-list-box.
   3492 
   3493  if (aAttribute == nsGkAtoms::border) {
   3494    return aElement.IsHTMLElement(nsGkAtoms::table);
   3495  }
   3496 
   3497  if (aAttribute == nsGkAtoms::multiple || aAttribute == nsGkAtoms::size) {
   3498    return aElement.IsHTMLElement(nsGkAtoms::select);
   3499  }
   3500 
   3501  return false;
   3502 }
   3503 
   3504 static inline bool NeedToRecordAttrChange(
   3505    const ServoStyleSet& aStyleSet, const Element& aElement,
   3506    int32_t aNameSpaceID, nsAtom* aAttribute,
   3507    bool* aInfluencesOtherPseudoClassState) {
   3508  *aInfluencesOtherPseudoClassState =
   3509      AttributeInfluencesOtherPseudoClassState(aElement, aAttribute);
   3510 
   3511  // If the attribute influences one of the pseudo-classes that are backed by
   3512  // attributes, we just record it.
   3513  if (*aInfluencesOtherPseudoClassState) {
   3514    return true;
   3515  }
   3516 
   3517  // We assume that id and class attributes are used in class/id selectors, and
   3518  // thus record them.
   3519  //
   3520  // TODO(emilio): We keep a filter of the ids in use somewhere in the StyleSet,
   3521  // presumably we could try to filter the old and new id, but it's not clear
   3522  // it's worth it.
   3523  if (aNameSpaceID == kNameSpaceID_None &&
   3524      (aAttribute == nsGkAtoms::id || aAttribute == nsGkAtoms::_class)) {
   3525    return true;
   3526  }
   3527 
   3528  // We always record lang="", even though we force a subtree restyle when it
   3529  // changes, since it can change how its siblings match :lang(..) due to
   3530  // selectors like :lang(..) + div.
   3531  if (aAttribute == nsGkAtoms::lang) {
   3532    return true;
   3533  }
   3534 
   3535  // Otherwise, just record the attribute change if a selector in the page may
   3536  // reference it from an attribute selector.
   3537  return aStyleSet.MightHaveAttributeDependency(aElement, aAttribute);
   3538 }
   3539 
   3540 void RestyleManager::AttributeWillChange(Element* aElement,
   3541                                         int32_t aNameSpaceID,
   3542                                         nsAtom* aAttribute,
   3543                                         AttrModType aModType) {
   3544  TakeSnapshotForAttributeChange(*aElement, aNameSpaceID, aAttribute);
   3545 }
   3546 
   3547 void RestyleManager::ClassAttributeWillBeChangedBySMIL(Element* aElement) {
   3548  TakeSnapshotForAttributeChange(*aElement, kNameSpaceID_None,
   3549                                 nsGkAtoms::_class);
   3550 }
   3551 
   3552 void RestyleManager::TakeSnapshotForAttributeChange(Element& aElement,
   3553                                                    int32_t aNameSpaceID,
   3554                                                    nsAtom* aAttribute) {
   3555  MOZ_DIAGNOSTIC_ASSERT(!mInStyleRefresh);
   3556 
   3557  bool influencesOtherPseudoClassState;
   3558  if (!NeedToRecordAttrChange(*StyleSet(), aElement, aNameSpaceID, aAttribute,
   3559                              &influencesOtherPseudoClassState)) {
   3560    return;
   3561  }
   3562 
   3563  // We cannot tell if the attribute change will affect the styles of
   3564  // undisplayed elements, because we don't actually restyle those elements
   3565  // during the restyle traversal. So just assume that the attribute change can
   3566  // cause the style to change.
   3567  IncrementUndisplayedRestyleGeneration();
   3568 
   3569  // Relative selector invalidation travels ancestor and earlier sibling
   3570  // direction, so it's very possible that it invalidates a styled element.
   3571  if (!aElement.HasServoData() &&
   3572      !(aElement.GetSelectorFlags() &
   3573        NodeSelectorFlags::RelativeSelectorSearchDirectionAncestorSibling)) {
   3574    return;
   3575  }
   3576 
   3577  // Some other random attribute changes may also affect the transitions,
   3578  // so we also set this true here.
   3579  mHaveNonAnimationRestyles = true;
   3580 
   3581  ServoElementSnapshot& snapshot = SnapshotFor(aElement);
   3582  snapshot.AddAttrs(aElement, aNameSpaceID, aAttribute);
   3583 
   3584  if (influencesOtherPseudoClassState) {
   3585    snapshot.AddOtherPseudoClassState(aElement);
   3586  }
   3587 }
   3588 
   3589 // For some attribute changes we must restyle the whole subtree:
   3590 //
   3591 // * lang="" and xml:lang="" can affect all descendants due to :lang()
   3592 // * exportparts can affect all descendant parts. We could certainly integrate
   3593 //   it better in the invalidation machinery if it was necessary.
   3594 static inline bool AttributeChangeRequiresSubtreeRestyle(
   3595    const Element& aElement, nsAtom* aAttr) {
   3596  if (aAttr == nsGkAtoms::exportparts) {
   3597    // TODO(emilio, bug 1598094): Maybe finer-grained invalidation for
   3598    // exportparts attribute changes?
   3599    return !!aElement.GetShadowRoot();
   3600  }
   3601  return aAttr == nsGkAtoms::lang;
   3602 }
   3603 
   3604 void RestyleManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
   3605                                      nsAtom* aAttribute, AttrModType aModType,
   3606                                      const nsAttrValue* aOldValue) {
   3607  MOZ_ASSERT(!mInStyleRefresh);
   3608 
   3609  auto changeHint = nsChangeHint(0);
   3610  auto restyleHint = RestyleHint{0};
   3611 
   3612  changeHint |= aElement->GetAttributeChangeHint(aAttribute, aModType);
   3613 
   3614  MaybeRestyleForNthOfAttribute(aElement, aNameSpaceID, aAttribute, aOldValue);
   3615  MaybeRestyleForRelativeSelectorAttribute(aElement, aNameSpaceID, aAttribute,
   3616                                           aOldValue);
   3617 
   3618  if (aAttribute == nsGkAtoms::style) {
   3619    restyleHint |= RestyleHint::RESTYLE_STYLE_ATTRIBUTE;
   3620  } else if (AttributeChangeRequiresSubtreeRestyle(*aElement, aAttribute)) {
   3621    restyleHint |= RestyleHint::RestyleSubtree();
   3622  } else if (aElement->IsInShadowTree() && aAttribute == nsGkAtoms::part) {
   3623    // TODO(emilio, bug 1598094): Maybe finer-grained invalidation for part
   3624    // attribute changes?
   3625    restyleHint |= RestyleHint::RESTYLE_SELF | RestyleHint::RESTYLE_PSEUDOS;
   3626  }
   3627 
   3628  if (nsIFrame* primaryFrame = aElement->GetPrimaryFrame()) {
   3629    // See if we have appearance information for a theme.
   3630    StyleAppearance appearance =
   3631        primaryFrame->StyleDisplay()->EffectiveAppearance();
   3632    if (appearance != StyleAppearance::None) {
   3633      nsITheme* theme = PresContext()->Theme();
   3634      if (theme->ThemeSupportsWidget(PresContext(), primaryFrame, appearance) &&
   3635          theme->WidgetAttributeChangeRequiresRepaint(appearance, aAttribute)) {
   3636        changeHint |= nsChangeHint_RepaintFrame;
   3637      }
   3638    }
   3639 
   3640    primaryFrame->AttributeChanged(aNameSpaceID, aAttribute, aModType);
   3641  }
   3642 
   3643  if (restyleHint || changeHint) {
   3644    Servo_NoteExplicitHints(aElement, restyleHint, changeHint);
   3645  }
   3646 
   3647  if (restyleHint) {
   3648    // Assuming we need to invalidate cached style in getComputedStyle for
   3649    // undisplayed elements, since we don't know if it is needed.
   3650    IncrementUndisplayedRestyleGeneration();
   3651 
   3652    // If we change attributes, we have to mark this to be true, so we will
   3653    // increase the animation generation for the new created transition if any.
   3654    mHaveNonAnimationRestyles = true;
   3655  }
   3656 }
   3657 
   3658 void RestyleManager::RestyleSiblingsForNthOf(Element* aChild,
   3659                                             NodeSelectorFlags aParentFlags) {
   3660  StyleSet()->RestyleSiblingsForNthOf(*aChild,
   3661                                      static_cast<uint32_t>(aParentFlags));
   3662 }
   3663 
   3664 void RestyleManager::MaybeRestyleForNthOfAttribute(
   3665    Element* aChild, int32_t aNameSpaceID, nsAtom* aAttribute,
   3666    const nsAttrValue* aOldValue) {
   3667  const auto* parentNode = aChild->GetParentNode();
   3668  MOZ_ASSERT(parentNode);
   3669  const auto parentFlags = parentNode->GetSelectorFlags();
   3670  if (!(parentFlags & NodeSelectorFlags::HasSlowSelectorNthOf)) {
   3671    return;
   3672  }
   3673  if (!aChild->HasServoData()) {
   3674    return;
   3675  }
   3676 
   3677  bool mightHaveNthOfDependency;
   3678  auto& styleSet = *StyleSet();
   3679  if (aAttribute == nsGkAtoms::id &&
   3680      MOZ_LIKELY(aNameSpaceID == kNameSpaceID_None)) {
   3681    auto* const oldAtom = aOldValue->Type() == nsAttrValue::eAtom
   3682                              ? aOldValue->GetAtomValue()
   3683                              : nullptr;
   3684    mightHaveNthOfDependency =
   3685        styleSet.MightHaveNthOfIDDependency(*aChild, oldAtom, aChild->GetID());
   3686  } else if (aAttribute == nsGkAtoms::_class &&
   3687             MOZ_LIKELY(aNameSpaceID == kNameSpaceID_None)) {
   3688    mightHaveNthOfDependency = styleSet.MightHaveNthOfClassDependency(*aChild);
   3689  } else {
   3690    mightHaveNthOfDependency =
   3691        styleSet.MightHaveNthOfAttributeDependency(*aChild, aAttribute);
   3692  }
   3693 
   3694  if (mightHaveNthOfDependency) {
   3695    RestyleSiblingsForNthOf(aChild, parentFlags);
   3696  }
   3697 }
   3698 
   3699 void RestyleManager::MaybeRestyleForRelativeSelectorAttribute(
   3700    Element* aElement, int32_t aNameSpaceID, nsAtom* aAttribute,
   3701    const nsAttrValue* aOldValue) {
   3702  if (!aElement->HasFlag(ELEMENT_HAS_SNAPSHOT)) {
   3703    return;
   3704  }
   3705  auto& styleSet = *StyleSet();
   3706  if (aAttribute == nsGkAtoms::id &&
   3707      MOZ_LIKELY(aNameSpaceID == kNameSpaceID_None)) {
   3708    auto* const oldAtom = aOldValue->Type() == nsAttrValue::eAtom
   3709                              ? aOldValue->GetAtomValue()
   3710                              : nullptr;
   3711    styleSet.MaybeInvalidateRelativeSelectorIDDependency(
   3712        *aElement, oldAtom, aElement->GetID(), Snapshots());
   3713  } else if (aAttribute == nsGkAtoms::_class &&
   3714             MOZ_LIKELY(aNameSpaceID == kNameSpaceID_None)) {
   3715    styleSet.MaybeInvalidateRelativeSelectorClassDependency(*aElement,
   3716                                                            Snapshots());
   3717  } else {
   3718    styleSet.MaybeInvalidateRelativeSelectorAttributeDependency(
   3719        *aElement, aAttribute, Snapshots());
   3720  }
   3721 }
   3722 
   3723 void RestyleManager::MaybeRestyleForRelativeSelectorState(
   3724    ServoStyleSet& aStyleSet, Element* aElement, ElementState aChangedBits) {
   3725  if (!aElement->HasFlag(ELEMENT_HAS_SNAPSHOT)) {
   3726    return;
   3727  }
   3728  aStyleSet.MaybeInvalidateRelativeSelectorStateDependency(
   3729      *aElement, aChangedBits, Snapshots());
   3730 }
   3731 
   3732 void RestyleManager::ReparentComputedStyleForFirstLine(nsIFrame* aFrame) {
   3733  // This is only called when moving frames in or out of the first-line
   3734  // pseudo-element (or one of its descendants).  We can't say much about
   3735  // aFrame's ancestors, unfortunately (e.g. during a dynamic insert into
   3736  // something inside an inline-block on the first line the ancestors could be
   3737  // totally arbitrary), but we will definitely find a line frame on the
   3738  // ancestor chain.  Note that the lineframe may not actually be the one that
   3739  // corresponds to ::first-line; when we're moving _out_ of the ::first-line it
   3740  // will be one of the continuations instead.
   3741 #ifdef DEBUG
   3742  {
   3743    nsIFrame* f = aFrame->GetParent();
   3744    while (f && !f->IsLineFrame()) {
   3745      f = f->GetParent();
   3746    }
   3747    MOZ_ASSERT(f, "Must have found a first-line frame");
   3748  }
   3749 #endif
   3750 
   3751  DoReparentComputedStyleForFirstLine(aFrame, *StyleSet());
   3752 }
   3753 
   3754 static bool IsFrameAboutToGoAway(nsIFrame* aFrame) {
   3755  auto* element = Element::FromNode(aFrame->GetContent());
   3756  if (!element) {
   3757    return false;
   3758  }
   3759  return !element->HasServoData();
   3760 }
   3761 
   3762 void RestyleManager::DoReparentComputedStyleForFirstLine(
   3763    nsIFrame* aFrame, ServoStyleSet& aStyleSet) {
   3764  if (IsFrameAboutToGoAway(aFrame)) {
   3765    // We're entering a display: none subtree, which we know it's going to get
   3766    // rebuilt. Don't bother reparenting.
   3767    return;
   3768  }
   3769 
   3770  if (aFrame->IsPlaceholderFrame()) {
   3771    // Also reparent the out-of-flow and all its continuations.  We're doing
   3772    // this to match Gecko for now, but it's not clear that this behavior is
   3773    // correct per spec.  It's certainly pretty odd for out-of-flows whose
   3774    // containing block is not within the first line.
   3775    //
   3776    // Right now we're somewhat inconsistent in this testcase:
   3777    //
   3778    //  <style>
   3779    //    div { color: orange; clear: left; }
   3780    //    div::first-line { color: blue; }
   3781    //  </style>
   3782    //  <div>
   3783    //    <span style="float: left">What color is this text?</span>
   3784    //  </div>
   3785    //  <div>
   3786    //    <span><span style="float: left">What color is this text?</span></span>
   3787    //  </div>
   3788    //
   3789    // We make the first float orange and the second float blue.  On the other
   3790    // hand, if the float were within an inline-block that was on the first
   3791    // line, arguably it _should_ inherit from the ::first-line...
   3792    nsIFrame* outOfFlow =
   3793        nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
   3794    MOZ_ASSERT(outOfFlow, "no out-of-flow frame");
   3795    for (; outOfFlow; outOfFlow = outOfFlow->GetNextContinuation()) {
   3796      DoReparentComputedStyleForFirstLine(outOfFlow, aStyleSet);
   3797    }
   3798  }
   3799 
   3800  nsIFrame* providerFrame;
   3801  ComputedStyle* newParentStyle =
   3802      aFrame->GetParentComputedStyle(&providerFrame);
   3803  // If our provider is our child, we want to reparent it first, because we
   3804  // inherit style from it.
   3805  bool isChild = providerFrame && providerFrame->GetParent() == aFrame;
   3806  nsIFrame* providerChild = nullptr;
   3807  if (isChild) {
   3808    DoReparentComputedStyleForFirstLine(providerFrame, aStyleSet);
   3809    // Get the style again after ReparentComputedStyle() which might have
   3810    // changed it.
   3811    newParentStyle = providerFrame->Style();
   3812    providerChild = providerFrame;
   3813    MOZ_ASSERT(!providerFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW),
   3814               "Out of flow provider?");
   3815  }
   3816 
   3817  if (!newParentStyle) {
   3818    // No need to do anything here for this frame, but we should still reparent
   3819    // its descendants, because those may have styles that inherit from the
   3820    // parent of this frame (e.g. non-anonymous columns in an anonymous
   3821    // colgroup).
   3822    MOZ_ASSERT(aFrame->Style()->IsNonInheritingAnonBox(),
   3823               "Why did this frame not end up with a parent context?");
   3824    ReparentFrameDescendants(aFrame, providerChild, aStyleSet);
   3825    return;
   3826  }
   3827 
   3828  bool isElement = aFrame->GetContent()->IsElement();
   3829 
   3830  // We probably don't want to initiate transitions from ReparentComputedStyle,
   3831  // since we call it during frame construction rather than in response to
   3832  // dynamic changes.
   3833  // Also see the comment at the start of
   3834  // nsTransitionManager::ConsiderInitiatingTransition.
   3835  //
   3836  // We don't try to do the fancy copying from previous continuations that
   3837  // GeckoRestyleManager does here, because that relies on knowing the parents
   3838  // of ComputedStyles, and we don't know those.
   3839  ComputedStyle* oldStyle = aFrame->Style();
   3840  Element* ourElement = isElement ? aFrame->GetContent()->AsElement() : nullptr;
   3841  ComputedStyle* newParent = newParentStyle;
   3842 
   3843  if (!providerFrame) {
   3844    // No providerFrame means we inherited from a display:contents thing.  Our
   3845    // layout parent style is the style of our nearest ancestor frame.  But we
   3846    // have to be careful to do that with our placeholder, not with us, if we're
   3847    // out of flow.
   3848    if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
   3849      aFrame->FirstContinuation()
   3850          ->GetPlaceholderFrame()
   3851          ->GetLayoutParentStyleForOutOfFlow(&providerFrame);
   3852    } else {
   3853      providerFrame = nsIFrame::CorrectStyleParentFrame(
   3854          aFrame->GetParent(), oldStyle->GetPseudoType());
   3855    }
   3856  }
   3857  ComputedStyle* layoutParent = providerFrame->Style();
   3858 
   3859  RefPtr<ComputedStyle> newStyle = aStyleSet.ReparentComputedStyle(
   3860      oldStyle, newParent, layoutParent, ourElement);
   3861  aFrame->SetComputedStyle(newStyle);
   3862 
   3863  // This logic somewhat mirrors the logic in
   3864  // RestyleManager::ProcessPostTraversal.
   3865  if (isElement) {
   3866    // We can't use UpdateAdditionalComputedStyles as-is because it needs a
   3867    // ServoRestyleState and maintaining one of those during a _frametree_
   3868    // traversal is basically impossible.
   3869    int32_t index = 0;
   3870    while (auto* oldAdditionalStyle =
   3871               aFrame->GetAdditionalComputedStyle(index)) {
   3872      RefPtr<ComputedStyle> newAdditionalContext =
   3873          aStyleSet.ReparentComputedStyle(oldAdditionalStyle, newStyle,
   3874                                          newStyle, nullptr);
   3875      aFrame->SetAdditionalComputedStyle(index, newAdditionalContext);
   3876      ++index;
   3877    }
   3878  }
   3879 
   3880  // Generally, owned anon boxes are our descendants.  The only exceptions are
   3881  // tables (for the table wrapper) and inline frames (for the block part of the
   3882  // block-in-inline split).  We're going to update our descendants when looping
   3883  // over kids, and we don't want to update the block part of a block-in-inline
   3884  // split if the inline is on the first line but the block is not (and if the
   3885  // block is, it's the child of something else on the first line and will get
   3886  // updated as a child).  And given how this method ends up getting called, if
   3887  // we reach here for a table frame, we are already in the middle of
   3888  // reparenting the table wrapper frame.  So no need to
   3889  // UpdateStyleOfOwnedAnonBoxes() here.
   3890 
   3891  ReparentFrameDescendants(aFrame, providerChild, aStyleSet);
   3892 
   3893  // We do not need to do the equivalent of UpdateFramePseudoElementStyles,
   3894  // because those are handled by our descendant walk.
   3895 }
   3896 
   3897 void RestyleManager::ReparentFrameDescendants(nsIFrame* aFrame,
   3898                                              nsIFrame* aProviderChild,
   3899                                              ServoStyleSet& aStyleSet) {
   3900  for (const auto& childList : aFrame->ChildLists()) {
   3901    for (nsIFrame* child : childList.mList) {
   3902      // only do frames that are in flow
   3903      if (!child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
   3904          child != aProviderChild) {
   3905        DoReparentComputedStyleForFirstLine(child, aStyleSet);
   3906      }
   3907    }
   3908  }
   3909 }
   3910 
   3911 }  // namespace mozilla