NS_IMETHODIMP nsFirstLetterFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowState& aReflowState, nsReflowStatus& aReflowStatus) { DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aReflowStatus); nsresult rv = NS_OK; // Grab overflow list DrainOverflowFrames(aPresContext); nsIFrame* kid = mFrames.FirstChild(); // Setup reflow state for our child nsSize availSize(aReflowState.availableWidth, aReflowState.availableHeight); const nsMargin& bp = aReflowState.mComputedBorderPadding; nscoord lr = bp.left + bp.right; nscoord tb = bp.top + bp.bottom; NS_ASSERTION(availSize.width != NS_UNCONSTRAINEDSIZE, "should no longer use unconstrained widths"); availSize.width -= lr; if (NS_UNCONSTRAINEDSIZE != availSize.height) { availSize.height -= tb; } // Reflow the child if (!aReflowState.mLineLayout) { // When there is no lineLayout provided, we provide our own. The // only time that the first-letter-frame is not reflowing in a // line context is when its floating. nsHTMLReflowState rs(aPresContext, aReflowState, kid, availSize); nsLineLayout ll(aPresContext, nsnull, &aReflowState, nsnull); // For unicode-bidi: plaintext, we need to get the direction of the line // from the resolved paragraph level of the child, not the block frame, // because the block frame could be split by hard line breaks into // multiple paragraphs with different base direction PRUint8 direction; nsIFrame* containerFrame = ll.GetLineContainerFrame(); if (containerFrame->GetStyleTextReset()->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT) { FramePropertyTable *propTable = aPresContext->PropertyTable(); direction = NS_PTR_TO_INT32(propTable->Get(kid, BaseLevelProperty())) & 1; } else { direction = containerFrame->GetStyleVisibility()->mDirection; } ll.BeginLineReflow(bp.left, bp.top, availSize.width, NS_UNCONSTRAINEDSIZE, false, true, direction); rs.mLineLayout = ≪ ll.SetInFirstLetter(true); ll.SetFirstLetterStyleOK(true); kid->WillReflow(aPresContext); kid->Reflow(aPresContext, aMetrics, rs, aReflowStatus); ll.EndLineReflow(); ll.SetInFirstLetter(false); // In the floating first-letter case, we need to set this ourselves; // nsLineLayout::BeginSpan will set it in the other case mBaseline = aMetrics.ascent; } else { // Pretend we are a span and reflow the child frame nsLineLayout* ll = aReflowState.mLineLayout; bool pushedFrame; ll->SetInFirstLetter( mStyleContext->GetPseudo() == nsCSSPseudoElements::firstLetter); ll->BeginSpan(this, &aReflowState, bp.left, availSize.width, &mBaseline); ll->ReflowFrame(kid, aReflowStatus, &aMetrics, pushedFrame); ll->EndSpan(this); ll->SetInFirstLetter(false); } // Place and size the child and update the output metrics kid->SetRect(nsRect(bp.left, bp.top, aMetrics.width, aMetrics.height)); kid->FinishAndStoreOverflow(&aMetrics); kid->DidReflow(aPresContext, nsnull, NS_FRAME_REFLOW_FINISHED); aMetrics.width += lr; aMetrics.height += tb; aMetrics.ascent += bp.top; // Ensure that the overflow rect contains the child textframe's overflow rect. // Note that if this is floating, the overline/underline drawable area is in // the overflow rect of the child textframe. aMetrics.UnionOverflowAreasWithDesiredBounds(); ConsiderChildOverflow(aMetrics.mOverflowAreas, kid); if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) { // Create a continuation or remove existing continuations based on // the reflow completion status. if (NS_FRAME_IS_COMPLETE(aReflowStatus)) { if (aReflowState.mLineLayout) { aReflowState.mLineLayout->SetFirstLetterStyleOK(false); } nsIFrame* kidNextInFlow = kid->GetNextInFlow(); if (kidNextInFlow) { // Remove all of the childs next-in-flows static_cast<nsContainerFrame*>(kidNextInFlow->GetParent()) ->DeleteNextInFlowChild(aPresContext, kidNextInFlow, true); } } else { // Create a continuation for the child frame if it doesn't already // have one. if (!IsFloating()) { nsIFrame* nextInFlow; rv = CreateNextInFlow(aPresContext, kid, nextInFlow); if (NS_FAILED(rv)) { return rv; } // And then push it to our overflow list const nsFrameList& overflow = mFrames.RemoveFramesAfter(kid); if (overflow.NotEmpty()) { SetOverflowFrames(aPresContext, overflow); } } else if (!kid->GetNextInFlow()) { // For floating first letter frames (if a continuation wasn't already // created for us) we need to put the continuation with the rest of the // text that the first letter frame was made out of. nsIFrame* continuation; rv = CreateContinuationForFloatingParent(aPresContext, kid, &continuation, true); } } } FinishAndStoreOverflow(&aMetrics); NS_FRAME_SET_TRUNCATION(aReflowStatus, aReflowState, aMetrics); return rv; }
nsresult RestyleManagerBase::ProcessRestyledFrames(nsStyleChangeList& aChangeList) { NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), "Someone forgot a script blocker"); int32_t count = aChangeList.Count(); if (!count) return NS_OK; PROFILER_LABEL("RestyleManager", "ProcessRestyledFrames", js::ProfileEntry::Category::CSS); nsPresContext* presContext = PresContext(); FramePropertyTable* propTable = presContext->PropertyTable(); nsCSSFrameConstructor* frameConstructor = presContext->FrameConstructor(); // Make sure to not rebuild quote or counter lists while we're // processing restyles frameConstructor->BeginUpdate(); // Mark frames so that we skip frames that die along the way, bug 123049. // A frame can be in the list multiple times with different hints. Further // optmization is possible if nsStyleChangeList::AppendChange could coalesce int32_t index = count; while (0 <= --index) { const nsStyleChangeData* changeData; aChangeList.ChangeAt(index, &changeData); if (changeData->mFrame) { propTable->Set(changeData->mFrame, ChangeListProperty(), true); } } index = count; bool didUpdateCursor = false; while (0 <= --index) { nsIFrame* frame; nsIContent* content; bool didReflowThisFrame = false; nsChangeHint hint; aChangeList.ChangeAt(index, frame, content, hint); NS_ASSERTION(!(hint & nsChangeHint_AllReflowHints) || (hint & nsChangeHint_NeedReflow), "Reflow hint bits set without actually asking for a reflow"); // skip any frame that has been destroyed due to a ripple effect if (frame && !propTable->Get(frame, ChangeListProperty())) { continue; } if (frame && frame->GetContent() != content) { // XXXbz this is due to image maps messing with the primary frame of // <area>s. See bug 135040. Remove this block once that's fixed. frame = nullptr; if (!(hint & nsChangeHint_ReconstructFrame)) { continue; } } if ((hint & nsChangeHint_UpdateContainingBlock) && frame && !(hint & nsChangeHint_ReconstructFrame)) { if (NeedToReframeForAddingOrRemovingTransform(frame) || frame->GetType() == nsGkAtoms::fieldSetFrame || frame->GetContentInsertionFrame() != frame) { // The frame has positioned children that need to be reparented, or // it can't easily be converted to/from being an abs-pos container correctly. hint |= nsChangeHint_ReconstructFrame; } else { for (nsIFrame* cont = frame; cont; cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) { // Normally frame construction would set state bits as needed, // but we're not going to reconstruct the frame so we need to set them. // It's because we need to set this state on each affected frame // that we can't coalesce nsChangeHint_UpdateContainingBlock hints up // to ancestors (i.e. it can't be an inherited change hint). if (cont->IsAbsPosContaininingBlock()) { if (cont->StyleDisplay()->HasTransform(cont)) { cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED); } if (!cont->IsAbsoluteContainer() && (cont->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) { cont->MarkAsAbsoluteContainingBlock(); } } else { // Don't remove NS_FRAME_MAY_BE_TRANSFORMED since it may still by // transformed by other means. It's OK to have the bit even if it's // not needed. if (cont->IsAbsoluteContainer()) { cont->MarkAsNotAbsoluteContainingBlock(); } } } } } if (hint & nsChangeHint_ReconstructFrame) { // If we ever start passing true here, be careful of restyles // that involve a reframe and animations. In particular, if the // restyle we're processing here is an animation restyle, but // the style resolution we will do for the frame construction // happens async when we're not in an animation restyle already, // problems could arise. // We could also have problems with triggering of CSS transitions // on elements whose frames are reconstructed, since we depend on // the reconstruction happening synchronously. frameConstructor->RecreateFramesForContent(content, false, nsCSSFrameConstructor::REMOVE_FOR_RECONSTRUCTION, nullptr); } else { NS_ASSERTION(frame, "This shouldn't happen"); if (!frame->FrameMaintainsOverflow()) { // frame does not maintain overflow rects, so avoid calling // FinishAndStoreOverflow on it: hint &= ~(nsChangeHint_UpdateOverflow | nsChangeHint_ChildrenOnlyTransform | nsChangeHint_UpdatePostTransformOverflow | nsChangeHint_UpdateParentOverflow); } if (!(frame->GetStateBits() & NS_FRAME_MAY_BE_TRANSFORMED)) { // Frame can not be transformed, and thus a change in transform will // have no effect and we should not use the // nsChangeHint_UpdatePostTransformOverflow hint. hint &= ~nsChangeHint_UpdatePostTransformOverflow; } if (hint & nsChangeHint_UpdateEffects) { for (nsIFrame* cont = frame; cont; cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) { nsSVGEffects::UpdateEffects(cont); } } if ((hint & nsChangeHint_InvalidateRenderingObservers) || ((hint & nsChangeHint_UpdateOpacityLayer) && frame->IsFrameOfType(nsIFrame::eSVG) && !(frame->GetStateBits() & NS_STATE_IS_OUTER_SVG))) { nsSVGEffects::InvalidateRenderingObservers(frame); } if (hint & nsChangeHint_NeedReflow) { StyleChangeReflow(frame, hint); didReflowThisFrame = true; } if ((hint & nsChangeHint_UpdateUsesOpacity) && frame->IsFrameOfType(nsIFrame::eTablePart)) { NS_ASSERTION(hint & nsChangeHint_UpdateOpacityLayer, "should only return UpdateUsesOpacity hint " "when also returning UpdateOpacityLayer hint"); // When an internal table part (including cells) changes between // having opacity 1 and non-1, it changes whether its // backgrounds (and those of table parts inside of it) are // painted as part of the table's nsDisplayTableBorderBackground // display item, or part of its own display item. That requires // invalidation, so change UpdateOpacityLayer to RepaintFrame. hint &= ~nsChangeHint_UpdateOpacityLayer; hint |= nsChangeHint_RepaintFrame; } if (hint & nsChangeHint_UpdateBackgroundPosition) { // For most frame types, DLBI can detect background position changes, // so we only need to schedule a paint. hint |= nsChangeHint_SchedulePaint; if (frame->IsFrameOfType(nsIFrame::eTablePart) || frame->IsFrameOfType(nsIFrame::eMathML)) { // Table parts and MathML frames don't build display items for their // backgrounds, so DLBI can't detect background-position changes for // these frames. Repaint the whole frame. hint |= nsChangeHint_RepaintFrame; } } if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView | nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer | nsChangeHint_ChildrenOnlyTransform | nsChangeHint_SchedulePaint)) { ApplyRenderingChangeToTree(presContext->PresShell(), frame, hint); } if ((hint & nsChangeHint_RecomputePosition) && !didReflowThisFrame) { ActiveLayerTracker::NotifyOffsetRestyle(frame); // It is possible for this to fall back to a reflow if (!RecomputePosition(frame)) { didReflowThisFrame = true; } } NS_ASSERTION(!(hint & nsChangeHint_ChildrenOnlyTransform) || (hint & nsChangeHint_UpdateOverflow), "nsChangeHint_UpdateOverflow should be passed too"); if (!didReflowThisFrame && (hint & (nsChangeHint_UpdateOverflow | nsChangeHint_UpdatePostTransformOverflow | nsChangeHint_UpdateParentOverflow | nsChangeHint_UpdateSubtreeOverflow))) { if (hint & nsChangeHint_UpdateSubtreeOverflow) { for (nsIFrame* cont = frame; cont; cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) { AddSubtreeToOverflowTracker(cont, mOverflowChangedTracker); } // The work we just did in AddSubtreeToOverflowTracker // subsumes some of the other hints: hint &= ~(nsChangeHint_UpdateOverflow | nsChangeHint_UpdatePostTransformOverflow); } if (hint & nsChangeHint_ChildrenOnlyTransform) { // The overflow areas of the child frames need to be updated: nsIFrame* hintFrame = GetFrameForChildrenOnlyTransformHint(frame); nsIFrame* childFrame = hintFrame->PrincipalChildList().FirstChild(); NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame), "SVG frames should not have continuations " "or ib-split siblings"); NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(hintFrame), "SVG frames should not have continuations " "or ib-split siblings"); for ( ; childFrame; childFrame = childFrame->GetNextSibling()) { MOZ_ASSERT(childFrame->IsFrameOfType(nsIFrame::eSVG), "Not expecting non-SVG children"); // If |childFrame| is dirty or has dirty children, we don't bother // updating overflows since that will happen when it's reflowed. if (!(childFrame->GetStateBits() & (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) { mOverflowChangedTracker.AddFrame(childFrame, OverflowChangedTracker::CHILDREN_CHANGED); } NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(childFrame), "SVG frames should not have continuations " "or ib-split siblings"); NS_ASSERTION(childFrame->GetParent() == hintFrame, "SVG child frame not expected to have different parent"); } } // If |frame| is dirty or has dirty children, we don't bother updating // overflows since that will happen when it's reflowed. if (!(frame->GetStateBits() & (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) { if (hint & (nsChangeHint_UpdateOverflow | nsChangeHint_UpdatePostTransformOverflow)) { OverflowChangedTracker::ChangeKind changeKind; // If we have both nsChangeHint_UpdateOverflow and // nsChangeHint_UpdatePostTransformOverflow, // CHILDREN_CHANGED is selected as it is // strictly stronger. if (hint & nsChangeHint_UpdateOverflow) { changeKind = OverflowChangedTracker::CHILDREN_CHANGED; } else { changeKind = OverflowChangedTracker::TRANSFORM_CHANGED; } for (nsIFrame* cont = frame; cont; cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) { mOverflowChangedTracker.AddFrame(cont, changeKind); } } // UpdateParentOverflow hints need to be processed in addition // to the above, since if the processing of the above hints // yields no change, the update will not propagate to the // parent. if (hint & nsChangeHint_UpdateParentOverflow) { MOZ_ASSERT(frame->GetParent(), "shouldn't get style hints for the root frame"); for (nsIFrame* cont = frame; cont; cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) { mOverflowChangedTracker.AddFrame(cont->GetParent(), OverflowChangedTracker::CHILDREN_CHANGED); } } } } if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) { presContext->PresShell()->SynthesizeMouseMove(false); didUpdateCursor = true; } } } frameConstructor->EndUpdate(); // cleanup references and verify the style tree. Note that the latter needs // to happen once we've processed the whole list, since until then the tree // is not in fact in a consistent state. index = count; while (0 <= --index) { const nsStyleChangeData* changeData; aChangeList.ChangeAt(index, &changeData); if (changeData->mFrame) { propTable->Delete(changeData->mFrame, ChangeListProperty()); } #ifdef DEBUG // reget frame from content since it may have been regenerated... if (changeData->mContent) { nsIFrame* frame = changeData->mContent->GetPrimaryFrame(); if (frame) { DebugVerifyStyleTree(frame); } } else if (!changeData->mFrame || changeData->mFrame->GetType() != nsGkAtoms::viewportFrame) { NS_WARNING("Unable to test style tree integrity -- no content node " "(and not a viewport frame)"); } #endif } aChangeList.Clear(); return NS_OK; }