/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * Code responsible for managing style changes: tracking what style
 * changes need to happen, scheduling them, and doing them.
 */

#include "mozilla/RestyleManager.h"

#include <algorithm> // For std::max
#include "mozilla/EffectSet.h"
#include "mozilla/EventStates.h"
#include "nsLayoutUtils.h"
#include "AnimationCommon.h" // For GetLayerAnimationInfo
#include "FrameLayerBuilder.h"
#include "GeckoProfiler.h"
#include "LayerAnimationInfo.h" // For LayerAnimationInfo::sRecords
#include "nsAutoPtr.h"
#include "nsStyleChangeList.h"
#include "nsRuleProcessorData.h"
#include "nsStyleSet.h"
#include "nsStyleUtil.h"
#include "nsCSSFrameConstructor.h"
#include "nsSVGEffects.h"
#include "nsCSSPseudoElements.h"
#include "nsCSSRendering.h"
#include "nsAnimationManager.h"
#include "nsTransitionManager.h"
#include "nsViewManager.h"
#include "nsRenderingContext.h"
#include "nsSVGIntegrationUtils.h"
#include "nsCSSAnonBoxes.h"
#include "nsContainerFrame.h"
#include "nsPlaceholderFrame.h"
#include "nsBlockFrame.h"
#include "nsViewportFrame.h"
#include "SVGTextFrame.h"
#include "StickyScrollContainer.h"
#include "nsIRootBox.h"
#include "nsIDOMMutationEvent.h"
#include "nsContentUtils.h"
#include "nsIFrameInlines.h"
#include "ActiveLayerTracker.h"
#include "nsDisplayList.h"
#include "RestyleTrackerInlines.h"
#include "nsSMILAnimationController.h"
#include "nsCSSRuleProcessor.h"
#include "ChildIterator.h"
#include "Layers.h"

#ifdef ACCESSIBILITY
#include "nsAccessibilityService.h"
#endif

namespace mozilla {

using namespace layers;
using namespace dom;

#define LOG_RESTYLE_CONTINUE(reason_, ...) \
  LOG_RESTYLE("continuing restyle since " reason_, ##__VA_ARGS__)

#ifdef RESTYLE_LOGGING
static nsCString
FrameTagToString(const nsIFrame* aFrame)
{
  nsCString result;
  aFrame->ListTag(result);
  return result;
}

static nsCString
ElementTagToString(dom::Element* aElement)
{
  nsCString result;
  nsDependentAtomString buf(aElement->NodeInfo()->NameAtom());
  result.AppendPrintf("(%s@%p)", NS_ConvertUTF16toUTF8(buf).get(), aElement);
  return result;
}
#endif

RestyleManager::RestyleManager(nsPresContext* aPresContext)
  : RestyleManagerBase(aPresContext)
  , mDoRebuildAllStyleData(false)
  , mInRebuildAllStyleData(false)
  , mSkipAnimationRules(false)
  , mHavePendingNonAnimationRestyles(false)
  , mRebuildAllExtraHint(nsChangeHint(0))
  , mRebuildAllRestyleHint(nsRestyleHint(0))
  , mAnimationGeneration(0)
  , mReframingStyleContexts(nullptr)
  , mAnimationsWithDestroyedFrame(nullptr)
  , mPendingRestyles(ELEMENT_HAS_PENDING_RESTYLE |
                     ELEMENT_IS_POTENTIAL_RESTYLE_ROOT |
                     ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR)
  , mIsProcessingRestyles(false)
#ifdef RESTYLE_LOGGING
  , mLoggingDepth(0)
#endif
{
  mPendingRestyles.Init(this);
}

void
RestyleManager::RestyleElement(Element*               aElement,
                               nsIFrame*              aPrimaryFrame,
                               nsChangeHint           aMinHint,
                               RestyleTracker&        aRestyleTracker,
                               nsRestyleHint          aRestyleHint,
                               const RestyleHintData& aRestyleHintData)
{
  MOZ_ASSERT(mReframingStyleContexts, "should have rsc");
  NS_ASSERTION(aPrimaryFrame == aElement->GetPrimaryFrame(),
               "frame/content mismatch");
  if (aPrimaryFrame && aPrimaryFrame->GetContent() != aElement) {
    // XXXbz this is due to image maps messing with the primary frame pointer
    // of <area>s.  See bug 135040.  We can remove this block once that's fixed.
    aPrimaryFrame = nullptr;
  }
  NS_ASSERTION(!aPrimaryFrame || aPrimaryFrame->GetContent() == aElement,
               "frame/content mismatch");

  // If we're restyling the root element and there are 'rem' units in
  // use, handle dynamic changes to the definition of a 'rem' here.
  if (PresContext()->UsesRootEMUnits() && aPrimaryFrame &&
      !mInRebuildAllStyleData) {
    nsStyleContext* oldContext = aPrimaryFrame->StyleContext();
    if (!oldContext->GetParent()) { // check that we're the root element
      RefPtr<nsStyleContext> newContext = StyleSet()->
        ResolveStyleFor(aElement, nullptr /* == oldContext->GetParent() */);
      if (oldContext->StyleFont()->mFont.size !=
          newContext->StyleFont()->mFont.size) {
        // The basis for 'rem' units has changed.
        mRebuildAllRestyleHint |= aRestyleHint;
        if (aRestyleHint & eRestyle_SomeDescendants) {
          mRebuildAllRestyleHint |= eRestyle_Subtree;
        }
        mRebuildAllExtraHint |= aMinHint;
        StartRebuildAllStyleData(aRestyleTracker);
        return;
      }
    }
  }

  if (aMinHint & nsChangeHint_ReconstructFrame) {
    FrameConstructor()->RecreateFramesForContent(aElement, false,
      nsCSSFrameConstructor::REMOVE_FOR_RECONSTRUCTION, nullptr);
  } else if (aPrimaryFrame) {
    ComputeAndProcessStyleChange(aPrimaryFrame, aMinHint, aRestyleTracker,
                                 aRestyleHint, aRestyleHintData);
  } else if (aRestyleHint & ~eRestyle_LaterSiblings) {
    // We're restyling an element with no frame, so we should try to
    // make one if its new style says it should have one.  But in order
    // to try to honor the restyle hint (which we'd like to do so that,
    // for example, an animation-only style flush doesn't flush other
    // buffered style changes), we only do this if the restyle hint says
    // we have *some* restyling for this frame.  This means we'll
    // potentially get ahead of ourselves in that case, but not as much
    // as we would if we didn't check the restyle hint.
    nsStyleContext* newContext =
      FrameConstructor()->MaybeRecreateFramesForElement(aElement);
    if (newContext &&
        newContext->StyleDisplay()->mDisplay == StyleDisplay::Contents) {
      // Style change for a display:contents node that did not recreate frames.
      ComputeAndProcessStyleChange(newContext, aElement, aMinHint,
                                   aRestyleTracker, aRestyleHint,
                                   aRestyleHintData);
    }
  }
}

RestyleManager::ReframingStyleContexts::ReframingStyleContexts(
                                          RestyleManager* aRestyleManager)
  : mRestyleManager(aRestyleManager)
  , mRestorePointer(mRestyleManager->mReframingStyleContexts)
{
  MOZ_ASSERT(!mRestyleManager->mReframingStyleContexts,
             "shouldn't construct recursively");
  mRestyleManager->mReframingStyleContexts = this;
}

RestyleManager::ReframingStyleContexts::~ReframingStyleContexts()
{
  // Before we go away, we need to flush out any frame construction that
  // was enqueued, so that we start transitions.
  // Note that this is a little bit evil in that we're calling into code
  // that calls our member functions from our destructor, but it's at
  // the beginning of our destructor, so it shouldn't be too bad.
  mRestyleManager->PresContext()->FrameConstructor()->CreateNeededFrames();
}

RestyleManager::AnimationsWithDestroyedFrame::AnimationsWithDestroyedFrame(
                                          RestyleManager* aRestyleManager)
  : mRestyleManager(aRestyleManager)
  , mRestorePointer(mRestyleManager->mAnimationsWithDestroyedFrame)
{
  MOZ_ASSERT(!mRestyleManager->mAnimationsWithDestroyedFrame,
             "shouldn't construct recursively");
  mRestyleManager->mAnimationsWithDestroyedFrame = this;
}

void
RestyleManager::AnimationsWithDestroyedFrame::StopAnimationsForElementsWithoutFrames()
{
  StopAnimationsWithoutFrame(mContents, CSSPseudoElementType::NotPseudo);
  StopAnimationsWithoutFrame(mBeforeContents, CSSPseudoElementType::before);
  StopAnimationsWithoutFrame(mAfterContents, CSSPseudoElementType::after);
}

void
RestyleManager::AnimationsWithDestroyedFrame::StopAnimationsWithoutFrame(
  nsTArray<RefPtr<nsIContent>>& aArray,
  CSSPseudoElementType aPseudoType)
{
  nsAnimationManager* animationManager =
    mRestyleManager->PresContext()->AnimationManager();
  nsTransitionManager* transitionManager =
    mRestyleManager->PresContext()->TransitionManager();
  for (nsIContent* content : aArray) {
    if (content->GetPrimaryFrame()) {
      continue;
    }
    dom::Element* element = content->AsElement();

    animationManager->StopAnimationsForElement(element, aPseudoType);
    transitionManager->StopTransitionsForElement(element, aPseudoType);

    // All other animations should keep running but not running on the
    // *compositor* at this point.
    EffectSet* effectSet = EffectSet::GetEffectSet(element, aPseudoType);
    if (effectSet) {
      for (KeyframeEffectReadOnly* effect : *effectSet) {
        effect->ResetIsRunningOnCompositor();
      }
    }
  }
}

static inline dom::Element*
ElementForStyleContext(nsIContent* aParentContent,
                       nsIFrame* aFrame,
                       CSSPseudoElementType aPseudoType);

// Forwarded nsIDocumentObserver method, to handle restyling (and
// passing the notification to the frame).
nsresult
RestyleManager::ContentStateChanged(nsIContent* aContent,
                                    EventStates aStateMask)
{
  // XXXbz it would be good if this function only took Elements, but
  // we'd have to make ESM guarantee that usefully.
  if (!aContent->IsElement()) {
    return NS_OK;
  }

  Element* aElement = aContent->AsElement();

  nsChangeHint changeHint;
  nsRestyleHint restyleHint;
  ContentStateChangedInternal(aElement, aStateMask, &changeHint, &restyleHint);

  PostRestyleEvent(aElement, restyleHint, changeHint);
  return NS_OK;
}

// Forwarded nsIMutationObserver method, to handle restyling.
void
RestyleManager::AttributeWillChange(Element* aElement,
                                    int32_t aNameSpaceID,
                                    nsIAtom* aAttribute,
                                    int32_t aModType,
                                    const nsAttrValue* aNewValue)
{
  RestyleHintData rsdata;
  nsRestyleHint rshint =
    StyleSet()->HasAttributeDependentStyle(aElement,
                                           aNameSpaceID,
                                           aAttribute,
                                           aModType,
                                           false,
                                           aNewValue,
                                           rsdata);
  PostRestyleEvent(aElement, rshint, nsChangeHint(0), &rsdata);
}

// Forwarded nsIMutationObserver method, to handle restyling (and
// passing the notification to the frame).
void
RestyleManager::AttributeChanged(Element* aElement,
                                 int32_t aNameSpaceID,
                                 nsIAtom* aAttribute,
                                 int32_t aModType,
                                 const nsAttrValue* aOldValue)
{
  // Hold onto the PresShell to prevent ourselves from being destroyed.
  // XXXbz how, exactly, would this attribute change cause us to be
  // destroyed from inside this function?
  nsCOMPtr<nsIPresShell> shell = PresContext()->GetPresShell();
  mozilla::Unused << shell; // Unused within this function

  // Get the frame associated with the content which is the highest in the frame tree
  nsIFrame* primaryFrame = aElement->GetPrimaryFrame();

#if 0
  NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
     ("RestyleManager::AttributeChanged: content=%p[%s] frame=%p",
      aContent, ContentTag(aElement, 0), frame));
#endif

  // the style tag has its own interpretation based on aHint
  nsChangeHint hint = aElement->GetAttributeChangeHint(aAttribute, aModType);

  bool reframe = (hint & nsChangeHint_ReconstructFrame) != 0;

#ifdef MOZ_XUL
  // The following listbox widget trap prevents offscreen listbox widget
  // content from being removed and re-inserted (which is what would
  // happen otherwise).
  if (!primaryFrame && !reframe) {
    int32_t namespaceID;
    nsIAtom* tag = PresContext()->Document()->BindingManager()->
                     ResolveTag(aElement, &namespaceID);

    if (namespaceID == kNameSpaceID_XUL &&
        (tag == nsGkAtoms::listitem ||
         tag == nsGkAtoms::listcell))
      return;
  }

  if (aAttribute == nsGkAtoms::tooltiptext ||
      aAttribute == nsGkAtoms::tooltip)
  {
    nsIRootBox* rootBox = nsIRootBox::GetRootBox(PresContext()->GetPresShell());
    if (rootBox) {
      if (aModType == nsIDOMMutationEvent::REMOVAL)
        rootBox->RemoveTooltipSupport(aElement);
      if (aModType == nsIDOMMutationEvent::ADDITION)
        rootBox->AddTooltipSupport(aElement);
    }
  }

#endif // MOZ_XUL

  if (primaryFrame) {
    // See if we have appearance information for a theme.
    const nsStyleDisplay* disp = primaryFrame->StyleDisplay();
    if (disp->mAppearance) {
      nsITheme* theme = PresContext()->GetTheme();
      if (theme && theme->ThemeSupportsWidget(PresContext(), primaryFrame, disp->mAppearance)) {
        bool repaint = false;
        theme->WidgetStateChanged(primaryFrame, disp->mAppearance, aAttribute,
            &repaint, aOldValue);
        if (repaint)
          hint |= nsChangeHint_RepaintFrame;
      }
    }

    // let the frame deal with it now, so we don't have to deal later
    primaryFrame->AttributeChanged(aNameSpaceID, aAttribute, aModType);
    // XXXwaterson should probably check for IB split siblings
    // here, and propagate the AttributeChanged notification to
    // them, as well. Currently, inline frames don't do anything on
    // this notification, so it's not that big a deal.
  }

  // See if we can optimize away the style re-resolution -- must be called after
  // the frame's AttributeChanged() in case it does something that affects the style
  RestyleHintData rsdata;
  nsRestyleHint rshint =
    StyleSet()->HasAttributeDependentStyle(aElement,
                                           aNameSpaceID,
                                           aAttribute,
                                           aModType,
                                           true,
                                           aOldValue,
                                           rsdata);
  PostRestyleEvent(aElement, rshint, hint, &rsdata);
}

/* static */ uint64_t
RestyleManager::GetAnimationGenerationForFrame(nsIFrame* aFrame)
{
  EffectSet* effectSet = EffectSet::GetEffectSet(aFrame);
  return effectSet ? effectSet->GetAnimationGeneration() : 0;
}

void
RestyleManager::RestyleForEmptyChange(Element* aContainer)
{
  // In some cases (:empty + E, :empty ~ E), a change in the content of
  // an element requires restyling its parent's siblings.
  nsRestyleHint hint = eRestyle_Subtree;
  nsIContent* grandparent = aContainer->GetParent();
  if (grandparent &&
      (grandparent->GetFlags() & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS)) {
    hint = nsRestyleHint(hint | eRestyle_LaterSiblings);
  }
  PostRestyleEvent(aContainer, hint, nsChangeHint(0));
}

void
RestyleManager::RestyleForAppend(nsIContent* aContainer,
                                 nsIContent* aFirstNewContent)
{
  // The container cannot be a document, but might be a ShadowRoot.
  if (!aContainer->IsElement()) {
    return;
  }
  Element* container = aContainer->AsElement();

#ifdef DEBUG
  {
    for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
      NS_ASSERTION(!cur->IsRootOfAnonymousSubtree(),
                   "anonymous nodes should not be in child lists");
    }
  }
#endif
  uint32_t selectorFlags =
    container->GetFlags() & (NODE_ALL_SELECTOR_FLAGS &
                             ~NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
  if (selectorFlags == 0)
    return;

  if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
    // see whether we need to restyle the container
    bool wasEmpty = true; // :empty or :-moz-only-whitespace
    for (nsIContent* cur = container->GetFirstChild();
         cur != aFirstNewContent;
         cur = cur->GetNextSibling()) {
      // We don't know whether we're testing :empty or :-moz-only-whitespace,
      // so be conservative and assume :-moz-only-whitespace (i.e., make
      // IsSignificantChild less likely to be true, and thus make us more
      // likely to restyle).
      if (nsStyleUtil::IsSignificantChild(cur, true, false)) {
        wasEmpty = false;
        break;
      }
    }
    if (wasEmpty) {
      RestyleForEmptyChange(container);
      return;
    }
  }

  if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
    PostRestyleEvent(container, eRestyle_Subtree, nsChangeHint(0));
    // Restyling the container is the most we can do here, so we're done.
    return;
  }

  if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
    // restyle the last element child before this node
    for (nsIContent* cur = aFirstNewContent->GetPreviousSibling();
         cur;
         cur = cur->GetPreviousSibling()) {
      if (cur->IsElement()) {
        PostRestyleEvent(cur->AsElement(), eRestyle_Subtree, nsChangeHint(0));
        break;
      }
    }
  }
}

// Needed since we can't use PostRestyleEvent on non-elements (with
// eRestyle_LaterSiblings or nsRestyleHint(eRestyle_Subtree |
// eRestyle_LaterSiblings) as appropriate).
static void
RestyleSiblingsStartingWith(RestyleManager* aRestyleManager,
                            nsIContent* aStartingSibling /* may be null */)
{
  for (nsIContent* sibling = aStartingSibling; sibling;
       sibling = sibling->GetNextSibling()) {
    if (sibling->IsElement()) {
      aRestyleManager->
        PostRestyleEvent(sibling->AsElement(),
                         nsRestyleHint(eRestyle_Subtree | eRestyle_LaterSiblings),
                         nsChangeHint(0));
      break;
    }
  }
}

// Restyling for a ContentInserted or CharacterDataChanged notification.
// This could be used for ContentRemoved as well if we got the
// notification before the removal happened (and sometimes
// CharacterDataChanged is more like a removal than an addition).
// The comments are written and variables are named in terms of it being
// a ContentInserted notification.
void
RestyleManager::RestyleForInsertOrChange(nsINode* aContainer,
                                         nsIContent* aChild)
{
  // The container might be a document or a ShadowRoot.
  if (!aContainer->IsElement()) {
    return;
  }
  Element* container = aContainer->AsElement();

  NS_ASSERTION(!aChild->IsRootOfAnonymousSubtree(),
               "anonymous nodes should not be in child lists");
  uint32_t selectorFlags =
    container ? (container->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
  if (selectorFlags == 0)
    return;

  if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
    // see whether we need to restyle the container
    bool wasEmpty = true; // :empty or :-moz-only-whitespace
    for (nsIContent* child = container->GetFirstChild();
         child;
         child = child->GetNextSibling()) {
      if (child == aChild)
        continue;
      // We don't know whether we're testing :empty or :-moz-only-whitespace,
      // so be conservative and assume :-moz-only-whitespace (i.e., make
      // IsSignificantChild less likely to be true, and thus make us more
      // likely to restyle).
      if (nsStyleUtil::IsSignificantChild(child, true, false)) {
        wasEmpty = false;
        break;
      }
    }
    if (wasEmpty) {
      RestyleForEmptyChange(container);
      return;
    }
  }

  if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
    PostRestyleEvent(container, eRestyle_Subtree, nsChangeHint(0));
    // Restyling the container is the most we can do here, so we're done.
    return;
  }

  if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
    // Restyle all later siblings.
    RestyleSiblingsStartingWith(this, aChild->GetNextSibling());
  }

  if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
    // restyle the previously-first element child if it is after this node
    bool passedChild = false;
    for (nsIContent* content = container->GetFirstChild();
         content;
         content = content->GetNextSibling()) {
      if (content == aChild) {
        passedChild = true;
        continue;
      }
      if (content->IsElement()) {
        if (passedChild) {
          PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
                           nsChangeHint(0));
        }
        break;
      }
    }
    // restyle the previously-last element child if it is before this node
    passedChild = false;
    for (nsIContent* content = container->GetLastChild();
         content;
         content = content->GetPreviousSibling()) {
      if (content == aChild) {
        passedChild = true;
        continue;
      }
      if (content->IsElement()) {
        if (passedChild) {
          PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
                           nsChangeHint(0));
        }
        break;
      }
    }
  }
}

void
RestyleManager::ContentRemoved(nsINode* aContainer,
                               nsIContent* aOldChild,
                               nsIContent* aFollowingSibling)
{
  // The container might be a document or a ShadowRoot.
  if (!aContainer->IsElement()) {
    return;
  }
  Element* container = aContainer->AsElement();

  if (aOldChild->IsRootOfAnonymousSubtree()) {
    // This should be an assert, but this is called incorrectly in
    // HTMLEditor::DeleteRefToAnonymousNode and the assertions were clogging
    // up the logs.  Make it an assert again when that's fixed.
    MOZ_ASSERT(aOldChild->GetProperty(nsGkAtoms::restylableAnonymousNode),
               "anonymous nodes should not be in child lists (bug 439258)");
  }
  uint32_t selectorFlags =
    container ? (container->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
  if (selectorFlags == 0)
    return;

  if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
    // see whether we need to restyle the container
    bool isEmpty = true; // :empty or :-moz-only-whitespace
    for (nsIContent* child = container->GetFirstChild();
         child;
         child = child->GetNextSibling()) {
      // We don't know whether we're testing :empty or :-moz-only-whitespace,
      // so be conservative and assume :-moz-only-whitespace (i.e., make
      // IsSignificantChild less likely to be true, and thus make us more
      // likely to restyle).
      if (nsStyleUtil::IsSignificantChild(child, true, false)) {
        isEmpty = false;
        break;
      }
    }
    if (isEmpty) {
      RestyleForEmptyChange(container);
      return;
    }
  }

  if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
    PostRestyleEvent(container, eRestyle_Subtree, nsChangeHint(0));
    // Restyling the container is the most we can do here, so we're done.
    return;
  }

  if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
    // Restyle all later siblings.
    RestyleSiblingsStartingWith(this, aFollowingSibling);
  }

  if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
    // restyle the now-first element child if it was after aOldChild
    bool reachedFollowingSibling = false;
    for (nsIContent* content = container->GetFirstChild();
         content;
         content = content->GetNextSibling()) {
      if (content == aFollowingSibling) {
        reachedFollowingSibling = true;
        // do NOT continue here; we might want to restyle this node
      }
      if (content->IsElement()) {
        if (reachedFollowingSibling) {
          PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
                           nsChangeHint(0));
        }
        break;
      }
    }
    // restyle the now-last element child if it was before aOldChild
    reachedFollowingSibling = (aFollowingSibling == nullptr);
    for (nsIContent* content = container->GetLastChild();
         content;
         content = content->GetPreviousSibling()) {
      if (content->IsElement()) {
        if (reachedFollowingSibling) {
          PostRestyleEvent(content->AsElement(), eRestyle_Subtree, nsChangeHint(0));
        }
        break;
      }
      if (content == aFollowingSibling) {
        reachedFollowingSibling = true;
      }
    }
  }
}

void
RestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint,
                                    nsRestyleHint aRestyleHint)
{
  NS_ASSERTION(!(aExtraHint & nsChangeHint_ReconstructFrame),
               "Should not reconstruct the root of the frame tree.  "
               "Use ReconstructDocElementHierarchy instead.");
  MOZ_ASSERT(!(aRestyleHint & ~(eRestyle_Subtree | eRestyle_ForceDescendants)),
             "the only bits allowed in aRestyleHint are eRestyle_Subtree and "
             "eRestyle_ForceDescendants");

  mRebuildAllExtraHint |= aExtraHint;
  mRebuildAllRestyleHint |= aRestyleHint;

  // Processing the style changes could cause a flush that propagates to
  // the parent frame and thus destroys the pres shell, so we must hold
  // a reference.
  nsCOMPtr<nsIPresShell> presShell = PresContext()->GetPresShell();
  if (!presShell || !presShell->GetRootFrame()) {
    mDoRebuildAllStyleData = false;
    return;
  }

  // Make sure that the viewmanager will outlive the presshell
  RefPtr<nsViewManager> vm = presShell->GetViewManager();
  mozilla::Unused << vm; // Not used within this function

  // We may reconstruct frames below and hence process anything that is in the
  // tree. We don't want to get notified to process those items again after.
  presShell->GetDocument()->FlushPendingNotifications(Flush_ContentAndNotify);

  nsAutoScriptBlocker scriptBlocker;

  mDoRebuildAllStyleData = true;

  ProcessPendingRestyles();
}

void
RestyleManager::StartRebuildAllStyleData(RestyleTracker& aRestyleTracker)
{
  MOZ_ASSERT(mIsProcessingRestyles);

  nsIFrame* rootFrame = PresContext()->PresShell()->GetRootFrame();
  if (!rootFrame) {
    // No need to do anything.
    return;
  }

  mInRebuildAllStyleData = true;

  // Tell the style set to get the old rule tree out of the way
  // so we can recalculate while maintaining rule tree immutability
  nsresult rv = StyleSet()->BeginReconstruct();
  if (NS_FAILED(rv)) {
    MOZ_CRASH("unable to rebuild style data");
  }

  nsRestyleHint restyleHint = mRebuildAllRestyleHint;
  nsChangeHint changeHint = mRebuildAllExtraHint;
  mRebuildAllExtraHint = nsChangeHint(0);
  mRebuildAllRestyleHint = nsRestyleHint(0);

  restyleHint |= eRestyle_ForceDescendants;

  if (!(restyleHint & eRestyle_Subtree) &&
      (restyleHint & ~(eRestyle_Force | eRestyle_ForceDescendants))) {
    // We want this hint to apply to the root node's primary frame
    // rather than the root frame, since it's the primary frame that has
    // the styles for the root element (rather than the ancestors of the
    // primary frame whose mContent is the root node but which have
    // different styles).  If we use up the hint for one of the
    // ancestors that we hit first, then we'll fail to do the restyling
    // we need to do.
    Element* root = PresContext()->Document()->GetRootElement();
    if (root) {
      // If the root element is gone, dropping the hint on the floor
      // should be fine.
      aRestyleTracker.AddPendingRestyle(root, restyleHint, nsChangeHint(0));
    }
    restyleHint = nsRestyleHint(0);
  }

  // Recalculate all of the style contexts for the document, from the
  // root frame.  We can't do this with a change hint, since we can't
  // post a change hint for the root frame.
  // Note that we can ignore the return value of ComputeStyleChangeFor
  // because we never need to reframe the root frame.
  // XXX Does it matter that we're passing aExtraHint to the real root
  // frame and not the root node's primary frame?  (We could do
  // roughly what we do for aRestyleHint above.)
  ComputeAndProcessStyleChange(rootFrame,
                               changeHint, aRestyleTracker, restyleHint,
                               RestyleHintData());
}

void
RestyleManager::FinishRebuildAllStyleData()
{
  MOZ_ASSERT(mInRebuildAllStyleData, "bad caller");

  // Tell the style set it's safe to destroy the old rule tree.  We
  // must do this after the ProcessRestyledFrames call in case the
  // change list has frame reconstructs in it (since frames to be
  // reconstructed will still have their old style context pointers
  // until they are destroyed).
  StyleSet()->EndReconstruct();

  mInRebuildAllStyleData = false;
}

void
RestyleManager::ProcessPendingRestyles()
{
  NS_PRECONDITION(PresContext()->Document(), "No document?  Pshaw!");
  NS_PRECONDITION(!nsContentUtils::IsSafeToRunScript(),
                  "Missing a script blocker!");

  // First do any queued-up frame creation.  (We should really
  // merge this into the rest of the process, though; see bug 827239.)
  PresContext()->FrameConstructor()->CreateNeededFrames();

  // Process non-animation restyles...
  MOZ_ASSERT(!mIsProcessingRestyles,
             "Nesting calls to ProcessPendingRestyles?");
  mIsProcessingRestyles = true;

  // Before we process any restyles, we need to ensure that style
  // resulting from any animations is up-to-date, so that if any style
  // changes we cause trigger transitions, we have the correct old style
  // for starting the transition.
  bool haveNonAnimation =
    mHavePendingNonAnimationRestyles || mDoRebuildAllStyleData;
  if (haveNonAnimation) {
    ++mAnimationGeneration;
    UpdateOnlyAnimationStyles();
  } else {
    // If we don't have non-animation style updates, then we have queued
    // up animation style updates from the refresh driver tick.  This
    // doesn't necessarily include *all* animation style updates, since
    // we might be suppressing main-thread updates for some animations,
    // so we don't want to call UpdateOnlyAnimationStyles, which updates
    // all animations.  In other words, the work that we're about to do
    // to process the pending restyles queue is a *subset* of the work
    // that UpdateOnlyAnimationStyles would do, since we're *not*
    // updating transitions that are running on the compositor thread
    // and suppressed on the main thread.
    //
    // But when we update those styles, we want to suppress updates to
    // transitions just like we do in UpdateOnlyAnimationStyles.  So we
    // want to tell the transition manager to act as though we're in
    // UpdateOnlyAnimationStyles.
    //
    // FIXME: In the future, we might want to refactor the way the
    // animation and transition manager do their refresh driver ticks so
    // that we can use UpdateOnlyAnimationStyles, with a different
    // boolean argument, for this update as well, instead of having them
    // post style updates in their WillRefresh methods.
    PresContext()->TransitionManager()->SetInAnimationOnlyStyleUpdate(true);
  }

  ProcessRestyles(mPendingRestyles);

  if (!haveNonAnimation) {
    PresContext()->TransitionManager()->SetInAnimationOnlyStyleUpdate(false);
  }

  mIsProcessingRestyles = false;

  NS_ASSERTION(haveNonAnimation || !mHavePendingNonAnimationRestyles,
               "should not have added restyles");
  mHavePendingNonAnimationRestyles = false;

  if (mDoRebuildAllStyleData) {
    // We probably wasted a lot of work up above, but this seems safest
    // and it should be rarely used.
    // This might add us as a refresh observer again; that's ok.
    ProcessPendingRestyles();

    NS_ASSERTION(!mDoRebuildAllStyleData,
                 "repeatedly setting mDoRebuildAllStyleData?");
  }

  MOZ_ASSERT(!mInRebuildAllStyleData,
             "should have called FinishRebuildAllStyleData");
}

void
RestyleManager::BeginProcessingRestyles(RestyleTracker& aRestyleTracker)
{
  // Make sure to not rebuild quote or counter lists while we're
  // processing restyles
  PresContext()->FrameConstructor()->BeginUpdate();

  mInStyleRefresh = true;

  if (ShouldStartRebuildAllFor(aRestyleTracker)) {
    mDoRebuildAllStyleData = false;
    StartRebuildAllStyleData(aRestyleTracker);
  }
}

void
RestyleManager::EndProcessingRestyles()
{
  FlushOverflowChangedTracker();

  MOZ_ASSERT(mAnimationsWithDestroyedFrame);
  mAnimationsWithDestroyedFrame->
    StopAnimationsForElementsWithoutFrames();

  // Set mInStyleRefresh to false now, since the EndUpdate call might
  // add more restyles.
  mInStyleRefresh = false;

  if (mInRebuildAllStyleData) {
    FinishRebuildAllStyleData();
  }

  PresContext()->FrameConstructor()->EndUpdate();

#ifdef DEBUG
  PresContext()->PresShell()->VerifyStyleTree();
#endif
}

void
RestyleManager::UpdateOnlyAnimationStyles()
{
  bool doCSS = PresContext()->EffectCompositor()->HasPendingStyleUpdates();

  nsIDocument* document = PresContext()->Document();
  nsSMILAnimationController* animationController =
    document->HasAnimationController() ?
    document->GetAnimationController() :
    nullptr;
  bool doSMIL = animationController &&
                animationController->MightHavePendingStyleUpdates();

  if (!doCSS && !doSMIL) {
    return;
  }

  nsTransitionManager* transitionManager = PresContext()->TransitionManager();

  transitionManager->SetInAnimationOnlyStyleUpdate(true);

  RestyleTracker tracker(ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE |
                         ELEMENT_IS_POTENTIAL_ANIMATION_ONLY_RESTYLE_ROOT);
  tracker.Init(this);

  if (doCSS) {
    // FIXME:  We should have the transition manager and animation manager
    // add only the elements for which animations are currently throttled
    // (i.e., animating on the compositor with main-thread style updates
    // suppressed).
    PresContext()->EffectCompositor()->AddStyleUpdatesTo(tracker);
  }

  if (doSMIL) {
    animationController->AddStyleUpdatesTo(tracker);
  }

  ProcessRestyles(tracker);

  transitionManager->SetInAnimationOnlyStyleUpdate(false);
}

void
RestyleManager::PostRestyleEvent(Element* aElement,
                                 nsRestyleHint aRestyleHint,
                                 nsChangeHint aMinChangeHint,
                                 const RestyleHintData* aRestyleHintData)
{
  if (MOZ_UNLIKELY(IsDisconnected()) ||
      MOZ_UNLIKELY(PresContext()->PresShell()->IsDestroying())) {
    return;
  }

  if (aRestyleHint == 0 && !aMinChangeHint) {
    // Nothing to do here
    return;
  }

  mPendingRestyles.AddPendingRestyle(aElement, aRestyleHint, aMinChangeHint,
                                     aRestyleHintData);

  // Set mHavePendingNonAnimationRestyles for any restyle that could
  // possibly contain non-animation styles (i.e., those that require us
  // to do an animation-only style flush before processing style changes
  // to ensure correct initialization of CSS transitions).
  if (aRestyleHint & ~eRestyle_AllHintsWithAnimations) {
    mHavePendingNonAnimationRestyles = true;
  }

  PostRestyleEventInternal(false);
}

void
RestyleManager::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
                                             nsRestyleHint aRestyleHint)
{
  NS_ASSERTION(!(aExtraHint & nsChangeHint_ReconstructFrame),
               "Should not reconstruct the root of the frame tree.  "
               "Use ReconstructDocElementHierarchy instead.");
  MOZ_ASSERT(!(aRestyleHint & eRestyle_SomeDescendants),
             "PostRebuildAllStyleDataEvent does not handle "
             "eRestyle_SomeDescendants");

  mDoRebuildAllStyleData = true;
  mRebuildAllExtraHint |= aExtraHint;
  mRebuildAllRestyleHint |= aRestyleHint;

  // Get a restyle event posted if necessary
  PostRestyleEventInternal(false);
}

// aContent must be the content for the frame in question, which may be
// :before/:after content
/* static */ bool
RestyleManager::TryStartingTransition(nsPresContext* aPresContext,
                                      nsIContent* aContent,
                                      nsStyleContext* aOldStyleContext,
                                      RefPtr<nsStyleContext>*
                                        aNewStyleContext /* inout */)
{
  if (!aContent || !aContent->IsElement()) {
    return false;
  }

  // Notify the transition manager.  If it starts a transition,
  // it might modify the new style context.
  RefPtr<nsStyleContext> sc = *aNewStyleContext;
  aPresContext->TransitionManager()->StyleContextChanged(
    aContent->AsElement(), aOldStyleContext, aNewStyleContext);
  return *aNewStyleContext != sc;
}

static dom::Element*
ElementForStyleContext(nsIContent* aParentContent,
                       nsIFrame* aFrame,
                       CSSPseudoElementType aPseudoType)
{
  // We don't expect XUL tree stuff here.
  NS_PRECONDITION(aPseudoType == CSSPseudoElementType::NotPseudo ||
                  aPseudoType == CSSPseudoElementType::AnonBox ||
                  aPseudoType < CSSPseudoElementType::Count,
                  "Unexpected pseudo");
  // XXX see the comments about the various element confusion in
  // ElementRestyler::Restyle.
  if (aPseudoType == CSSPseudoElementType::NotPseudo) {
    return aFrame->GetContent()->AsElement();
  }

  if (aPseudoType == CSSPseudoElementType::AnonBox) {
    return nullptr;
  }

  if (aPseudoType == CSSPseudoElementType::firstLetter) {
    NS_ASSERTION(aFrame->GetType() == nsGkAtoms::letterFrame,
                 "firstLetter pseudoTag without a nsFirstLetterFrame");
    nsBlockFrame* block = nsBlockFrame::GetNearestAncestorBlock(aFrame);
    return block->GetContent()->AsElement();
  }

  if (aPseudoType == CSSPseudoElementType::mozColorSwatch) {
    MOZ_ASSERT(aFrame->GetParent() &&
               aFrame->GetParent()->GetParent(),
               "Color swatch frame should have a parent & grandparent");

    nsIFrame* grandparentFrame = aFrame->GetParent()->GetParent();
    MOZ_ASSERT(grandparentFrame->GetType() == nsGkAtoms::colorControlFrame,
               "Color swatch's grandparent should be nsColorControlFrame");

    return grandparentFrame->GetContent()->AsElement();
  }

  if (aPseudoType == CSSPseudoElementType::mozNumberText ||
      aPseudoType == CSSPseudoElementType::mozNumberWrapper ||
      aPseudoType == CSSPseudoElementType::mozNumberSpinBox ||
      aPseudoType == CSSPseudoElementType::mozNumberSpinUp ||
      aPseudoType == CSSPseudoElementType::mozNumberSpinDown) {
    // Get content for nearest nsNumberControlFrame:
    nsIFrame* f = aFrame->GetParent();
    MOZ_ASSERT(f);
    while (f->GetType() != nsGkAtoms::numberControlFrame) {
      f = f->GetParent();
      MOZ_ASSERT(f);
    }
    return f->GetContent()->AsElement();
  }

  if (aParentContent) {
    return aParentContent->AsElement();
  }

  MOZ_ASSERT(aFrame->GetContent()->GetParent(),
             "should not have got here for the root element");
  return aFrame->GetContent()->GetParent()->AsElement();
}

/**
 * Some pseudo-elements actually have a content node created for them,
 * whereas others have only a frame but not a content node.  In some
 * cases, we want to support style attributes or states on those
 * elements.  For those pseudo-elements, we need to pass the
 * anonymous pseudo-element content to selector matching processes in
 * addition to the element that the pseudo-element is for; in other
 * cases we should pass null instead.  This function returns the
 * pseudo-element content that we should pass.
 */
static dom::Element*
PseudoElementForStyleContext(nsIFrame* aFrame,
                             CSSPseudoElementType aPseudoType)
{
  if (aPseudoType >= CSSPseudoElementType::Count) {
    return nullptr;
  }

  if (nsCSSPseudoElements::PseudoElementSupportsStyleAttribute(aPseudoType) ||
      nsCSSPseudoElements::PseudoElementSupportsUserActionState(aPseudoType)) {
    return aFrame->GetContent()->AsElement();
  }

  return nullptr;
}

/**
 * FIXME: Temporary.  Should merge with following function.
 */
static nsIFrame*
GetPrevContinuationWithPossiblySameStyle(nsIFrame* aFrame)
{
  // Account for {ib} splits when looking for "prevContinuation".  In
  // particular, for the first-continuation of a part of an {ib} split
  // we want to use the previous ib-split sibling of the previous
  // ib-split sibling of aFrame, which should have the same style
  // context as aFrame itself.  In particular, if aFrame is the first
  // continuation of an inline part of a block-in-inline split then its
  // previous ib-split sibling is a block, and the previous ib-split
  // sibling of _that_ is an inline, just like aFrame.  Similarly, if
  // aFrame is the first continuation of a block part of an
  // block-in-inline split (a block-in-inline wrapper block), then its
  // previous ib-split sibling is an inline and the previous ib-split
  // sibling of that is either another block-in-inline wrapper block box
  // or null.
  nsIFrame* prevContinuation = aFrame->GetPrevContinuation();
  if (!prevContinuation &&
      (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
    // We're the first continuation, so we can just get the frame
    // property directly
    prevContinuation =
      aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling());
    if (prevContinuation) {
      prevContinuation =
        prevContinuation->Properties().Get(nsIFrame::IBSplitPrevSibling());
    }
  }

  NS_ASSERTION(!prevContinuation ||
               prevContinuation->GetContent() == aFrame->GetContent(),
               "unexpected content mismatch");

  return prevContinuation;
}

/**
 * Get the previous continuation or similar ib-split sibling (assuming
 * block/inline alternation), conditionally on it having the same style.
 * This assumes that we're not between resolving the two (i.e., that
 * they're both already resolved.
 */
static nsIFrame*
GetPrevContinuationWithSameStyle(nsIFrame* aFrame)
{
  nsIFrame* prevContinuation = GetPrevContinuationWithPossiblySameStyle(aFrame);
  if (!prevContinuation) {
    return nullptr;
  }

  nsStyleContext* prevStyle = prevContinuation->StyleContext();
  nsStyleContext* selfStyle = aFrame->StyleContext();
  if (prevStyle != selfStyle) {
    NS_ASSERTION(prevStyle->GetPseudo() != selfStyle->GetPseudo() ||
                 prevStyle->GetParent() != selfStyle->GetParent(),
                 "continuations should have the same style context");
    prevContinuation = nullptr;
  }
  return prevContinuation;
}

nsresult
RestyleManager::ReparentStyleContext(nsIFrame* aFrame)
{
  nsIAtom* frameType = aFrame->GetType();
  if (frameType == nsGkAtoms::placeholderFrame) {
    // Also reparent the out-of-flow and all its continuations.
    nsIFrame* outOfFlow =
      nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
    NS_ASSERTION(outOfFlow, "no out-of-flow frame");
    do {
      ReparentStyleContext(outOfFlow);
    } while ((outOfFlow = outOfFlow->GetNextContinuation()));
  } else if (frameType == nsGkAtoms::backdropFrame) {
    // Style context of backdrop frame has no parent style context, and
    // thus we do not need to reparent it.
    return NS_OK;
  }

  // DO NOT verify the style tree before reparenting.  The frame
  // tree has already been changed, so this check would just fail.
  nsStyleContext* oldContext = aFrame->StyleContext();

  RefPtr<nsStyleContext> newContext;
  nsIFrame* providerFrame;
  nsStyleContext* newParentContext = aFrame->GetParentStyleContext(&providerFrame);
  bool isChild = providerFrame && providerFrame->GetParent() == aFrame;
  nsIFrame* providerChild = nullptr;
  if (isChild) {
    ReparentStyleContext(providerFrame);
    // Get the style context again after ReparentStyleContext() which might have
    // changed it.
    newParentContext = providerFrame->StyleContext();
    providerChild = providerFrame;
  }
  NS_ASSERTION(newParentContext, "Reparenting something that has no usable"
               " parent? Shouldn't happen!");
  // XXX need to do something here to produce the correct style context for
  // an IB split whose first inline part is inside a first-line frame.
  // Currently the first IB anonymous block's style context takes the first
  // part's style context as parent, which is wrong since first-line style
  // should not apply to the anonymous block.

#ifdef DEBUG
  {
    // Check that our assumption that continuations of the same
    // pseudo-type and with the same style context parent have the
    // same style context is valid before the reresolution.  (We need
    // to check the pseudo-type and style context parent because of
    // :first-letter and :first-line, where we create styled and
    // unstyled letter/line frames distinguished by pseudo-type, and
    // then need to distinguish their descendants based on having
    // different parents.)
    nsIFrame* nextContinuation = aFrame->GetNextContinuation();
    if (nextContinuation) {
      nsStyleContext* nextContinuationContext =
        nextContinuation->StyleContext();
      NS_ASSERTION(oldContext == nextContinuationContext ||
                   oldContext->GetPseudo() !=
                     nextContinuationContext->GetPseudo() ||
                   oldContext->GetParent() !=
                     nextContinuationContext->GetParent(),
                   "continuations should have the same style context");
    }
  }
#endif

  nsIFrame* prevContinuation =
    GetPrevContinuationWithPossiblySameStyle(aFrame);
  nsStyleContext* prevContinuationContext;
  bool copyFromContinuation =
    prevContinuation &&
    (prevContinuationContext = prevContinuation->StyleContext())
      ->GetPseudo() == oldContext->GetPseudo() &&
     prevContinuationContext->GetParent() == newParentContext;
  if (copyFromContinuation) {
    // Just use the style context from the frame's previous
    // continuation (see assertion about aFrame->GetNextContinuation()
    // above, which we would have previously hit for aFrame's previous
    // continuation).
    newContext = prevContinuationContext;
  } else {
    nsIFrame* parentFrame = aFrame->GetParent();
    Element* element =
      ElementForStyleContext(parentFrame ? parentFrame->GetContent() : nullptr,
                             aFrame,
                             oldContext->GetPseudoType());
    newContext = StyleSet()->
                   ReparentStyleContext(oldContext, newParentContext, element);
  }

  if (newContext) {
    if (newContext != oldContext) {
      // We probably don't want to initiate transitions from
      // ReparentStyleContext, since we call it during frame
      // construction rather than in response to dynamic changes.
      // Also see the comment at the start of
      // nsTransitionManager::ConsiderStartingTransition.
#if 0
      if (!copyFromContinuation) {
        TryStartingTransition(mPresContext, aFrame->GetContent(),
                              oldContext, &newContext);
      }
#endif

      // Make sure to call CalcStyleDifference so that the new context ends
      // up resolving all the structs the old context resolved.
      if (!copyFromContinuation) {
        uint32_t equalStructs;
        uint32_t samePointerStructs;
        DebugOnly<nsChangeHint> styleChange =
          oldContext->CalcStyleDifference(newContext, nsChangeHint(0),
                                          &equalStructs,
                                          &samePointerStructs);
        // The style change is always 0 because we have the same rulenode and
        // CalcStyleDifference optimizes us away.  That's OK, though:
        // reparenting should never trigger a frame reconstruct, and whenever
        // it's happening we already plan to reflow and repaint the frames.
        NS_ASSERTION(!(styleChange & nsChangeHint_ReconstructFrame),
                     "Our frame tree is likely to be bogus!");
      }

      aFrame->SetStyleContext(newContext);

      nsIFrame::ChildListIterator lists(aFrame);
      for (; !lists.IsDone(); lists.Next()) {
        for (nsIFrame* child : lists.CurrentList()) {
          // only do frames that are in flow
          if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
              child != providerChild) {
#ifdef DEBUG
            if (nsGkAtoms::placeholderFrame == child->GetType()) {
              nsIFrame* outOfFlowFrame =
                nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
              NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");

              NS_ASSERTION(outOfFlowFrame != providerChild,
                           "Out of flow provider?");
            }
#endif
            ReparentStyleContext(child);
          }
        }
      }

      // If this frame is part of an IB split, then the style context of
      // the next part of the split might be a child of our style context.
      // Reparent its style context just in case one of our ancestors
      // (split or not) hasn't done so already). It's not a problem to
      // reparent the same frame twice because the "if (newContext !=
      // oldContext)" check will prevent us from redoing work.
      if ((aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) &&
          !aFrame->GetPrevContinuation()) {
        nsIFrame* sib =
          aFrame->Properties().Get(nsIFrame::IBSplitSibling());
        if (sib) {
          ReparentStyleContext(sib);
        }
      }

      // do additional contexts
      int32_t contextIndex = 0;
      for (nsStyleContext* oldExtraContext;
           (oldExtraContext = aFrame->GetAdditionalStyleContext(contextIndex));
           ++contextIndex) {
        RefPtr<nsStyleContext> newExtraContext;
        newExtraContext = StyleSet()->
                            ReparentStyleContext(oldExtraContext,
                                                 newContext, nullptr);
        if (newExtraContext) {
          if (newExtraContext != oldExtraContext) {
            // Make sure to call CalcStyleDifference so that the new
            // context ends up resolving all the structs the old context
            // resolved.
            uint32_t equalStructs;
            uint32_t samePointerStructs;
            DebugOnly<nsChangeHint> styleChange =
              oldExtraContext->CalcStyleDifference(newExtraContext,
                                                   nsChangeHint(0),
                                                   &equalStructs,
                                                   &samePointerStructs);
            // The style change is always 0 because we have the same
            // rulenode and CalcStyleDifference optimizes us away.  That's
            // OK, though: reparenting should never trigger a frame
            // reconstruct, and whenever it's happening we already plan to
            // reflow and repaint the frames.
            NS_ASSERTION(!(styleChange & nsChangeHint_ReconstructFrame),
                         "Our frame tree is likely to be bogus!");
          }

          aFrame->SetAdditionalStyleContext(contextIndex, newExtraContext);
        }
      }
#ifdef DEBUG
      DebugVerifyStyleTree(aFrame);
#endif
    }
  }

  return NS_OK;
}

ElementRestyler::ElementRestyler(nsPresContext* aPresContext,
                                 nsIFrame* aFrame,
                                 nsStyleChangeList* aChangeList,
                                 nsChangeHint aHintsHandledByAncestors,
                                 RestyleTracker& aRestyleTracker,
                                 nsTArray<nsCSSSelector*>&
                                   aSelectorsForDescendants,
                                 TreeMatchContext& aTreeMatchContext,
                                 nsTArray<nsIContent*>&
                                   aVisibleKidsOfHiddenElement,
                                 nsTArray<ContextToClear>& aContextsToClear,
                                 nsTArray<RefPtr<nsStyleContext>>&
                                   aSwappedStructOwners)
  : mPresContext(aPresContext)
  , mFrame(aFrame)
  , mParentContent(nullptr)
    // XXXldb Why does it make sense to use aParentContent?  (See
    // comment above assertion at start of ElementRestyler::Restyle.)
  , mContent(mFrame->GetContent() ? mFrame->GetContent() : mParentContent)
  , mChangeList(aChangeList)
  , mHintsHandled(aHintsHandledByAncestors &
                  ~NS_HintsNotHandledForDescendantsIn(aHintsHandledByAncestors))
  , mParentFrameHintsNotHandledForDescendants(nsChangeHint(0))
  , mHintsNotHandledForDescendants(nsChangeHint(0))
  , mRestyleTracker(aRestyleTracker)
  , mSelectorsForDescendants(aSelectorsForDescendants)
  , mTreeMatchContext(aTreeMatchContext)
  , mResolvedChild(nullptr)
  , mContextsToClear(aContextsToClear)
  , mSwappedStructOwners(aSwappedStructOwners)
  , mIsRootOfRestyle(true)
#ifdef ACCESSIBILITY
  , mDesiredA11yNotifications(eSendAllNotifications)
  , mKidsDesiredA11yNotifications(mDesiredA11yNotifications)
  , mOurA11yNotification(eDontNotify)
  , mVisibleKidsOfHiddenElement(aVisibleKidsOfHiddenElement)
#endif
#ifdef RESTYLE_LOGGING
  , mLoggingDepth(aRestyleTracker.LoggingDepth() + 1)
#endif
{
  MOZ_ASSERT_IF(mContent, !mContent->IsStyledByServo());
}

ElementRestyler::ElementRestyler(const ElementRestyler& aParentRestyler,
                                 nsIFrame* aFrame,
                                 uint32_t aConstructorFlags)
  : mPresContext(aParentRestyler.mPresContext)
  , mFrame(aFrame)
  , mParentContent(aParentRestyler.mContent)
    // XXXldb Why does it make sense to use aParentContent?  (See
    // comment above assertion at start of ElementRestyler::Restyle.)
  , mContent(mFrame->GetContent() ? mFrame->GetContent() : mParentContent)
  , mChangeList(aParentRestyler.mChangeList)
  , mHintsHandled(aParentRestyler.mHintsHandled &
                  ~NS_HintsNotHandledForDescendantsIn(aParentRestyler.mHintsHandled))
  , mParentFrameHintsNotHandledForDescendants(
      aParentRestyler.mHintsNotHandledForDescendants)
  , mHintsNotHandledForDescendants(nsChangeHint(0))
  , mRestyleTracker(aParentRestyler.mRestyleTracker)
  , mSelectorsForDescendants(aParentRestyler.mSelectorsForDescendants)
  , mTreeMatchContext(aParentRestyler.mTreeMatchContext)
  , mResolvedChild(nullptr)
  , mContextsToClear(aParentRestyler.mContextsToClear)
  , mSwappedStructOwners(aParentRestyler.mSwappedStructOwners)
  , mIsRootOfRestyle(false)
#ifdef ACCESSIBILITY
  , mDesiredA11yNotifications(aParentRestyler.mKidsDesiredA11yNotifications)
  , mKidsDesiredA11yNotifications(mDesiredA11yNotifications)
  , mOurA11yNotification(eDontNotify)
  , mVisibleKidsOfHiddenElement(aParentRestyler.mVisibleKidsOfHiddenElement)
#endif
#ifdef RESTYLE_LOGGING
  , mLoggingDepth(aParentRestyler.mLoggingDepth + 1)
#endif
{
  MOZ_ASSERT_IF(mContent, !mContent->IsStyledByServo());
  if (aConstructorFlags & FOR_OUT_OF_FLOW_CHILD) {
    // Note that the out-of-flow may not be a geometric descendant of
    // the frame where we started the reresolve.  Therefore, even if
    // mHintsHandled already includes nsChangeHint_AllReflowHints we
    // don't want to pass that on to the out-of-flow reresolve, since
    // that can lead to the out-of-flow not getting reflowed when it
    // should be (eg a reresolve starting at <body> that involves
    // reflowing the <body> would miss reflowing fixed-pos nodes that
    // also need reflow).  In the cases when the out-of-flow _is_ a
    // geometric descendant of a frame we already have a reflow hint
    // for, reflow coalescing should keep us from doing the work twice.
    mHintsHandled &= ~nsChangeHint_AllReflowHints;
  }
}

ElementRestyler::ElementRestyler(ParentContextFromChildFrame,
                                 const ElementRestyler& aParentRestyler,
                                 nsIFrame* aFrame)
  : mPresContext(aParentRestyler.mPresContext)
  , mFrame(aFrame)
  , mParentContent(aParentRestyler.mParentContent)
    // XXXldb Why does it make sense to use aParentContent?  (See
    // comment above assertion at start of ElementRestyler::Restyle.)
  , mContent(mFrame->GetContent() ? mFrame->GetContent() : mParentContent)
  , mChangeList(aParentRestyler.mChangeList)
  , mHintsHandled(aParentRestyler.mHintsHandled &
                  ~NS_HintsNotHandledForDescendantsIn(aParentRestyler.mHintsHandled))
  , mParentFrameHintsNotHandledForDescendants(
      // assume the worst
      nsChangeHint_Hints_NotHandledForDescendants)
  , mHintsNotHandledForDescendants(nsChangeHint(0))
  , mRestyleTracker(aParentRestyler.mRestyleTracker)
  , mSelectorsForDescendants(aParentRestyler.mSelectorsForDescendants)
  , mTreeMatchContext(aParentRestyler.mTreeMatchContext)
  , mResolvedChild(nullptr)
  , mContextsToClear(aParentRestyler.mContextsToClear)
  , mSwappedStructOwners(aParentRestyler.mSwappedStructOwners)
  , mIsRootOfRestyle(false)
#ifdef ACCESSIBILITY
  , mDesiredA11yNotifications(aParentRestyler.mDesiredA11yNotifications)
  , mKidsDesiredA11yNotifications(mDesiredA11yNotifications)
  , mOurA11yNotification(eDontNotify)
  , mVisibleKidsOfHiddenElement(aParentRestyler.mVisibleKidsOfHiddenElement)
#endif
#ifdef RESTYLE_LOGGING
  , mLoggingDepth(aParentRestyler.mLoggingDepth + 1)
#endif
{
  MOZ_ASSERT_IF(mContent, !mContent->IsStyledByServo());
}

ElementRestyler::ElementRestyler(nsPresContext* aPresContext,
                                 nsIContent* aContent,
                                 nsStyleChangeList* aChangeList,
                                 nsChangeHint aHintsHandledByAncestors,
                                 RestyleTracker& aRestyleTracker,
                                 nsTArray<nsCSSSelector*>& aSelectorsForDescendants,
                                 TreeMatchContext& aTreeMatchContext,
                                 nsTArray<nsIContent*>&
                                   aVisibleKidsOfHiddenElement,
                                 nsTArray<ContextToClear>& aContextsToClear,
                                 nsTArray<RefPtr<nsStyleContext>>&
                                   aSwappedStructOwners)
  : mPresContext(aPresContext)
  , mFrame(nullptr)
  , mParentContent(nullptr)
  , mContent(aContent)
  , mChangeList(aChangeList)
  , mHintsHandled(aHintsHandledByAncestors &
                  ~NS_HintsNotHandledForDescendantsIn(aHintsHandledByAncestors))
  , mParentFrameHintsNotHandledForDescendants(nsChangeHint(0))
  , mHintsNotHandledForDescendants(nsChangeHint(0))
  , mRestyleTracker(aRestyleTracker)
  , mSelectorsForDescendants(aSelectorsForDescendants)
  , mTreeMatchContext(aTreeMatchContext)
  , mResolvedChild(nullptr)
  , mContextsToClear(aContextsToClear)
  , mSwappedStructOwners(aSwappedStructOwners)
  , mIsRootOfRestyle(true)
#ifdef ACCESSIBILITY
  , mDesiredA11yNotifications(eSendAllNotifications)
  , mKidsDesiredA11yNotifications(mDesiredA11yNotifications)
  , mOurA11yNotification(eDontNotify)
  , mVisibleKidsOfHiddenElement(aVisibleKidsOfHiddenElement)
#endif
{
}

void
ElementRestyler::AddLayerChangesForAnimation()
{
  // Bug 847286 - We should have separate animation generation counters
  // on layers for transitions and animations and use != comparison below
  // rather than a > comparison.
  uint64_t frameGeneration =
    RestyleManager::GetAnimationGenerationForFrame(mFrame);

  nsChangeHint hint = nsChangeHint(0);
  for (const LayerAnimationInfo::Record& layerInfo :
         LayerAnimationInfo::sRecords) {
    Layer* layer =
      FrameLayerBuilder::GetDedicatedLayer(mFrame, layerInfo.mLayerType);
    if (layer && frameGeneration > layer->GetAnimationGeneration()) {
      // If we have a transform layer but don't have any transform style, we
      // probably just removed the transform but haven't destroyed the layer
      // yet. In this case we will add the appropriate change hint
      // (nsChangeHint_UpdateContainingBlock) when we compare style contexts
      // so we can skip adding any change hint here. (If we *were* to add
      // nsChangeHint_UpdateTransformLayer, ApplyRenderingChangeToTree would
      // complain that we're updating a transform layer without a transform).
      if (layerInfo.mLayerType == nsDisplayItem::TYPE_TRANSFORM &&
          !mFrame->StyleDisplay()->HasTransformStyle()) {
        continue;
      }
      hint |= layerInfo.mChangeHint;
    }

    // We consider it's the first paint for the frame if we have an animation
    // for the property but have no layer.
    // Note that in case of animations which has properties preventing running
    // on the compositor, e.g., width or height, corresponding layer is not
    // created at all, but even in such cases, we normally set valid change
    // hint for such animations in each tick, i.e. restyles in each tick. As
    // a result, we usually do restyles for such animations in every tick on
    // the main-thread.  The only animations which will be affected by this
    // explicit change hint are animations that have opacity/transform but did
    // not have those properies just before. e.g,  setting transform by
    // setKeyframes or changing target element from other target which prevents
    // running on the compositor, etc.
    if (!layer &&
        nsLayoutUtils::HasRelevantAnimationOfProperty(mFrame,
                                                      layerInfo.mProperty)) {
      hint |= layerInfo.mChangeHint;
    }
  }
  if (hint) {
    mChangeList->AppendChange(mFrame, mContent, hint);
  }
}

void
ElementRestyler::CaptureChange(nsStyleContext* aOldContext,
                               nsStyleContext* aNewContext,
                               nsChangeHint aChangeToAssume,
                               uint32_t* aEqualStructs,
                               uint32_t* aSamePointerStructs)
{
  static_assert(nsStyleStructID_Length <= 32,
                "aEqualStructs is not big enough");

  // Check some invariants about replacing one style context with another.
  NS_ASSERTION(aOldContext->GetPseudo() == aNewContext->GetPseudo(),
               "old and new style contexts should have the same pseudo");
  NS_ASSERTION(aOldContext->GetPseudoType() == aNewContext->GetPseudoType(),
               "old and new style contexts should have the same pseudo");

  nsChangeHint ourChange =
    aOldContext->CalcStyleDifference(aNewContext,
                                     mParentFrameHintsNotHandledForDescendants,
                                     aEqualStructs,
                                     aSamePointerStructs);
  NS_ASSERTION(!(ourChange & nsChangeHint_AllReflowHints) ||
               (ourChange & nsChangeHint_NeedReflow),
               "Reflow hint bits set without actually asking for a reflow");

  LOG_RESTYLE("CaptureChange, ourChange = %s, aChangeToAssume = %s",
              RestyleManager::ChangeHintToString(ourChange).get(),
              RestyleManager::ChangeHintToString(aChangeToAssume).get());
  LOG_RESTYLE_INDENT();

  // nsChangeHint_UpdateEffects is inherited, but it can be set due to changes
  // in inherited properties (fill and stroke).  Avoid propagating it into
  // text nodes.
  if ((ourChange & nsChangeHint_UpdateEffects) &&
      mContent && !mContent->IsElement()) {
    ourChange &= ~nsChangeHint_UpdateEffects;
  }

  ourChange |= aChangeToAssume;
  if (!NS_IsHintSubset(ourChange, mHintsHandled)) {
    mHintsHandled |= ourChange;
    if (!(ourChange & nsChangeHint_ReconstructFrame) || mContent) {
      LOG_RESTYLE("appending change %s",
                  RestyleManager::ChangeHintToString(ourChange).get());
      mChangeList->AppendChange(mFrame, mContent, ourChange);
    } else {
      LOG_RESTYLE("change has already been handled");
    }
  }
  mHintsNotHandledForDescendants |=
    NS_HintsNotHandledForDescendantsIn(ourChange);
  LOG_RESTYLE("mHintsNotHandledForDescendants = %s",
              RestyleManager::ChangeHintToString(mHintsNotHandledForDescendants).get());
}

class MOZ_RAII AutoSelectorArrayTruncater final
{
public:
  explicit AutoSelectorArrayTruncater(
        nsTArray<nsCSSSelector*>& aSelectorsForDescendants)
    : mSelectorsForDescendants(aSelectorsForDescendants)
    , mOriginalLength(aSelectorsForDescendants.Length())
  {
  }

  ~AutoSelectorArrayTruncater()
  {
    mSelectorsForDescendants.TruncateLength(mOriginalLength);
  }

private:
  nsTArray<nsCSSSelector*>& mSelectorsForDescendants;
  size_t mOriginalLength;
};

/**
 * Called when we are stopping a restyle with eRestyle_SomeDescendants, to
 * search for descendants that match any of the selectors in
 * mSelectorsForDescendants.  If the element does match one of the selectors,
 * we cause it to be restyled with eRestyle_Self.
 *
 * We traverse down the frame tree (and through the flattened content tree
 * when we find undisplayed content) unless we find an element that (a) already
 * has a pending restyle, or (b) does not have a pending restyle but does match
 * one of the selectors in mSelectorsForDescendants.  For (a), we add the
 * current mSelectorsForDescendants into the existing restyle data, and for (b)
 * we add a new pending restyle with that array.  So in both cases, when we
 * come to restyling this element back up in ProcessPendingRestyles, we will
 * again find the eRestyle_SomeDescendants hint and its selectors array.
 *
 * This ensures that we don't visit descendant elements and check them
 * against mSelectorsForDescendants more than once.
 */
void
ElementRestyler::ConditionallyRestyleChildren()
{
  MOZ_ASSERT(mContent == mFrame->GetContent());

  if (!mContent->IsElement() || mSelectorsForDescendants.IsEmpty()) {
    return;
  }

  Element* element = mContent->AsElement();

  LOG_RESTYLE("traversing descendants of frame %s (with element %s) to "
              "propagate eRestyle_SomeDescendants for these %d selectors:",
              FrameTagToString(mFrame).get(),
              ElementTagToString(element).get(),
              int(mSelectorsForDescendants.Length()));
  LOG_RESTYLE_INDENT();
#ifdef RESTYLE_LOGGING
  for (nsCSSSelector* sel : mSelectorsForDescendants) {
    LOG_RESTYLE("%s", sel->RestrictedSelectorToString().get());
  }
#endif

  Element* restyleRoot = mRestyleTracker.FindClosestRestyleRoot(element);
  ConditionallyRestyleChildren(mFrame, restyleRoot);
}

void
ElementRestyler::ConditionallyRestyleChildren(nsIFrame* aFrame,
                                              Element* aRestyleRoot)
{
  MOZ_ASSERT(aFrame->GetContent());
  MOZ_ASSERT(aFrame->GetContent()->IsElement());
  MOZ_ASSERT(!aFrame->GetContent()->IsStyledByServo());

  ConditionallyRestyleUndisplayedDescendants(aFrame, aRestyleRoot);
  ConditionallyRestyleContentChildren(aFrame, aRestyleRoot);
}

// The structure of this method parallels RestyleContentChildren.
// If you update this method, you probably want to update that one too.
void
ElementRestyler::ConditionallyRestyleContentChildren(nsIFrame* aFrame,
                                                     Element* aRestyleRoot)
{
  MOZ_ASSERT(aFrame->GetContent());
  MOZ_ASSERT(aFrame->GetContent()->IsElement());
  MOZ_ASSERT(!aFrame->GetContent()->IsStyledByServo());

  if (aFrame->GetContent()->HasFlag(mRestyleTracker.RootBit())) {
    aRestyleRoot = aFrame->GetContent()->AsElement();
  }

  for (nsIFrame* f = aFrame; f;
       f = RestyleManager::GetNextContinuationWithSameStyle(f, f->StyleContext())) {
    nsIFrame::ChildListIterator lists(f);
    for (; !lists.IsDone(); lists.Next()) {
      for (nsIFrame* child : lists.CurrentList()) {
        // Out-of-flows are reached through their placeholders.  Continuations
        // and block-in-inline splits are reached through those chains.
        if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
            !GetPrevContinuationWithSameStyle(child)) {
          // only do frames that are in flow
          if (child->GetType() == nsGkAtoms::placeholderFrame) { // placeholder
            // get out of flow frame and recur there
            nsIFrame* outOfFlowFrame =
              nsPlaceholderFrame::GetRealFrameForPlaceholder(child);

            // |nsFrame::GetParentStyleContext| checks being out
            // of flow so that this works correctly.
            do {
              if (GetPrevContinuationWithSameStyle(outOfFlowFrame)) {
                continue;
              }
              if (!ConditionallyRestyle(outOfFlowFrame, aRestyleRoot)) {
                ConditionallyRestyleChildren(outOfFlowFrame, aRestyleRoot);
              }
            } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
          } else {  // regular child frame
            if (child != mResolvedChild) {
              if (!ConditionallyRestyle(child, aRestyleRoot)) {
                ConditionallyRestyleChildren(child, aRestyleRoot);
              }
            }
          }
        }
      }
    }
  }
}

// The structure of this method parallels RestyleUndisplayedDescendants.
// If you update this method, you probably want to update that one too.
void
ElementRestyler::ConditionallyRestyleUndisplayedDescendants(
    nsIFrame* aFrame,
    Element* aRestyleRoot)
{
  nsIContent* undisplayedParent;
  if (MustCheckUndisplayedContent(aFrame, undisplayedParent)) {
    DoConditionallyRestyleUndisplayedDescendants(undisplayedParent,
                                                 aRestyleRoot);
  }
}

// The structure of this method parallels DoRestyleUndisplayedDescendants.
// If you update this method, you probably want to update that one too.
void
ElementRestyler::DoConditionallyRestyleUndisplayedDescendants(
    nsIContent* aParent,
    Element* aRestyleRoot)
{
  nsCSSFrameConstructor* fc = mPresContext->FrameConstructor();
  UndisplayedNode* nodes = fc->GetAllUndisplayedContentIn(aParent);
  ConditionallyRestyleUndisplayedNodes(nodes, aParent,
                                       StyleDisplay::None, aRestyleRoot);
  nodes = fc->GetAllDisplayContentsIn(aParent);
  ConditionallyRestyleUndisplayedNodes(nodes, aParent,
                                       StyleDisplay::Contents, aRestyleRoot);
}

// The structure of this method parallels RestyleUndisplayedNodes.
// If you update this method, you probably want to update that one too.
void
ElementRestyler::ConditionallyRestyleUndisplayedNodes(
    UndisplayedNode* aUndisplayed,
    nsIContent* aUndisplayedParent,
    const StyleDisplay aDisplay,
    Element* aRestyleRoot)
{
  MOZ_ASSERT(aDisplay == StyleDisplay::None ||
             aDisplay == StyleDisplay::Contents);
  if (!aUndisplayed) {
    return;
  }

  if (aUndisplayedParent &&
      aUndisplayedParent->IsElement() &&
      aUndisplayedParent->HasFlag(mRestyleTracker.RootBit())) {
    MOZ_ASSERT(!aUndisplayedParent->IsStyledByServo());
    aRestyleRoot = aUndisplayedParent->AsElement();
  }

  for (UndisplayedNode* undisplayed = aUndisplayed; undisplayed;
       undisplayed = undisplayed->mNext) {

    if (!undisplayed->mContent->IsElement()) {
      continue;
    }

    Element* element = undisplayed->mContent->AsElement();

    if (!ConditionallyRestyle(element, aRestyleRoot)) {
      if (aDisplay == StyleDisplay::None) {
        ConditionallyRestyleContentDescendants(element, aRestyleRoot);
      } else {  // StyleDisplay::Contents
        DoConditionallyRestyleUndisplayedDescendants(element, aRestyleRoot);
      }
    }
  }
}

void
ElementRestyler::ConditionallyRestyleContentDescendants(Element* aElement,
                                                        Element* aRestyleRoot)
{
  MOZ_ASSERT(!aElement->IsStyledByServo());
  if (aElement->HasFlag(mRestyleTracker.RootBit())) {
    aRestyleRoot = aElement;
  }

  FlattenedChildIterator it(aElement);
  for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
    if (n->IsElement()) {
      Element* e = n->AsElement();
      if (!ConditionallyRestyle(e, aRestyleRoot)) {
        ConditionallyRestyleContentDescendants(e, aRestyleRoot);
      }
    }
  }
}

bool
ElementRestyler::ConditionallyRestyle(nsIFrame* aFrame, Element* aRestyleRoot)
{
  MOZ_ASSERT(aFrame->GetContent());

  if (!aFrame->GetContent()->IsElement()) {
    return true;
  }

  return ConditionallyRestyle(aFrame->GetContent()->AsElement(), aRestyleRoot);
}

bool
ElementRestyler::ConditionallyRestyle(Element* aElement, Element* aRestyleRoot)
{
  MOZ_ASSERT(!aElement->IsStyledByServo());
  LOG_RESTYLE("considering element %s for eRestyle_SomeDescendants",
              ElementTagToString(aElement).get());
  LOG_RESTYLE_INDENT();

  if (aElement->HasFlag(mRestyleTracker.RootBit())) {
    aRestyleRoot = aElement;
  }

  if (mRestyleTracker.HasRestyleData(aElement)) {
    nsRestyleHint rshint = eRestyle_SomeDescendants;
    if (SelectorMatchesForRestyle(aElement)) {
      LOG_RESTYLE("element has existing restyle data and matches a selector");
      rshint |= eRestyle_Self;
    } else {
      LOG_RESTYLE("element has existing restyle data but doesn't match selectors");
    }
    RestyleHintData data;
    data.mSelectorsForDescendants = mSelectorsForDescendants;
    mRestyleTracker.AddPendingRestyle(aElement, rshint, nsChangeHint(0), &data,
                                      Some(aRestyleRoot));
    return true;
  }

  if (SelectorMatchesForRestyle(aElement)) {
    LOG_RESTYLE("element has no restyle data but matches a selector");
    RestyleHintData data;
    data.mSelectorsForDescendants = mSelectorsForDescendants;
    mRestyleTracker.AddPendingRestyle(aElement,
                                      eRestyle_Self | eRestyle_SomeDescendants,
                                      nsChangeHint(0), &data,
                                      Some(aRestyleRoot));
    return true;
  }

  return false;
}

bool
ElementRestyler::MustCheckUndisplayedContent(nsIFrame* aFrame,
                                             nsIContent*& aUndisplayedParent)
{
  // When the root element is display:none, we still construct *some*
  // frames that have the root element as their mContent, down to the
  // DocElementContainingBlock.
  if (aFrame->StyleContext()->GetPseudo()) {
    aUndisplayedParent = nullptr;
    return aFrame == mPresContext->FrameConstructor()->
                       GetDocElementContainingBlock();
  }

  aUndisplayedParent = aFrame->GetContent();
  return !!aUndisplayedParent;
}

/**
 * Helper for MoveStyleContextsForChildren, below.  Appends the style
 * contexts to be moved to mFrame's current (new) style context to
 * aContextsToMove.
 */
bool
ElementRestyler::MoveStyleContextsForContentChildren(
    nsIFrame* aParent,
    nsStyleContext* aOldContext,
    nsTArray<nsStyleContext*>& aContextsToMove)
{
  nsIFrame::ChildListIterator lists(aParent);
  for (; !lists.IsDone(); lists.Next()) {
    for (nsIFrame* child : lists.CurrentList()) {
      // Bail out if we have out-of-flow frames.
      // FIXME: It might be safe to just continue here instead of bailing out.
      if (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
        return false;
      }
      if (GetPrevContinuationWithSameStyle(child)) {
        continue;
      }
      // Bail out if we have placeholder frames.
      // FIXME: It is probably safe to just continue here instead of bailing out.
      if (nsGkAtoms::placeholderFrame == child->GetType()) {
        return false;
      }
      nsStyleContext* sc = child->StyleContext();
      if (sc->GetParent() != aOldContext) {
        return false;
      }
      nsIAtom* type = child->GetType();
      if (type == nsGkAtoms::letterFrame ||
          type == nsGkAtoms::lineFrame) {
        return false;
      }
      if (sc->HasChildThatUsesGrandancestorStyle()) {
        // XXX Not sure if we need this?
        return false;
      }
      nsIAtom* pseudoTag = sc->GetPseudo();
      if (pseudoTag && !nsCSSAnonBoxes::IsNonElement(pseudoTag)) {
        return false;
      }
      aContextsToMove.AppendElement(sc);
    }
  }
  return true;
}

/**
 * Traverses to child elements (through the current frame's same style
 * continuations, just like RestyleChildren does) and moves any style context
 * for those children to be parented under mFrame's current (new) style
 * context.
 *
 * False is returned if it encounters any conditions on the child elements'
 * frames and style contexts that means it is impossible to move a
 * style context.  If false is returned, no style contexts will have been
 * moved.
 */
bool
ElementRestyler::MoveStyleContextsForChildren(nsStyleContext* aOldContext)
{
  // Bail out if there are undisplayed or display:contents children.
  // FIXME: We could get this to work if we need to.
  nsIContent* undisplayedParent;
  if (MustCheckUndisplayedContent(mFrame, undisplayedParent)) {
    nsCSSFrameConstructor* fc = mPresContext->FrameConstructor();
    if (fc->GetAllUndisplayedContentIn(undisplayedParent) ||
        fc->GetAllDisplayContentsIn(undisplayedParent)) {
      return false;
    }
  }

  nsTArray<nsStyleContext*> contextsToMove;

  MOZ_ASSERT(!MustReframeForBeforePseudo(),
             "shouldn't need to reframe ::before as we would have had "
             "eRestyle_Subtree and wouldn't get in here");

  DebugOnly<nsIFrame*> lastContinuation;
  for (nsIFrame* f = mFrame; f;
       f = RestyleManager::GetNextContinuationWithSameStyle(f, f->StyleContext())) {
    lastContinuation = f;
    if (!MoveStyleContextsForContentChildren(f, aOldContext, contextsToMove)) {
      return false;
    }
  }

  MOZ_ASSERT(!MustReframeForAfterPseudo(lastContinuation),
             "shouldn't need to reframe ::after as we would have had "
             "eRestyle_Subtree and wouldn't get in here");

  nsStyleContext* newParent = mFrame->StyleContext();
  for (nsStyleContext* child : contextsToMove) {
    // We can have duplicate entries in contextsToMove, so only move
    // each style context once.
    if (child->GetParent() != newParent) {
      child->MoveTo(newParent);
    }
  }

  return true;
}

/**
 * Recompute style for mFrame (which should not have a prev continuation
 * with the same style), all of its next continuations with the same
 * style, and all ib-split siblings of the same type (either block or
 * inline, skipping the intermediates of the other type) and accumulate
 * changes into mChangeList given that mHintsHandled is already accumulated
 * for an ancestor.
 * mParentContent is the content node used to resolve the parent style
 * context.  This means that, for pseudo-elements, it is the content
 * that should be used for selector matching (rather than the fake
 * content node attached to the frame).
 */
void
ElementRestyler::Restyle(nsRestyleHint aRestyleHint)
{
  // It would be nice if we could make stronger assertions here; they
  // would let us simplify the ?: expressions below setting |content|
  // and |pseudoContent| in sensible ways as well as making what
  // |content| and |pseudoContent| mean, and their relationship to
  // |mFrame->GetContent()|, make more sense.  However, we can't,
  // because of frame trees like the one in
  // https://bugzilla.mozilla.org/show_bug.cgi?id=472353#c14 .  Once we
  // fix bug 242277 we should be able to make this make more sense.
  NS_ASSERTION(mFrame->GetContent() || !mParentContent ||
               !mParentContent->GetParent(),
               "frame must have content (unless at the top of the tree)");
  MOZ_ASSERT(mPresContext == mFrame->PresContext(), "pres contexts match");

  NS_ASSERTION(!GetPrevContinuationWithSameStyle(mFrame),
               "should not be trying to restyle this frame separately");

  MOZ_ASSERT(!(aRestyleHint & eRestyle_LaterSiblings),
             "eRestyle_LaterSiblings must not be part of aRestyleHint");

  mPresContext->RestyledElement();

  AutoDisplayContentsAncestorPusher adcp(mTreeMatchContext, mPresContext,
      mFrame->GetContent() ? mFrame->GetContent()->GetParent() : nullptr);

  AutoSelectorArrayTruncater asat(mSelectorsForDescendants);

  // List of descendant elements of mContent we know we will eventually need to
  // restyle.  Before we return from this function, we call
  // RestyleTracker::AddRestyleRootsIfAwaitingRestyle to ensure they get
  // restyled in RestyleTracker::DoProcessRestyles.
  nsTArray<RefPtr<Element>> descendants;

  nsRestyleHint hintToRestore = nsRestyleHint(0);
  RestyleHintData hintDataToRestore;
  if (mContent && mContent->IsElement() &&
      // If we're resolving from the root of the frame tree (which
      // we do when mDoRebuildAllStyleData), we need to avoid getting the
      // root's restyle data until we get to its primary frame, since
      // it's the primary frame that has the styles for the root element
      // (rather than the ancestors of the primary frame whose mContent
      // is the root node but which have different styles).  If we use
      // up the hint for one of the ancestors that we hit first, then
      // we'll fail to do the restyling we need to do.
      // Likewise, if we're restyling something with two nested frames,
      // and we post a restyle from the transition manager while
      // computing style for the outer frame (to be computed after the
      // descendants have been resolved), we don't want to consume it
      // for the inner frame.
      mContent->GetPrimaryFrame() == mFrame) {
    mContent->OwnerDoc()->FlushPendingLinkUpdates();
    nsAutoPtr<RestyleTracker::RestyleData> restyleData;
    if (mRestyleTracker.GetRestyleData(mContent->AsElement(), restyleData)) {
      if (!NS_IsHintSubset(restyleData->mChangeHint, mHintsHandled)) {
        mHintsHandled |= restyleData->mChangeHint;
        mChangeList->AppendChange(mFrame, mContent, restyleData->mChangeHint);
      }
      mSelectorsForDescendants.AppendElements(
          restyleData->mRestyleHintData.mSelectorsForDescendants);
      hintToRestore = restyleData->mRestyleHint;
      hintDataToRestore = Move(restyleData->mRestyleHintData);
      aRestyleHint = nsRestyleHint(aRestyleHint | restyleData->mRestyleHint);
      descendants.SwapElements(restyleData->mDescendants);
    }
  }

  // If we are restyling this frame with eRestyle_Self or weaker hints,
  // we restyle children with nsRestyleHint(0).  But we pass the
  // eRestyle_ForceDescendants flag down too.
  nsRestyleHint childRestyleHint =
    nsRestyleHint(aRestyleHint & (eRestyle_SomeDescendants |
                                  eRestyle_Subtree |
                                  eRestyle_ForceDescendants));

  RefPtr<nsStyleContext> oldContext = mFrame->StyleContext();

  nsTArray<SwapInstruction> swaps;

  // TEMPORARY (until bug 918064):  Call RestyleSelf for each
  // continuation or block-in-inline sibling.

  // We must make a single decision on how to process this frame and
  // its descendants, yet RestyleSelf might return different RestyleResult
  // values for the different same-style continuations.  |result| is our
  // overall decision.
  RestyleResult result = RestyleResult::eNone;
  uint32_t swappedStructs = 0;

  nsRestyleHint thisRestyleHint = aRestyleHint;

  bool haveMoreContinuations = false;
  for (nsIFrame* f = mFrame; f; ) {
    RestyleResult thisResult =
      RestyleSelf(f, thisRestyleHint, &swappedStructs, swaps);

    if (thisResult != RestyleResult::eStop) {
      // Calls to RestyleSelf for later same-style continuations must not
      // return RestyleResult::eStop, so pass eRestyle_Force in to them.
      thisRestyleHint = nsRestyleHint(thisRestyleHint | eRestyle_Force);

      if (result == RestyleResult::eStop) {
        // We received RestyleResult::eStop for earlier same-style
        // continuations, and RestyleResult::eStopWithStyleChange or
        // RestyleResult::eContinue(AndForceDescendants) for this one; go
        // back and force-restyle the earlier continuations.
        result = thisResult;
        f = mFrame;
        continue;
      }
    }

    if (thisResult > result) {
      // We take the highest RestyleResult value when working out what to do
      // with this frame and its descendants.  Higher RestyleResult values
      // represent a superset of the work done by lower values.
      result = thisResult;
    }

    f = RestyleManager::GetNextContinuationWithSameStyle(f,
                                                         oldContext,
                                                         &haveMoreContinuations);
  }

  // Some changes to animations don't affect the computed style and yet still
  // require the layer to be updated. For example, pausing an animation via
  // the Web Animations API won't affect an element's style but still
  // requires us to pull the animation off the layer.
  //
  // Although we only expect this code path to be called when computed style
  // is not changing, we can sometimes reach this at the end of a transition
  // when the animated style is being removed. Since
  // AddLayerChangesForAnimation checks if mFrame has a transform style or not,
  // we need to call it *after* calling RestyleSelf to ensure the animated
  // transform has been removed first.
  AddLayerChangesForAnimation();

  if (haveMoreContinuations && hintToRestore) {
    // If we have more continuations with different style (e.g., because
    // we're inside a ::first-letter or ::first-line), put the restyle
    // hint back.
    mRestyleTracker.AddPendingRestyleToTable(mContent->AsElement(),
                                             hintToRestore, nsChangeHint(0));
  }

  if (result == RestyleResult::eStop) {
    MOZ_ASSERT(mFrame->StyleContext() == oldContext,
               "frame should have been left with its old style context");

    nsIFrame* unused;
    nsStyleContext* newParent = mFrame->GetParentStyleContext(&unused);
    if (oldContext->GetParent() != newParent) {
      // If we received RestyleResult::eStop, then the old style context was
      // left on mFrame.  Since we ended up restyling our parent, change
      // this old style context to point to its new parent.
      LOG_RESTYLE("moving style context %p from old parent %p to new parent %p",
                  oldContext.get(), oldContext->GetParent(), newParent);
      // We keep strong references to the new parent around until the end
      // of the restyle, in case:
      //   (a) we swapped structs between the old and new parent,
      //   (b) some descendants of the old parent are not getting restyled
      //       (which is the reason for the existence of
      //       ClearCachedInheritedStyleDataOnDescendants),
      //   (c) something under ProcessPendingRestyles (which notably is called
      //       *before* ClearCachedInheritedStyleDataOnDescendants is called
      //       on the old context) causes the new parent to be destroyed, thus
      //       destroying its owned structs, and
      //   (d) something under ProcessPendingRestyles then wants to use of those
      //       now destroyed structs (through the old parent's descendants).
      mSwappedStructOwners.AppendElement(newParent);
      oldContext->MoveTo(newParent);
    }

    // Send the accessibility notifications that RestyleChildren otherwise
    // would have sent.
    if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
      InitializeAccessibilityNotifications(mFrame->StyleContext());
      SendAccessibilityNotifications();
    }

    mRestyleTracker.AddRestyleRootsIfAwaitingRestyle(descendants);
    if (aRestyleHint & eRestyle_SomeDescendants) {
      ConditionallyRestyleChildren();
    }
    return;
  }

  if (result == RestyleResult::eStopWithStyleChange &&
      !(mHintsHandled & nsChangeHint_ReconstructFrame)) {
    MOZ_ASSERT(mFrame->StyleContext() != oldContext,
               "RestyleResult::eStopWithStyleChange should only be returned "
               "if we got a new style context or we will reconstruct");
    MOZ_ASSERT(swappedStructs == 0,
               "should have ensured we didn't swap structs when "
               "returning RestyleResult::eStopWithStyleChange");

    // We need to ensure that all of the frames that inherit their style
    // from oldContext are able to be moved across to newContext.
    // MoveStyleContextsForChildren will check for certain conditions
    // to ensure it is safe to move all of the relevant child style
    // contexts to newContext.  If these conditions fail, it will
    // return false, and we'll have to continue restyling.
    const bool canStop = MoveStyleContextsForChildren(oldContext);

    if (canStop) {
      // Send the accessibility notifications that RestyleChildren otherwise
      // would have sent.
      if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
        InitializeAccessibilityNotifications(mFrame->StyleContext());
        SendAccessibilityNotifications();
      }

      mRestyleTracker.AddRestyleRootsIfAwaitingRestyle(descendants);
      if (aRestyleHint & eRestyle_SomeDescendants) {
        ConditionallyRestyleChildren();
      }
      return;
    }

    // Turns out we couldn't stop restyling here.  Process the struct
    // swaps that RestyleSelf would've done had we not returned
    // RestyleResult::eStopWithStyleChange.
    for (SwapInstruction& swap : swaps) {
      LOG_RESTYLE("swapping style structs between %p and %p",
                  swap.mOldContext.get(), swap.mNewContext.get());
      swap.mOldContext->SwapStyleData(swap.mNewContext, swap.mStructsToSwap);
      swappedStructs |= swap.mStructsToSwap;
    }
    swaps.Clear();
  }

  if (!swappedStructs) {
    // If we swapped any structs from the old context, then we need to keep
    // it alive until after the RestyleChildren call so that we can fix up
    // its descendants' cached structs.
    oldContext = nullptr;
  }

  if (result == RestyleResult::eContinueAndForceDescendants) {
    childRestyleHint =
      nsRestyleHint(childRestyleHint | eRestyle_ForceDescendants);
  }

  // No need to do this if we're planning to reframe already.
  // It's also important to check mHintsHandled since we use
  // mFrame->StyleContext(), which is out of date if mHintsHandled
  // has a ReconstructFrame hint.  Using an out of date style
  // context could trigger assertions about mismatched rule trees.
  if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
    RestyleChildren(childRestyleHint);
  }

  if (oldContext && !oldContext->HasSingleReference()) {
    // If we swapped some structs out of oldContext in the RestyleSelf call
    // and after the RestyleChildren call we still have other strong references
    // to it, we need to make ensure its descendants don't cache any of the
    // structs that were swapped out.
    //
    // Much of the time we will not get in here; we do for example when the
    // style context is shared with a later IB split sibling (which we won't
    // restyle until a bit later) or if other code is holding a strong reference
    // to the style context (as is done by nsTransformedTextRun objects, which
    // can be referenced by a text frame's mTextRun longer than the frame's
    // mStyleContext).
    //
    // Also, we don't want this style context to get any more uses by being
    // returned from nsStyleContext::FindChildWithRules, so we add the
    // NS_STYLE_INELIGIBLE_FOR_SHARING bit to it.
    oldContext->SetIneligibleForSharing();

    ContextToClear* toClear = mContextsToClear.AppendElement();
    toClear->mStyleContext = Move(oldContext);
    toClear->mStructs = swappedStructs;
  }

  mRestyleTracker.AddRestyleRootsIfAwaitingRestyle(descendants);
}

/**
 * Depending on the details of the frame we are restyling or its old style
 * context, we may or may not be able to stop restyling after this frame if
 * we find we had no style changes.
 *
 * This function returns RestyleResult::eStop if it does not find any
 * conditions that would preclude stopping restyling, and
 * RestyleResult::eContinue if it does.
 */
void
ElementRestyler::ComputeRestyleResultFromFrame(nsIFrame* aSelf,
                                               RestyleResult& aRestyleResult,
                                               bool& aCanStopWithStyleChange)
{
  // We can't handle situations where the primary style context of a frame
  // has not had any style data changes, but its additional style contexts
  // have, so we don't considering stopping if this frame has any additional
  // style contexts.
  if (aSelf->GetAdditionalStyleContext(0)) {
    LOG_RESTYLE_CONTINUE("there are additional style contexts");
    aRestyleResult = RestyleResult::eContinue;
    aCanStopWithStyleChange = false;
    return;
  }

  // Style changes might have moved children between the two nsLetterFrames
  // (the one matching ::first-letter and the one containing the rest of the
  // content).  Continue restyling to the children of the nsLetterFrame so
  // that they get the correct style context parent.  Similarly for
  // nsLineFrames.
  nsIAtom* type = aSelf->GetType();

  if (type == nsGkAtoms::letterFrame) {
    LOG_RESTYLE_CONTINUE("frame is a letter frame");
    aRestyleResult = RestyleResult::eContinue;
    aCanStopWithStyleChange = false;
    return;
  }

  if (type == nsGkAtoms::lineFrame) {
    LOG_RESTYLE_CONTINUE("frame is a line frame");
    aRestyleResult = RestyleResult::eContinue;
    aCanStopWithStyleChange = false;
    return;
  }

  // Some style computations depend not on the parent's style, but a grandparent
  // or one the grandparent's ancestors.  An example is an explicit 'inherit'
  // value for align-self, where if the parent frame's value for the property is
  // 'auto' we end up inheriting the computed value from the grandparent.  We
  // can't stop the restyling process on this frame (the one with 'auto', in
  // this example), as the grandparent's computed value might have changed
  // and we need to recompute the child's 'inherit' to that new value.
  nsStyleContext* oldContext = aSelf->StyleContext();
  if (oldContext->HasChildThatUsesGrandancestorStyle()) {
    LOG_RESTYLE_CONTINUE("the old context uses grandancestor style");
    aRestyleResult = RestyleResult::eContinue;
    aCanStopWithStyleChange = false;
    return;
  }

  // We ignore all situations that involve :visited style.
  if (oldContext->GetStyleIfVisited()) {
    LOG_RESTYLE_CONTINUE("the old style context has StyleIfVisited");
    aRestyleResult = RestyleResult::eContinue;
    aCanStopWithStyleChange = false;
    return;
  }

  nsStyleContext* parentContext = oldContext->GetParent();
  if (parentContext && parentContext->GetStyleIfVisited()) {
    LOG_RESTYLE_CONTINUE("the old style context's parent has StyleIfVisited");
    aRestyleResult = RestyleResult::eContinue;
    aCanStopWithStyleChange = false;
    return;
  }

  // We also ignore frames for pseudos, as their style contexts have
  // inheritance structures that do not match the frame inheritance
  // structure.  To avoid enumerating and checking all of the cases
  // where we have this kind of inheritance, we keep restyling past
  // pseudos.
  nsIAtom* pseudoTag = oldContext->GetPseudo();
  if (pseudoTag && !nsCSSAnonBoxes::IsNonElement(pseudoTag)) {
    LOG_RESTYLE_CONTINUE("the old style context is for a pseudo");
    aRestyleResult = RestyleResult::eContinue;
    aCanStopWithStyleChange = false;
    return;
  }

  nsIFrame* parent = mFrame->GetParent();

  if (parent) {
    // Also if the parent has a pseudo, as this frame's style context will
    // be inheriting from a grandparent frame's style context (or a further
    // ancestor).
    nsIAtom* parentPseudoTag = parent->StyleContext()->GetPseudo();
    if (parentPseudoTag &&
        parentPseudoTag != nsCSSAnonBoxes::mozOtherNonElement) {
      MOZ_ASSERT(parentPseudoTag != nsCSSAnonBoxes::mozText,
                 "Style of text node should not be parent of anything");
      LOG_RESTYLE_CONTINUE("the old style context's parent is for a pseudo");
      aRestyleResult = RestyleResult::eContinue;
      // Parent style context pseudo-ness doesn't affect whether we can
      // return RestyleResult::eStopWithStyleChange.
      //
      // If we had later conditions to check in this function, we would
      // continue to check them, in case we set aCanStopWithStyleChange to
      // false.
    }
  }
}

void
ElementRestyler::ComputeRestyleResultFromNewContext(nsIFrame* aSelf,
                                                    nsStyleContext* aNewContext,
                                                    RestyleResult& aRestyleResult,
                                                    bool& aCanStopWithStyleChange)
{
  // If we've already determined that we must continue styling, we don't
  // need to check anything.
  if (aRestyleResult == RestyleResult::eContinue && !aCanStopWithStyleChange) {
    return;
  }

  // Keep restyling if the new style context has any style-if-visted style, so
  // that we can avoid the style context tree surgery having to deal to deal
  // with visited styles.
  if (aNewContext->GetStyleIfVisited()) {
    LOG_RESTYLE_CONTINUE("the new style context has StyleIfVisited");
    aRestyleResult = RestyleResult::eContinue;
    aCanStopWithStyleChange = false;
    return;
  }

  // If link-related information has changed, or the pseudo for the frame has
  // changed, or the new style context points to a different rule node, we can't
  // leave the old style context on the frame.
  nsStyleContext* oldContext = aSelf->StyleContext();
  if (oldContext->IsLinkContext() != aNewContext->IsLinkContext() ||
      oldContext->RelevantLinkVisited() != aNewContext->RelevantLinkVisited() ||
      oldContext->GetPseudo() != aNewContext->GetPseudo() ||
      oldContext->GetPseudoType() != aNewContext->GetPseudoType()) {
    LOG_RESTYLE_CONTINUE("the old and new style contexts have different link/"
                         "visited/pseudo");
    aRestyleResult = RestyleResult::eContinue;
    aCanStopWithStyleChange = false;
    return;
  }

  if (oldContext->RuleNode() != aNewContext->RuleNode()) {
    LOG_RESTYLE_CONTINUE("the old and new style contexts have different "
                         "rulenodes");
    aRestyleResult = RestyleResult::eContinue;
    // Continue to check other conditions if aCanStopWithStyleChange might
    // still need to be set to false.
    if (!aCanStopWithStyleChange) {
      return;
    }
  }

  // If the old and new style contexts differ in their
  // NS_STYLE_HAS_TEXT_DECORATION_LINES or NS_STYLE_HAS_PSEUDO_ELEMENT_DATA
  // bits, then we must keep restyling so that those new bit values are
  // propagated.
  if (oldContext->HasTextDecorationLines() !=
        aNewContext->HasTextDecorationLines()) {
    LOG_RESTYLE_CONTINUE("NS_STYLE_HAS_TEXT_DECORATION_LINES differs between old"
                         " and new style contexts");
    aRestyleResult = RestyleResult::eContinue;
    aCanStopWithStyleChange = false;
    return;
  }

  if (oldContext->HasPseudoElementData() !=
        aNewContext->HasPseudoElementData()) {
    LOG_RESTYLE_CONTINUE("NS_STYLE_HAS_PSEUDO_ELEMENT_DATA differs between old"
                         " and new style contexts");
    aRestyleResult = RestyleResult::eContinue;
    aCanStopWithStyleChange = false;
    return;
  }

  if (oldContext->ShouldSuppressLineBreak() !=
        aNewContext->ShouldSuppressLineBreak()) {
    LOG_RESTYLE_CONTINUE("NS_STYLE_SUPPRESS_LINEBREAK differs"
                         "between old and new style contexts");
    aRestyleResult = RestyleResult::eContinue;
    aCanStopWithStyleChange = false;
    return;
  }

  if (oldContext->IsInDisplayNoneSubtree() !=
        aNewContext->IsInDisplayNoneSubtree()) {
    LOG_RESTYLE_CONTINUE("NS_STYLE_IN_DISPLAY_NONE_SUBTREE differs between old"
                         " and new style contexts");
    aRestyleResult = RestyleResult::eContinue;
    aCanStopWithStyleChange = false;
    return;
  }

  if (oldContext->IsTextCombined() != aNewContext->IsTextCombined()) {
    LOG_RESTYLE_CONTINUE("NS_STYLE_IS_TEXT_COMBINED differs between "
                         "old and new style contexts");
    aRestyleResult = RestyleResult::eContinue;
    aCanStopWithStyleChange = false;
    return;
  }
}

bool
ElementRestyler::SelectorMatchesForRestyle(Element* aElement)
{
  if (!aElement) {
    return false;
  }
  for (nsCSSSelector* selector : mSelectorsForDescendants) {
    if (nsCSSRuleProcessor::RestrictedSelectorMatches(aElement, selector,
                                                      mTreeMatchContext)) {
      return true;
    }
  }
  return false;
}

bool
ElementRestyler::MustRestyleSelf(nsRestyleHint aRestyleHint,
                                 Element* aElement)
{
  return (aRestyleHint & (eRestyle_Self | eRestyle_Subtree)) ||
         ((aRestyleHint & eRestyle_SomeDescendants) &&
          SelectorMatchesForRestyle(aElement));
}

bool
ElementRestyler::CanReparentStyleContext(nsRestyleHint aRestyleHint)
{
  // If we had any restyle hints other than the ones listed below,
  // which don't control whether the current frame/element needs
  // a new style context by looking up a new rule node, or if
  // we are reconstructing the entire rule tree, then we can't
  // use ReparentStyleContext.
  return !(aRestyleHint & ~(eRestyle_Force |
                            eRestyle_ForceDescendants |
                            eRestyle_SomeDescendants)) &&
         !StyleSet()->IsInRuleTreeReconstruct();
}

// Returns true iff any rule node that is an ancestor-or-self of the
// two specified rule nodes, but which is not an ancestor of both,
// has any inherited style data.  If false is returned, then we know
// that a change from one rule node to the other must not result in
// any change in inherited style data.
static bool
CommonInheritedStyleData(nsRuleNode* aRuleNode1, nsRuleNode* aRuleNode2)
{
  if (aRuleNode1 == aRuleNode2) {
    return true;
  }

  nsRuleNode* n1 = aRuleNode1->GetParent();
  nsRuleNode* n2 = aRuleNode2->GetParent();

  if (n1 == n2) {
    // aRuleNode1 and aRuleNode2 sharing a parent is a common case, e.g.
    // when modifying a style="" attribute.  (We must null check GetRule()'s
    // result since although we know the two parents are the same, it might
    // be null, as in the case of the two rule nodes being roots of two
    // different rule trees.)
    if (aRuleNode1->GetRule() &&
        aRuleNode1->GetRule()->MightMapInheritedStyleData()) {
      return false;
    }
    if (aRuleNode2->GetRule() &&
        aRuleNode2->GetRule()->MightMapInheritedStyleData()) {
      return false;
    }
    return true;
  }

  // Compute the depths of aRuleNode1 and aRuleNode2.
  int d1 = 0, d2 = 0;
  while (n1) {
    ++d1;
    n1 = n1->GetParent();
  }
  while (n2) {
    ++d2;
    n2 = n2->GetParent();
  }

  // Make aRuleNode1 be the deeper node.
  if (d2 > d1) {
    std::swap(d1, d2);
    std::swap(aRuleNode1, aRuleNode2);
  }

  // Check all of the rule nodes in the deeper branch until we reach
  // the same depth as the shallower branch.
  n1 = aRuleNode1;
  n2 = aRuleNode2;
  while (d1 > d2) {
    nsIStyleRule* rule = n1->GetRule();
    MOZ_ASSERT(rule, "non-root rule node should have a rule");
    if (rule->MightMapInheritedStyleData()) {
      return false;
    }
    n1 = n1->GetParent();
    --d1;
  }

  // Check both branches simultaneously until we reach a common ancestor.
  while (n1 != n2) {
    MOZ_ASSERT(n1);
    MOZ_ASSERT(n2);
    // As above, we must null check GetRule()'s result since we won't find
    // a common ancestor if the two rule nodes come from different rule trees,
    // and thus we might reach the root (which has a null rule).
    if (n1->GetRule() && n1->GetRule()->MightMapInheritedStyleData()) {
      return false;
    }
    if (n2->GetRule() && n2->GetRule()->MightMapInheritedStyleData()) {
      return false;
    }
    n1 = n1->GetParent();
    n2 = n2->GetParent();
  }

  return true;
}

ElementRestyler::RestyleResult
ElementRestyler::RestyleSelf(nsIFrame* aSelf,
                             nsRestyleHint aRestyleHint,
                             uint32_t* aSwappedStructs,
                             nsTArray<SwapInstruction>& aSwaps)
{
  MOZ_ASSERT(!(aRestyleHint & eRestyle_LaterSiblings),
             "eRestyle_LaterSiblings must not be part of aRestyleHint");

  // XXXldb get new context from prev-in-flow if possible, to avoid
  // duplication.  (Or should we just let |GetContext| handle that?)
  // Getting the hint would be nice too, but that's harder.

  // XXXbryner we may be able to avoid some of the refcounting goop here.
  // We do need a reference to oldContext for the lifetime of this function, and it's possible
  // that the frame has the last reference to it, so AddRef it here.

  LOG_RESTYLE("RestyleSelf %s, aRestyleHint = %s",
              FrameTagToString(aSelf).get(),
              RestyleManagerBase::RestyleHintToString(aRestyleHint).get());
  LOG_RESTYLE_INDENT();

  // Initially assume that it is safe to stop restyling.
  //
  // Throughout most of this function, we update the following two variables
  // independently.  |result| is set to RestyleResult::eContinue when we
  // detect a condition that would not allow us to return RestyleResult::eStop.
  // |canStopWithStyleChange| is set to false when we detect a condition
  // that would not allow us to return RestyleResult::eStopWithStyleChange.
  //
  // Towards the end of this function, we reconcile these two variables --
  // if |canStopWithStyleChange| is true, we convert |result| into
  // RestyleResult::eStopWithStyleChange.
  RestyleResult result = RestyleResult::eStop;
  bool canStopWithStyleChange = true;

  if (aRestyleHint & ~eRestyle_SomeDescendants) {
    // If we are doing any restyling of the current element, or if we're
    // forced to continue, we must.
    result = RestyleResult::eContinue;

    // If we have to restyle children, we can't return
    // RestyleResult::eStopWithStyleChange.
    if (aRestyleHint & (eRestyle_Subtree | eRestyle_Force |
                        eRestyle_ForceDescendants)) {
      canStopWithStyleChange = false;
    }
  }

  // We only consider returning RestyleResult::eStopWithStyleChange if this
  // is the root of the restyle.  (Otherwise, we would need to track the
  // style changes of the ancestors we just restyled.)
  if (!mIsRootOfRestyle) {
    canStopWithStyleChange = false;
  }

  // Look at the frame and its current style context for conditions
  // that would change our RestyleResult.
  ComputeRestyleResultFromFrame(aSelf, result, canStopWithStyleChange);

  nsChangeHint assumeDifferenceHint = nsChangeHint(0);
  RefPtr<nsStyleContext> oldContext = aSelf->StyleContext();
  nsStyleSet* styleSet = StyleSet();

#ifdef ACCESSIBILITY
  mWasFrameVisible = nsIPresShell::IsAccessibilityActive() ?
    oldContext->StyleVisibility()->IsVisible() : false;
#endif

  nsIAtom* const pseudoTag = oldContext->GetPseudo();
  const CSSPseudoElementType pseudoType = oldContext->GetPseudoType();

  // Get the frame providing the parent style context.  If it is a
  // child, then resolve the provider first.
  nsIFrame* providerFrame;
  nsStyleContext* parentContext = aSelf->GetParentStyleContext(&providerFrame);
  bool isChild = providerFrame && providerFrame->GetParent() == aSelf;
  if (isChild) {
    MOZ_ASSERT(providerFrame->GetContent() == aSelf->GetContent(),
               "Postcondition for GetParentStyleContext() violated. "
               "That means we need to add the current element to the "
               "ancestor filter.");

    // resolve the provider here (before aSelf below).
    LOG_RESTYLE("resolving child provider frame");

    // assumeDifferenceHint forces the parent's change to be also
    // applied to this frame, no matter what
    // nsStyleContext::CalcStyleDifference says. CalcStyleDifference
    // can't be trusted because it assumes any changes to the parent
    // style context provider will be automatically propagated to
    // the frame(s) with child style contexts.

    ElementRestyler providerRestyler(PARENT_CONTEXT_FROM_CHILD_FRAME,
                                     *this, providerFrame);
    providerRestyler.Restyle(aRestyleHint);
    assumeDifferenceHint = providerRestyler.HintsHandledForFrame();

    // The provider's new context becomes the parent context of
    // aSelf's context.
    parentContext = providerFrame->StyleContext();
    // Set |mResolvedChild| so we don't bother resolving the
    // provider again.
    mResolvedChild = providerFrame;
    LOG_RESTYLE_CONTINUE("we had a provider frame");
    // Continue restyling past the odd style context inheritance.
    result = RestyleResult::eContinue;
    canStopWithStyleChange = false;
  }

  if (providerFrame != aSelf->GetParent()) {
    // We don't actually know what the parent style context's
    // non-inherited hints were, so assume the worst.
    mParentFrameHintsNotHandledForDescendants =
      nsChangeHint_Hints_NotHandledForDescendants;
  }

  LOG_RESTYLE("parentContext = %p", parentContext);

  // do primary context
  RefPtr<nsStyleContext> newContext;
  nsIFrame* prevContinuation =
    GetPrevContinuationWithPossiblySameStyle(aSelf);
  nsStyleContext* prevContinuationContext;
  bool copyFromContinuation =
    prevContinuation &&
    (prevContinuationContext = prevContinuation->StyleContext())
      ->GetPseudo() == oldContext->GetPseudo() &&
     prevContinuationContext->GetParent() == parentContext;
  if (copyFromContinuation) {
    // Just use the style context from the frame's previous
    // continuation.
    LOG_RESTYLE("using previous continuation's context");
    newContext = prevContinuationContext;
  } else if (pseudoTag == nsCSSAnonBoxes::mozText) {
    MOZ_ASSERT(aSelf->GetType() == nsGkAtoms::textFrame);
    newContext =
      styleSet->ResolveStyleForText(aSelf->GetContent(), parentContext);
  } else if (nsCSSAnonBoxes::IsNonElement(pseudoTag)) {
    newContext = styleSet->ResolveStyleForOtherNonElement(parentContext);
  }
  else {
    Element* element = ElementForStyleContext(mParentContent, aSelf, pseudoType);
    if (!MustRestyleSelf(aRestyleHint, element)) {
      if (CanReparentStyleContext(aRestyleHint)) {
        LOG_RESTYLE("reparenting style context");
        newContext =
          styleSet->ReparentStyleContext(oldContext, parentContext, element);
      } else {
        // Use ResolveStyleWithReplacement either for actual replacements
        // or, with no replacements, as a substitute for
        // ReparentStyleContext that rebuilds the path in the rule tree
        // rather than reusing the rule node, as we need to do during a
        // rule tree reconstruct.
        Element* pseudoElement = PseudoElementForStyleContext(aSelf, pseudoType);
        MOZ_ASSERT(!element || element != pseudoElement,
                   "pseudo-element for selector matching should be "
                   "the anonymous content node that we create, "
                   "not the real element");
        LOG_RESTYLE("resolving style with replacement");
        nsRestyleHint rshint = aRestyleHint & ~eRestyle_SomeDescendants;
        newContext =
          styleSet->ResolveStyleWithReplacement(element, pseudoElement,
                                                parentContext, oldContext,
                                                rshint);
      }
    } else if (pseudoType == CSSPseudoElementType::AnonBox) {
      newContext = styleSet->ResolveAnonymousBoxStyle(pseudoTag,
                                                      parentContext);
    }
    else {
      if (pseudoTag) {
        if (pseudoTag == nsCSSPseudoElements::before ||
            pseudoTag == nsCSSPseudoElements::after) {
          // XXX what other pseudos do we need to treat like this?
          newContext = styleSet->ProbePseudoElementStyle(element,
                                                         pseudoType,
                                                         parentContext,
                                                         mTreeMatchContext);
          if (!newContext) {
            // This pseudo should no longer exist; gotta reframe
            mHintsHandled |= nsChangeHint_ReconstructFrame;
            mChangeList->AppendChange(aSelf, element,
                                      nsChangeHint_ReconstructFrame);
            // We're reframing anyway; just keep the same context
            newContext = oldContext;
#ifdef DEBUG
            // oldContext's parent might have had its style structs swapped out
            // with parentContext, so to avoid any assertions that might
            // otherwise trigger in oldContext's parent's destructor, we set a
            // flag on oldContext to skip it and its descendants in
            // nsStyleContext::AssertStructsNotUsedElsewhere.
            if (oldContext->GetParent() != parentContext) {
              oldContext->AddStyleBit(NS_STYLE_IS_GOING_AWAY);
            }
#endif
          }
        } else {
          // Don't expect XUL tree stuff here, since it needs a comparator and
          // all.
          NS_ASSERTION(pseudoType < CSSPseudoElementType::Count,
                       "Unexpected pseudo type");
          Element* pseudoElement =
            PseudoElementForStyleContext(aSelf, pseudoType);
          MOZ_ASSERT(element != pseudoElement,
                     "pseudo-element for selector matching should be "
                     "the anonymous content node that we create, "
                     "not the real element");
          newContext = styleSet->ResolvePseudoElementStyle(element,
                                                           pseudoType,
                                                           parentContext,
                                                           pseudoElement);
        }
      }
      else {
        NS_ASSERTION(aSelf->GetContent(),
                     "non pseudo-element frame without content node");
        // Skip parent display based style fixup for anonymous subtrees:
        TreeMatchContext::AutoParentDisplayBasedStyleFixupSkipper
          parentDisplayBasedFixupSkipper(mTreeMatchContext,
                                 element->IsRootOfNativeAnonymousSubtree());
        newContext = styleSet->ResolveStyleFor(element, parentContext,
                                               mTreeMatchContext);
      }
    }
  }

  MOZ_ASSERT(newContext);

  if (!parentContext) {
    if (oldContext->RuleNode() == newContext->RuleNode() &&
        oldContext->IsLinkContext() == newContext->IsLinkContext() &&
        oldContext->RelevantLinkVisited() ==
          newContext->RelevantLinkVisited()) {
      // We're the root of the style context tree and the new style
      // context returned has the same rule node.  This means that
      // we can use FindChildWithRules to keep a lot of the old
      // style contexts around.  However, we need to start from the
      // same root.
      LOG_RESTYLE("restyling root and keeping old context");
      LOG_RESTYLE_IF(this, result != RestyleResult::eContinue,
                     "continuing restyle since this is the root");
      newContext = oldContext;
      // Never consider stopping restyling at the root.
      result = RestyleResult::eContinue;
      canStopWithStyleChange = false;
    }
  }

  LOG_RESTYLE("oldContext = %p, newContext = %p%s",
              oldContext.get(), newContext.get(),
              oldContext == newContext ? (const char*) " (same)" :
                                         (const char*) "");

  if (newContext != oldContext) {
    if (oldContext->IsShared()) {
      // If the old style context was shared, then we can't return
      // RestyleResult::eStop and patch its parent to point to the
      // new parent style context, as that change might not be valid
      // for the other frames sharing the style context.
      LOG_RESTYLE_CONTINUE("the old style context is shared");
      result = RestyleResult::eContinue;

      // It is not safe to return RestyleResult::eStopWithStyleChange
      // when oldContext is shared and newContext has different
      // inherited style data, regardless of whether the oldContext has
      // that inherited style data cached.  We can't simply rely on the
      // samePointerStructs check later on, as the descendent style
      // contexts just might not have had their inherited style data
      // requested yet (which is possible for example if we flush style
      // between resolving an initial style context for a frame and
      // building its display list items).  Therefore we must compare
      // the rule nodes of oldContext and newContext to see if the
      // restyle results in new inherited style data.  If not, then
      // we can continue assuming that RestyleResult::eStopWithStyleChange
      // is safe.  Without this check, we could end up with style contexts
      // shared between elements which should have different styles.
      if (!CommonInheritedStyleData(oldContext->RuleNode(),
                                    newContext->RuleNode())) {
        canStopWithStyleChange = false;
      }
    }

    // Look at some details of the new style context to see if it would
    // be safe to stop restyling, if we discover it has the same style
    // data as the old style context.
    ComputeRestyleResultFromNewContext(aSelf, newContext,
                                       result, canStopWithStyleChange);

    uint32_t equalStructs = 0;
    uint32_t samePointerStructs = 0;

    if (copyFromContinuation) {
      // In theory we should know whether there was any style data difference,
      // since we would have calculated that in the previous call to
      // RestyleSelf, so until we perform only one restyling per chain-of-
      // same-style continuations (bug 918064), we need to check again here to
      // determine whether it is safe to stop restyling.
      if (result == RestyleResult::eStop) {
        oldContext->CalcStyleDifference(newContext, nsChangeHint(0),
                                        &equalStructs,
                                        &samePointerStructs);
        if (equalStructs != NS_STYLE_INHERIT_MASK) {
          // At least one struct had different data in it, so we must
          // continue restyling children.
          LOG_RESTYLE_CONTINUE("there is different style data: %s",
                      RestyleManager::StructNamesToString(
                        ~equalStructs & NS_STYLE_INHERIT_MASK).get());
          result = RestyleResult::eContinue;
        }
      }
    } else {
      bool changedStyle =
        RestyleManager::TryStartingTransition(mPresContext, aSelf->GetContent(),
                                              oldContext, &newContext);
      if (changedStyle) {
        LOG_RESTYLE_CONTINUE("TryStartingTransition changed the new style context");
        result = RestyleResult::eContinue;
        canStopWithStyleChange = false;
      }
      CaptureChange(oldContext, newContext, assumeDifferenceHint,
                    &equalStructs, &samePointerStructs);
      if (equalStructs != NS_STYLE_INHERIT_MASK) {
        // At least one struct had different data in it, so we must
        // continue restyling children.
        LOG_RESTYLE_CONTINUE("there is different style data: %s",
                    RestyleManager::StructNamesToString(
                      ~equalStructs & NS_STYLE_INHERIT_MASK).get());
        result = RestyleResult::eContinue;
      }
    }

    if (canStopWithStyleChange) {
      // If any inherited struct pointers are different, or if any
      // reset struct pointers are different and we have descendants
      // that rely on those reset struct pointers, we can't return
      // RestyleResult::eStopWithStyleChange.
      if ((samePointerStructs & NS_STYLE_INHERITED_STRUCT_MASK) !=
            NS_STYLE_INHERITED_STRUCT_MASK) {
        LOG_RESTYLE("can't return RestyleResult::eStopWithStyleChange since "
                    "there is different inherited data");
        canStopWithStyleChange = false;
      } else if ((samePointerStructs & NS_STYLE_RESET_STRUCT_MASK) !=
                   NS_STYLE_RESET_STRUCT_MASK &&
                 oldContext->HasChildThatUsesResetStyle()) {
        LOG_RESTYLE("can't return RestyleResult::eStopWithStyleChange since "
                    "there is different reset data and descendants use it");
        canStopWithStyleChange = false;
      }
    }

    if (result == RestyleResult::eStop) {
      // Since we currently have RestyleResult::eStop, we know at this
      // point that all of our style structs are equal in terms of styles.
      // However, some of them might be different pointers.  Since our
      // descendants might share those pointers, we have to continue to
      // restyling our descendants.
      //
      // However, because of the swapping of equal structs we've done on
      // ancestors (later in this function), we've ensured that for structs
      // that cannot be stored in the rule tree, we keep the old equal structs
      // around rather than replacing them with new ones.  This means that we
      // only time we hit this deoptimization is either
      //
      // (a) when at least one of the (old or new) equal structs could be stored
      //     in the rule tree, and those structs are then inherited (by pointer
      //     sharing) to descendant style contexts; or
      //
      // (b) when we were unable to swap the structs on the parent because
      //     either or both of the old parent and new parent are shared.
      //
      // FIXME This loop could be rewritten as bit operations on
      //       oldContext->mBits and samePointerStructs.
      for (nsStyleStructID sid = nsStyleStructID(0);
           sid < nsStyleStructID_Length;
           sid = nsStyleStructID(sid + 1)) {
        if (oldContext->HasCachedDependentStyleData(sid) &&
            !(samePointerStructs & nsCachedStyleData::GetBitForSID(sid))) {
          LOG_RESTYLE_CONTINUE("there are different struct pointers");
          result = RestyleResult::eContinue;
          break;
        }
      }
    }

    // From this point we no longer do any assignments of
    // RestyleResult::eContinue to |result|.  If canStopWithStyleChange is true,
    // it means that we can convert |result| (whether it is
    // RestyleResult::eContinue or RestyleResult::eStop) into
    // RestyleResult::eStopWithStyleChange.
    if (canStopWithStyleChange) {
      LOG_RESTYLE("converting %s into RestyleResult::eStopWithStyleChange",
                  RestyleResultToString(result).get());
      result = RestyleResult::eStopWithStyleChange;
    }

    if (aRestyleHint & eRestyle_ForceDescendants) {
      result = RestyleResult::eContinueAndForceDescendants;
    }

    if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
      // If the frame gets regenerated, let it keep its old context,
      // which is important to maintain various invariants about
      // frame types matching their style contexts.
      // Note that this check even makes sense if we didn't call
      // CaptureChange because of copyFromContinuation being true,
      // since we'll have copied the existing context from the
      // previous continuation, so newContext == oldContext.

      if (result != RestyleResult::eStop) {
        if (copyFromContinuation) {
          LOG_RESTYLE("not swapping style structs, since we copied from a "
                      "continuation");
        } else if (oldContext->IsShared() && newContext->IsShared()) {
          LOG_RESTYLE("not swapping style structs, since both old and contexts "
                      "are shared");
        } else if (oldContext->IsShared()) {
          LOG_RESTYLE("not swapping style structs, since the old context is "
                      "shared");
        } else if (newContext->IsShared()) {
          LOG_RESTYLE("not swapping style structs, since the new context is "
                      "shared");
        } else {
          if (result == RestyleResult::eStopWithStyleChange) {
            LOG_RESTYLE("recording a style struct swap between %p and %p to "
                        "do if RestyleResult::eStopWithStyleChange fails",
                        oldContext.get(), newContext.get());
            SwapInstruction* swap = aSwaps.AppendElement();
            swap->mOldContext = oldContext;
            swap->mNewContext = newContext;
            swap->mStructsToSwap = equalStructs;
          } else {
            LOG_RESTYLE("swapping style structs between %p and %p",
                        oldContext.get(), newContext.get());
            oldContext->SwapStyleData(newContext, equalStructs);
            *aSwappedStructs |= equalStructs;
          }
#ifdef RESTYLE_LOGGING
          uint32_t structs = RestyleManager::StructsToLog() & equalStructs;
          if (structs) {
            LOG_RESTYLE_INDENT();
            LOG_RESTYLE("old style context now has: %s",
                        oldContext->GetCachedStyleDataAsString(structs).get());
            LOG_RESTYLE("new style context now has: %s",
                        newContext->GetCachedStyleDataAsString(structs).get());
          }
#endif
        }
        LOG_RESTYLE("setting new style context");
        aSelf->SetStyleContext(newContext);
      }
    } else {
      LOG_RESTYLE("not setting new style context, since we'll reframe");
      // We need to keep the new parent alive, in case it had structs
      // swapped into it that our frame's style context still has cached.
      // This is a similar scenario to the one described in the
      // ElementRestyler::Restyle comment where we append to
      // mSwappedStructOwners.
      //
      // We really only need to do this if we did swap structs on the
      // parent, but we don't have that information here.
      mSwappedStructOwners.AppendElement(newContext->GetParent());
    }
  } else {
    if (aRestyleHint & eRestyle_ForceDescendants) {
      result = RestyleResult::eContinueAndForceDescendants;
    }
  }
  oldContext = nullptr;

  // do additional contexts
  // XXXbz might be able to avoid selector matching here in some
  // cases; won't worry about it for now.
  int32_t contextIndex = 0;
  for (nsStyleContext* oldExtraContext;
       (oldExtraContext = aSelf->GetAdditionalStyleContext(contextIndex));
       ++contextIndex) {
    LOG_RESTYLE("extra context %d", contextIndex);
    LOG_RESTYLE_INDENT();
    RefPtr<nsStyleContext> newExtraContext;
    nsIAtom* const extraPseudoTag = oldExtraContext->GetPseudo();
    const CSSPseudoElementType extraPseudoType =
      oldExtraContext->GetPseudoType();
    NS_ASSERTION(extraPseudoTag &&
                 !nsCSSAnonBoxes::IsNonElement(extraPseudoTag),
                 "extra style context is not pseudo element");
    Element* element = extraPseudoType != CSSPseudoElementType::AnonBox
                         ? mContent->AsElement() : nullptr;
    if (!MustRestyleSelf(aRestyleHint, element)) {
      if (CanReparentStyleContext(aRestyleHint)) {
        newExtraContext =
          styleSet->ReparentStyleContext(oldExtraContext, newContext, element);
      } else {
        // Use ResolveStyleWithReplacement as a substitute for
        // ReparentStyleContext that rebuilds the path in the rule tree
        // rather than reusing the rule node, as we need to do during a
        // rule tree reconstruct.
        Element* pseudoElement =
          PseudoElementForStyleContext(aSelf, extraPseudoType);
        MOZ_ASSERT(!element || element != pseudoElement,
                   "pseudo-element for selector matching should be "
                   "the anonymous content node that we create, "
                   "not the real element");
        newExtraContext =
          styleSet->ResolveStyleWithReplacement(element, pseudoElement,
                                                newContext, oldExtraContext,
                                                nsRestyleHint(0));
      }
    } else if (extraPseudoType == CSSPseudoElementType::AnonBox) {
      newExtraContext = styleSet->ResolveAnonymousBoxStyle(extraPseudoTag,
                                                           newContext);
    } else {
      // Don't expect XUL tree stuff here, since it needs a comparator and
      // all.
      NS_ASSERTION(extraPseudoType < CSSPseudoElementType::Count,
                   "Unexpected type");
      newExtraContext = styleSet->ResolvePseudoElementStyle(mContent->AsElement(),
                                                            extraPseudoType,
                                                            newContext,
                                                            nullptr);
    }

    MOZ_ASSERT(newExtraContext);

    LOG_RESTYLE("newExtraContext = %p", newExtraContext.get());

    if (oldExtraContext != newExtraContext) {
      uint32_t equalStructs;
      uint32_t samePointerStructs;
      CaptureChange(oldExtraContext, newExtraContext, assumeDifferenceHint,
                    &equalStructs, &samePointerStructs);
      if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
        LOG_RESTYLE("setting new extra style context");
        aSelf->SetAdditionalStyleContext(contextIndex, newExtraContext);
      } else {
        LOG_RESTYLE("not setting new extra style context, since we'll reframe");
      }
    }
  }

  LOG_RESTYLE("returning %s", RestyleResultToString(result).get());

  return result;
}

void
ElementRestyler::RestyleChildren(nsRestyleHint aChildRestyleHint)
{
  MOZ_ASSERT(!(mHintsHandled & nsChangeHint_ReconstructFrame),
             "No need to do this if we're planning to reframe already.");

  // We'd like style resolution to be exact in the sense that an
  // animation-only style flush flushes only the styles it requests
  // flushing and doesn't update any other styles.  This means avoiding
  // constructing new frames during such a flush.
  //
  // For a ::before or ::after, we'll do an eRestyle_Subtree due to
  // RestyleHintForOp in nsCSSRuleProcessor.cpp (via its
  // HasAttributeDependentStyle or HasStateDependentStyle), given that
  // we store pseudo-elements in selectors like they were children.
  //
  // Also, it's faster to skip the work we do on undisplayed children
  // and pseudo-elements when we can skip it.
  bool mightReframePseudos = aChildRestyleHint & eRestyle_Subtree;

  RestyleUndisplayedDescendants(aChildRestyleHint);

  // Check whether we might need to create a new ::before frame.
  // There's no need to do this if we're planning to reframe already
  // or if we're not forcing restyles on kids.
  // It's also important to check mHintsHandled since we use
  // mFrame->StyleContext(), which is out of date if mHintsHandled has a
  // ReconstructFrame hint.  Using an out of date style context could
  // trigger assertions about mismatched rule trees.
  if (!(mHintsHandled & nsChangeHint_ReconstructFrame) &&
      mightReframePseudos) {
    MaybeReframeForBeforePseudo();
  }

  // There is no need to waste time crawling into a frame's children
  // on a frame change.  The act of reconstructing frames will force
  // new style contexts to be resolved on all of this frame's
  // descendants anyway, so we want to avoid wasting time processing
  // style contexts that we're just going to throw away anyway. - dwh
  // It's also important to check mHintsHandled since reresolving the
  // kids would use mFrame->StyleContext(), which is out of date if
  // mHintsHandled has a ReconstructFrame hint; doing this could trigger
  // assertions about mismatched rule trees.
  nsIFrame* lastContinuation;
  if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
    InitializeAccessibilityNotifications(mFrame->StyleContext());

    for (nsIFrame* f = mFrame; f;
         f = RestyleManager::GetNextContinuationWithSameStyle(f, f->StyleContext())) {
      lastContinuation = f;
      RestyleContentChildren(f, aChildRestyleHint);
    }

    SendAccessibilityNotifications();
  }

  // Check whether we might need to create a new ::after frame.
  // See comments above regarding :before.
  if (!(mHintsHandled & nsChangeHint_ReconstructFrame) &&
      mightReframePseudos) {
    MaybeReframeForAfterPseudo(lastContinuation);
  }
}

void
ElementRestyler::RestyleChildrenOfDisplayContentsElement(
  nsIFrame*              aParentFrame,
  nsStyleContext*        aNewContext,
  nsChangeHint           aMinHint,
  RestyleTracker&        aRestyleTracker,
  nsRestyleHint          aRestyleHint,
  const RestyleHintData& aRestyleHintData)
{
  MOZ_ASSERT(!(mHintsHandled & nsChangeHint_ReconstructFrame), "why call me?");

  const bool mightReframePseudos = aRestyleHint & eRestyle_Subtree;
  DoRestyleUndisplayedDescendants(nsRestyleHint(0), mContent, aNewContext);
  if (!(mHintsHandled & nsChangeHint_ReconstructFrame) && mightReframePseudos) {
    MaybeReframeForPseudo(CSSPseudoElementType::before,
                          aParentFrame, nullptr, mContent, aNewContext);
  }
  if (!(mHintsHandled & nsChangeHint_ReconstructFrame) && mightReframePseudos) {
    MaybeReframeForPseudo(CSSPseudoElementType::after,
                          aParentFrame, nullptr, mContent, aNewContext);
  }
  if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
    InitializeAccessibilityNotifications(aNewContext);

    // Then process child frames for content that is a descendant of mContent.
    // XXX perhaps it's better to walk child frames (before reresolving
    // XXX undisplayed contexts above) and mark those that has a stylecontext
    // XXX leading up to mContent's old context? (instead of the
    // XXX ContentIsDescendantOf check below)
    nsIFrame::ChildListIterator lists(aParentFrame);
    for ( ; !lists.IsDone(); lists.Next()) {
      for (nsIFrame* f : lists.CurrentList()) {
        if (nsContentUtils::ContentIsDescendantOf(f->GetContent(), mContent) &&
            !f->GetPrevContinuation()) {
          if (!(f->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
            ComputeStyleChangeFor(f, mChangeList, aMinHint, aRestyleTracker,
                                  aRestyleHint, aRestyleHintData,
                                  mContextsToClear, mSwappedStructOwners);
          }
        }
      }
    }
  }
  if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
    SendAccessibilityNotifications();
  }
}

void
ElementRestyler::ComputeStyleChangeFor(nsIFrame*          aFrame,
                                       nsStyleChangeList* aChangeList,
                                       nsChangeHint       aMinChange,
                                       RestyleTracker&    aRestyleTracker,
                                       nsRestyleHint      aRestyleHint,
                                       const RestyleHintData& aRestyleHintData,
                                       nsTArray<ContextToClear>&
                                         aContextsToClear,
                                       nsTArray<RefPtr<nsStyleContext>>&
                                         aSwappedStructOwners)
{
  nsIContent* content = aFrame->GetContent();
  nsAutoCString localDescriptor;
  if (profiler_is_active() && content) {
    std::string elemDesc = ToString(*content);
    localDescriptor.Assign(elemDesc.c_str());
  }

  PROFILER_LABEL_PRINTF("ElementRestyler", "ComputeStyleChangeFor",
                        js::ProfileEntry::Category::CSS,
                        content ? "Element: %s" : "%s",
                        content ? localDescriptor.get() : "");
  if (aMinChange) {
    aChangeList->AppendChange(aFrame, content, aMinChange);
  }

  NS_ASSERTION(!aFrame->GetPrevContinuation(),
               "must start with the first continuation");

  // We want to start with this frame and walk all its next-in-flows,
  // as well as all its ib-split siblings and their next-in-flows,
  // reresolving style on all the frames we encounter in this walk that
  // we didn't reach already.  In the normal case, this will mean only
  // restyling the first two block-in-inline splits and no
  // continuations, and skipping everything else.  However, when we have
  // a style change targeted at an element inside a context where styles
  // vary between continuations (e.g., a style change on an element that
  // extends from inside a styled ::first-line to outside of that first
  // line), we might restyle more than that.

  nsPresContext* presContext = aFrame->PresContext();
  FramePropertyTable* propTable = presContext->PropertyTable();

  TreeMatchContext treeMatchContext(true,
                                    nsRuleWalker::eRelevantLinkUnvisited,
                                    presContext->Document());
  Element* parent =
    content ? content->GetParentElementCrossingShadowRoot() : nullptr;
  treeMatchContext.InitAncestors(parent);
  nsTArray<nsCSSSelector*> selectorsForDescendants;
  selectorsForDescendants.AppendElements(
      aRestyleHintData.mSelectorsForDescendants);
  nsTArray<nsIContent*> visibleKidsOfHiddenElement;
  nsIFrame* nextIBSibling;
  for (nsIFrame* ibSibling = aFrame; ibSibling; ibSibling = nextIBSibling) {
    nextIBSibling = RestyleManager::GetNextBlockInInlineSibling(propTable, ibSibling);

    if (nextIBSibling) {
      // Don't allow some ib-split siblings to be processed with
      // RestyleResult::eStopWithStyleChange and others not.
      aRestyleHint |= eRestyle_Force;
    }

    // Outer loop over ib-split siblings
    for (nsIFrame* cont = ibSibling; cont; cont = cont->GetNextContinuation()) {
      if (GetPrevContinuationWithSameStyle(cont)) {
        // We already handled this element when dealing with its earlier
        // continuation.
        continue;
      }

      // Inner loop over next-in-flows of the current frame
      ElementRestyler restyler(presContext, cont, aChangeList,
                               aMinChange, aRestyleTracker,
                               selectorsForDescendants,
                               treeMatchContext,
                               visibleKidsOfHiddenElement,
                               aContextsToClear, aSwappedStructOwners);

      restyler.Restyle(aRestyleHint);

      if (restyler.HintsHandledForFrame() & nsChangeHint_ReconstructFrame) {
        // If it's going to cause a framechange, then don't bother
        // with the continuations or ib-split siblings since they'll be
        // clobbered by the frame reconstruct anyway.
        NS_ASSERTION(!cont->GetPrevContinuation(),
                     "continuing frame had more severe impact than first-in-flow");
        return;
      }
    }
  }
}

// The structure of this method parallels ConditionallyRestyleUndisplayedDescendants.
// If you update this method, you probably want to update that one too.
void
ElementRestyler::RestyleUndisplayedDescendants(nsRestyleHint aChildRestyleHint)
{
  nsIContent* undisplayedParent;
  if (MustCheckUndisplayedContent(mFrame, undisplayedParent)) {
    DoRestyleUndisplayedDescendants(aChildRestyleHint, undisplayedParent,
                                    mFrame->StyleContext());
  }
}

// The structure of this method parallels DoConditionallyRestyleUndisplayedDescendants.
// If you update this method, you probably want to update that one too.
void
ElementRestyler::DoRestyleUndisplayedDescendants(nsRestyleHint aChildRestyleHint,
                                                 nsIContent* aParent,
                                                 nsStyleContext* aParentContext)
{
  nsCSSFrameConstructor* fc = mPresContext->FrameConstructor();
  UndisplayedNode* nodes = fc->GetAllUndisplayedContentIn(aParent);
  RestyleUndisplayedNodes(aChildRestyleHint, nodes, aParent,
                          aParentContext, StyleDisplay::None);
  nodes = fc->GetAllDisplayContentsIn(aParent);
  RestyleUndisplayedNodes(aChildRestyleHint, nodes, aParent,
                          aParentContext, StyleDisplay::Contents);
}

// The structure of this method parallels ConditionallyRestyleUndisplayedNodes.
// If you update this method, you probably want to update that one too.
void
ElementRestyler::RestyleUndisplayedNodes(nsRestyleHint      aChildRestyleHint,
                                         UndisplayedNode*   aUndisplayed,
                                         nsIContent*        aUndisplayedParent,
                                         nsStyleContext*    aParentContext,
                                         const StyleDisplay aDisplay)
{
  nsIContent* undisplayedParent = aUndisplayedParent;
  UndisplayedNode* undisplayed = aUndisplayed;
  TreeMatchContext::AutoAncestorPusher pusher(mTreeMatchContext);
  if (undisplayed) {
    pusher.PushAncestorAndStyleScope(undisplayedParent);
  }
  for (; undisplayed; undisplayed = undisplayed->mNext) {
    NS_ASSERTION(undisplayedParent ||
                 undisplayed->mContent ==
                   mPresContext->Document()->GetRootElement(),
                 "undisplayed node child of null must be root");
    NS_ASSERTION(!undisplayed->mStyle->GetPseudo(),
                 "Shouldn't have random pseudo style contexts in the "
                 "undisplayed map");

    LOG_RESTYLE("RestyleUndisplayedChildren: undisplayed->mContent = %p",
                undisplayed->mContent.get());

    // Get the parent of the undisplayed content and check if it is a XBL
    // children element. Push the children element as an ancestor here because it does
    // not have a frame and would not otherwise be pushed as an ancestor.
    nsIContent* parent = undisplayed->mContent->GetParent();
    TreeMatchContext::AutoAncestorPusher insertionPointPusher(mTreeMatchContext);
    if (parent && nsContentUtils::IsContentInsertionPoint(parent)) {
      insertionPointPusher.PushAncestorAndStyleScope(parent);
    }

    nsRestyleHint thisChildHint = aChildRestyleHint;
    nsAutoPtr<RestyleTracker::RestyleData> undisplayedRestyleData;
    Element* element = undisplayed->mContent->AsElement();
    if (mRestyleTracker.GetRestyleData(element,
                                       undisplayedRestyleData)) {
      thisChildHint =
        nsRestyleHint(thisChildHint | undisplayedRestyleData->mRestyleHint);
    }
    RefPtr<nsStyleContext> undisplayedContext;
    nsStyleSet* styleSet = StyleSet();
    if (MustRestyleSelf(thisChildHint, element)) {
      undisplayedContext =
        styleSet->ResolveStyleFor(element, aParentContext, mTreeMatchContext);
    } else if (CanReparentStyleContext(thisChildHint)) {
      undisplayedContext =
        styleSet->ReparentStyleContext(undisplayed->mStyle,
                                       aParentContext,
                                       element);
    } else {
      // Use ResolveStyleWithReplacement either for actual
      // replacements, or as a substitute for ReparentStyleContext
      // that rebuilds the path in the rule tree rather than reusing
      // the rule node, as we need to do during a rule tree
      // reconstruct.
      nsRestyleHint rshint = thisChildHint & ~eRestyle_SomeDescendants;
      undisplayedContext =
        styleSet->ResolveStyleWithReplacement(element, nullptr,
                                              aParentContext,
                                              undisplayed->mStyle,
                                              rshint);
    }
    const nsStyleDisplay* display = undisplayedContext->StyleDisplay();
    if (display->mDisplay != aDisplay) {
      NS_ASSERTION(element, "Must have undisplayed content");
      mChangeList->AppendChange(nullptr, element,
                                nsChangeHint_ReconstructFrame);
      // The node should be removed from the undisplayed map when
      // we reframe it.
    } else {
      // update the undisplayed node with the new context
      undisplayed->mStyle = undisplayedContext;

      if (aDisplay == StyleDisplay::Contents) {
        DoRestyleUndisplayedDescendants(aChildRestyleHint, element,
                                        undisplayed->mStyle);
      }
    }
  }
}

void
ElementRestyler::MaybeReframeForBeforePseudo()
{
  MaybeReframeForPseudo(CSSPseudoElementType::before,
                        mFrame, mFrame, mFrame->GetContent(),
                        mFrame->StyleContext());
}

/**
 * aFrame is the last continuation or block-in-inline sibling that this
 * ElementRestyler is restyling.
 */
void
ElementRestyler::MaybeReframeForAfterPseudo(nsIFrame* aFrame)
{
  MOZ_ASSERT(aFrame);
  MaybeReframeForPseudo(CSSPseudoElementType::after,
                        aFrame, aFrame, aFrame->GetContent(),
                        aFrame->StyleContext());
}

#ifdef DEBUG
bool
ElementRestyler::MustReframeForBeforePseudo()
{
  return MustReframeForPseudo(CSSPseudoElementType::before,
                              mFrame, mFrame, mFrame->GetContent(),
                              mFrame->StyleContext());
}

bool
ElementRestyler::MustReframeForAfterPseudo(nsIFrame* aFrame)
{
  MOZ_ASSERT(aFrame);
  return MustReframeForPseudo(CSSPseudoElementType::after,
                              aFrame, aFrame, aFrame->GetContent(),
                              aFrame->StyleContext());
}
#endif

void
ElementRestyler::MaybeReframeForPseudo(CSSPseudoElementType aPseudoType,
                                       nsIFrame* aGenConParentFrame,
                                       nsIFrame* aFrame,
                                       nsIContent* aContent,
                                       nsStyleContext* aStyleContext)
{
  if (MustReframeForPseudo(aPseudoType, aGenConParentFrame, aFrame, aContent,
                           aStyleContext)) {
    // Have to create the new ::before/::after frame.
    LOG_RESTYLE("MaybeReframeForPseudo, appending "
                "nsChangeHint_ReconstructFrame");
    mHintsHandled |= nsChangeHint_ReconstructFrame;
    mChangeList->AppendChange(aFrame, aContent, nsChangeHint_ReconstructFrame);
  }
}

bool
ElementRestyler::MustReframeForPseudo(CSSPseudoElementType aPseudoType,
                                      nsIFrame* aGenConParentFrame,
                                      nsIFrame* aFrame,
                                      nsIContent* aContent,
                                      nsStyleContext* aStyleContext)
{
  MOZ_ASSERT(aPseudoType == CSSPseudoElementType::before ||
             aPseudoType == CSSPseudoElementType::after);

  // Make sure not to do this for pseudo-frames...
  if (aStyleContext->GetPseudo()) {
    return false;
  }

  // ... or frames that can't have generated content.
  if (!(aGenConParentFrame->GetStateBits() & NS_FRAME_MAY_HAVE_GENERATED_CONTENT)) {
    // Our content insertion frame might have gotten flagged.
    nsContainerFrame* cif = aGenConParentFrame->GetContentInsertionFrame();
    if (!cif || !(cif->GetStateBits() & NS_FRAME_MAY_HAVE_GENERATED_CONTENT)) {
      return false;
    }
  }

  if (aPseudoType == CSSPseudoElementType::before) {
    // Check for a ::before pseudo style and the absence of a ::before content,
    // but only if aFrame is null or is the first continuation/ib-split.
    if ((aFrame && !nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(aFrame)) ||
        nsLayoutUtils::GetBeforeFrameForContent(aGenConParentFrame, aContent)) {
      return false;
    }
  } else {
    // Similarly for ::after, but check for being the last continuation/
    // ib-split.
    if ((aFrame && nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) ||
        nsLayoutUtils::GetAfterFrameForContent(aGenConParentFrame, aContent)) {
      return false;
    }
  }

  // Checking for a ::before frame (which we do above) is cheaper than getting
  // the ::before style context here.
  return nsLayoutUtils::HasPseudoStyle(aContent, aStyleContext, aPseudoType,
                                       mPresContext);
}

void
ElementRestyler::InitializeAccessibilityNotifications(nsStyleContext* aNewContext)
{
#ifdef ACCESSIBILITY
  // Notify a11y for primary frame only if it's a root frame of visibility
  // changes or its parent frame was hidden while it stays visible and
  // it is not inside a {ib} split or is the first frame of {ib} split.
  if (nsIPresShell::IsAccessibilityActive() &&
      (!mFrame ||
       (!mFrame->GetPrevContinuation() &&
        !mFrame->FrameIsNonFirstInIBSplit()))) {
    if (mDesiredA11yNotifications == eSendAllNotifications) {
      bool isFrameVisible = aNewContext->StyleVisibility()->IsVisible();
      if (isFrameVisible != mWasFrameVisible) {
        if (isFrameVisible) {
          // Notify a11y the element (perhaps with its children) was shown.
          // We don't fall into this case if this element gets or stays shown
          // while its parent becomes hidden.
          mKidsDesiredA11yNotifications = eSkipNotifications;
          mOurA11yNotification = eNotifyShown;
        } else {
          // The element is being hidden; its children may stay visible, or
          // become visible after being hidden previously. If we'll find
          // visible children then we should notify a11y about that as if
          // they were inserted into tree. Notify a11y this element was
          // hidden.
          mKidsDesiredA11yNotifications = eNotifyIfShown;
          mOurA11yNotification = eNotifyHidden;
        }
      }
    } else if (mDesiredA11yNotifications == eNotifyIfShown &&
               aNewContext->StyleVisibility()->IsVisible()) {
      // Notify a11y that element stayed visible while its parent was hidden.
      nsIContent* c = mFrame ? mFrame->GetContent() : mContent;
      mVisibleKidsOfHiddenElement.AppendElement(c);
      mKidsDesiredA11yNotifications = eSkipNotifications;
    }
  }
#endif
}

// The structure of this method parallels ConditionallyRestyleContentChildren.
// If you update this method, you probably want to update that one too.
void
ElementRestyler::RestyleContentChildren(nsIFrame* aParent,
                                        nsRestyleHint aChildRestyleHint)
{
  LOG_RESTYLE("RestyleContentChildren");

  nsIFrame::ChildListIterator lists(aParent);
  TreeMatchContext::AutoAncestorPusher ancestorPusher(mTreeMatchContext);
  if (!lists.IsDone()) {
    ancestorPusher.PushAncestorAndStyleScope(mContent);
  }
  for (; !lists.IsDone(); lists.Next()) {
    for (nsIFrame* child : lists.CurrentList()) {
      // Out-of-flows are reached through their placeholders.  Continuations
      // and block-in-inline splits are reached through those chains.
      if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
          !GetPrevContinuationWithSameStyle(child)) {
        // Get the parent of the child frame's content and check if it
        // is a XBL children element. Push the children element as an
        // ancestor here because it does not have a frame and would not
        // otherwise be pushed as an ancestor.

        // Check if the frame has a content because |child| may be a
        // nsPageFrame that does not have a content.
        nsIContent* parent = child->GetContent() ? child->GetContent()->GetParent() : nullptr;
        TreeMatchContext::AutoAncestorPusher insertionPointPusher(mTreeMatchContext);
        if (parent && nsContentUtils::IsContentInsertionPoint(parent)) {
          insertionPointPusher.PushAncestorAndStyleScope(parent);
        }

        // only do frames that are in flow
        if (nsGkAtoms::placeholderFrame == child->GetType()) { // placeholder
          // get out of flow frame and recur there
          nsIFrame* outOfFlowFrame =
            nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
          NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
          NS_ASSERTION(outOfFlowFrame != mResolvedChild,
                       "out-of-flow frame not a true descendant");

          // |nsFrame::GetParentStyleContext| checks being out
          // of flow so that this works correctly.
          do {
            if (GetPrevContinuationWithSameStyle(outOfFlowFrame)) {
              // Later continuations are likely restyled as a result of
              // the restyling of the previous continuation.
              // (Currently that's always true, but it's likely to
              // change if we implement overflow:fragments or similar.)
              continue;
            }
            ElementRestyler oofRestyler(*this, outOfFlowFrame,
                                        FOR_OUT_OF_FLOW_CHILD);
            oofRestyler.Restyle(aChildRestyleHint);
          } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));

          // reresolve placeholder's context under the same parent
          // as the out-of-flow frame
          ElementRestyler phRestyler(*this, child, 0);
          phRestyler.Restyle(aChildRestyleHint);
        }
        else {  // regular child frame
          if (child != mResolvedChild) {
            ElementRestyler childRestyler(*this, child, 0);
            childRestyler.Restyle(aChildRestyleHint);
          }
        }
      }
    }
  }
  // XXX need to do overflow frames???
}

void
ElementRestyler::SendAccessibilityNotifications()
{
#ifdef ACCESSIBILITY
  // Send notifications about visibility changes.
  if (mOurA11yNotification == eNotifyShown) {
    nsAccessibilityService* accService = nsIPresShell::AccService();
    if (accService) {
      nsIPresShell* presShell = mPresContext->GetPresShell();
      nsIContent* content = mFrame ? mFrame->GetContent() : mContent;

      accService->ContentRangeInserted(presShell, content->GetParent(),
                                       content,
                                       content->GetNextSibling());
    }
  } else if (mOurA11yNotification == eNotifyHidden) {
    nsAccessibilityService* accService = nsIPresShell::AccService();
    if (accService) {
      nsIPresShell* presShell = mPresContext->GetPresShell();
      nsIContent* content = mFrame ? mFrame->GetContent() : mContent;
      accService->ContentRemoved(presShell, content);

      // Process children staying shown.
      uint32_t visibleContentCount = mVisibleKidsOfHiddenElement.Length();
      for (uint32_t idx = 0; idx < visibleContentCount; idx++) {
        nsIContent* childContent = mVisibleKidsOfHiddenElement[idx];
        accService->ContentRangeInserted(presShell, childContent->GetParent(),
                                         childContent,
                                         childContent->GetNextSibling());
      }
      mVisibleKidsOfHiddenElement.Clear();
    }
  }
#endif
}

static void
ClearCachedInheritedStyleDataOnDescendants(
    nsTArray<ElementRestyler::ContextToClear>& aContextsToClear)
{
  for (size_t i = 0; i < aContextsToClear.Length(); i++) {
    auto& entry = aContextsToClear[i];
    if (!entry.mStyleContext->HasSingleReference()) {
      entry.mStyleContext->ClearCachedInheritedStyleDataOnDescendants(
          entry.mStructs);
    }
    entry.mStyleContext = nullptr;
  }
}

void
RestyleManager::ComputeAndProcessStyleChange(nsIFrame*              aFrame,
                                             nsChangeHint           aMinChange,
                                             RestyleTracker&        aRestyleTracker,
                                             nsRestyleHint          aRestyleHint,
                                             const RestyleHintData& aRestyleHintData)
{
  MOZ_ASSERT(mReframingStyleContexts, "should have rsc");
  nsStyleChangeList changeList;
  nsTArray<ElementRestyler::ContextToClear> contextsToClear;

  // swappedStructOwners needs to be kept alive until after
  // ProcessRestyledFrames and ClearCachedInheritedStyleDataOnDescendants
  // calls; see comment in ElementRestyler::Restyle.
  nsTArray<RefPtr<nsStyleContext>> swappedStructOwners;
  ElementRestyler::ComputeStyleChangeFor(aFrame, &changeList, aMinChange,
                                         aRestyleTracker, aRestyleHint,
                                         aRestyleHintData,
                                         contextsToClear, swappedStructOwners);
  ProcessRestyledFrames(changeList);
  ClearCachedInheritedStyleDataOnDescendants(contextsToClear);
}

void
RestyleManager::ComputeAndProcessStyleChange(nsStyleContext*        aNewContext,
                                             Element*               aElement,
                                             nsChangeHint           aMinChange,
                                             RestyleTracker&        aRestyleTracker,
                                             nsRestyleHint          aRestyleHint,
                                             const RestyleHintData& aRestyleHintData)
{
  MOZ_ASSERT(mReframingStyleContexts, "should have rsc");
  MOZ_ASSERT(aNewContext->StyleDisplay()->mDisplay == StyleDisplay::Contents);
  nsIFrame* frame = GetNearestAncestorFrame(aElement);
  MOZ_ASSERT(frame, "display:contents node in map although it's a "
                    "display:none descendant?");
  TreeMatchContext treeMatchContext(true,
                                    nsRuleWalker::eRelevantLinkUnvisited,
                                    frame->PresContext()->Document());
  nsIContent* parent = aElement->GetParent();
  Element* parentElement =
    parent && parent->IsElement() ? parent->AsElement() : nullptr;
  treeMatchContext.InitAncestors(parentElement);

  nsTArray<nsCSSSelector*> selectorsForDescendants;
  nsTArray<nsIContent*> visibleKidsOfHiddenElement;
  nsTArray<ElementRestyler::ContextToClear> contextsToClear;

  // swappedStructOwners needs to be kept alive until after
  // ProcessRestyledFrames and ClearCachedInheritedStyleDataOnDescendants
  // calls; see comment in ElementRestyler::Restyle.
  nsTArray<RefPtr<nsStyleContext>> swappedStructOwners;
  nsStyleChangeList changeList;
  ElementRestyler r(frame->PresContext(), aElement, &changeList, aMinChange,
                    aRestyleTracker, selectorsForDescendants, treeMatchContext,
                    visibleKidsOfHiddenElement, contextsToClear,
                    swappedStructOwners);
  r.RestyleChildrenOfDisplayContentsElement(frame, aNewContext, aMinChange,
                                            aRestyleTracker,
                                            aRestyleHint, aRestyleHintData);
  ProcessRestyledFrames(changeList);
  ClearCachedInheritedStyleDataOnDescendants(contextsToClear);
}

nsStyleSet*
ElementRestyler::StyleSet() const
{
  MOZ_ASSERT(mPresContext->StyleSet()->IsGecko(),
             "ElementRestyler should only be used with a Gecko-flavored "
             "style backend");
  return mPresContext->StyleSet()->AsGecko();
}

AutoDisplayContentsAncestorPusher::AutoDisplayContentsAncestorPusher(
  TreeMatchContext& aTreeMatchContext, nsPresContext* aPresContext,
  nsIContent* aParent)
  : mTreeMatchContext(aTreeMatchContext)
  , mPresContext(aPresContext)
{
  if (aParent) {
    nsFrameManager* fm = mPresContext->FrameManager();
    // Push display:contents mAncestors onto mTreeMatchContext.
    for (nsIContent* p = aParent; p && fm->GetDisplayContentsStyleFor(p);
         p = p->GetParent()) {
      mAncestors.AppendElement(p->AsElement());
    }
    bool hasFilter = mTreeMatchContext.mAncestorFilter.HasFilter();
    nsTArray<mozilla::dom::Element*>::size_type i = mAncestors.Length();
    while (i--) {
      if (hasFilter) {
        mTreeMatchContext.mAncestorFilter.PushAncestor(mAncestors[i]);
      }
      mTreeMatchContext.PushStyleScope(mAncestors[i]);
    }
  }
}

AutoDisplayContentsAncestorPusher::~AutoDisplayContentsAncestorPusher()
{
  // Pop the ancestors we pushed in the CTOR, if any.
  typedef nsTArray<mozilla::dom::Element*>::size_type sz;
  sz len = mAncestors.Length();
  bool hasFilter = mTreeMatchContext.mAncestorFilter.HasFilter();
  for (sz i = 0; i < len; ++i) {
    if (hasFilter) {
      mTreeMatchContext.mAncestorFilter.PopAncestor();
    }
    mTreeMatchContext.PopStyleScope(mAncestors[i]);
  }
}

#ifdef RESTYLE_LOGGING
uint32_t
RestyleManager::StructsToLog()
{
  static bool initialized = false;
  static uint32_t structs;
  if (!initialized) {
    structs = 0;
    const char* value = getenv("MOZ_DEBUG_RESTYLE_STRUCTS");
    if (value) {
      nsCString s(value);
      while (!s.IsEmpty()) {
        int32_t index = s.FindChar(',');
        nsStyleStructID sid;
        bool found;
        if (index == -1) {
          found = nsStyleContext::LookupStruct(s, sid);
          s.Truncate();
        } else {
          found = nsStyleContext::LookupStruct(Substring(s, 0, index), sid);
          s = Substring(s, index + 1);
        }
        if (found) {
          structs |= nsCachedStyleData::GetBitForSID(sid);
        }
      }
    }
    initialized = true;
  }
  return structs;
}
#endif

#ifdef DEBUG
/* static */ nsCString
RestyleManager::StructNamesToString(uint32_t aSIDs)
{
  nsCString result;
  bool any = false;
  for (nsStyleStructID sid = nsStyleStructID(0);
       sid < nsStyleStructID_Length;
       sid = nsStyleStructID(sid + 1)) {
    if (aSIDs & nsCachedStyleData::GetBitForSID(sid)) {
      if (any) {
        result.AppendLiteral(",");
      }
      result.AppendPrintf("%s", nsStyleContext::StructName(sid));
      any = true;
    }
  }
  return result;
}

/* static */ nsCString
ElementRestyler::RestyleResultToString(RestyleResult aRestyleResult)
{
  nsCString result;
  switch (aRestyleResult) {
    case RestyleResult::eStop:
      result.AssignLiteral("RestyleResult::eStop");
      break;
    case RestyleResult::eStopWithStyleChange:
      result.AssignLiteral("RestyleResult::eStopWithStyleChange");
      break;
    case RestyleResult::eContinue:
      result.AssignLiteral("RestyleResult::eContinue");
      break;
    case RestyleResult::eContinueAndForceDescendants:
      result.AssignLiteral("RestyleResult::eContinueAndForceDescendants");
      break;
    default:
      MOZ_ASSERT(aRestyleResult == RestyleResult::eNone,
                 "Unexpected RestyleResult");
  }
  return result;
}
#endif

} // namespace mozilla
