layout/base/RestyleManager.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 /**
     7  * Code responsible for managing style changes: tracking what style
     8  * changes need to happen, scheduling them, and doing them.
     9  */
    11 #include "RestyleManager.h"
    12 #include "mozilla/EventStates.h"
    13 #include "nsLayoutUtils.h"
    14 #include "GeckoProfiler.h"
    15 #include "nsStyleChangeList.h"
    16 #include "nsRuleProcessorData.h"
    17 #include "nsStyleUtil.h"
    18 #include "nsCSSFrameConstructor.h"
    19 #include "nsSVGEffects.h"
    20 #include "nsCSSRendering.h"
    21 #include "nsAnimationManager.h"
    22 #include "nsTransitionManager.h"
    23 #include "nsViewManager.h"
    24 #include "nsRenderingContext.h"
    25 #include "nsSVGIntegrationUtils.h"
    26 #include "nsCSSAnonBoxes.h"
    27 #include "nsContainerFrame.h"
    28 #include "nsPlaceholderFrame.h"
    29 #include "nsBlockFrame.h"
    30 #include "nsViewportFrame.h"
    31 #include "SVGTextFrame.h"
    32 #include "StickyScrollContainer.h"
    33 #include "nsIRootBox.h"
    34 #include "nsIDOMMutationEvent.h"
    35 #include "nsContentUtils.h"
    36 #include "nsIFrameInlines.h"
    37 #include "ActiveLayerTracker.h"
    38 #include "nsDisplayList.h"
    40 #ifdef ACCESSIBILITY
    41 #include "nsAccessibilityService.h"
    42 #endif
    44 namespace mozilla {
    46 using namespace layers;
    48 RestyleManager::RestyleManager(nsPresContext* aPresContext)
    49   : mPresContext(aPresContext)
    50   , mRebuildAllStyleData(false)
    51   , mObservingRefreshDriver(false)
    52   , mInStyleRefresh(false)
    53   , mHoverGeneration(0)
    54   , mRebuildAllExtraHint(nsChangeHint(0))
    55   , mAnimationGeneration(0)
    56   , mPendingRestyles(ELEMENT_HAS_PENDING_RESTYLE |
    57                      ELEMENT_IS_POTENTIAL_RESTYLE_ROOT)
    58   , mPendingAnimationRestyles(ELEMENT_HAS_PENDING_ANIMATION_RESTYLE |
    59                               ELEMENT_IS_POTENTIAL_ANIMATION_RESTYLE_ROOT)
    60 {
    61   mPendingRestyles.Init(this);
    62   mPendingAnimationRestyles.Init(this);
    63 }
    65 void
    66 RestyleManager::NotifyDestroyingFrame(nsIFrame* aFrame)
    67 {
    68   mOverflowChangedTracker.RemoveFrame(aFrame);
    69 }
    71 #ifdef DEBUG
    72   // To ensure that the functions below are only called within
    73   // |ApplyRenderingChangeToTree|.
    74 static bool gInApplyRenderingChangeToTree = false;
    75 #endif
    77 static void
    78 DoApplyRenderingChangeToTree(nsIFrame* aFrame,
    79                              nsChangeHint aChange);
    81 /**
    82  * Sync views on aFrame and all of aFrame's descendants (following placeholders),
    83  * if aChange has nsChangeHint_SyncFrameView.
    84  * Calls DoApplyRenderingChangeToTree on all aFrame's out-of-flow descendants
    85  * (following placeholders), if aChange has nsChangeHint_RepaintFrame.
    86  * aFrame should be some combination of nsChangeHint_SyncFrameView and
    87  * nsChangeHint_RepaintFrame and nsChangeHint_UpdateOpacityLayer, nothing else.
    88 */
    89 static void
    90 SyncViewsAndInvalidateDescendants(nsIFrame* aFrame,
    91                                   nsChangeHint aChange)
    92 {
    93   NS_PRECONDITION(gInApplyRenderingChangeToTree,
    94                   "should only be called within ApplyRenderingChangeToTree");
    95   NS_ASSERTION(aChange == (aChange & (nsChangeHint_RepaintFrame |
    96                                       nsChangeHint_SyncFrameView |
    97                                       nsChangeHint_UpdateOpacityLayer)),
    98                "Invalid change flag");
   100   nsView* view = aFrame->GetView();
   101   if (view) {
   102     if (aChange & nsChangeHint_SyncFrameView) {
   103       nsContainerFrame::SyncFrameViewProperties(aFrame->PresContext(),
   104                                                 aFrame, nullptr, view);
   105     }
   106   }
   108   nsIFrame::ChildListIterator lists(aFrame);
   109   for (; !lists.IsDone(); lists.Next()) {
   110     nsFrameList::Enumerator childFrames(lists.CurrentList());
   111     for (; !childFrames.AtEnd(); childFrames.Next()) {
   112       nsIFrame* child = childFrames.get();
   113       if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
   114         // only do frames that don't have placeholders
   115         if (nsGkAtoms::placeholderFrame == child->GetType()) {
   116           // do the out-of-flow frame and its continuations
   117           nsIFrame* outOfFlowFrame =
   118             nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
   119           DoApplyRenderingChangeToTree(outOfFlowFrame, aChange);
   120         } else if (lists.CurrentID() == nsIFrame::kPopupList) {
   121           DoApplyRenderingChangeToTree(child, aChange);
   122         } else {  // regular frame
   123           SyncViewsAndInvalidateDescendants(child, aChange);
   124         }
   125       }
   126     }
   127   }
   128 }
   130 /**
   131  * To handle nsChangeHint_ChildrenOnlyTransform we must iterate over the child
   132  * frames of the SVG frame concerned. This helper function is used to find that
   133  * SVG frame when we encounter nsChangeHint_ChildrenOnlyTransform to ensure
   134  * that we iterate over the intended children, since sometimes we end up
   135  * handling that hint while processing hints for one of the SVG frame's
   136  * ancestor frames.
   137  *
   138  * The reason that we sometimes end up trying to process the hint for an
   139  * ancestor of the SVG frame that the hint is intended for is due to the way we
   140  * process restyle events. ApplyRenderingChangeToTree adjusts the frame from
   141  * the restyled element's principle frame to one of its ancestor frames based
   142  * on what nsCSSRendering::FindBackground returns, since the background style
   143  * may have been propagated up to an ancestor frame. Processing hints using an
   144  * ancestor frame is fine in general, but nsChangeHint_ChildrenOnlyTransform is
   145  * a special case since it is intended to update the children of a specific
   146  * frame.
   147  */
   148 static nsIFrame*
   149 GetFrameForChildrenOnlyTransformHint(nsIFrame *aFrame)
   150 {
   151   if (aFrame->GetType() == nsGkAtoms::viewportFrame) {
   152     // This happens if the root-<svg> is fixed positioned, in which case we
   153     // can't use aFrame->GetContent() to find the primary frame, since
   154     // GetContent() returns nullptr for ViewportFrame.
   155     aFrame = aFrame->GetFirstPrincipalChild();
   156   }
   157   // For an nsHTMLScrollFrame, this will get the SVG frame that has the
   158   // children-only transforms:
   159   aFrame = aFrame->GetContent()->GetPrimaryFrame();
   160   if (aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
   161     aFrame = aFrame->GetFirstPrincipalChild();
   162     NS_ABORT_IF_FALSE(aFrame->GetType() == nsGkAtoms::svgOuterSVGAnonChildFrame,
   163                       "Where is the nsSVGOuterSVGFrame's anon child??");
   164   }
   165   NS_ABORT_IF_FALSE(aFrame->IsFrameOfType(nsIFrame::eSVG |
   166                                           nsIFrame::eSVGContainer),
   167                     "Children-only transforms only expected on SVG frames");
   168   return aFrame;
   169 }
   171 static void
   172 DoApplyRenderingChangeToTree(nsIFrame* aFrame,
   173                              nsChangeHint aChange)
   174 {
   175   NS_PRECONDITION(gInApplyRenderingChangeToTree,
   176                   "should only be called within ApplyRenderingChangeToTree");
   178   for ( ; aFrame; aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) {
   179     // Invalidate and sync views on all descendant frames, following placeholders.
   180     // We don't need to update transforms in SyncViewsAndInvalidateDescendants, because
   181     // there can't be any out-of-flows or popups that need to be transformed;
   182     // all out-of-flow descendants of the transformed element must also be
   183     // descendants of the transformed frame.
   184     SyncViewsAndInvalidateDescendants(aFrame,
   185       nsChangeHint(aChange & (nsChangeHint_RepaintFrame |
   186                               nsChangeHint_SyncFrameView |
   187                               nsChangeHint_UpdateOpacityLayer)));
   188     // This must be set to true if the rendering change needs to
   189     // invalidate content.  If it's false, a composite-only paint
   190     // (empty transaction) will be scheduled.
   191     bool needInvalidatingPaint = false;
   193     // if frame has view, will already be invalidated
   194     if (aChange & nsChangeHint_RepaintFrame) {
   195       // Note that this whole block will be skipped when painting is suppressed
   196       // (due to our caller ApplyRendingChangeToTree() discarding the
   197       // nsChangeHint_RepaintFrame hint).  If you add handling for any other
   198       // hints within this block, be sure that they too should be ignored when
   199       // painting is suppressed.
   200       needInvalidatingPaint = true;
   201       aFrame->InvalidateFrameSubtree();
   202       if (aChange & nsChangeHint_UpdateEffects &&
   203           aFrame->IsFrameOfType(nsIFrame::eSVG) &&
   204           !(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
   205         // Need to update our overflow rects:
   206         nsSVGUtils::ScheduleReflowSVG(aFrame);
   207       }
   208     }
   209     if (aChange & nsChangeHint_UpdateTextPath) {
   210       if (aFrame->IsSVGText()) {
   211         // Invalidate and reflow the entire SVGTextFrame:
   212         NS_ASSERTION(aFrame->GetContent()->IsSVG(nsGkAtoms::textPath),
   213                      "expected frame for a <textPath> element");
   214         nsIFrame* text = nsLayoutUtils::GetClosestFrameOfType(
   215                                                       aFrame,
   216                                                       nsGkAtoms::svgTextFrame);
   217         NS_ASSERTION(text, "expected to find an ancestor SVGTextFrame");
   218         static_cast<SVGTextFrame*>(text)->NotifyGlyphMetricsChange();
   219       } else {
   220         NS_ABORT_IF_FALSE(false, "unexpected frame got "
   221                                  "nsChangeHint_UpdateTextPath");
   222       }
   223     }
   224     if (aChange & nsChangeHint_UpdateOpacityLayer) {
   225       // FIXME/bug 796697: we can get away with empty transactions for
   226       // opacity updates in many cases.
   227       needInvalidatingPaint = true;
   229       ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_opacity);
   230       if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
   231         // SVG effects paints the opacity without using
   232         // nsDisplayOpacity. We need to invalidate manually.
   233         aFrame->InvalidateFrameSubtree();
   234       }
   235     }
   236     if ((aChange & nsChangeHint_UpdateTransformLayer) &&
   237         aFrame->IsTransformed()) {
   238       ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_transform);
   239       // If we're not already going to do an invalidating paint, see
   240       // if we can get away with only updating the transform on a
   241       // layer for this frame, and not scheduling an invalidating
   242       // paint.
   243       if (!needInvalidatingPaint) {
   244         Layer* layer;
   245         needInvalidatingPaint |= !aFrame->TryUpdateTransformOnly(&layer);
   247         if (!needInvalidatingPaint) {
   248           // Since we're not going to paint, we need to resend animation
   249           // data to the layer.
   250           MOZ_ASSERT(layer, "this can't happen if there's no layer");
   251           nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(layer,
   252             nullptr, nullptr, aFrame, eCSSProperty_transform);
   253         }
   254       }
   255     }
   256     if (aChange & nsChangeHint_ChildrenOnlyTransform) {
   257       needInvalidatingPaint = true;
   258       nsIFrame* childFrame =
   259         GetFrameForChildrenOnlyTransformHint(aFrame)->GetFirstPrincipalChild();
   260       for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
   261         ActiveLayerTracker::NotifyRestyle(childFrame, eCSSProperty_transform);
   262       }
   263     }
   264     aFrame->SchedulePaint(needInvalidatingPaint ?
   265                           nsIFrame::PAINT_DEFAULT :
   266                           nsIFrame::PAINT_COMPOSITE_ONLY);
   267   }
   268 }
   270 static void
   271 ApplyRenderingChangeToTree(nsPresContext* aPresContext,
   272                            nsIFrame* aFrame,
   273                            nsChangeHint aChange)
   274 {
   275   // We check StyleDisplay()->HasTransform() in addition to checking
   276   // IsTransformed() since we can get here for some frames that don't support
   277   // CSS transforms.
   278   NS_ASSERTION(!(aChange & nsChangeHint_UpdateTransformLayer) ||
   279                aFrame->IsTransformed() ||
   280                aFrame->StyleDisplay()->HasTransformStyle(),
   281                "Unexpected UpdateTransformLayer hint");
   283   nsIPresShell *shell = aPresContext->PresShell();
   284   if (shell->IsPaintingSuppressed()) {
   285     // Don't allow synchronous rendering changes when painting is turned off.
   286     aChange = NS_SubtractHint(aChange, nsChangeHint_RepaintFrame);
   287     if (!aChange) {
   288       return;
   289     }
   290   }
   292   // If the frame's background is propagated to an ancestor, walk up to
   293   // that ancestor.
   294   nsStyleContext *bgSC;
   295   while (!nsCSSRendering::FindBackground(aFrame, &bgSC)) {
   296     aFrame = aFrame->GetParent();
   297     NS_ASSERTION(aFrame, "root frame must paint");
   298   }
   300   // Trigger rendering updates by damaging this frame and any
   301   // continuations of this frame.
   303   // XXX this needs to detect the need for a view due to an opacity change and deal with it...
   305 #ifdef DEBUG
   306   gInApplyRenderingChangeToTree = true;
   307 #endif
   308   DoApplyRenderingChangeToTree(aFrame, aChange);
   309 #ifdef DEBUG
   310   gInApplyRenderingChangeToTree = false;
   311 #endif
   312 }
   314 bool
   315 RestyleManager::RecomputePosition(nsIFrame* aFrame)
   316 {
   317   // Don't process position changes on table frames, since we already handle
   318   // the dynamic position change on the outer table frame, and the reflow-based
   319   // fallback code path also ignores positions on inner table frames.
   320   if (aFrame->GetType() == nsGkAtoms::tableFrame) {
   321     return true;
   322   }
   324   const nsStyleDisplay* display = aFrame->StyleDisplay();
   325   // Changes to the offsets of a non-positioned element can safely be ignored.
   326   if (display->mPosition == NS_STYLE_POSITION_STATIC) {
   327     return true;
   328   }
   330   // Don't process position changes on frames which have views or the ones which
   331   // have a view somewhere in their descendants, because the corresponding view
   332   // needs to be repositioned properly as well.
   333   if (aFrame->HasView() ||
   334       (aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
   335     StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
   336     return false;
   337   }
   339   aFrame->SchedulePaint();
   341   // For relative positioning, we can simply update the frame rect
   342   if (display->IsRelativelyPositionedStyle()) {
   343     if (display->IsInnerTableStyle()) {
   344       // We don't currently support relative positioning of inner table
   345       // elements (bug 35168).  If we apply offsets to things we haven't
   346       // previously offset, we'll get confused.  So bail.
   347       return true;
   348     }
   351     // Move the frame
   352     if (display->mPosition == NS_STYLE_POSITION_STICKY) {
   353       // Update sticky positioning for an entire element at once when
   354       // RecomputePosition is called with the first continuation in a chain.
   355       StickyScrollContainer::ComputeStickyOffsets(aFrame);
   356       StickyScrollContainer* ssc =
   357         StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame);
   358       if (ssc) {
   359         ssc->PositionContinuations(aFrame);
   360       }
   361     } else {
   362       MOZ_ASSERT(NS_STYLE_POSITION_RELATIVE == display->mPosition,
   363                  "Unexpected type of positioning");
   364       for (nsIFrame *cont = aFrame; cont;
   365            cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
   366         nsIFrame* cb = cont->GetContainingBlock();
   367         nsMargin newOffsets;
   368         const nsSize size = cb->GetContentRectRelativeToSelf().Size();
   370         nsHTMLReflowState::ComputeRelativeOffsets(
   371             cb->StyleVisibility()->mDirection,
   372             cont, size.width, size.height, newOffsets);
   373         NS_ASSERTION(newOffsets.left == -newOffsets.right &&
   374                      newOffsets.top == -newOffsets.bottom,
   375                      "ComputeRelativeOffsets should return valid results");
   377         // nsHTMLReflowState::ApplyRelativePositioning would work here, but
   378         // since we've already checked mPosition and aren't changing the frame's
   379         // normal position, go ahead and add the offsets directly.
   380         cont->SetPosition(cont->GetNormalPosition() +
   381                           nsPoint(newOffsets.left, newOffsets.top));
   382       }
   383     }
   385     return true;
   386   }
   388   // For the absolute positioning case, set up a fake HTML reflow state for
   389   // the frame, and then get the offsets and size from it. If the frame's size
   390   // doesn't need to change, we can simply update the frame position. Otherwise
   391   // we fall back to a reflow.
   392   nsRefPtr<nsRenderingContext> rc =
   393     aFrame->PresContext()->PresShell()->CreateReferenceRenderingContext();
   395   // Construct a bogus parent reflow state so that there's a usable
   396   // containing block reflow state.
   397   nsIFrame* parentFrame = aFrame->GetParent();
   398   nsSize parentSize = parentFrame->GetSize();
   400   nsFrameState savedState = parentFrame->GetStateBits();
   401   nsHTMLReflowState parentReflowState(aFrame->PresContext(), parentFrame,
   402                                       rc, parentSize);
   403   parentFrame->RemoveStateBits(~nsFrameState(0));
   404   parentFrame->AddStateBits(savedState);
   406   NS_WARN_IF_FALSE(parentSize.width != NS_INTRINSICSIZE &&
   407                    parentSize.height != NS_INTRINSICSIZE,
   408                    "parentSize should be valid");
   409   parentReflowState.SetComputedWidth(std::max(parentSize.width, 0));
   410   parentReflowState.SetComputedHeight(std::max(parentSize.height, 0));
   411   parentReflowState.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
   413   parentReflowState.ComputedPhysicalPadding() = parentFrame->GetUsedPadding();
   414   parentReflowState.ComputedPhysicalBorderPadding() =
   415     parentFrame->GetUsedBorderAndPadding();
   416   nsSize availSize(parentSize.width, NS_INTRINSICSIZE);
   418   ViewportFrame* viewport = do_QueryFrame(parentFrame);
   419   nsSize cbSize = viewport ?
   420     viewport->AdjustReflowStateAsContainingBlock(&parentReflowState).Size()
   421     : aFrame->GetContainingBlock()->GetSize();
   422   const nsMargin& parentBorder =
   423     parentReflowState.mStyleBorder->GetComputedBorder();
   424   cbSize -= nsSize(parentBorder.LeftRight(), parentBorder.TopBottom());
   425   nsHTMLReflowState reflowState(aFrame->PresContext(), parentReflowState,
   426                                 aFrame, availSize, cbSize.width,
   427                                 cbSize.height);
   428   nsSize computedSize(reflowState.ComputedWidth(), reflowState.ComputedHeight());
   429   computedSize.width += reflowState.ComputedPhysicalBorderPadding().LeftRight();
   430   if (computedSize.height != NS_INTRINSICSIZE) {
   431     computedSize.height += reflowState.ComputedPhysicalBorderPadding().TopBottom();
   432   }
   433   nsSize size = aFrame->GetSize();
   434   // The RecomputePosition hint is not used if any offset changed between auto
   435   // and non-auto. If computedSize.height == NS_INTRINSICSIZE then the new
   436   // element height will be its intrinsic height, and since 'top' and 'bottom''s
   437   // auto-ness hasn't changed, the old height must also be its intrinsic
   438   // height, which we can assume hasn't changed (or reflow would have
   439   // been triggered).
   440   if (computedSize.width == size.width &&
   441       (computedSize.height == NS_INTRINSICSIZE || computedSize.height == size.height)) {
   442     // If we're solving for 'left' or 'top', then compute it here, in order to
   443     // match the reflow code path.
   444     if (NS_AUTOOFFSET == reflowState.ComputedPhysicalOffsets().left) {
   445       reflowState.ComputedPhysicalOffsets().left = cbSize.width -
   446                                           reflowState.ComputedPhysicalOffsets().right -
   447                                           reflowState.ComputedPhysicalMargin().right -
   448                                           size.width -
   449                                           reflowState.ComputedPhysicalMargin().left;
   450     }
   452     if (NS_AUTOOFFSET == reflowState.ComputedPhysicalOffsets().top) {
   453       reflowState.ComputedPhysicalOffsets().top = cbSize.height -
   454                                          reflowState.ComputedPhysicalOffsets().bottom -
   455                                          reflowState.ComputedPhysicalMargin().bottom -
   456                                          size.height -
   457                                          reflowState.ComputedPhysicalMargin().top;
   458     }
   460     // Move the frame
   461     nsPoint pos(parentBorder.left + reflowState.ComputedPhysicalOffsets().left +
   462                 reflowState.ComputedPhysicalMargin().left,
   463                 parentBorder.top + reflowState.ComputedPhysicalOffsets().top +
   464                 reflowState.ComputedPhysicalMargin().top);
   465     aFrame->SetPosition(pos);
   467     return true;
   468   }
   470   // Fall back to a reflow
   471   StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
   472   return false;
   473 }
   475 nsresult
   476 RestyleManager::StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint)
   477 {
   478   nsIPresShell::IntrinsicDirty dirtyType;
   479   if (aHint & nsChangeHint_ClearDescendantIntrinsics) {
   480     NS_ASSERTION(aHint & nsChangeHint_ClearAncestorIntrinsics,
   481                  "Please read the comments in nsChangeHint.h");
   482     dirtyType = nsIPresShell::eStyleChange;
   483   } else if (aHint & nsChangeHint_ClearAncestorIntrinsics) {
   484     dirtyType = nsIPresShell::eTreeChange;
   485   } else {
   486     dirtyType = nsIPresShell::eResize;
   487   }
   489   nsFrameState dirtyBits;
   490   if (aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
   491     dirtyBits = nsFrameState(0);
   492   } else if (aHint & nsChangeHint_NeedDirtyReflow) {
   493     dirtyBits = NS_FRAME_IS_DIRTY;
   494   } else {
   495     dirtyBits = NS_FRAME_HAS_DIRTY_CHILDREN;
   496   }
   498   // If we're not going to clear any intrinsic sizes on the frames, and
   499   // there are no dirty bits to set, then there's nothing to do.
   500   if (dirtyType == nsIPresShell::eResize && !dirtyBits)
   501     return NS_OK;
   503   do {
   504     mPresContext->PresShell()->FrameNeedsReflow(aFrame, dirtyType, dirtyBits);
   505     aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
   506   } while (aFrame);
   508   return NS_OK;
   509 }
   511 NS_DECLARE_FRAME_PROPERTY(ChangeListProperty, nullptr)
   513 /**
   514  * Return true if aFrame's subtree has placeholders for out-of-flow content
   515  * whose 'position' style's bit in aPositionMask is set.
   516  */
   517 static bool
   518 FrameHasPositionedPlaceholderDescendants(nsIFrame* aFrame, uint32_t aPositionMask)
   519 {
   520   const nsIFrame::ChildListIDs skip(nsIFrame::kAbsoluteList |
   521                                     nsIFrame::kFixedList);
   522   for (nsIFrame::ChildListIterator lists(aFrame); !lists.IsDone(); lists.Next()) {
   523     if (!skip.Contains(lists.CurrentID())) {
   524       for (nsFrameList::Enumerator childFrames(lists.CurrentList());
   525            !childFrames.AtEnd(); childFrames.Next()) {
   526         nsIFrame* f = childFrames.get();
   527         if (f->GetType() == nsGkAtoms::placeholderFrame) {
   528           nsIFrame* outOfFlow = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
   529           // If SVG text frames could appear here, they could confuse us since
   530           // they ignore their position style ... but they can't.
   531           NS_ASSERTION(!outOfFlow->IsSVGText(),
   532                        "SVG text frames can't be out of flow");
   533           if (aPositionMask & (1 << outOfFlow->StyleDisplay()->mPosition)) {
   534             return true;
   535           }
   536         }
   537         if (FrameHasPositionedPlaceholderDescendants(f, aPositionMask)) {
   538           return true;
   539         }
   540       }
   541     }
   542   }
   543   return false;
   544 }
   546 static bool
   547 NeedToReframeForAddingOrRemovingTransform(nsIFrame* aFrame)
   548 {
   549   static_assert(0 <= NS_STYLE_POSITION_ABSOLUTE &&
   550                 NS_STYLE_POSITION_ABSOLUTE < 32, "Style constant out of range");
   551   static_assert(0 <= NS_STYLE_POSITION_FIXED &&
   552                 NS_STYLE_POSITION_FIXED < 32, "Style constant out of range");
   554   uint32_t positionMask;
   555   // Don't call aFrame->IsPositioned here, since that returns true if
   556   // the frame already has a transform, and we want to ignore that here
   557   if (aFrame->IsAbsolutelyPositioned() ||
   558       aFrame->IsRelativelyPositioned()) {
   559     // This frame is a container for abs-pos descendants whether or not it
   560     // has a transform.
   561     // So abs-pos descendants are no problem; we only need to reframe if
   562     // we have fixed-pos descendants.
   563     positionMask = 1 << NS_STYLE_POSITION_FIXED;
   564   } else {
   565     // This frame may not be a container for abs-pos descendants already.
   566     // So reframe if we have abs-pos or fixed-pos descendants.
   567     positionMask = (1 << NS_STYLE_POSITION_FIXED) |
   568         (1 << NS_STYLE_POSITION_ABSOLUTE);
   569   }
   570   for (nsIFrame* f = aFrame; f;
   571        f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
   572     if (FrameHasPositionedPlaceholderDescendants(f, positionMask)) {
   573       return true;
   574     }
   575   }
   576   return false;
   577 }
   579 nsresult
   580 RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
   581 {
   582   NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
   583                "Someone forgot a script blocker");
   584   int32_t count = aChangeList.Count();
   585   if (!count)
   586     return NS_OK;
   588   PROFILER_LABEL("CSS", "ProcessRestyledFrames");
   590   // Make sure to not rebuild quote or counter lists while we're
   591   // processing restyles
   592   FrameConstructor()->BeginUpdate();
   594   FramePropertyTable* propTable = mPresContext->PropertyTable();
   596   // Mark frames so that we skip frames that die along the way, bug 123049.
   597   // A frame can be in the list multiple times with different hints. Further
   598   // optmization is possible if nsStyleChangeList::AppendChange could coalesce
   599   int32_t index = count;
   601   while (0 <= --index) {
   602     const nsStyleChangeData* changeData;
   603     aChangeList.ChangeAt(index, &changeData);
   604     if (changeData->mFrame) {
   605       propTable->Set(changeData->mFrame, ChangeListProperty(),
   606                      NS_INT32_TO_PTR(1));
   607     }
   608   }
   610   index = count;
   612   bool didUpdateCursor = false;
   614   while (0 <= --index) {
   615     nsIFrame* frame;
   616     nsIContent* content;
   617     bool didReflowThisFrame = false;
   618     nsChangeHint hint;
   619     aChangeList.ChangeAt(index, frame, content, hint);
   621     NS_ASSERTION(!(hint & nsChangeHint_AllReflowHints) ||
   622                  (hint & nsChangeHint_NeedReflow),
   623                  "Reflow hint bits set without actually asking for a reflow");
   625     // skip any frame that has been destroyed due to a ripple effect
   626     if (frame && !propTable->Get(frame, ChangeListProperty())) {
   627       continue;
   628     }
   630     if (frame && frame->GetContent() != content) {
   631       // XXXbz this is due to image maps messing with the primary frame of
   632       // <area>s.  See bug 135040.  Remove this block once that's fixed.
   633       frame = nullptr;
   634       if (!(hint & nsChangeHint_ReconstructFrame)) {
   635         continue;
   636       }
   637     }
   639     if ((hint & nsChangeHint_AddOrRemoveTransform) && frame &&
   640         !(hint & nsChangeHint_ReconstructFrame)) {
   641       if (NeedToReframeForAddingOrRemovingTransform(frame) ||
   642           frame->GetType() == nsGkAtoms::fieldSetFrame ||
   643           frame->GetContentInsertionFrame() != frame) {
   644         // The frame has positioned children that need to be reparented, or
   645         // it can't easily be converted to/from being an abs-pos container correctly.
   646         NS_UpdateHint(hint, nsChangeHint_ReconstructFrame);
   647       } else {
   648         for (nsIFrame *cont = frame; cont;
   649              cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
   650           // Normally frame construction would set state bits as needed,
   651           // but we're not going to reconstruct the frame so we need to set them.
   652           // It's because we need to set this state on each affected frame
   653           // that we can't coalesce nsChangeHint_AddOrRemoveTransform hints up
   654           // to ancestors (i.e. it can't be an inherited change hint).
   655           if (cont->IsPositioned()) {
   656             // If a transform has been added, we'll be taking this path,
   657             // but we may be taking this path even if a transform has been
   658             // removed. It's OK to add the bit even if it's not needed.
   659             cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
   660             if (!cont->IsAbsoluteContainer() &&
   661                 (cont->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) {
   662               cont->MarkAsAbsoluteContainingBlock();
   663             }
   664           } else {
   665             // Don't remove NS_FRAME_MAY_BE_TRANSFORMED since it may still by
   666             // transformed by other means. It's OK to have the bit even if it's
   667             // not needed.
   668             if (cont->IsAbsoluteContainer()) {
   669               cont->MarkAsNotAbsoluteContainingBlock();
   670             }
   671           }
   672         }
   673       }
   674     }
   675     if (hint & nsChangeHint_ReconstructFrame) {
   676       // If we ever start passing true here, be careful of restyles
   677       // that involve a reframe and animations.  In particular, if the
   678       // restyle we're processing here is an animation restyle, but
   679       // the style resolution we will do for the frame construction
   680       // happens async when we're not in an animation restyle already,
   681       // problems could arise.
   682       FrameConstructor()->RecreateFramesForContent(content, false);
   683     } else {
   684       NS_ASSERTION(frame, "This shouldn't happen");
   686       if ((frame->GetStateBits() & NS_FRAME_SVG_LAYOUT) &&
   687           (frame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
   688         // frame does not maintain overflow rects, so avoid calling
   689         // FinishAndStoreOverflow on it:
   690         hint = NS_SubtractHint(hint,
   691                  NS_CombineHint(nsChangeHint_UpdateOverflow,
   692                    NS_CombineHint(nsChangeHint_ChildrenOnlyTransform,
   693                                   nsChangeHint_UpdatePostTransformOverflow)));
   694       }
   696       if (!(frame->GetStateBits() & NS_FRAME_MAY_BE_TRANSFORMED)) {
   697         // Frame can not be transformed, and thus a change in transform will
   698         // have no effect and we should not use the
   699         // nsChangeHint_UpdatePostTransformOverflow hint.
   700         hint = NS_SubtractHint(hint, nsChangeHint_UpdatePostTransformOverflow);
   701       }
   703       if (hint & nsChangeHint_UpdateEffects) {
   704         for (nsIFrame *cont = frame; cont;
   705              cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
   706           nsSVGEffects::UpdateEffects(cont);
   707         }
   708       }
   709       if (hint & nsChangeHint_NeedReflow) {
   710         StyleChangeReflow(frame, hint);
   711         didReflowThisFrame = true;
   712       }
   713       if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView |
   714                   nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer |
   715                   nsChangeHint_ChildrenOnlyTransform)) {
   716         ApplyRenderingChangeToTree(mPresContext, frame, hint);
   717       }
   718       if ((hint & nsChangeHint_RecomputePosition) && !didReflowThisFrame) {
   719         ActiveLayerTracker::NotifyOffsetRestyle(frame);
   720         // It is possible for this to fall back to a reflow
   721         if (!RecomputePosition(frame)) {
   722           didReflowThisFrame = true;
   723         }
   724       }
   725       NS_ASSERTION(!(hint & nsChangeHint_ChildrenOnlyTransform) ||
   726                    (hint & nsChangeHint_UpdateOverflow),
   727                    "nsChangeHint_UpdateOverflow should be passed too");
   728       if (!didReflowThisFrame &&
   729           (hint & (nsChangeHint_UpdateOverflow |
   730                    nsChangeHint_UpdatePostTransformOverflow))) {
   731         OverflowChangedTracker::ChangeKind changeKind;
   732         if (hint & nsChangeHint_ChildrenOnlyTransform) {
   733           // The overflow areas of the child frames need to be updated:
   734           nsIFrame* hintFrame = GetFrameForChildrenOnlyTransformHint(frame);
   735           nsIFrame* childFrame = hintFrame->GetFirstPrincipalChild();
   736           NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame),
   737                        "SVG frames should not have continuations "
   738                        "or ib-split siblings");
   739           NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(hintFrame),
   740                        "SVG frames should not have continuations "
   741                        "or ib-split siblings");
   742           for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
   743             NS_ABORT_IF_FALSE(childFrame->IsFrameOfType(nsIFrame::eSVG),
   744                               "Not expecting non-SVG children");
   745             // If |childFrame| is dirty or has dirty children, we don't bother
   746             // updating overflows since that will happen when it's reflowed.
   747             if (!(childFrame->GetStateBits() &
   748                   (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
   749               mOverflowChangedTracker.AddFrame(childFrame,
   750                            OverflowChangedTracker::CHILDREN_AND_PARENT_CHANGED);
   751             }
   752             NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(childFrame),
   753                          "SVG frames should not have continuations "
   754                          "or ib-split siblings");
   755             NS_ASSERTION(childFrame->GetParent() == hintFrame,
   756                          "SVG child frame not expected to have different parent");
   757           }
   758         }
   759         // If |frame| is dirty or has dirty children, we don't bother updating
   760         // overflows since that will happen when it's reflowed.
   761         if (!(frame->GetStateBits() &
   762               (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
   763           // If we have both nsChangeHint_UpdateOverflow and
   764           // nsChangeHint_UpdatePostTransformOverflow, CHILDREN_AND_PARENT_CHANGED
   765           // is selected as it is stronger.
   766           if (hint & nsChangeHint_UpdateOverflow) {
   767             changeKind = OverflowChangedTracker::CHILDREN_AND_PARENT_CHANGED;
   768           } else {
   769             changeKind = OverflowChangedTracker::TRANSFORM_CHANGED;
   770           }
   771           for (nsIFrame *cont = frame; cont; cont =
   772                  nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
   773             mOverflowChangedTracker.AddFrame(cont, changeKind);
   774           }
   775         }
   776       }
   777       if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) {
   778         mPresContext->PresShell()->SynthesizeMouseMove(false);
   779         didUpdateCursor = true;
   780       }
   781     }
   782   }
   784   FrameConstructor()->EndUpdate();
   786   // cleanup references and verify the style tree.  Note that the latter needs
   787   // to happen once we've processed the whole list, since until then the tree
   788   // is not in fact in a consistent state.
   789   index = count;
   790   while (0 <= --index) {
   791     const nsStyleChangeData* changeData;
   792     aChangeList.ChangeAt(index, &changeData);
   793     if (changeData->mFrame) {
   794       propTable->Delete(changeData->mFrame, ChangeListProperty());
   795     }
   797 #ifdef DEBUG
   798     // reget frame from content since it may have been regenerated...
   799     if (changeData->mContent) {
   800       if (!nsAnimationManager::ContentOrAncestorHasAnimation(changeData->mContent) &&
   801           !nsTransitionManager::ContentOrAncestorHasTransition(changeData->mContent)) {
   802         nsIFrame* frame = changeData->mContent->GetPrimaryFrame();
   803         if (frame) {
   804           DebugVerifyStyleTree(frame);
   805         }
   806       }
   807     } else if (!changeData->mFrame ||
   808                changeData->mFrame->GetType() != nsGkAtoms::viewportFrame) {
   809       NS_WARNING("Unable to test style tree integrity -- no content node "
   810                  "(and not a viewport frame)");
   811     }
   812 #endif
   813   }
   815   aChangeList.Clear();
   816   return NS_OK;
   817 }
   819 void
   820 RestyleManager::RestyleElement(Element*        aElement,
   821                                nsIFrame*       aPrimaryFrame,
   822                                nsChangeHint    aMinHint,
   823                                RestyleTracker& aRestyleTracker,
   824                                bool            aRestyleDescendants)
   825 {
   826   NS_ASSERTION(aPrimaryFrame == aElement->GetPrimaryFrame(),
   827                "frame/content mismatch");
   828   if (aPrimaryFrame && aPrimaryFrame->GetContent() != aElement) {
   829     // XXXbz this is due to image maps messing with the primary frame pointer
   830     // of <area>s.  See bug 135040.  We can remove this block once that's fixed.
   831     aPrimaryFrame = nullptr;
   832   }
   833   NS_ASSERTION(!aPrimaryFrame || aPrimaryFrame->GetContent() == aElement,
   834                "frame/content mismatch");
   836   // If we're restyling the root element and there are 'rem' units in
   837   // use, handle dynamic changes to the definition of a 'rem' here.
   838   if (mPresContext->UsesRootEMUnits() && aPrimaryFrame) {
   839     nsStyleContext *oldContext = aPrimaryFrame->StyleContext();
   840     if (!oldContext->GetParent()) { // check that we're the root element
   841       nsRefPtr<nsStyleContext> newContext = mPresContext->StyleSet()->
   842         ResolveStyleFor(aElement, nullptr /* == oldContext->GetParent() */);
   843       if (oldContext->StyleFont()->mFont.size !=
   844           newContext->StyleFont()->mFont.size) {
   845         // The basis for 'rem' units has changed.
   846         newContext = nullptr;
   847         DoRebuildAllStyleData(aRestyleTracker, nsChangeHint(0));
   848         if (aMinHint == 0) {
   849           return;
   850         }
   851         aPrimaryFrame = aElement->GetPrimaryFrame();
   852       }
   853     }
   854   }
   856   if (aMinHint & nsChangeHint_ReconstructFrame) {
   857     FrameConstructor()->RecreateFramesForContent(aElement, false);
   858   } else if (aPrimaryFrame) {
   859     nsStyleChangeList changeList;
   860     ComputeStyleChangeFor(aPrimaryFrame, &changeList, aMinHint,
   861                           aRestyleTracker, aRestyleDescendants);
   862     ProcessRestyledFrames(changeList);
   863   } else {
   864     // no frames, reconstruct for content
   865     FrameConstructor()->MaybeRecreateFramesForElement(aElement);
   866   }
   867 }
   869 static inline dom::Element*
   870 ElementForStyleContext(nsIContent* aParentContent,
   871                        nsIFrame* aFrame,
   872                        nsCSSPseudoElements::Type aPseudoType);
   874 // Forwarded nsIDocumentObserver method, to handle restyling (and
   875 // passing the notification to the frame).
   876 nsresult
   877 RestyleManager::ContentStateChanged(nsIContent* aContent,
   878                                     EventStates aStateMask)
   879 {
   880   // XXXbz it would be good if this function only took Elements, but
   881   // we'd have to make ESM guarantee that usefully.
   882   if (!aContent->IsElement()) {
   883     return NS_OK;
   884   }
   886   Element* aElement = aContent->AsElement();
   888   nsStyleSet* styleSet = mPresContext->StyleSet();
   889   NS_ASSERTION(styleSet, "couldn't get style set");
   891   nsChangeHint hint = NS_STYLE_HINT_NONE;
   892   // Any change to a content state that affects which frames we construct
   893   // must lead to a frame reconstruct here if we already have a frame.
   894   // Note that we never decide through non-CSS means to not create frames
   895   // based on content states, so if we already don't have a frame we don't
   896   // need to force a reframe -- if it's needed, the HasStateDependentStyle
   897   // call will handle things.
   898   nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
   899   nsCSSPseudoElements::Type pseudoType =
   900     nsCSSPseudoElements::ePseudo_NotPseudoElement;
   901   if (primaryFrame) {
   902     // If it's generated content, ignore LOADING/etc state changes on it.
   903     if (!primaryFrame->IsGeneratedContentFrame() &&
   904         aStateMask.HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
   905                                          NS_EVENT_STATE_USERDISABLED |
   906                                          NS_EVENT_STATE_SUPPRESSED |
   907                                          NS_EVENT_STATE_LOADING)) {
   908       hint = nsChangeHint_ReconstructFrame;
   909     } else {
   910       uint8_t app = primaryFrame->StyleDisplay()->mAppearance;
   911       if (app) {
   912         nsITheme *theme = mPresContext->GetTheme();
   913         if (theme && theme->ThemeSupportsWidget(mPresContext,
   914                                                 primaryFrame, app)) {
   915           bool repaint = false;
   916           theme->WidgetStateChanged(primaryFrame, app, nullptr, &repaint);
   917           if (repaint) {
   918             NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
   919           }
   920         }
   921       }
   922     }
   924     pseudoType = primaryFrame->StyleContext()->GetPseudoType();
   926     primaryFrame->ContentStatesChanged(aStateMask);
   927   }
   930   nsRestyleHint rshint;
   932   if (pseudoType >= nsCSSPseudoElements::ePseudo_PseudoElementCount) {
   933     rshint = styleSet->HasStateDependentStyle(mPresContext, aElement,
   934                                               aStateMask);
   935   } else if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(
   936                                                                   pseudoType)) {
   937     // If aElement is a pseudo-element, we want to check to see whether there
   938     // are any state-dependent rules applying to that pseudo.
   939     Element* ancestor = ElementForStyleContext(nullptr, primaryFrame,
   940                                                pseudoType);
   941     rshint = styleSet->HasStateDependentStyle(mPresContext, ancestor,
   942                                               pseudoType, aElement,
   943                                               aStateMask);
   944   } else {
   945     rshint = nsRestyleHint(0);
   946   }
   948   if (aStateMask.HasState(NS_EVENT_STATE_HOVER) && rshint != 0) {
   949     ++mHoverGeneration;
   950   }
   952   if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
   953     // Exposing information to the page about whether the link is
   954     // visited or not isn't really something we can worry about here.
   955     // FIXME: We could probably do this a bit better.
   956     NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
   957   }
   959   PostRestyleEvent(aElement, rshint, hint);
   960   return NS_OK;
   961 }
   963 // Forwarded nsIMutationObserver method, to handle restyling.
   964 void
   965 RestyleManager::AttributeWillChange(Element* aElement,
   966                                     int32_t aNameSpaceID,
   967                                     nsIAtom* aAttribute,
   968                                     int32_t aModType)
   969 {
   970   nsRestyleHint rshint =
   971     mPresContext->StyleSet()->HasAttributeDependentStyle(mPresContext,
   972                                                          aElement,
   973                                                          aAttribute,
   974                                                          aModType,
   975                                                          false);
   976   PostRestyleEvent(aElement, rshint, NS_STYLE_HINT_NONE);
   977 }
   979 // Forwarded nsIMutationObserver method, to handle restyling (and
   980 // passing the notification to the frame).
   981 void
   982 RestyleManager::AttributeChanged(Element* aElement,
   983                                  int32_t aNameSpaceID,
   984                                  nsIAtom* aAttribute,
   985                                  int32_t aModType)
   986 {
   987   // Hold onto the PresShell to prevent ourselves from being destroyed.
   988   // XXXbz how, exactly, would this attribute change cause us to be
   989   // destroyed from inside this function?
   990   nsCOMPtr<nsIPresShell> shell = mPresContext->GetPresShell();
   992   // Get the frame associated with the content which is the highest in the frame tree
   993   nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
   995 #if 0
   996   NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
   997      ("RestyleManager::AttributeChanged: content=%p[%s] frame=%p",
   998       aContent, ContentTag(aElement, 0), frame));
   999 #endif
  1001   // the style tag has its own interpretation based on aHint
  1002   nsChangeHint hint = aElement->GetAttributeChangeHint(aAttribute, aModType);
  1004   bool reframe = (hint & nsChangeHint_ReconstructFrame) != 0;
  1006 #ifdef MOZ_XUL
  1007   // The following listbox widget trap prevents offscreen listbox widget
  1008   // content from being removed and re-inserted (which is what would
  1009   // happen otherwise).
  1010   if (!primaryFrame && !reframe) {
  1011     int32_t namespaceID;
  1012     nsIAtom* tag = mPresContext->Document()->BindingManager()->
  1013                      ResolveTag(aElement, &namespaceID);
  1015     if (namespaceID == kNameSpaceID_XUL &&
  1016         (tag == nsGkAtoms::listitem ||
  1017          tag == nsGkAtoms::listcell))
  1018       return;
  1021   if (aAttribute == nsGkAtoms::tooltiptext ||
  1022       aAttribute == nsGkAtoms::tooltip)
  1024     nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresContext->GetPresShell());
  1025     if (rootBox) {
  1026       if (aModType == nsIDOMMutationEvent::REMOVAL)
  1027         rootBox->RemoveTooltipSupport(aElement);
  1028       if (aModType == nsIDOMMutationEvent::ADDITION)
  1029         rootBox->AddTooltipSupport(aElement);
  1033 #endif // MOZ_XUL
  1035   if (primaryFrame) {
  1036     // See if we have appearance information for a theme.
  1037     const nsStyleDisplay* disp = primaryFrame->StyleDisplay();
  1038     if (disp->mAppearance) {
  1039       nsITheme *theme = mPresContext->GetTheme();
  1040       if (theme && theme->ThemeSupportsWidget(mPresContext, primaryFrame, disp->mAppearance)) {
  1041         bool repaint = false;
  1042         theme->WidgetStateChanged(primaryFrame, disp->mAppearance, aAttribute, &repaint);
  1043         if (repaint)
  1044           NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
  1048     // let the frame deal with it now, so we don't have to deal later
  1049     primaryFrame->AttributeChanged(aNameSpaceID, aAttribute, aModType);
  1050     // XXXwaterson should probably check for IB split siblings
  1051     // here, and propagate the AttributeChanged notification to
  1052     // them, as well. Currently, inline frames don't do anything on
  1053     // this notification, so it's not that big a deal.
  1056   // See if we can optimize away the style re-resolution -- must be called after
  1057   // the frame's AttributeChanged() in case it does something that affects the style
  1058   nsRestyleHint rshint =
  1059     mPresContext->StyleSet()->HasAttributeDependentStyle(mPresContext,
  1060                                                          aElement,
  1061                                                          aAttribute,
  1062                                                          aModType,
  1063                                                          true);
  1065   PostRestyleEvent(aElement, rshint, hint);
  1068 void
  1069 RestyleManager::RestyleForEmptyChange(Element* aContainer)
  1071   // In some cases (:empty + E, :empty ~ E), a change if the content of
  1072   // an element requires restyling its parent's siblings.
  1073   nsRestyleHint hint = eRestyle_Subtree;
  1074   nsIContent* grandparent = aContainer->GetParent();
  1075   if (grandparent &&
  1076       (grandparent->GetFlags() & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS)) {
  1077     hint = nsRestyleHint(hint | eRestyle_LaterSiblings);
  1079   PostRestyleEvent(aContainer, hint, NS_STYLE_HINT_NONE);
  1082 void
  1083 RestyleManager::RestyleForAppend(Element* aContainer,
  1084                                  nsIContent* aFirstNewContent)
  1086   NS_ASSERTION(aContainer, "must have container for append");
  1087 #ifdef DEBUG
  1089     for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
  1090       NS_ASSERTION(!cur->IsRootOfAnonymousSubtree(),
  1091                    "anonymous nodes should not be in child lists");
  1094 #endif
  1095   uint32_t selectorFlags =
  1096     aContainer->GetFlags() & (NODE_ALL_SELECTOR_FLAGS &
  1097                               ~NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
  1098   if (selectorFlags == 0)
  1099     return;
  1101   if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
  1102     // see whether we need to restyle the container
  1103     bool wasEmpty = true; // :empty or :-moz-only-whitespace
  1104     for (nsIContent* cur = aContainer->GetFirstChild();
  1105          cur != aFirstNewContent;
  1106          cur = cur->GetNextSibling()) {
  1107       // We don't know whether we're testing :empty or :-moz-only-whitespace,
  1108       // so be conservative and assume :-moz-only-whitespace (i.e., make
  1109       // IsSignificantChild less likely to be true, and thus make us more
  1110       // likely to restyle).
  1111       if (nsStyleUtil::IsSignificantChild(cur, true, false)) {
  1112         wasEmpty = false;
  1113         break;
  1116     if (wasEmpty) {
  1117       RestyleForEmptyChange(aContainer);
  1118       return;
  1122   if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
  1123     PostRestyleEvent(aContainer, eRestyle_Subtree, NS_STYLE_HINT_NONE);
  1124     // Restyling the container is the most we can do here, so we're done.
  1125     return;
  1128   if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
  1129     // restyle the last element child before this node
  1130     for (nsIContent* cur = aFirstNewContent->GetPreviousSibling();
  1131          cur;
  1132          cur = cur->GetPreviousSibling()) {
  1133       if (cur->IsElement()) {
  1134         PostRestyleEvent(cur->AsElement(), eRestyle_Subtree, NS_STYLE_HINT_NONE);
  1135         break;
  1141 // Needed since we can't use PostRestyleEvent on non-elements (with
  1142 // eRestyle_LaterSiblings or nsRestyleHint(eRestyle_Subtree |
  1143 // eRestyle_LaterSiblings) as appropriate).
  1144 static void
  1145 RestyleSiblingsStartingWith(RestyleManager* aRestyleManager,
  1146                             nsIContent* aStartingSibling /* may be null */)
  1148   for (nsIContent *sibling = aStartingSibling; sibling;
  1149        sibling = sibling->GetNextSibling()) {
  1150     if (sibling->IsElement()) {
  1151       aRestyleManager->
  1152         PostRestyleEvent(sibling->AsElement(),
  1153                          nsRestyleHint(eRestyle_Subtree | eRestyle_LaterSiblings),
  1154                          NS_STYLE_HINT_NONE);
  1155       break;
  1160 // Restyling for a ContentInserted or CharacterDataChanged notification.
  1161 // This could be used for ContentRemoved as well if we got the
  1162 // notification before the removal happened (and sometimes
  1163 // CharacterDataChanged is more like a removal than an addition).
  1164 // The comments are written and variables are named in terms of it being
  1165 // a ContentInserted notification.
  1166 void
  1167 RestyleManager::RestyleForInsertOrChange(Element* aContainer,
  1168                                          nsIContent* aChild)
  1170   NS_ASSERTION(!aChild->IsRootOfAnonymousSubtree(),
  1171                "anonymous nodes should not be in child lists");
  1172   uint32_t selectorFlags =
  1173     aContainer ? (aContainer->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
  1174   if (selectorFlags == 0)
  1175     return;
  1177   if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
  1178     // see whether we need to restyle the container
  1179     bool wasEmpty = true; // :empty or :-moz-only-whitespace
  1180     for (nsIContent* child = aContainer->GetFirstChild();
  1181          child;
  1182          child = child->GetNextSibling()) {
  1183       if (child == aChild)
  1184         continue;
  1185       // We don't know whether we're testing :empty or :-moz-only-whitespace,
  1186       // so be conservative and assume :-moz-only-whitespace (i.e., make
  1187       // IsSignificantChild less likely to be true, and thus make us more
  1188       // likely to restyle).
  1189       if (nsStyleUtil::IsSignificantChild(child, true, false)) {
  1190         wasEmpty = false;
  1191         break;
  1194     if (wasEmpty) {
  1195       RestyleForEmptyChange(aContainer);
  1196       return;
  1200   if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
  1201     PostRestyleEvent(aContainer, eRestyle_Subtree, NS_STYLE_HINT_NONE);
  1202     // Restyling the container is the most we can do here, so we're done.
  1203     return;
  1206   if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
  1207     // Restyle all later siblings.
  1208     RestyleSiblingsStartingWith(this, aChild->GetNextSibling());
  1211   if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
  1212     // restyle the previously-first element child if it is after this node
  1213     bool passedChild = false;
  1214     for (nsIContent* content = aContainer->GetFirstChild();
  1215          content;
  1216          content = content->GetNextSibling()) {
  1217       if (content == aChild) {
  1218         passedChild = true;
  1219         continue;
  1221       if (content->IsElement()) {
  1222         if (passedChild) {
  1223           PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
  1224                            NS_STYLE_HINT_NONE);
  1226         break;
  1229     // restyle the previously-last element child if it is before this node
  1230     passedChild = false;
  1231     for (nsIContent* content = aContainer->GetLastChild();
  1232          content;
  1233          content = content->GetPreviousSibling()) {
  1234       if (content == aChild) {
  1235         passedChild = true;
  1236         continue;
  1238       if (content->IsElement()) {
  1239         if (passedChild) {
  1240           PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
  1241                            NS_STYLE_HINT_NONE);
  1243         break;
  1249 void
  1250 RestyleManager::RestyleForRemove(Element* aContainer,
  1251                                  nsIContent* aOldChild,
  1252                                  nsIContent* aFollowingSibling)
  1254   if (aOldChild->IsRootOfAnonymousSubtree()) {
  1255     // This should be an assert, but this is called incorrectly in
  1256     // nsHTMLEditor::DeleteRefToAnonymousNode and the assertions were clogging
  1257     // up the logs.  Make it an assert again when that's fixed.
  1258     NS_WARNING("anonymous nodes should not be in child lists (bug 439258)");
  1260   uint32_t selectorFlags =
  1261     aContainer ? (aContainer->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
  1262   if (selectorFlags == 0)
  1263     return;
  1265   if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
  1266     // see whether we need to restyle the container
  1267     bool isEmpty = true; // :empty or :-moz-only-whitespace
  1268     for (nsIContent* child = aContainer->GetFirstChild();
  1269          child;
  1270          child = child->GetNextSibling()) {
  1271       // We don't know whether we're testing :empty or :-moz-only-whitespace,
  1272       // so be conservative and assume :-moz-only-whitespace (i.e., make
  1273       // IsSignificantChild less likely to be true, and thus make us more
  1274       // likely to restyle).
  1275       if (nsStyleUtil::IsSignificantChild(child, true, false)) {
  1276         isEmpty = false;
  1277         break;
  1280     if (isEmpty) {
  1281       RestyleForEmptyChange(aContainer);
  1282       return;
  1286   if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
  1287     PostRestyleEvent(aContainer, eRestyle_Subtree, NS_STYLE_HINT_NONE);
  1288     // Restyling the container is the most we can do here, so we're done.
  1289     return;
  1292   if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
  1293     // Restyle all later siblings.
  1294     RestyleSiblingsStartingWith(this, aFollowingSibling);
  1297   if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
  1298     // restyle the now-first element child if it was after aOldChild
  1299     bool reachedFollowingSibling = false;
  1300     for (nsIContent* content = aContainer->GetFirstChild();
  1301          content;
  1302          content = content->GetNextSibling()) {
  1303       if (content == aFollowingSibling) {
  1304         reachedFollowingSibling = true;
  1305         // do NOT continue here; we might want to restyle this node
  1307       if (content->IsElement()) {
  1308         if (reachedFollowingSibling) {
  1309           PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
  1310                            NS_STYLE_HINT_NONE);
  1312         break;
  1315     // restyle the now-last element child if it was before aOldChild
  1316     reachedFollowingSibling = (aFollowingSibling == nullptr);
  1317     for (nsIContent* content = aContainer->GetLastChild();
  1318          content;
  1319          content = content->GetPreviousSibling()) {
  1320       if (content->IsElement()) {
  1321         if (reachedFollowingSibling) {
  1322           PostRestyleEvent(content->AsElement(), eRestyle_Subtree, NS_STYLE_HINT_NONE);
  1324         break;
  1326       if (content == aFollowingSibling) {
  1327         reachedFollowingSibling = true;
  1333 void
  1334 RestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint)
  1336   NS_ASSERTION(!(aExtraHint & nsChangeHint_ReconstructFrame),
  1337                "Should not reconstruct the root of the frame tree.  "
  1338                "Use ReconstructDocElementHierarchy instead.");
  1340   mRebuildAllStyleData = false;
  1341   NS_UpdateHint(aExtraHint, mRebuildAllExtraHint);
  1342   mRebuildAllExtraHint = nsChangeHint(0);
  1344   nsIPresShell* presShell = mPresContext->GetPresShell();
  1345   if (!presShell || !presShell->GetRootFrame())
  1346     return;
  1348   // Make sure that the viewmanager will outlive the presshell
  1349   nsRefPtr<nsViewManager> vm = presShell->GetViewManager();
  1351   // Processing the style changes could cause a flush that propagates to
  1352   // the parent frame and thus destroys the pres shell.
  1353   nsCOMPtr<nsIPresShell> kungFuDeathGrip(presShell);
  1355   // We may reconstruct frames below and hence process anything that is in the
  1356   // tree. We don't want to get notified to process those items again after.
  1357   presShell->GetDocument()->FlushPendingNotifications(Flush_ContentAndNotify);
  1359   nsAutoScriptBlocker scriptBlocker;
  1361   mPresContext->SetProcessingRestyles(true);
  1363   DoRebuildAllStyleData(mPendingRestyles, aExtraHint);
  1365   mPresContext->SetProcessingRestyles(false);
  1367   // Make sure that we process any pending animation restyles from the
  1368   // above style change.  Note that we can *almost* implement the above
  1369   // by just posting a style change -- except we really need to restyle
  1370   // the root frame rather than the root element's primary frame.
  1371   ProcessPendingRestyles();
  1374 void
  1375 RestyleManager::DoRebuildAllStyleData(RestyleTracker& aRestyleTracker,
  1376                                       nsChangeHint aExtraHint)
  1378   // Tell the style set to get the old rule tree out of the way
  1379   // so we can recalculate while maintaining rule tree immutability
  1380   nsresult rv = mPresContext->StyleSet()->BeginReconstruct();
  1381   if (NS_FAILED(rv)) {
  1382     return;
  1385   // Recalculate all of the style contexts for the document
  1386   // Note that we can ignore the return value of ComputeStyleChangeFor
  1387   // because we never need to reframe the root frame
  1388   // XXX This could be made faster by not rerunning rule matching
  1389   // (but note that nsPresShell::SetPreferenceStyleRules currently depends
  1390   // on us re-running rule matching here
  1391   nsStyleChangeList changeList;
  1392   // XXX Does it matter that we're passing aExtraHint to the real root
  1393   // frame and not the root node's primary frame?
  1394   // Note: The restyle tracker we pass in here doesn't matter.
  1395   ComputeStyleChangeFor(mPresContext->PresShell()->GetRootFrame(),
  1396                         &changeList, aExtraHint,
  1397                         aRestyleTracker, true);
  1398   // Process the required changes
  1399   ProcessRestyledFrames(changeList);
  1400   FlushOverflowChangedTracker();
  1402   // Tell the style set it's safe to destroy the old rule tree.  We
  1403   // must do this after the ProcessRestyledFrames call in case the
  1404   // change list has frame reconstructs in it (since frames to be
  1405   // reconstructed will still have their old style context pointers
  1406   // until they are destroyed).
  1407   mPresContext->StyleSet()->EndReconstruct();
  1410 void
  1411 RestyleManager::ProcessPendingRestyles()
  1413   NS_PRECONDITION(mPresContext->Document(), "No document?  Pshaw!");
  1414   NS_PRECONDITION(!nsContentUtils::IsSafeToRunScript(),
  1415                   "Missing a script blocker!");
  1417   // First do any queued-up frame creation.  (We should really
  1418   // merge this into the rest of the process, though; see bug 827239.)
  1419   mPresContext->FrameConstructor()->CreateNeededFrames();
  1421   // Process non-animation restyles...
  1422   NS_ABORT_IF_FALSE(!mPresContext->IsProcessingRestyles(),
  1423                     "Nesting calls to ProcessPendingRestyles?");
  1424   mPresContext->SetProcessingRestyles(true);
  1426   // Before we process any restyles, we need to ensure that style
  1427   // resulting from any throttled animations (animations that we're
  1428   // running entirely on the compositor thread) is up-to-date, so that
  1429   // if any style changes we cause trigger transitions, we have the
  1430   // correct old style for starting the transition.
  1431   if (nsLayoutUtils::AreAsyncAnimationsEnabled() &&
  1432       mPendingRestyles.Count() > 0) {
  1433     ++mAnimationGeneration;
  1434     mPresContext->TransitionManager()->UpdateAllThrottledStyles();
  1437   mPendingRestyles.ProcessRestyles();
  1439 #ifdef DEBUG
  1440   uint32_t oldPendingRestyleCount = mPendingRestyles.Count();
  1441 #endif
  1443   // ...and then process animation restyles.  This needs to happen
  1444   // second because we need to start animations that resulted from the
  1445   // first set of restyles (e.g., CSS transitions with negative
  1446   // transition-delay), and because we need to immediately
  1447   // restyle-with-animation any just-restyled elements that are
  1448   // mid-transition (since processing the non-animation restyle ignores
  1449   // the running transition so it can check for a new change on the same
  1450   // property, and then posts an immediate animation style change).
  1451   mPresContext->SetProcessingAnimationStyleChange(true);
  1452   mPendingAnimationRestyles.ProcessRestyles();
  1453   mPresContext->SetProcessingAnimationStyleChange(false);
  1455   mPresContext->SetProcessingRestyles(false);
  1456   NS_POSTCONDITION(mPendingRestyles.Count() == oldPendingRestyleCount,
  1457                    "We should not have posted new non-animation restyles while "
  1458                    "processing animation restyles");
  1460   if (mRebuildAllStyleData) {
  1461     // We probably wasted a lot of work up above, but this seems safest
  1462     // and it should be rarely used.
  1463     // This might add us as a refresh observer again; that's ok.
  1464     RebuildAllStyleData(nsChangeHint(0));
  1468 void
  1469 RestyleManager::BeginProcessingRestyles()
  1471   // Make sure to not rebuild quote or counter lists while we're
  1472   // processing restyles
  1473   mPresContext->FrameConstructor()->BeginUpdate();
  1475   mInStyleRefresh = true;
  1478 void
  1479 RestyleManager::EndProcessingRestyles()
  1481   FlushOverflowChangedTracker();
  1483   // Set mInStyleRefresh to false now, since the EndUpdate call might
  1484   // add more restyles.
  1485   mInStyleRefresh = false;
  1487   mPresContext->FrameConstructor()->EndUpdate();
  1489 #ifdef DEBUG
  1490   mPresContext->PresShell()->VerifyStyleTree();
  1491 #endif
  1494 void
  1495 RestyleManager::PostRestyleEventCommon(Element* aElement,
  1496                                        nsRestyleHint aRestyleHint,
  1497                                        nsChangeHint aMinChangeHint,
  1498                                        bool aForAnimation)
  1500   if (MOZ_UNLIKELY(mPresContext->PresShell()->IsDestroying())) {
  1501     return;
  1504   if (aRestyleHint == 0 && !aMinChangeHint) {
  1505     // Nothing to do here
  1506     return;
  1509   RestyleTracker& tracker =
  1510     aForAnimation ? mPendingAnimationRestyles : mPendingRestyles;
  1511   tracker.AddPendingRestyle(aElement, aRestyleHint, aMinChangeHint);
  1513   PostRestyleEventInternal(false);
  1516 void
  1517 RestyleManager::PostRestyleEventInternal(bool aForLazyConstruction)
  1519   // Make sure we're not in a style refresh; if we are, we still have
  1520   // a call to ProcessPendingRestyles coming and there's no need to
  1521   // add ourselves as a refresh observer until then.
  1522   bool inRefresh = !aForLazyConstruction && mInStyleRefresh;
  1523   nsIPresShell* presShell = mPresContext->PresShell();
  1524   if (!mObservingRefreshDriver && !inRefresh) {
  1525     mObservingRefreshDriver = mPresContext->RefreshDriver()->
  1526       AddStyleFlushObserver(presShell);
  1529   // Unconditionally flag our document as needing a flush.  The other
  1530   // option here would be a dedicated boolean to track whether we need
  1531   // to do so (set here and unset in ProcessPendingRestyles).
  1532   presShell->GetDocument()->SetNeedStyleFlush();
  1535 void
  1536 RestyleManager::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint)
  1538   NS_ASSERTION(!(aExtraHint & nsChangeHint_ReconstructFrame),
  1539                "Should not reconstruct the root of the frame tree.  "
  1540                "Use ReconstructDocElementHierarchy instead.");
  1542   mRebuildAllStyleData = true;
  1543   NS_UpdateHint(mRebuildAllExtraHint, aExtraHint);
  1545   // Get a restyle event posted if necessary
  1546   PostRestyleEventInternal(false);
  1549 #ifdef DEBUG
  1550 static void
  1551 DumpContext(nsIFrame* aFrame, nsStyleContext* aContext)
  1553   if (aFrame) {
  1554     fputs("frame: ", stdout);
  1555     nsAutoString  name;
  1556     aFrame->GetFrameName(name);
  1557     fputs(NS_LossyConvertUTF16toASCII(name).get(), stdout);
  1558     fprintf(stdout, " (%p)", static_cast<void*>(aFrame));
  1560   if (aContext) {
  1561     fprintf(stdout, " style: %p ", static_cast<void*>(aContext));
  1563     nsIAtom* pseudoTag = aContext->GetPseudo();
  1564     if (pseudoTag) {
  1565       nsAutoString  buffer;
  1566       pseudoTag->ToString(buffer);
  1567       fputs(NS_LossyConvertUTF16toASCII(buffer).get(), stdout);
  1568       fputs(" ", stdout);
  1570     fputs("{}\n", stdout);
  1574 static void
  1575 VerifySameTree(nsStyleContext* aContext1, nsStyleContext* aContext2)
  1577   nsStyleContext* top1 = aContext1;
  1578   nsStyleContext* top2 = aContext2;
  1579   nsStyleContext* parent;
  1580   for (;;) {
  1581     parent = top1->GetParent();
  1582     if (!parent)
  1583       break;
  1584     top1 = parent;
  1586   for (;;) {
  1587     parent = top2->GetParent();
  1588     if (!parent)
  1589       break;
  1590     top2 = parent;
  1592   NS_ASSERTION(top1 == top2,
  1593                "Style contexts are not in the same style context tree");
  1596 static void
  1597 VerifyContextParent(nsPresContext* aPresContext, nsIFrame* aFrame,
  1598                     nsStyleContext* aContext, nsStyleContext* aParentContext)
  1600   // get the contexts not provided
  1601   if (!aContext) {
  1602     aContext = aFrame->StyleContext();
  1605   if (!aParentContext) {
  1606     // Get the correct parent context from the frame
  1607     //  - if the frame is a placeholder, we get the out of flow frame's context
  1608     //    as the parent context instead of asking the frame
  1610     // get the parent context from the frame (indirectly)
  1611     nsIFrame* providerFrame = aFrame->GetParentStyleContextFrame();
  1612     if (providerFrame)
  1613       aParentContext = providerFrame->StyleContext();
  1614     // aParentContext could still be null
  1617   NS_ASSERTION(aContext, "Failure to get required contexts");
  1618   nsStyleContext* actualParentContext = aContext->GetParent();
  1620   if (aParentContext) {
  1621     if (aParentContext != actualParentContext) {
  1622       DumpContext(aFrame, aContext);
  1623       if (aContext == aParentContext) {
  1624         NS_ERROR("Using parent's style context");
  1626       else {
  1627         NS_ERROR("Wrong parent style context");
  1628         fputs("Wrong parent style context: ", stdout);
  1629         DumpContext(nullptr, actualParentContext);
  1630         fputs("should be using: ", stdout);
  1631         DumpContext(nullptr, aParentContext);
  1632         VerifySameTree(actualParentContext, aParentContext);
  1633         fputs("\n", stdout);
  1638   else {
  1639     if (actualParentContext) {
  1640       NS_ERROR("Have parent context and shouldn't");
  1641       DumpContext(aFrame, aContext);
  1642       fputs("Has parent context: ", stdout);
  1643       DumpContext(nullptr, actualParentContext);
  1644       fputs("Should be null\n\n", stdout);
  1648   nsStyleContext* childStyleIfVisited = aContext->GetStyleIfVisited();
  1649   // Either childStyleIfVisited has aContext->GetParent()->GetStyleIfVisited()
  1650   // as the parent or it has a different rulenode from aContext _and_ has
  1651   // aContext->GetParent() as the parent.
  1652   if (childStyleIfVisited &&
  1653       !((childStyleIfVisited->RuleNode() != aContext->RuleNode() &&
  1654          childStyleIfVisited->GetParent() == aContext->GetParent()) ||
  1655         childStyleIfVisited->GetParent() ==
  1656           aContext->GetParent()->GetStyleIfVisited())) {
  1657     NS_ERROR("Visited style has wrong parent");
  1658     DumpContext(aFrame, aContext);
  1659     fputs("\n", stdout);
  1663 static void
  1664 VerifyStyleTree(nsPresContext* aPresContext, nsIFrame* aFrame,
  1665                 nsStyleContext* aParentContext)
  1667   nsStyleContext*  context = aFrame->StyleContext();
  1668   VerifyContextParent(aPresContext, aFrame, context, nullptr);
  1670   nsIFrame::ChildListIterator lists(aFrame);
  1671   for (; !lists.IsDone(); lists.Next()) {
  1672     nsFrameList::Enumerator childFrames(lists.CurrentList());
  1673     for (; !childFrames.AtEnd(); childFrames.Next()) {
  1674       nsIFrame* child = childFrames.get();
  1675       if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
  1676         // only do frames that are in flow
  1677         if (nsGkAtoms::placeholderFrame == child->GetType()) {
  1678           // placeholder: first recurse and verify the out of flow frame,
  1679           // then verify the placeholder's context
  1680           nsIFrame* outOfFlowFrame =
  1681             nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
  1683           // recurse to out of flow frame, letting the parent context get resolved
  1684           do {
  1685             VerifyStyleTree(aPresContext, outOfFlowFrame, nullptr);
  1686           } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
  1688           // verify placeholder using the parent frame's context as
  1689           // parent context
  1690           VerifyContextParent(aPresContext, child, nullptr, nullptr);
  1692         else { // regular frame
  1693           VerifyStyleTree(aPresContext, child, nullptr);
  1699   // do additional contexts
  1700   int32_t contextIndex = 0;
  1701   for (nsStyleContext* extraContext;
  1702        (extraContext = aFrame->GetAdditionalStyleContext(contextIndex));
  1703        ++contextIndex) {
  1704     VerifyContextParent(aPresContext, aFrame, extraContext, context);
  1708 void
  1709 RestyleManager::DebugVerifyStyleTree(nsIFrame* aFrame)
  1711   if (aFrame) {
  1712     nsStyleContext* context = aFrame->StyleContext();
  1713     nsStyleContext* parentContext = context->GetParent();
  1714     VerifyStyleTree(mPresContext, aFrame, parentContext);
  1718 #endif // DEBUG
  1720 // aContent must be the content for the frame in question, which may be
  1721 // :before/:after content
  1722 static void
  1723 TryStartingTransition(nsPresContext *aPresContext, nsIContent *aContent,
  1724                       nsStyleContext *aOldStyleContext,
  1725                       nsRefPtr<nsStyleContext> *aNewStyleContext /* inout */)
  1727   if (!aContent || !aContent->IsElement()) {
  1728     return;
  1731   // Notify the transition manager, and if it starts a transition,
  1732   // it will give us back a transition-covering style rule which
  1733   // we'll use to get *another* style context.  We want to ignore
  1734   // any already-running transitions, but cover up any that we're
  1735   // currently starting with their start value so we don't start
  1736   // them again for descendants that inherit that value.
  1737   nsCOMPtr<nsIStyleRule> coverRule =
  1738     aPresContext->TransitionManager()->StyleContextChanged(
  1739       aContent->AsElement(), aOldStyleContext, *aNewStyleContext);
  1740   if (coverRule) {
  1741     nsCOMArray<nsIStyleRule> rules;
  1742     rules.AppendObject(coverRule);
  1743     *aNewStyleContext = aPresContext->StyleSet()->
  1744                           ResolveStyleByAddingRules(*aNewStyleContext, rules);
  1748 static inline dom::Element*
  1749 ElementForStyleContext(nsIContent* aParentContent,
  1750                        nsIFrame* aFrame,
  1751                        nsCSSPseudoElements::Type aPseudoType)
  1753   // We don't expect XUL tree stuff here.
  1754   NS_PRECONDITION(aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ||
  1755                   aPseudoType == nsCSSPseudoElements::ePseudo_AnonBox ||
  1756                   aPseudoType < nsCSSPseudoElements::ePseudo_PseudoElementCount,
  1757                   "Unexpected pseudo");
  1758   // XXX see the comments about the various element confusion in
  1759   // ElementRestyler::Restyle.
  1760   if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
  1761     return aFrame->GetContent()->AsElement();
  1764   if (aPseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
  1765     return nullptr;
  1768   if (aPseudoType == nsCSSPseudoElements::ePseudo_firstLetter) {
  1769     NS_ASSERTION(aFrame->GetType() == nsGkAtoms::letterFrame,
  1770                  "firstLetter pseudoTag without a nsFirstLetterFrame");
  1771     nsBlockFrame* block = nsBlockFrame::GetNearestAncestorBlock(aFrame);
  1772     return block->GetContent()->AsElement();
  1775   if (aPseudoType == nsCSSPseudoElements::ePseudo_mozColorSwatch) {
  1776     MOZ_ASSERT(aFrame->GetParent() &&
  1777                aFrame->GetParent()->GetParent(),
  1778                "Color swatch frame should have a parent & grandparent");
  1780     nsIFrame* grandparentFrame = aFrame->GetParent()->GetParent();
  1781     MOZ_ASSERT(grandparentFrame->GetType() == nsGkAtoms::colorControlFrame,
  1782                "Color swatch's grandparent should be nsColorControlFrame");
  1784     return grandparentFrame->GetContent()->AsElement();
  1787   if (aPseudoType == nsCSSPseudoElements::ePseudo_mozNumberText ||
  1788       aPseudoType == nsCSSPseudoElements::ePseudo_mozNumberWrapper ||
  1789       aPseudoType == nsCSSPseudoElements::ePseudo_mozNumberSpinBox ||
  1790       aPseudoType == nsCSSPseudoElements::ePseudo_mozNumberSpinUp ||
  1791       aPseudoType == nsCSSPseudoElements::ePseudo_mozNumberSpinDown) {
  1792     // Get content for nearest nsNumberControlFrame:
  1793     nsIFrame* f = aFrame->GetParent();
  1794     MOZ_ASSERT(f);
  1795     while (f->GetType() != nsGkAtoms::numberControlFrame) {
  1796       f = f->GetParent();
  1797       MOZ_ASSERT(f);
  1799     return f->GetContent()->AsElement();
  1802   if (aParentContent) {
  1803     return aParentContent->AsElement();
  1806   MOZ_ASSERT(aFrame->GetContent()->GetParent(),
  1807              "should not have got here for the root element");
  1808   return aFrame->GetContent()->GetParent()->AsElement();
  1811 /**
  1812  * FIXME: Temporary.  Should merge with following function.
  1813  */
  1814 static nsIFrame*
  1815 GetPrevContinuationWithPossiblySameStyle(nsIFrame* aFrame)
  1817   // Account for {ib} splits when looking for "prevContinuation".  In
  1818   // particular, for the first-continuation of a part of an {ib} split
  1819   // we want to use the previous ib-split sibling of the previous
  1820   // ib-split sibling of aFrame, which should have the same style
  1821   // context as aFrame itself.  In particular, if aFrame is the first
  1822   // continuation of an inline part of a block-in-inline split then its
  1823   // previous ib-split sibling is a block, and the previous ib-split
  1824   // sibling of _that_ is an inline, just like aFrame.  Similarly, if
  1825   // aFrame is the first continuation of a block part of an
  1826   // block-in-inline split (a block-in-inline wrapper block), then its
  1827   // previous ib-split sibling is an inline and the previous ib-split
  1828   // sibling of that is either another block-in-inline wrapper block box
  1829   // or null.
  1830   nsIFrame *prevContinuation = aFrame->GetPrevContinuation();
  1831   if (!prevContinuation &&
  1832       (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
  1833     // We're the first continuation, so we can just get the frame
  1834     // property directly
  1835     prevContinuation = static_cast<nsIFrame*>(
  1836       aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling()));
  1837     if (prevContinuation) {
  1838       prevContinuation = static_cast<nsIFrame*>(
  1839         prevContinuation->Properties().Get(nsIFrame::IBSplitPrevSibling()));
  1843   NS_ASSERTION(!prevContinuation ||
  1844                prevContinuation->GetContent() == aFrame->GetContent(),
  1845                "unexpected content mismatch");
  1847   return prevContinuation;
  1850 /**
  1851  * Get the previous continuation or similar ib-split sibling (assuming
  1852  * block/inline alternation), conditionally on it having the same style.
  1853  * This assumes that we're not between resolving the two (i.e., that
  1854  * they're both already resolved.
  1855  */
  1856 static nsIFrame*
  1857 GetPrevContinuationWithSameStyle(nsIFrame* aFrame)
  1859   nsIFrame* prevContinuation = GetPrevContinuationWithPossiblySameStyle(aFrame);
  1860   if (!prevContinuation) {
  1861     return nullptr;
  1864   nsStyleContext* prevStyle = prevContinuation->StyleContext();
  1865   nsStyleContext* selfStyle = aFrame->StyleContext();
  1866   if (prevStyle != selfStyle) {
  1867     NS_ASSERTION(prevStyle->GetPseudo() != selfStyle->GetPseudo() ||
  1868                  prevStyle->GetParent() != selfStyle->GetParent(),
  1869                  "continuations should have the same style context");
  1870     prevContinuation = nullptr;
  1872   return prevContinuation;
  1875 /**
  1876  * Get the next continuation or similar ib-split sibling (assuming
  1877  * block/inline alternation), conditionally on it having the same style.
  1879  * Since this is used when deciding to copy the new style context, it
  1880  * takes as an argument the old style context to check if the style is
  1881  * the same.  When it is used in other contexts (i.e., where the next
  1882  * continuation would already have the new style context), the current
  1883  * style context should be passed.
  1884  */
  1885 static nsIFrame*
  1886 GetNextContinuationWithSameStyle(nsIFrame* aFrame,
  1887                                  nsStyleContext* aOldStyleContext)
  1889   // See GetPrevContinuationWithSameStyle about {ib} splits.
  1891   nsIFrame *nextContinuation = aFrame->GetNextContinuation();
  1892   if (!nextContinuation &&
  1893       (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
  1894     // We're the last continuation, so we have to hop back to the first
  1895     // before getting the frame property
  1896     nextContinuation = static_cast<nsIFrame*>(aFrame->FirstContinuation()->
  1897       Properties().Get(nsIFrame::IBSplitSibling()));
  1898     if (nextContinuation) {
  1899       nextContinuation = static_cast<nsIFrame*>(
  1900         nextContinuation->Properties().Get(nsIFrame::IBSplitSibling()));
  1904   if (!nextContinuation) {
  1905     return nullptr;
  1908   NS_ASSERTION(nextContinuation->GetContent() == aFrame->GetContent(),
  1909                "unexpected content mismatch");
  1911   nsStyleContext* nextStyle = nextContinuation->StyleContext();
  1912   if (nextStyle != aOldStyleContext) {
  1913     NS_ASSERTION(aOldStyleContext->GetPseudo() != nextStyle->GetPseudo() ||
  1914                  aOldStyleContext->GetParent() != nextStyle->GetParent(),
  1915                  "continuations should have the same style context");
  1916     nextContinuation = nullptr;
  1918   return nextContinuation;
  1921 nsresult
  1922 RestyleManager::ReparentStyleContext(nsIFrame* aFrame)
  1924   if (nsGkAtoms::placeholderFrame == aFrame->GetType()) {
  1925     // Also reparent the out-of-flow and all its continuations.
  1926     nsIFrame* outOfFlow =
  1927       nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
  1928     NS_ASSERTION(outOfFlow, "no out-of-flow frame");
  1929     do {
  1930       ReparentStyleContext(outOfFlow);
  1931     } while ((outOfFlow = outOfFlow->GetNextContinuation()));
  1934   // DO NOT verify the style tree before reparenting.  The frame
  1935   // tree has already been changed, so this check would just fail.
  1936   nsStyleContext* oldContext = aFrame->StyleContext();
  1938   nsRefPtr<nsStyleContext> newContext;
  1939   nsIFrame* providerFrame = aFrame->GetParentStyleContextFrame();
  1940   bool isChild = providerFrame && providerFrame->GetParent() == aFrame;
  1941   nsStyleContext* newParentContext = nullptr;
  1942   nsIFrame* providerChild = nullptr;
  1943   if (isChild) {
  1944     ReparentStyleContext(providerFrame);
  1945     newParentContext = providerFrame->StyleContext();
  1946     providerChild = providerFrame;
  1947   } else if (providerFrame) {
  1948     newParentContext = providerFrame->StyleContext();
  1949   } else {
  1950     NS_NOTREACHED("Reparenting something that has no usable parent? "
  1951                   "Shouldn't happen!");
  1953   // XXX need to do something here to produce the correct style context for
  1954   // an IB split whose first inline part is inside a first-line frame.
  1955   // Currently the first IB anonymous block's style context takes the first
  1956   // part's style context as parent, which is wrong since first-line style
  1957   // should not apply to the anonymous block.
  1959 #ifdef DEBUG
  1961     // Check that our assumption that continuations of the same
  1962     // pseudo-type and with the same style context parent have the
  1963     // same style context is valid before the reresolution.  (We need
  1964     // to check the pseudo-type and style context parent because of
  1965     // :first-letter and :first-line, where we create styled and
  1966     // unstyled letter/line frames distinguished by pseudo-type, and
  1967     // then need to distinguish their descendants based on having
  1968     // different parents.)
  1969     nsIFrame *nextContinuation = aFrame->GetNextContinuation();
  1970     if (nextContinuation) {
  1971       nsStyleContext *nextContinuationContext =
  1972         nextContinuation->StyleContext();
  1973       NS_ASSERTION(oldContext == nextContinuationContext ||
  1974                    oldContext->GetPseudo() !=
  1975                      nextContinuationContext->GetPseudo() ||
  1976                    oldContext->GetParent() !=
  1977                      nextContinuationContext->GetParent(),
  1978                    "continuations should have the same style context");
  1981 #endif
  1983   nsIFrame *prevContinuation =
  1984     GetPrevContinuationWithPossiblySameStyle(aFrame);
  1985   nsStyleContext *prevContinuationContext;
  1986   bool copyFromContinuation =
  1987     prevContinuation &&
  1988     (prevContinuationContext = prevContinuation->StyleContext())
  1989       ->GetPseudo() == oldContext->GetPseudo() &&
  1990      prevContinuationContext->GetParent() == newParentContext;
  1991   if (copyFromContinuation) {
  1992     // Just use the style context from the frame's previous
  1993     // continuation (see assertion about aFrame->GetNextContinuation()
  1994     // above, which we would have previously hit for aFrame's previous
  1995     // continuation).
  1996     newContext = prevContinuationContext;
  1997   } else {
  1998     nsIFrame* parentFrame = aFrame->GetParent();
  1999     Element* element =
  2000       ElementForStyleContext(parentFrame ? parentFrame->GetContent() : nullptr,
  2001                              aFrame,
  2002                              oldContext->GetPseudoType());
  2003     newContext = mPresContext->StyleSet()->
  2004                    ReparentStyleContext(oldContext, newParentContext, element);
  2007   if (newContext) {
  2008     if (newContext != oldContext) {
  2009       // We probably don't want to initiate transitions from
  2010       // ReparentStyleContext, since we call it during frame
  2011       // construction rather than in response to dynamic changes.
  2012       // Also see the comment at the start of
  2013       // nsTransitionManager::ConsiderStartingTransition.
  2014 #if 0
  2015       if (!copyFromContinuation) {
  2016         TryStartingTransition(mPresContext, aFrame->GetContent(),
  2017                               oldContext, &newContext);
  2019 #endif
  2021       // Make sure to call CalcStyleDifference so that the new context ends
  2022       // up resolving all the structs the old context resolved.
  2023       if (!copyFromContinuation) {
  2024         DebugOnly<nsChangeHint> styleChange =
  2025           oldContext->CalcStyleDifference(newContext, nsChangeHint(0));
  2026         // The style change is always 0 because we have the same rulenode and
  2027         // CalcStyleDifference optimizes us away.  That's OK, though:
  2028         // reparenting should never trigger a frame reconstruct, and whenever
  2029         // it's happening we already plan to reflow and repaint the frames.
  2030         NS_ASSERTION(!(styleChange & nsChangeHint_ReconstructFrame),
  2031                      "Our frame tree is likely to be bogus!");
  2034       aFrame->SetStyleContext(newContext);
  2036       nsIFrame::ChildListIterator lists(aFrame);
  2037       for (; !lists.IsDone(); lists.Next()) {
  2038         nsFrameList::Enumerator childFrames(lists.CurrentList());
  2039         for (; !childFrames.AtEnd(); childFrames.Next()) {
  2040           nsIFrame* child = childFrames.get();
  2041           // only do frames that are in flow
  2042           if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
  2043               child != providerChild) {
  2044 #ifdef DEBUG
  2045             if (nsGkAtoms::placeholderFrame == child->GetType()) {
  2046               nsIFrame* outOfFlowFrame =
  2047                 nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
  2048               NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
  2050               NS_ASSERTION(outOfFlowFrame != providerChild,
  2051                            "Out of flow provider?");
  2053 #endif
  2054             ReparentStyleContext(child);
  2059       // If this frame is part of an IB split, then the style context of
  2060       // the next part of the split might be a child of our style context.
  2061       // Reparent its style context just in case one of our ancestors
  2062       // (split or not) hasn't done so already). It's not a problem to
  2063       // reparent the same frame twice because the "if (newContext !=
  2064       // oldContext)" check will prevent us from redoing work.
  2065       if ((aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) &&
  2066           !aFrame->GetPrevContinuation()) {
  2067         nsIFrame* sib = static_cast<nsIFrame*>
  2068           (aFrame->Properties().Get(nsIFrame::IBSplitSibling()));
  2069         if (sib) {
  2070           ReparentStyleContext(sib);
  2074       // do additional contexts
  2075       int32_t contextIndex = 0;
  2076       for (nsStyleContext* oldExtraContext;
  2077            (oldExtraContext = aFrame->GetAdditionalStyleContext(contextIndex));
  2078            ++contextIndex) {
  2079         nsRefPtr<nsStyleContext> newExtraContext;
  2080         newExtraContext = mPresContext->StyleSet()->
  2081                             ReparentStyleContext(oldExtraContext,
  2082                                                  newContext, nullptr);
  2083         if (newExtraContext) {
  2084           if (newExtraContext != oldExtraContext) {
  2085             // Make sure to call CalcStyleDifference so that the new
  2086             // context ends up resolving all the structs the old context
  2087             // resolved.
  2088             DebugOnly<nsChangeHint> styleChange =
  2089               oldExtraContext->CalcStyleDifference(newExtraContext,
  2090                                                    nsChangeHint(0));
  2091             // The style change is always 0 because we have the same
  2092             // rulenode and CalcStyleDifference optimizes us away.  That's
  2093             // OK, though: reparenting should never trigger a frame
  2094             // reconstruct, and whenever it's happening we already plan to
  2095             // reflow and repaint the frames.
  2096             NS_ASSERTION(!(styleChange & nsChangeHint_ReconstructFrame),
  2097                          "Our frame tree is likely to be bogus!");
  2100           aFrame->SetAdditionalStyleContext(contextIndex, newExtraContext);
  2103 #ifdef DEBUG
  2104       VerifyStyleTree(mPresContext, aFrame, newParentContext);
  2105 #endif
  2109   return NS_OK;
  2112 ElementRestyler::ElementRestyler(nsPresContext* aPresContext,
  2113                                  nsIFrame* aFrame,
  2114                                  nsStyleChangeList* aChangeList,
  2115                                  nsChangeHint aHintsHandledByAncestors,
  2116                                  RestyleTracker& aRestyleTracker,
  2117                                  TreeMatchContext& aTreeMatchContext,
  2118                                  nsTArray<nsIContent*>&
  2119                                    aVisibleKidsOfHiddenElement)
  2120   : mPresContext(aPresContext)
  2121   , mFrame(aFrame)
  2122   , mParentContent(nullptr)
  2123     // XXXldb Why does it make sense to use aParentContent?  (See
  2124     // comment above assertion at start of ElementRestyler::Restyle.)
  2125   , mContent(mFrame->GetContent() ? mFrame->GetContent() : mParentContent)
  2126   , mChangeList(aChangeList)
  2127   , mHintsHandled(NS_SubtractHint(aHintsHandledByAncestors,
  2128                   NS_HintsNotHandledForDescendantsIn(aHintsHandledByAncestors)))
  2129   , mParentFrameHintsNotHandledForDescendants(nsChangeHint(0))
  2130   , mHintsNotHandledForDescendants(nsChangeHint(0))
  2131   , mRestyleTracker(aRestyleTracker)
  2132   , mTreeMatchContext(aTreeMatchContext)
  2133   , mResolvedChild(nullptr)
  2134 #ifdef ACCESSIBILITY
  2135   , mDesiredA11yNotifications(eSendAllNotifications)
  2136   , mKidsDesiredA11yNotifications(mDesiredA11yNotifications)
  2137   , mOurA11yNotification(eDontNotify)
  2138   , mVisibleKidsOfHiddenElement(aVisibleKidsOfHiddenElement)
  2139 #endif
  2143 ElementRestyler::ElementRestyler(const ElementRestyler& aParentRestyler,
  2144                                  nsIFrame* aFrame,
  2145                                  uint32_t aConstructorFlags)
  2146   : mPresContext(aParentRestyler.mPresContext)
  2147   , mFrame(aFrame)
  2148   , mParentContent(aParentRestyler.mContent)
  2149     // XXXldb Why does it make sense to use aParentContent?  (See
  2150     // comment above assertion at start of ElementRestyler::Restyle.)
  2151   , mContent(mFrame->GetContent() ? mFrame->GetContent() : mParentContent)
  2152   , mChangeList(aParentRestyler.mChangeList)
  2153   , mHintsHandled(NS_SubtractHint(aParentRestyler.mHintsHandled,
  2154                   NS_HintsNotHandledForDescendantsIn(aParentRestyler.mHintsHandled)))
  2155   , mParentFrameHintsNotHandledForDescendants(
  2156       aParentRestyler.mHintsNotHandledForDescendants)
  2157   , mHintsNotHandledForDescendants(nsChangeHint(0))
  2158   , mRestyleTracker(aParentRestyler.mRestyleTracker)
  2159   , mTreeMatchContext(aParentRestyler.mTreeMatchContext)
  2160   , mResolvedChild(nullptr)
  2161 #ifdef ACCESSIBILITY
  2162   , mDesiredA11yNotifications(aParentRestyler.mKidsDesiredA11yNotifications)
  2163   , mKidsDesiredA11yNotifications(mDesiredA11yNotifications)
  2164   , mOurA11yNotification(eDontNotify)
  2165   , mVisibleKidsOfHiddenElement(aParentRestyler.mVisibleKidsOfHiddenElement)
  2166 #endif
  2168   if (aConstructorFlags & FOR_OUT_OF_FLOW_CHILD) {
  2169     // Note that the out-of-flow may not be a geometric descendant of
  2170     // the frame where we started the reresolve.  Therefore, even if
  2171     // mHintsHandled already includes nsChangeHint_AllReflowHints we
  2172     // don't want to pass that on to the out-of-flow reresolve, since
  2173     // that can lead to the out-of-flow not getting reflowed when it
  2174     // should be (eg a reresolve starting at <body> that involves
  2175     // reflowing the <body> would miss reflowing fixed-pos nodes that
  2176     // also need reflow).  In the cases when the out-of-flow _is_ a
  2177     // geometric descendant of a frame we already have a reflow hint
  2178     // for, reflow coalescing should keep us from doing the work twice.
  2179     mHintsHandled = NS_SubtractHint(mHintsHandled, nsChangeHint_AllReflowHints);
  2183 ElementRestyler::ElementRestyler(ParentContextFromChildFrame,
  2184                                  const ElementRestyler& aParentRestyler,
  2185                                  nsIFrame* aFrame)
  2186   : mPresContext(aParentRestyler.mPresContext)
  2187   , mFrame(aFrame)
  2188   , mParentContent(aParentRestyler.mParentContent)
  2189     // XXXldb Why does it make sense to use aParentContent?  (See
  2190     // comment above assertion at start of ElementRestyler::Restyle.)
  2191   , mContent(mFrame->GetContent() ? mFrame->GetContent() : mParentContent)
  2192   , mChangeList(aParentRestyler.mChangeList)
  2193   , mHintsHandled(NS_SubtractHint(aParentRestyler.mHintsHandled,
  2194                   NS_HintsNotHandledForDescendantsIn(aParentRestyler.mHintsHandled)))
  2195   , mParentFrameHintsNotHandledForDescendants(
  2196       // assume the worst
  2197       nsChangeHint_Hints_NotHandledForDescendants)
  2198   , mHintsNotHandledForDescendants(nsChangeHint(0))
  2199   , mRestyleTracker(aParentRestyler.mRestyleTracker)
  2200   , mTreeMatchContext(aParentRestyler.mTreeMatchContext)
  2201   , mResolvedChild(nullptr)
  2202 #ifdef ACCESSIBILITY
  2203   , mDesiredA11yNotifications(aParentRestyler.mDesiredA11yNotifications)
  2204   , mKidsDesiredA11yNotifications(mDesiredA11yNotifications)
  2205   , mOurA11yNotification(eDontNotify)
  2206   , mVisibleKidsOfHiddenElement(aParentRestyler.mVisibleKidsOfHiddenElement)
  2207 #endif
  2211 void
  2212 ElementRestyler::CaptureChange(nsStyleContext* aOldContext,
  2213                                nsStyleContext* aNewContext,
  2214                                nsChangeHint aChangeToAssume)
  2216   // Check some invariants about replacing one style context with another.
  2217   NS_ASSERTION(aOldContext->GetPseudo() == aNewContext->GetPseudo(),
  2218                "old and new style contexts should have the same pseudo");
  2219   NS_ASSERTION(aOldContext->GetPseudoType() == aNewContext->GetPseudoType(),
  2220                "old and new style contexts should have the same pseudo");
  2222   nsChangeHint ourChange = aOldContext->CalcStyleDifference(aNewContext,
  2223                              mParentFrameHintsNotHandledForDescendants);
  2224   NS_ASSERTION(!(ourChange & nsChangeHint_AllReflowHints) ||
  2225                (ourChange & nsChangeHint_NeedReflow),
  2226                "Reflow hint bits set without actually asking for a reflow");
  2228   // nsChangeHint_UpdateEffects is inherited, but it can be set due to changes
  2229   // in inherited properties (fill and stroke).  Avoid propagating it into
  2230   // text nodes.
  2231   if ((ourChange & nsChangeHint_UpdateEffects) &&
  2232       mContent && !mContent->IsElement()) {
  2233     ourChange = NS_SubtractHint(ourChange, nsChangeHint_UpdateEffects);
  2236   NS_UpdateHint(ourChange, aChangeToAssume);
  2237   if (NS_UpdateHint(mHintsHandled, ourChange)) {
  2238     if (!(ourChange & nsChangeHint_ReconstructFrame) || mContent) {
  2239       mChangeList->AppendChange(mFrame, mContent, ourChange);
  2242   NS_UpdateHint(mHintsNotHandledForDescendants,
  2243                 NS_HintsNotHandledForDescendantsIn(ourChange));
  2246 /**
  2247  * Recompute style for mFrame (which should not have a prev continuation
  2248  * with the same style), all of its next continuations with the same
  2249  * style, and all ib-split siblings of the same type (either block or
  2250  * inline, skipping the intermediates of the other type) and accumulate
  2251  * changes into mChangeList given that mHintsHandled is already accumulated
  2252  * for an ancestor.
  2253  * mParentContent is the content node used to resolve the parent style
  2254  * context.  This means that, for pseudo-elements, it is the content
  2255  * that should be used for selector matching (rather than the fake
  2256  * content node attached to the frame).
  2257  */
  2258 void
  2259 ElementRestyler::Restyle(nsRestyleHint aRestyleHint)
  2261   // It would be nice if we could make stronger assertions here; they
  2262   // would let us simplify the ?: expressions below setting |content|
  2263   // and |pseudoContent| in sensible ways as well as making what
  2264   // |content| and |pseudoContent| mean, and their relationship to
  2265   // |mFrame->GetContent()|, make more sense.  However, we can't,
  2266   // because of frame trees like the one in
  2267   // https://bugzilla.mozilla.org/show_bug.cgi?id=472353#c14 .  Once we
  2268   // fix bug 242277 we should be able to make this make more sense.
  2269   NS_ASSERTION(mFrame->GetContent() || !mParentContent ||
  2270                !mParentContent->GetParent(),
  2271                "frame must have content (unless at the top of the tree)");
  2273   NS_ASSERTION(!GetPrevContinuationWithSameStyle(mFrame),
  2274                "should not be trying to restyle this frame separately");
  2276   if (mContent && mContent->IsElement()) {
  2277     mContent->OwnerDoc()->FlushPendingLinkUpdates();
  2278     RestyleTracker::RestyleData restyleData;
  2279     if (mRestyleTracker.GetRestyleData(mContent->AsElement(), &restyleData)) {
  2280       if (NS_UpdateHint(mHintsHandled, restyleData.mChangeHint)) {
  2281         mChangeList->AppendChange(mFrame, mContent, restyleData.mChangeHint);
  2283       aRestyleHint = nsRestyleHint(aRestyleHint | restyleData.mRestyleHint);
  2287   nsRestyleHint childRestyleHint = aRestyleHint;
  2289   if (childRestyleHint == eRestyle_Self) {
  2290     childRestyleHint = nsRestyleHint(0);
  2294     nsRefPtr<nsStyleContext> oldContext = mFrame->StyleContext();
  2296     // TEMPORARY (until bug 918064):  Call RestyleSelf for each
  2297     // continuation or block-in-inline sibling.
  2299     for (nsIFrame* f = mFrame; f;
  2300          f = GetNextContinuationWithSameStyle(f, oldContext)) {
  2301       RestyleSelf(f, aRestyleHint);
  2305   RestyleChildren(childRestyleHint);
  2308 void
  2309 ElementRestyler::RestyleSelf(nsIFrame* aSelf, nsRestyleHint aRestyleHint)
  2311   // XXXldb get new context from prev-in-flow if possible, to avoid
  2312   // duplication.  (Or should we just let |GetContext| handle that?)
  2313   // Getting the hint would be nice too, but that's harder.
  2315   // XXXbryner we may be able to avoid some of the refcounting goop here.
  2316   // We do need a reference to oldContext for the lifetime of this function, and it's possible
  2317   // that the frame has the last reference to it, so AddRef it here.
  2319   nsChangeHint assumeDifferenceHint = NS_STYLE_HINT_NONE;
  2320   nsRefPtr<nsStyleContext> oldContext = aSelf->StyleContext();
  2321   nsStyleSet* styleSet = mPresContext->StyleSet();
  2323 #ifdef ACCESSIBILITY
  2324   mWasFrameVisible = nsIPresShell::IsAccessibilityActive() ?
  2325     oldContext->StyleVisibility()->IsVisible() : false;
  2326 #endif
  2328   nsIAtom* const pseudoTag = oldContext->GetPseudo();
  2329   const nsCSSPseudoElements::Type pseudoType = oldContext->GetPseudoType();
  2331   nsStyleContext* parentContext;
  2332   // Get the frame providing the parent style context.  If it is a
  2333   // child, then resolve the provider first.
  2334   nsIFrame* providerFrame = aSelf->GetParentStyleContextFrame();
  2335   bool isChild = providerFrame && providerFrame->GetParent() == aSelf;
  2336   if (!isChild) {
  2337     if (providerFrame)
  2338       parentContext = providerFrame->StyleContext();
  2339     else
  2340       parentContext = nullptr;
  2342   else {
  2343     MOZ_ASSERT(providerFrame->GetContent() == aSelf->GetContent(),
  2344                "Postcondition for GetParentStyleContextFrame() violated. "
  2345                "That means we need to add the current element to the "
  2346                "ancestor filter.");
  2348     // resolve the provider here (before aSelf below).
  2350     // assumeDifferenceHint forces the parent's change to be also
  2351     // applied to this frame, no matter what
  2352     // nsStyleContext::CalcStyleDifference says. CalcStyleDifference
  2353     // can't be trusted because it assumes any changes to the parent
  2354     // style context provider will be automatically propagated to
  2355     // the frame(s) with child style contexts.
  2357     ElementRestyler providerRestyler(PARENT_CONTEXT_FROM_CHILD_FRAME,
  2358                                      *this, providerFrame);
  2359     providerRestyler.Restyle(aRestyleHint);
  2360     assumeDifferenceHint = providerRestyler.HintsHandledForFrame();
  2362     // The provider's new context becomes the parent context of
  2363     // aSelf's context.
  2364     parentContext = providerFrame->StyleContext();
  2365     // Set |mResolvedChild| so we don't bother resolving the
  2366     // provider again.
  2367     mResolvedChild = providerFrame;
  2370   if (providerFrame != aSelf->GetParent()) {
  2371     // We don't actually know what the parent style context's
  2372     // non-inherited hints were, so assume the worst.
  2373     mParentFrameHintsNotHandledForDescendants =
  2374       nsChangeHint_Hints_NotHandledForDescendants;
  2377   // do primary context
  2378   nsRefPtr<nsStyleContext> newContext;
  2379   nsIFrame *prevContinuation =
  2380     GetPrevContinuationWithPossiblySameStyle(aSelf);
  2381   nsStyleContext *prevContinuationContext;
  2382   bool copyFromContinuation =
  2383     prevContinuation &&
  2384     (prevContinuationContext = prevContinuation->StyleContext())
  2385       ->GetPseudo() == oldContext->GetPseudo() &&
  2386      prevContinuationContext->GetParent() == parentContext;
  2387   if (copyFromContinuation) {
  2388     // Just use the style context from the frame's previous
  2389     // continuation.
  2390     newContext = prevContinuationContext;
  2392   else if (pseudoTag == nsCSSAnonBoxes::mozNonElement) {
  2393     NS_ASSERTION(aSelf->GetContent(),
  2394                  "non pseudo-element frame without content node");
  2395     newContext = styleSet->ResolveStyleForNonElement(parentContext);
  2397   else if (!aRestyleHint && !prevContinuation) {
  2398     // Unfortunately, if prevContinuation is non-null then we may have
  2399     // already stolen the restyle tracker entry for this element while
  2400     // processing prevContinuation.  So we don't know whether aRestyleHint
  2401     // should really be 0 here or whether it should be eRestyle_Self.  Be
  2402     // pessimistic and force an actual reresolve in that situation.  The good
  2403     // news is that in the common case when prevContinuation is non-null we
  2404     // just used prevContinuationContext anyway and aren't reaching this code
  2405     // to start with.
  2406     newContext =
  2407       styleSet->ReparentStyleContext(oldContext, parentContext,
  2408                                      ElementForStyleContext(mParentContent,
  2409                                                             aSelf, pseudoType));
  2410   } else if (pseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
  2411     newContext = styleSet->ResolveAnonymousBoxStyle(pseudoTag,
  2412                                                     parentContext);
  2414   else {
  2415     Element* element = ElementForStyleContext(mParentContent, aSelf, pseudoType);
  2416     if (pseudoTag) {
  2417       if (pseudoTag == nsCSSPseudoElements::before ||
  2418           pseudoTag == nsCSSPseudoElements::after) {
  2419         // XXX what other pseudos do we need to treat like this?
  2420         newContext = styleSet->ProbePseudoElementStyle(element,
  2421                                                        pseudoType,
  2422                                                        parentContext,
  2423                                                        mTreeMatchContext);
  2424         if (!newContext) {
  2425           // This pseudo should no longer exist; gotta reframe
  2426           NS_UpdateHint(mHintsHandled, nsChangeHint_ReconstructFrame);
  2427           mChangeList->AppendChange(aSelf, element,
  2428                                     nsChangeHint_ReconstructFrame);
  2429           // We're reframing anyway; just keep the same context
  2430           newContext = oldContext;
  2432       } else {
  2433         // Don't expect XUL tree stuff here, since it needs a comparator and
  2434         // all.
  2435         NS_ASSERTION(pseudoType <
  2436                        nsCSSPseudoElements::ePseudo_PseudoElementCount,
  2437                      "Unexpected pseudo type");
  2438         Element* pseudoElement =
  2439           nsCSSPseudoElements::PseudoElementSupportsStyleAttribute(pseudoType) ||
  2440           nsCSSPseudoElements::PseudoElementSupportsUserActionState(pseudoType) ?
  2441             aSelf->GetContent()->AsElement() : nullptr;
  2442         MOZ_ASSERT(element != pseudoElement);
  2443         newContext = styleSet->ResolvePseudoElementStyle(element,
  2444                                                          pseudoType,
  2445                                                          parentContext,
  2446                                                          pseudoElement);
  2449     else {
  2450       NS_ASSERTION(aSelf->GetContent(),
  2451                    "non pseudo-element frame without content node");
  2452       // Skip flex-item style fixup for anonymous subtrees:
  2453       TreeMatchContext::AutoFlexItemStyleFixupSkipper
  2454         flexFixupSkipper(mTreeMatchContext,
  2455                          element->IsRootOfNativeAnonymousSubtree());
  2456       newContext = styleSet->ResolveStyleFor(element, parentContext,
  2457                                              mTreeMatchContext);
  2461   MOZ_ASSERT(newContext);
  2463   if (!parentContext) {
  2464     if (oldContext->RuleNode() == newContext->RuleNode() &&
  2465         oldContext->IsLinkContext() == newContext->IsLinkContext() &&
  2466         oldContext->RelevantLinkVisited() ==
  2467           newContext->RelevantLinkVisited()) {
  2468       // We're the root of the style context tree and the new style
  2469       // context returned has the same rule node.  This means that
  2470       // we can use FindChildWithRules to keep a lot of the old
  2471       // style contexts around.  However, we need to start from the
  2472       // same root.
  2473       newContext = oldContext;
  2477   if (newContext != oldContext) {
  2478     if (!copyFromContinuation) {
  2479       TryStartingTransition(mPresContext, aSelf->GetContent(),
  2480                             oldContext, &newContext);
  2482       CaptureChange(oldContext, newContext, assumeDifferenceHint);
  2485     if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
  2486       // If the frame gets regenerated, let it keep its old context,
  2487       // which is important to maintain various invariants about
  2488       // frame types matching their style contexts.
  2489       // Note that this check even makes sense if we didn't call
  2490       // CaptureChange because of copyFromContinuation being true,
  2491       // since we'll have copied the existing context from the
  2492       // previous continuation, so newContext == oldContext.
  2493       aSelf->SetStyleContext(newContext);
  2496   oldContext = nullptr;
  2498   // do additional contexts
  2499   // XXXbz might be able to avoid selector matching here in some
  2500   // cases; won't worry about it for now.
  2501   int32_t contextIndex = 0;
  2502   for (nsStyleContext* oldExtraContext;
  2503        (oldExtraContext = aSelf->GetAdditionalStyleContext(contextIndex));
  2504        ++contextIndex) {
  2505     nsRefPtr<nsStyleContext> newExtraContext;
  2506     nsIAtom* const extraPseudoTag = oldExtraContext->GetPseudo();
  2507     const nsCSSPseudoElements::Type extraPseudoType =
  2508       oldExtraContext->GetPseudoType();
  2509     NS_ASSERTION(extraPseudoTag &&
  2510                  extraPseudoTag != nsCSSAnonBoxes::mozNonElement,
  2511                  "extra style context is not pseudo element");
  2512     if (extraPseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
  2513       newExtraContext = styleSet->ResolveAnonymousBoxStyle(extraPseudoTag,
  2514                                                            newContext);
  2516     else {
  2517       // Don't expect XUL tree stuff here, since it needs a comparator and
  2518       // all.
  2519       NS_ASSERTION(extraPseudoType <
  2520                      nsCSSPseudoElements::ePseudo_PseudoElementCount,
  2521                    "Unexpected type");
  2522       newExtraContext = styleSet->ResolvePseudoElementStyle(mContent->AsElement(),
  2523                                                             extraPseudoType,
  2524                                                             newContext,
  2525                                                             nullptr);
  2528     MOZ_ASSERT(newExtraContext);
  2530     if (oldExtraContext != newExtraContext) {
  2531       CaptureChange(oldExtraContext, newExtraContext, assumeDifferenceHint);
  2532       if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
  2533         aSelf->SetAdditionalStyleContext(contextIndex, newExtraContext);
  2539 void
  2540 ElementRestyler::RestyleChildren(nsRestyleHint aChildRestyleHint)
  2542   RestyleUndisplayedChildren(aChildRestyleHint);
  2544   // Check whether we might need to create a new ::before frame.
  2545   // There's no need to do this if we're planning to reframe already
  2546   // or if we're not forcing restyles on kids.
  2547   // It's also important to check mHintsHandled since we use
  2548   // mFrame->StyleContext(), which is out of date if mHintsHandled has a
  2549   // ReconstructFrame hint.  Using an out of date style context could
  2550   // trigger assertions about mismatched rule trees.
  2551   if (!(mHintsHandled & nsChangeHint_ReconstructFrame) &&
  2552       aChildRestyleHint) {
  2553     RestyleBeforePseudo();
  2556   // There is no need to waste time crawling into a frame's children
  2557   // on a frame change.  The act of reconstructing frames will force
  2558   // new style contexts to be resolved on all of this frame's
  2559   // descendants anyway, so we want to avoid wasting time processing
  2560   // style contexts that we're just going to throw away anyway. - dwh
  2561   // It's also important to check mHintsHandled since reresolving the
  2562   // kids would use mFrame->StyleContext(), which is out of date if
  2563   // mHintsHandled has a ReconstructFrame hint; doing this could trigger
  2564   // assertions about mismatched rule trees.
  2565   nsIFrame *lastContinuation;
  2566   if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
  2567     InitializeAccessibilityNotifications();
  2569     for (nsIFrame* f = mFrame; f;
  2570          f = GetNextContinuationWithSameStyle(f, f->StyleContext())) {
  2571       lastContinuation = f;
  2572       RestyleContentChildren(f, aChildRestyleHint);
  2575     SendAccessibilityNotifications();
  2578   // Check whether we might need to create a new ::after frame.
  2579   // See comments above regarding :before.
  2580   if (!(mHintsHandled & nsChangeHint_ReconstructFrame) &&
  2581       aChildRestyleHint) {
  2582     RestyleAfterPseudo(lastContinuation);
  2586 void
  2587 ElementRestyler::RestyleUndisplayedChildren(nsRestyleHint aChildRestyleHint)
  2589   // When the root element is display:none, we still construct *some*
  2590   // frames that have the root element as their mContent, down to the
  2591   // DocElementContainingBlock.
  2592   bool checkUndisplayed;
  2593   nsIContent* undisplayedParent;
  2594   nsCSSFrameConstructor* frameConstructor = mPresContext->FrameConstructor();
  2595   if (mFrame->StyleContext()->GetPseudo()) {
  2596     checkUndisplayed = mFrame == frameConstructor->
  2597                                    GetDocElementContainingBlock();
  2598     undisplayedParent = nullptr;
  2599   } else {
  2600     checkUndisplayed = !!mFrame->GetContent();
  2601     undisplayedParent = mFrame->GetContent();
  2603   if (checkUndisplayed &&
  2604       // No need to do this if we're planning to reframe already.
  2605       // It's also important to check mHintsHandled since we use
  2606       // mFrame->StyleContext(), which is out of date if mHintsHandled
  2607       // has a ReconstructFrame hint.  Using an out of date style
  2608       // context could trigger assertions about mismatched rule trees.
  2609       !(mHintsHandled & nsChangeHint_ReconstructFrame)) {
  2610     UndisplayedNode* undisplayed =
  2611       frameConstructor->GetAllUndisplayedContentIn(undisplayedParent);
  2612     TreeMatchContext::AutoAncestorPusher pusher(mTreeMatchContext);
  2613     if (undisplayed) {
  2614       pusher.PushAncestorAndStyleScope(undisplayedParent);
  2616     for (; undisplayed; undisplayed = undisplayed->mNext) {
  2617       NS_ASSERTION(undisplayedParent ||
  2618                    undisplayed->mContent ==
  2619                      mPresContext->Document()->GetRootElement(),
  2620                    "undisplayed node child of null must be root");
  2621       NS_ASSERTION(!undisplayed->mStyle->GetPseudo(),
  2622                    "Shouldn't have random pseudo style contexts in the "
  2623                    "undisplayed map");
  2625       // Get the parent of the undisplayed content and check if it is a XBL
  2626       // children element. Push the children element as an ancestor here because it does
  2627       // not have a frame and would not otherwise be pushed as an ancestor.
  2628       nsIContent* parent = undisplayed->mContent->GetParent();
  2629       TreeMatchContext::AutoAncestorPusher insertionPointPusher(mTreeMatchContext);
  2630       if (parent && nsContentUtils::IsContentInsertionPoint(parent)) {
  2631         insertionPointPusher.PushAncestorAndStyleScope(parent);
  2634       nsRestyleHint thisChildHint = aChildRestyleHint;
  2635       RestyleTracker::RestyleData undisplayedRestyleData;
  2636       if (mRestyleTracker.GetRestyleData(undisplayed->mContent->AsElement(),
  2637                                          &undisplayedRestyleData)) {
  2638         thisChildHint =
  2639           nsRestyleHint(thisChildHint | undisplayedRestyleData.mRestyleHint);
  2641       nsRefPtr<nsStyleContext> undisplayedContext;
  2642       nsStyleSet* styleSet = mPresContext->StyleSet();
  2643       if (thisChildHint) {
  2644         undisplayedContext =
  2645           styleSet->ResolveStyleFor(undisplayed->mContent->AsElement(),
  2646                                     mFrame->StyleContext(),
  2647                                     mTreeMatchContext);
  2648       } else {
  2649         undisplayedContext =
  2650           styleSet->ReparentStyleContext(undisplayed->mStyle,
  2651                                          mFrame->StyleContext(),
  2652                                          undisplayed->mContent->AsElement());
  2654       const nsStyleDisplay* display = undisplayedContext->StyleDisplay();
  2655       if (display->mDisplay != NS_STYLE_DISPLAY_NONE) {
  2656         NS_ASSERTION(undisplayed->mContent,
  2657                      "Must have undisplayed content");
  2658         mChangeList->AppendChange(nullptr, undisplayed->mContent,
  2659                                   NS_STYLE_HINT_FRAMECHANGE);
  2660         // The node should be removed from the undisplayed map when
  2661         // we reframe it.
  2662       } else {
  2663         // update the undisplayed node with the new context
  2664         undisplayed->mStyle = undisplayedContext;
  2670 void
  2671 ElementRestyler::RestyleBeforePseudo()
  2673   // Make sure not to do this for pseudo-frames or frames that
  2674   // can't have generated content.
  2675   if (!mFrame->StyleContext()->GetPseudo() &&
  2676       ((mFrame->GetStateBits() & NS_FRAME_MAY_HAVE_GENERATED_CONTENT) ||
  2677        // Our content insertion frame might have gotten flagged
  2678        (mFrame->GetContentInsertionFrame()->GetStateBits() &
  2679         NS_FRAME_MAY_HAVE_GENERATED_CONTENT))) {
  2680     // Check for a new :before pseudo and an existing :before
  2681     // frame, but only if the frame is the first continuation.
  2682     nsIFrame* prevContinuation = mFrame->GetPrevContinuation();
  2683     if (!prevContinuation) {
  2684       // Checking for a :before frame is cheaper than getting the
  2685       // :before style context.
  2686       if (!nsLayoutUtils::GetBeforeFrame(mFrame) &&
  2687           nsLayoutUtils::HasPseudoStyle(mFrame->GetContent(),
  2688                                         mFrame->StyleContext(),
  2689                                         nsCSSPseudoElements::ePseudo_before,
  2690                                         mPresContext)) {
  2691         // Have to create the new :before frame
  2692         NS_UpdateHint(mHintsHandled, nsChangeHint_ReconstructFrame);
  2693         mChangeList->AppendChange(mFrame, mContent,
  2694                                   nsChangeHint_ReconstructFrame);
  2700 /**
  2701  * aFrame is the last continuation or block-in-inline sibling that this
  2702  * ElementRestyler is restyling.
  2703  */
  2704 void
  2705 ElementRestyler::RestyleAfterPseudo(nsIFrame* aFrame)
  2707   // Make sure not to do this for pseudo-frames or frames that
  2708   // can't have generated content.
  2709   if (!aFrame->StyleContext()->GetPseudo() &&
  2710       ((aFrame->GetStateBits() & NS_FRAME_MAY_HAVE_GENERATED_CONTENT) ||
  2711        // Our content insertion frame might have gotten flagged
  2712        (aFrame->GetContentInsertionFrame()->GetStateBits() &
  2713         NS_FRAME_MAY_HAVE_GENERATED_CONTENT))) {
  2714     // Check for new :after content, but only if the frame is the
  2715     // last continuation.
  2716     nsIFrame* nextContinuation = aFrame->GetNextContinuation();
  2718     if (!nextContinuation) {
  2719       // Getting the :after frame is more expensive than getting the pseudo
  2720       // context, so get the pseudo context first.
  2721       if (nsLayoutUtils::HasPseudoStyle(aFrame->GetContent(),
  2722                                         aFrame->StyleContext(),
  2723                                         nsCSSPseudoElements::ePseudo_after,
  2724                                         mPresContext) &&
  2725           !nsLayoutUtils::GetAfterFrame(aFrame)) {
  2726         // have to create the new :after frame
  2727         NS_UpdateHint(mHintsHandled, nsChangeHint_ReconstructFrame);
  2728         mChangeList->AppendChange(aFrame, mContent,
  2729                                   nsChangeHint_ReconstructFrame);
  2735 void
  2736 ElementRestyler::InitializeAccessibilityNotifications()
  2738 #ifdef ACCESSIBILITY
  2739   // Notify a11y for primary frame only if it's a root frame of visibility
  2740   // changes or its parent frame was hidden while it stays visible and
  2741   // it is not inside a {ib} split or is the first frame of {ib} split.
  2742   if (nsIPresShell::IsAccessibilityActive() &&
  2743       !mFrame->GetPrevContinuation() &&
  2744       !mFrame->FrameIsNonFirstInIBSplit()) {
  2745     if (mDesiredA11yNotifications == eSendAllNotifications) {
  2746       bool isFrameVisible = mFrame->StyleVisibility()->IsVisible();
  2747       if (isFrameVisible != mWasFrameVisible) {
  2748         if (isFrameVisible) {
  2749           // Notify a11y the element (perhaps with its children) was shown.
  2750           // We don't fall into this case if this element gets or stays shown
  2751           // while its parent becomes hidden.
  2752           mKidsDesiredA11yNotifications = eSkipNotifications;
  2753           mOurA11yNotification = eNotifyShown;
  2754         } else {
  2755           // The element is being hidden; its children may stay visible, or
  2756           // become visible after being hidden previously. If we'll find
  2757           // visible children then we should notify a11y about that as if
  2758           // they were inserted into tree. Notify a11y this element was
  2759           // hidden.
  2760           mKidsDesiredA11yNotifications = eNotifyIfShown;
  2761           mOurA11yNotification = eNotifyHidden;
  2764     } else if (mDesiredA11yNotifications == eNotifyIfShown &&
  2765                mFrame->StyleVisibility()->IsVisible()) {
  2766       // Notify a11y that element stayed visible while its parent was
  2767       // hidden.
  2768       mVisibleKidsOfHiddenElement.AppendElement(mFrame->GetContent());
  2769       mKidsDesiredA11yNotifications = eSkipNotifications;
  2772 #endif
  2775 void
  2776 ElementRestyler::RestyleContentChildren(nsIFrame* aParent,
  2777                                         nsRestyleHint aChildRestyleHint)
  2779   nsIFrame::ChildListIterator lists(aParent);
  2780   TreeMatchContext::AutoAncestorPusher ancestorPusher(mTreeMatchContext);
  2781   if (!lists.IsDone()) {
  2782     ancestorPusher.PushAncestorAndStyleScope(mContent);
  2784   for (; !lists.IsDone(); lists.Next()) {
  2785     nsFrameList::Enumerator childFrames(lists.CurrentList());
  2786     for (; !childFrames.AtEnd(); childFrames.Next()) {
  2787       nsIFrame* child = childFrames.get();
  2788       // Out-of-flows are reached through their placeholders.  Continuations
  2789       // and block-in-inline splits are reached through those chains.
  2790       if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
  2791           !GetPrevContinuationWithSameStyle(child)) {
  2792         // Get the parent of the child frame's content and check if it
  2793         // is a XBL children element. Push the children element as an
  2794         // ancestor here because it does not have a frame and would not
  2795         // otherwise be pushed as an ancestor.
  2797         // Check if the frame has a content because |child| may be a
  2798         // nsPageFrame that does not have a content.
  2799         nsIContent* parent = child->GetContent() ? child->GetContent()->GetParent() : nullptr;
  2800         TreeMatchContext::AutoAncestorPusher insertionPointPusher(mTreeMatchContext);
  2801         if (parent && nsContentUtils::IsContentInsertionPoint(parent)) {
  2802           insertionPointPusher.PushAncestorAndStyleScope(parent);
  2805         // only do frames that are in flow
  2806         if (nsGkAtoms::placeholderFrame == child->GetType()) { // placeholder
  2807           // get out of flow frame and recur there
  2808           nsIFrame* outOfFlowFrame =
  2809             nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
  2810           NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
  2811           NS_ASSERTION(outOfFlowFrame != mResolvedChild,
  2812                        "out-of-flow frame not a true descendant");
  2814           // |nsFrame::GetParentStyleContextFrame| checks being out
  2815           // of flow so that this works correctly.
  2816           do {
  2817             if (GetPrevContinuationWithSameStyle(outOfFlowFrame)) {
  2818               // Later continuations are likely restyled as a result of
  2819               // the restyling of the previous continuation.
  2820               // (Currently that's always true, but it's likely to
  2821               // change if we implement overflow:fragments or similar.)
  2822               continue;
  2824             ElementRestyler oofRestyler(*this, outOfFlowFrame,
  2825                                         FOR_OUT_OF_FLOW_CHILD);
  2826             oofRestyler.Restyle(aChildRestyleHint);
  2827           } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
  2829           // reresolve placeholder's context under the same parent
  2830           // as the out-of-flow frame
  2831           ElementRestyler phRestyler(*this, child, 0);
  2832           phRestyler.Restyle(aChildRestyleHint);
  2834         else {  // regular child frame
  2835           if (child != mResolvedChild) {
  2836             ElementRestyler childRestyler(*this, child, 0);
  2837             childRestyler.Restyle(aChildRestyleHint);
  2843   // XXX need to do overflow frames???
  2846 void
  2847 ElementRestyler::SendAccessibilityNotifications()
  2849 #ifdef ACCESSIBILITY
  2850   // Send notifications about visibility changes.
  2851   if (mOurA11yNotification == eNotifyShown) {
  2852     nsAccessibilityService* accService = nsIPresShell::AccService();
  2853     if (accService) {
  2854       nsIPresShell* presShell = mFrame->PresContext()->GetPresShell();
  2855       nsIContent* content = mFrame->GetContent();
  2857       accService->ContentRangeInserted(presShell, content->GetParent(),
  2858                                        content,
  2859                                        content->GetNextSibling());
  2861   } else if (mOurA11yNotification == eNotifyHidden) {
  2862     nsAccessibilityService* accService = nsIPresShell::AccService();
  2863     if (accService) {
  2864       nsIPresShell* presShell = mFrame->PresContext()->GetPresShell();
  2865       nsIContent* content = mFrame->GetContent();
  2866       accService->ContentRemoved(presShell, content->GetParent(), content);
  2868       // Process children staying shown.
  2869       uint32_t visibleContentCount = mVisibleKidsOfHiddenElement.Length();
  2870       for (uint32_t idx = 0; idx < visibleContentCount; idx++) {
  2871         nsIContent* childContent = mVisibleKidsOfHiddenElement[idx];
  2872         accService->ContentRangeInserted(presShell, childContent->GetParent(),
  2873                                          childContent,
  2874                                          childContent->GetNextSibling());
  2876       mVisibleKidsOfHiddenElement.Clear();
  2879 #endif
  2882 static inline nsIFrame*
  2883 GetNextBlockInInlineSibling(FramePropertyTable* aPropTable, nsIFrame* aFrame)
  2885   NS_ASSERTION(!aFrame->GetPrevContinuation(),
  2886                "must start with the first continuation");
  2887   // Might we have ib-split siblings?
  2888   if (!(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
  2889     // nothing more to do here
  2890     return nullptr;
  2893   return static_cast<nsIFrame*>
  2894     (aPropTable->Get(aFrame, nsIFrame::IBSplitSibling()));
  2897 void
  2898 RestyleManager::ComputeStyleChangeFor(nsIFrame*          aFrame,
  2899                                       nsStyleChangeList* aChangeList,
  2900                                       nsChangeHint       aMinChange,
  2901                                       RestyleTracker&    aRestyleTracker,
  2902                                       bool               aRestyleDescendants)
  2904   PROFILER_LABEL("CSS", "ComputeStyleChangeFor");
  2906   nsIContent *content = aFrame->GetContent();
  2907   if (aMinChange) {
  2908     aChangeList->AppendChange(aFrame, content, aMinChange);
  2911   NS_ASSERTION(!aFrame->GetPrevContinuation(),
  2912                "must start with the first continuation");
  2914   // We want to start with this frame and walk all its next-in-flows,
  2915   // as well as all its ib-split siblings and their next-in-flows,
  2916   // reresolving style on all the frames we encounter in this walk that
  2917   // we didn't reach already.  In the normal case, this will mean only
  2918   // restyling the first two block-in-inline splits and no
  2919   // continuations, and skipping everything else.  However, when we have
  2920   // a style change targeted at an element inside a context where styles
  2921   // vary between continuations (e.g., a style change on an element that
  2922   // extends from inside a styled ::first-line to outside of that first
  2923   // line), we might restyle more than that.
  2925   FramePropertyTable* propTable = mPresContext->PropertyTable();
  2927   TreeMatchContext treeMatchContext(true,
  2928                                     nsRuleWalker::eRelevantLinkUnvisited,
  2929                                     mPresContext->Document());
  2930   nsIContent *parent = content ? content->GetParent() : nullptr;
  2931   Element *parentElement =
  2932     parent && parent->IsElement() ? parent->AsElement() : nullptr;
  2933   treeMatchContext.InitAncestors(parentElement);
  2934   nsTArray<nsIContent*> visibleKidsOfHiddenElement;
  2935   for (nsIFrame* ibSibling = aFrame; ibSibling;
  2936        ibSibling = GetNextBlockInInlineSibling(propTable, ibSibling)) {
  2937     // Outer loop over ib-split siblings
  2938     for (nsIFrame* cont = ibSibling; cont; cont = cont->GetNextContinuation()) {
  2939       if (GetPrevContinuationWithSameStyle(cont)) {
  2940         // We already handled this element when dealing with its earlier
  2941         // continuation.
  2942         continue;
  2945       // Inner loop over next-in-flows of the current frame
  2946       ElementRestyler restyler(mPresContext, cont, aChangeList,
  2947                                aMinChange, aRestyleTracker,
  2948                                treeMatchContext,
  2949                                visibleKidsOfHiddenElement);
  2951       restyler.Restyle(aRestyleDescendants ? eRestyle_Subtree : eRestyle_Self);
  2953       if (restyler.HintsHandledForFrame() & nsChangeHint_ReconstructFrame) {
  2954         // If it's going to cause a framechange, then don't bother
  2955         // with the continuations or ib-split siblings since they'll be
  2956         // clobbered by the frame reconstruct anyway.
  2957         NS_ASSERTION(!cont->GetPrevContinuation(),
  2958                      "continuing frame had more severe impact than first-in-flow");
  2959         return;
  2965 } // namespace mozilla

mercurial