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