layout/base/RestyleManager.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:951d880a3d3f
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/. */
5
6 /**
7 * Code responsible for managing style changes: tracking what style
8 * changes need to happen, scheduling them, and doing them.
9 */
10
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"
39
40 #ifdef ACCESSIBILITY
41 #include "nsAccessibilityService.h"
42 #endif
43
44 namespace mozilla {
45
46 using namespace layers;
47
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 }
64
65 void
66 RestyleManager::NotifyDestroyingFrame(nsIFrame* aFrame)
67 {
68 mOverflowChangedTracker.RemoveFrame(aFrame);
69 }
70
71 #ifdef DEBUG
72 // To ensure that the functions below are only called within
73 // |ApplyRenderingChangeToTree|.
74 static bool gInApplyRenderingChangeToTree = false;
75 #endif
76
77 static void
78 DoApplyRenderingChangeToTree(nsIFrame* aFrame,
79 nsChangeHint aChange);
80
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");
99
100 nsView* view = aFrame->GetView();
101 if (view) {
102 if (aChange & nsChangeHint_SyncFrameView) {
103 nsContainerFrame::SyncFrameViewProperties(aFrame->PresContext(),
104 aFrame, nullptr, view);
105 }
106 }
107
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 }
129
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 }
170
171 static void
172 DoApplyRenderingChangeToTree(nsIFrame* aFrame,
173 nsChangeHint aChange)
174 {
175 NS_PRECONDITION(gInApplyRenderingChangeToTree,
176 "should only be called within ApplyRenderingChangeToTree");
177
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;
192
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;
228
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);
246
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 }
269
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");
282
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 }
291
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 }
299
300 // Trigger rendering updates by damaging this frame and any
301 // continuations of this frame.
302
303 // XXX this needs to detect the need for a view due to an opacity change and deal with it...
304
305 #ifdef DEBUG
306 gInApplyRenderingChangeToTree = true;
307 #endif
308 DoApplyRenderingChangeToTree(aFrame, aChange);
309 #ifdef DEBUG
310 gInApplyRenderingChangeToTree = false;
311 #endif
312 }
313
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 }
323
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 }
329
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 }
338
339 aFrame->SchedulePaint();
340
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 }
349
350
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();
369
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");
376
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 }
384
385 return true;
386 }
387
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();
394
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();
399
400 nsFrameState savedState = parentFrame->GetStateBits();
401 nsHTMLReflowState parentReflowState(aFrame->PresContext(), parentFrame,
402 rc, parentSize);
403 parentFrame->RemoveStateBits(~nsFrameState(0));
404 parentFrame->AddStateBits(savedState);
405
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);
412
413 parentReflowState.ComputedPhysicalPadding() = parentFrame->GetUsedPadding();
414 parentReflowState.ComputedPhysicalBorderPadding() =
415 parentFrame->GetUsedBorderAndPadding();
416 nsSize availSize(parentSize.width, NS_INTRINSICSIZE);
417
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 }
451
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 }
459
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);
466
467 return true;
468 }
469
470 // Fall back to a reflow
471 StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
472 return false;
473 }
474
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 }
488
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 }
497
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;
502
503 do {
504 mPresContext->PresShell()->FrameNeedsReflow(aFrame, dirtyType, dirtyBits);
505 aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
506 } while (aFrame);
507
508 return NS_OK;
509 }
510
511 NS_DECLARE_FRAME_PROPERTY(ChangeListProperty, nullptr)
512
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 }
545
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");
553
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 }
578
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;
587
588 PROFILER_LABEL("CSS", "ProcessRestyledFrames");
589
590 // Make sure to not rebuild quote or counter lists while we're
591 // processing restyles
592 FrameConstructor()->BeginUpdate();
593
594 FramePropertyTable* propTable = mPresContext->PropertyTable();
595
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;
600
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 }
609
610 index = count;
611
612 bool didUpdateCursor = false;
613
614 while (0 <= --index) {
615 nsIFrame* frame;
616 nsIContent* content;
617 bool didReflowThisFrame = false;
618 nsChangeHint hint;
619 aChangeList.ChangeAt(index, frame, content, hint);
620
621 NS_ASSERTION(!(hint & nsChangeHint_AllReflowHints) ||
622 (hint & nsChangeHint_NeedReflow),
623 "Reflow hint bits set without actually asking for a reflow");
624
625 // skip any frame that has been destroyed due to a ripple effect
626 if (frame && !propTable->Get(frame, ChangeListProperty())) {
627 continue;
628 }
629
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 }
638
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");
685
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 }
695
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 }
702
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 }
783
784 FrameConstructor()->EndUpdate();
785
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 }
796
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 }
814
815 aChangeList.Clear();
816 return NS_OK;
817 }
818
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");
835
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 }
855
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 }
868
869 static inline dom::Element*
870 ElementForStyleContext(nsIContent* aParentContent,
871 nsIFrame* aFrame,
872 nsCSSPseudoElements::Type aPseudoType);
873
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 }
885
886 Element* aElement = aContent->AsElement();
887
888 nsStyleSet* styleSet = mPresContext->StyleSet();
889 NS_ASSERTION(styleSet, "couldn't get style set");
890
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 }
923
924 pseudoType = primaryFrame->StyleContext()->GetPseudoType();
925
926 primaryFrame->ContentStatesChanged(aStateMask);
927 }
928
929
930 nsRestyleHint rshint;
931
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 }
947
948 if (aStateMask.HasState(NS_EVENT_STATE_HOVER) && rshint != 0) {
949 ++mHoverGeneration;
950 }
951
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 }
958
959 PostRestyleEvent(aElement, rshint, hint);
960 return NS_OK;
961 }
962
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 }
978
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();
991
992 // Get the frame associated with the content which is the highest in the frame tree
993 nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
994
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
1000
1001 // the style tag has its own interpretation based on aHint
1002 nsChangeHint hint = aElement->GetAttributeChangeHint(aAttribute, aModType);
1003
1004 bool reframe = (hint & nsChangeHint_ReconstructFrame) != 0;
1005
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);
1014
1015 if (namespaceID == kNameSpaceID_XUL &&
1016 (tag == nsGkAtoms::listitem ||
1017 tag == nsGkAtoms::listcell))
1018 return;
1019 }
1020
1021 if (aAttribute == nsGkAtoms::tooltiptext ||
1022 aAttribute == nsGkAtoms::tooltip)
1023 {
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);
1030 }
1031 }
1032
1033 #endif // MOZ_XUL
1034
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);
1045 }
1046 }
1047
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.
1054 }
1055
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);
1064
1065 PostRestyleEvent(aElement, rshint, hint);
1066 }
1067
1068 void
1069 RestyleManager::RestyleForEmptyChange(Element* aContainer)
1070 {
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);
1078 }
1079 PostRestyleEvent(aContainer, hint, NS_STYLE_HINT_NONE);
1080 }
1081
1082 void
1083 RestyleManager::RestyleForAppend(Element* aContainer,
1084 nsIContent* aFirstNewContent)
1085 {
1086 NS_ASSERTION(aContainer, "must have container for append");
1087 #ifdef DEBUG
1088 {
1089 for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
1090 NS_ASSERTION(!cur->IsRootOfAnonymousSubtree(),
1091 "anonymous nodes should not be in child lists");
1092 }
1093 }
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;
1100
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;
1114 }
1115 }
1116 if (wasEmpty) {
1117 RestyleForEmptyChange(aContainer);
1118 return;
1119 }
1120 }
1121
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;
1126 }
1127
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;
1136 }
1137 }
1138 }
1139 }
1140
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 */)
1147 {
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;
1156 }
1157 }
1158 }
1159
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)
1169 {
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;
1176
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;
1192 }
1193 }
1194 if (wasEmpty) {
1195 RestyleForEmptyChange(aContainer);
1196 return;
1197 }
1198 }
1199
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;
1204 }
1205
1206 if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
1207 // Restyle all later siblings.
1208 RestyleSiblingsStartingWith(this, aChild->GetNextSibling());
1209 }
1210
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;
1220 }
1221 if (content->IsElement()) {
1222 if (passedChild) {
1223 PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
1224 NS_STYLE_HINT_NONE);
1225 }
1226 break;
1227 }
1228 }
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;
1237 }
1238 if (content->IsElement()) {
1239 if (passedChild) {
1240 PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
1241 NS_STYLE_HINT_NONE);
1242 }
1243 break;
1244 }
1245 }
1246 }
1247 }
1248
1249 void
1250 RestyleManager::RestyleForRemove(Element* aContainer,
1251 nsIContent* aOldChild,
1252 nsIContent* aFollowingSibling)
1253 {
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)");
1259 }
1260 uint32_t selectorFlags =
1261 aContainer ? (aContainer->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
1262 if (selectorFlags == 0)
1263 return;
1264
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;
1278 }
1279 }
1280 if (isEmpty) {
1281 RestyleForEmptyChange(aContainer);
1282 return;
1283 }
1284 }
1285
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;
1290 }
1291
1292 if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
1293 // Restyle all later siblings.
1294 RestyleSiblingsStartingWith(this, aFollowingSibling);
1295 }
1296
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
1306 }
1307 if (content->IsElement()) {
1308 if (reachedFollowingSibling) {
1309 PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
1310 NS_STYLE_HINT_NONE);
1311 }
1312 break;
1313 }
1314 }
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);
1323 }
1324 break;
1325 }
1326 if (content == aFollowingSibling) {
1327 reachedFollowingSibling = true;
1328 }
1329 }
1330 }
1331 }
1332
1333 void
1334 RestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint)
1335 {
1336 NS_ASSERTION(!(aExtraHint & nsChangeHint_ReconstructFrame),
1337 "Should not reconstruct the root of the frame tree. "
1338 "Use ReconstructDocElementHierarchy instead.");
1339
1340 mRebuildAllStyleData = false;
1341 NS_UpdateHint(aExtraHint, mRebuildAllExtraHint);
1342 mRebuildAllExtraHint = nsChangeHint(0);
1343
1344 nsIPresShell* presShell = mPresContext->GetPresShell();
1345 if (!presShell || !presShell->GetRootFrame())
1346 return;
1347
1348 // Make sure that the viewmanager will outlive the presshell
1349 nsRefPtr<nsViewManager> vm = presShell->GetViewManager();
1350
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);
1354
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);
1358
1359 nsAutoScriptBlocker scriptBlocker;
1360
1361 mPresContext->SetProcessingRestyles(true);
1362
1363 DoRebuildAllStyleData(mPendingRestyles, aExtraHint);
1364
1365 mPresContext->SetProcessingRestyles(false);
1366
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();
1372 }
1373
1374 void
1375 RestyleManager::DoRebuildAllStyleData(RestyleTracker& aRestyleTracker,
1376 nsChangeHint aExtraHint)
1377 {
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;
1383 }
1384
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();
1401
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();
1408 }
1409
1410 void
1411 RestyleManager::ProcessPendingRestyles()
1412 {
1413 NS_PRECONDITION(mPresContext->Document(), "No document? Pshaw!");
1414 NS_PRECONDITION(!nsContentUtils::IsSafeToRunScript(),
1415 "Missing a script blocker!");
1416
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();
1420
1421 // Process non-animation restyles...
1422 NS_ABORT_IF_FALSE(!mPresContext->IsProcessingRestyles(),
1423 "Nesting calls to ProcessPendingRestyles?");
1424 mPresContext->SetProcessingRestyles(true);
1425
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();
1435 }
1436
1437 mPendingRestyles.ProcessRestyles();
1438
1439 #ifdef DEBUG
1440 uint32_t oldPendingRestyleCount = mPendingRestyles.Count();
1441 #endif
1442
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);
1454
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");
1459
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));
1465 }
1466 }
1467
1468 void
1469 RestyleManager::BeginProcessingRestyles()
1470 {
1471 // Make sure to not rebuild quote or counter lists while we're
1472 // processing restyles
1473 mPresContext->FrameConstructor()->BeginUpdate();
1474
1475 mInStyleRefresh = true;
1476 }
1477
1478 void
1479 RestyleManager::EndProcessingRestyles()
1480 {
1481 FlushOverflowChangedTracker();
1482
1483 // Set mInStyleRefresh to false now, since the EndUpdate call might
1484 // add more restyles.
1485 mInStyleRefresh = false;
1486
1487 mPresContext->FrameConstructor()->EndUpdate();
1488
1489 #ifdef DEBUG
1490 mPresContext->PresShell()->VerifyStyleTree();
1491 #endif
1492 }
1493
1494 void
1495 RestyleManager::PostRestyleEventCommon(Element* aElement,
1496 nsRestyleHint aRestyleHint,
1497 nsChangeHint aMinChangeHint,
1498 bool aForAnimation)
1499 {
1500 if (MOZ_UNLIKELY(mPresContext->PresShell()->IsDestroying())) {
1501 return;
1502 }
1503
1504 if (aRestyleHint == 0 && !aMinChangeHint) {
1505 // Nothing to do here
1506 return;
1507 }
1508
1509 RestyleTracker& tracker =
1510 aForAnimation ? mPendingAnimationRestyles : mPendingRestyles;
1511 tracker.AddPendingRestyle(aElement, aRestyleHint, aMinChangeHint);
1512
1513 PostRestyleEventInternal(false);
1514 }
1515
1516 void
1517 RestyleManager::PostRestyleEventInternal(bool aForLazyConstruction)
1518 {
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);
1527 }
1528
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();
1533 }
1534
1535 void
1536 RestyleManager::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint)
1537 {
1538 NS_ASSERTION(!(aExtraHint & nsChangeHint_ReconstructFrame),
1539 "Should not reconstruct the root of the frame tree. "
1540 "Use ReconstructDocElementHierarchy instead.");
1541
1542 mRebuildAllStyleData = true;
1543 NS_UpdateHint(mRebuildAllExtraHint, aExtraHint);
1544
1545 // Get a restyle event posted if necessary
1546 PostRestyleEventInternal(false);
1547 }
1548
1549 #ifdef DEBUG
1550 static void
1551 DumpContext(nsIFrame* aFrame, nsStyleContext* aContext)
1552 {
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));
1559 }
1560 if (aContext) {
1561 fprintf(stdout, " style: %p ", static_cast<void*>(aContext));
1562
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);
1569 }
1570 fputs("{}\n", stdout);
1571 }
1572 }
1573
1574 static void
1575 VerifySameTree(nsStyleContext* aContext1, nsStyleContext* aContext2)
1576 {
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;
1585 }
1586 for (;;) {
1587 parent = top2->GetParent();
1588 if (!parent)
1589 break;
1590 top2 = parent;
1591 }
1592 NS_ASSERTION(top1 == top2,
1593 "Style contexts are not in the same style context tree");
1594 }
1595
1596 static void
1597 VerifyContextParent(nsPresContext* aPresContext, nsIFrame* aFrame,
1598 nsStyleContext* aContext, nsStyleContext* aParentContext)
1599 {
1600 // get the contexts not provided
1601 if (!aContext) {
1602 aContext = aFrame->StyleContext();
1603 }
1604
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
1609
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
1615 }
1616
1617 NS_ASSERTION(aContext, "Failure to get required contexts");
1618 nsStyleContext* actualParentContext = aContext->GetParent();
1619
1620 if (aParentContext) {
1621 if (aParentContext != actualParentContext) {
1622 DumpContext(aFrame, aContext);
1623 if (aContext == aParentContext) {
1624 NS_ERROR("Using parent's style context");
1625 }
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);
1634 }
1635 }
1636
1637 }
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);
1645 }
1646 }
1647
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);
1660 }
1661 }
1662
1663 static void
1664 VerifyStyleTree(nsPresContext* aPresContext, nsIFrame* aFrame,
1665 nsStyleContext* aParentContext)
1666 {
1667 nsStyleContext* context = aFrame->StyleContext();
1668 VerifyContextParent(aPresContext, aFrame, context, nullptr);
1669
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);
1682
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()));
1687
1688 // verify placeholder using the parent frame's context as
1689 // parent context
1690 VerifyContextParent(aPresContext, child, nullptr, nullptr);
1691 }
1692 else { // regular frame
1693 VerifyStyleTree(aPresContext, child, nullptr);
1694 }
1695 }
1696 }
1697 }
1698
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);
1705 }
1706 }
1707
1708 void
1709 RestyleManager::DebugVerifyStyleTree(nsIFrame* aFrame)
1710 {
1711 if (aFrame) {
1712 nsStyleContext* context = aFrame->StyleContext();
1713 nsStyleContext* parentContext = context->GetParent();
1714 VerifyStyleTree(mPresContext, aFrame, parentContext);
1715 }
1716 }
1717
1718 #endif // DEBUG
1719
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 */)
1726 {
1727 if (!aContent || !aContent->IsElement()) {
1728 return;
1729 }
1730
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);
1745 }
1746 }
1747
1748 static inline dom::Element*
1749 ElementForStyleContext(nsIContent* aParentContent,
1750 nsIFrame* aFrame,
1751 nsCSSPseudoElements::Type aPseudoType)
1752 {
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();
1762 }
1763
1764 if (aPseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
1765 return nullptr;
1766 }
1767
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();
1773 }
1774
1775 if (aPseudoType == nsCSSPseudoElements::ePseudo_mozColorSwatch) {
1776 MOZ_ASSERT(aFrame->GetParent() &&
1777 aFrame->GetParent()->GetParent(),
1778 "Color swatch frame should have a parent & grandparent");
1779
1780 nsIFrame* grandparentFrame = aFrame->GetParent()->GetParent();
1781 MOZ_ASSERT(grandparentFrame->GetType() == nsGkAtoms::colorControlFrame,
1782 "Color swatch's grandparent should be nsColorControlFrame");
1783
1784 return grandparentFrame->GetContent()->AsElement();
1785 }
1786
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);
1798 }
1799 return f->GetContent()->AsElement();
1800 }
1801
1802 if (aParentContent) {
1803 return aParentContent->AsElement();
1804 }
1805
1806 MOZ_ASSERT(aFrame->GetContent()->GetParent(),
1807 "should not have got here for the root element");
1808 return aFrame->GetContent()->GetParent()->AsElement();
1809 }
1810
1811 /**
1812 * FIXME: Temporary. Should merge with following function.
1813 */
1814 static nsIFrame*
1815 GetPrevContinuationWithPossiblySameStyle(nsIFrame* aFrame)
1816 {
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()));
1840 }
1841 }
1842
1843 NS_ASSERTION(!prevContinuation ||
1844 prevContinuation->GetContent() == aFrame->GetContent(),
1845 "unexpected content mismatch");
1846
1847 return prevContinuation;
1848 }
1849
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)
1858 {
1859 nsIFrame* prevContinuation = GetPrevContinuationWithPossiblySameStyle(aFrame);
1860 if (!prevContinuation) {
1861 return nullptr;
1862 }
1863
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;
1871 }
1872 return prevContinuation;
1873 }
1874
1875 /**
1876 * Get the next continuation or similar ib-split sibling (assuming
1877 * block/inline alternation), conditionally on it having the same style.
1878 *
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)
1888 {
1889 // See GetPrevContinuationWithSameStyle about {ib} splits.
1890
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()));
1901 }
1902 }
1903
1904 if (!nextContinuation) {
1905 return nullptr;
1906 }
1907
1908 NS_ASSERTION(nextContinuation->GetContent() == aFrame->GetContent(),
1909 "unexpected content mismatch");
1910
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;
1917 }
1918 return nextContinuation;
1919 }
1920
1921 nsresult
1922 RestyleManager::ReparentStyleContext(nsIFrame* aFrame)
1923 {
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()));
1932 }
1933
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();
1937
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!");
1952 }
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.
1958
1959 #ifdef DEBUG
1960 {
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");
1979 }
1980 }
1981 #endif
1982
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);
2005 }
2006
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);
2018 }
2019 #endif
2020
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!");
2032 }
2033
2034 aFrame->SetStyleContext(newContext);
2035
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");
2049
2050 NS_ASSERTION(outOfFlowFrame != providerChild,
2051 "Out of flow provider?");
2052 }
2053 #endif
2054 ReparentStyleContext(child);
2055 }
2056 }
2057 }
2058
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);
2071 }
2072 }
2073
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!");
2098 }
2099
2100 aFrame->SetAdditionalStyleContext(contextIndex, newExtraContext);
2101 }
2102 }
2103 #ifdef DEBUG
2104 VerifyStyleTree(mPresContext, aFrame, newParentContext);
2105 #endif
2106 }
2107 }
2108
2109 return NS_OK;
2110 }
2111
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
2140 {
2141 }
2142
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
2167 {
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);
2180 }
2181 }
2182
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
2208 {
2209 }
2210
2211 void
2212 ElementRestyler::CaptureChange(nsStyleContext* aOldContext,
2213 nsStyleContext* aNewContext,
2214 nsChangeHint aChangeToAssume)
2215 {
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");
2221
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");
2227
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);
2234 }
2235
2236 NS_UpdateHint(ourChange, aChangeToAssume);
2237 if (NS_UpdateHint(mHintsHandled, ourChange)) {
2238 if (!(ourChange & nsChangeHint_ReconstructFrame) || mContent) {
2239 mChangeList->AppendChange(mFrame, mContent, ourChange);
2240 }
2241 }
2242 NS_UpdateHint(mHintsNotHandledForDescendants,
2243 NS_HintsNotHandledForDescendantsIn(ourChange));
2244 }
2245
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)
2260 {
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)");
2272
2273 NS_ASSERTION(!GetPrevContinuationWithSameStyle(mFrame),
2274 "should not be trying to restyle this frame separately");
2275
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);
2282 }
2283 aRestyleHint = nsRestyleHint(aRestyleHint | restyleData.mRestyleHint);
2284 }
2285 }
2286
2287 nsRestyleHint childRestyleHint = aRestyleHint;
2288
2289 if (childRestyleHint == eRestyle_Self) {
2290 childRestyleHint = nsRestyleHint(0);
2291 }
2292
2293 {
2294 nsRefPtr<nsStyleContext> oldContext = mFrame->StyleContext();
2295
2296 // TEMPORARY (until bug 918064): Call RestyleSelf for each
2297 // continuation or block-in-inline sibling.
2298
2299 for (nsIFrame* f = mFrame; f;
2300 f = GetNextContinuationWithSameStyle(f, oldContext)) {
2301 RestyleSelf(f, aRestyleHint);
2302 }
2303 }
2304
2305 RestyleChildren(childRestyleHint);
2306 }
2307
2308 void
2309 ElementRestyler::RestyleSelf(nsIFrame* aSelf, nsRestyleHint aRestyleHint)
2310 {
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.
2314
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.
2318
2319 nsChangeHint assumeDifferenceHint = NS_STYLE_HINT_NONE;
2320 nsRefPtr<nsStyleContext> oldContext = aSelf->StyleContext();
2321 nsStyleSet* styleSet = mPresContext->StyleSet();
2322
2323 #ifdef ACCESSIBILITY
2324 mWasFrameVisible = nsIPresShell::IsAccessibilityActive() ?
2325 oldContext->StyleVisibility()->IsVisible() : false;
2326 #endif
2327
2328 nsIAtom* const pseudoTag = oldContext->GetPseudo();
2329 const nsCSSPseudoElements::Type pseudoType = oldContext->GetPseudoType();
2330
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;
2341 }
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.");
2347
2348 // resolve the provider here (before aSelf below).
2349
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.
2356
2357 ElementRestyler providerRestyler(PARENT_CONTEXT_FROM_CHILD_FRAME,
2358 *this, providerFrame);
2359 providerRestyler.Restyle(aRestyleHint);
2360 assumeDifferenceHint = providerRestyler.HintsHandledForFrame();
2361
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;
2368 }
2369
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;
2375 }
2376
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;
2391 }
2392 else if (pseudoTag == nsCSSAnonBoxes::mozNonElement) {
2393 NS_ASSERTION(aSelf->GetContent(),
2394 "non pseudo-element frame without content node");
2395 newContext = styleSet->ResolveStyleForNonElement(parentContext);
2396 }
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);
2413 }
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;
2431 }
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);
2447 }
2448 }
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);
2458 }
2459 }
2460
2461 MOZ_ASSERT(newContext);
2462
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;
2474 }
2475 }
2476
2477 if (newContext != oldContext) {
2478 if (!copyFromContinuation) {
2479 TryStartingTransition(mPresContext, aSelf->GetContent(),
2480 oldContext, &newContext);
2481
2482 CaptureChange(oldContext, newContext, assumeDifferenceHint);
2483 }
2484
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);
2494 }
2495 }
2496 oldContext = nullptr;
2497
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);
2515 }
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);
2526 }
2527
2528 MOZ_ASSERT(newExtraContext);
2529
2530 if (oldExtraContext != newExtraContext) {
2531 CaptureChange(oldExtraContext, newExtraContext, assumeDifferenceHint);
2532 if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
2533 aSelf->SetAdditionalStyleContext(contextIndex, newExtraContext);
2534 }
2535 }
2536 }
2537 }
2538
2539 void
2540 ElementRestyler::RestyleChildren(nsRestyleHint aChildRestyleHint)
2541 {
2542 RestyleUndisplayedChildren(aChildRestyleHint);
2543
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();
2554 }
2555
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();
2568
2569 for (nsIFrame* f = mFrame; f;
2570 f = GetNextContinuationWithSameStyle(f, f->StyleContext())) {
2571 lastContinuation = f;
2572 RestyleContentChildren(f, aChildRestyleHint);
2573 }
2574
2575 SendAccessibilityNotifications();
2576 }
2577
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);
2583 }
2584 }
2585
2586 void
2587 ElementRestyler::RestyleUndisplayedChildren(nsRestyleHint aChildRestyleHint)
2588 {
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();
2602 }
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);
2615 }
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");
2624
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);
2632 }
2633
2634 nsRestyleHint thisChildHint = aChildRestyleHint;
2635 RestyleTracker::RestyleData undisplayedRestyleData;
2636 if (mRestyleTracker.GetRestyleData(undisplayed->mContent->AsElement(),
2637 &undisplayedRestyleData)) {
2638 thisChildHint =
2639 nsRestyleHint(thisChildHint | undisplayedRestyleData.mRestyleHint);
2640 }
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());
2653 }
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;
2665 }
2666 }
2667 }
2668 }
2669
2670 void
2671 ElementRestyler::RestyleBeforePseudo()
2672 {
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);
2695 }
2696 }
2697 }
2698 }
2699
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)
2706 {
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();
2717
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);
2730 }
2731 }
2732 }
2733 }
2734
2735 void
2736 ElementRestyler::InitializeAccessibilityNotifications()
2737 {
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;
2762 }
2763 }
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;
2770 }
2771 }
2772 #endif
2773 }
2774
2775 void
2776 ElementRestyler::RestyleContentChildren(nsIFrame* aParent,
2777 nsRestyleHint aChildRestyleHint)
2778 {
2779 nsIFrame::ChildListIterator lists(aParent);
2780 TreeMatchContext::AutoAncestorPusher ancestorPusher(mTreeMatchContext);
2781 if (!lists.IsDone()) {
2782 ancestorPusher.PushAncestorAndStyleScope(mContent);
2783 }
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.
2796
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);
2803 }
2804
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");
2813
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;
2823 }
2824 ElementRestyler oofRestyler(*this, outOfFlowFrame,
2825 FOR_OUT_OF_FLOW_CHILD);
2826 oofRestyler.Restyle(aChildRestyleHint);
2827 } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
2828
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);
2833 }
2834 else { // regular child frame
2835 if (child != mResolvedChild) {
2836 ElementRestyler childRestyler(*this, child, 0);
2837 childRestyler.Restyle(aChildRestyleHint);
2838 }
2839 }
2840 }
2841 }
2842 }
2843 // XXX need to do overflow frames???
2844 }
2845
2846 void
2847 ElementRestyler::SendAccessibilityNotifications()
2848 {
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();
2856
2857 accService->ContentRangeInserted(presShell, content->GetParent(),
2858 content,
2859 content->GetNextSibling());
2860 }
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);
2867
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());
2875 }
2876 mVisibleKidsOfHiddenElement.Clear();
2877 }
2878 }
2879 #endif
2880 }
2881
2882 static inline nsIFrame*
2883 GetNextBlockInInlineSibling(FramePropertyTable* aPropTable, nsIFrame* aFrame)
2884 {
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;
2891 }
2892
2893 return static_cast<nsIFrame*>
2894 (aPropTable->Get(aFrame, nsIFrame::IBSplitSibling()));
2895 }
2896
2897 void
2898 RestyleManager::ComputeStyleChangeFor(nsIFrame* aFrame,
2899 nsStyleChangeList* aChangeList,
2900 nsChangeHint aMinChange,
2901 RestyleTracker& aRestyleTracker,
2902 bool aRestyleDescendants)
2903 {
2904 PROFILER_LABEL("CSS", "ComputeStyleChangeFor");
2905
2906 nsIContent *content = aFrame->GetContent();
2907 if (aMinChange) {
2908 aChangeList->AppendChange(aFrame, content, aMinChange);
2909 }
2910
2911 NS_ASSERTION(!aFrame->GetPrevContinuation(),
2912 "must start with the first continuation");
2913
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.
2924
2925 FramePropertyTable* propTable = mPresContext->PropertyTable();
2926
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;
2943 }
2944
2945 // Inner loop over next-in-flows of the current frame
2946 ElementRestyler restyler(mPresContext, cont, aChangeList,
2947 aMinChange, aRestyleTracker,
2948 treeMatchContext,
2949 visibleKidsOfHiddenElement);
2950
2951 restyler.Restyle(aRestyleDescendants ? eRestyle_Subtree : eRestyle_Self);
2952
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;
2960 }
2961 }
2962 }
2963 }
2964
2965 } // namespace mozilla

mercurial