|
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 |