michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * rendering object for the point that anchors out-of-flow rendering michael@0: * objects such as floats and absolutely positioned elements michael@0: */ michael@0: michael@0: #include "nsPlaceholderFrame.h" michael@0: michael@0: #include "nsDisplayList.h" michael@0: #include "nsFrameManager.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "nsIFrameInlines.h" michael@0: michael@0: nsIFrame* michael@0: NS_NewPlaceholderFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, michael@0: nsFrameState aTypeBit) michael@0: { michael@0: return new (aPresShell) nsPlaceholderFrame(aContext, aTypeBit); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsPlaceholderFrame) michael@0: michael@0: #ifdef DEBUG michael@0: NS_QUERYFRAME_HEAD(nsPlaceholderFrame) michael@0: NS_QUERYFRAME_ENTRY(nsPlaceholderFrame) michael@0: NS_QUERYFRAME_TAIL_INHERITING(nsFrame) michael@0: #endif michael@0: michael@0: /* virtual */ nsSize michael@0: nsPlaceholderFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState) michael@0: { michael@0: nsSize size(0, 0); michael@0: DISPLAY_MIN_SIZE(this, size); michael@0: return size; michael@0: } michael@0: michael@0: /* virtual */ nsSize michael@0: nsPlaceholderFrame::GetPrefSize(nsBoxLayoutState& aBoxLayoutState) michael@0: { michael@0: nsSize size(0, 0); michael@0: DISPLAY_PREF_SIZE(this, size); michael@0: return size; michael@0: } michael@0: michael@0: /* virtual */ nsSize michael@0: nsPlaceholderFrame::GetMaxSize(nsBoxLayoutState& aBoxLayoutState) michael@0: { michael@0: nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE); michael@0: DISPLAY_MAX_SIZE(this, size); michael@0: return size; michael@0: } michael@0: michael@0: /* virtual */ void michael@0: nsPlaceholderFrame::AddInlineMinWidth(nsRenderingContext* aRenderingContext, michael@0: nsIFrame::InlineMinWidthData* aData) michael@0: { michael@0: // Override AddInlineMinWith so that *nothing* happens. In michael@0: // particular, we don't want to zero out |aData->trailingWhitespace|, michael@0: // since nsLineLayout skips placeholders when trimming trailing michael@0: // whitespace, and we don't want to set aData->skipWhitespace to michael@0: // false. michael@0: michael@0: // ...but push floats onto the list michael@0: if (mOutOfFlowFrame->IsFloating()) { michael@0: nscoord floatWidth = michael@0: nsLayoutUtils::IntrinsicForContainer(aRenderingContext, michael@0: mOutOfFlowFrame, michael@0: nsLayoutUtils::MIN_WIDTH); michael@0: aData->floats.AppendElement( michael@0: InlineIntrinsicWidthData::FloatInfo(mOutOfFlowFrame, floatWidth)); michael@0: } michael@0: } michael@0: michael@0: /* virtual */ void michael@0: nsPlaceholderFrame::AddInlinePrefWidth(nsRenderingContext* aRenderingContext, michael@0: nsIFrame::InlinePrefWidthData* aData) michael@0: { michael@0: // Override AddInlinePrefWith so that *nothing* happens. In michael@0: // particular, we don't want to zero out |aData->trailingWhitespace|, michael@0: // since nsLineLayout skips placeholders when trimming trailing michael@0: // whitespace, and we don't want to set aData->skipWhitespace to michael@0: // false. michael@0: michael@0: // ...but push floats onto the list michael@0: if (mOutOfFlowFrame->IsFloating()) { michael@0: nscoord floatWidth = michael@0: nsLayoutUtils::IntrinsicForContainer(aRenderingContext, michael@0: mOutOfFlowFrame, michael@0: nsLayoutUtils::PREF_WIDTH); michael@0: aData->floats.AppendElement( michael@0: InlineIntrinsicWidthData::FloatInfo(mOutOfFlowFrame, floatWidth)); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsPlaceholderFrame::Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: #ifdef DEBUG michael@0: // We should be getting reflowed before our out-of-flow. michael@0: // If this is our first reflow, and our out-of-flow has already received its michael@0: // first reflow (before us), complain. michael@0: // XXXdholbert This "look for a previous continuation or IB-split sibling" michael@0: // code could use nsLayoutUtils::GetPrevContinuationOrIBSplitSibling(), if michael@0: // we ever add a function like that. (We currently have a "Next" version.) michael@0: if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && michael@0: !(mOutOfFlowFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { michael@0: michael@0: // Unfortunately, this can currently happen when the placeholder is in a michael@0: // later continuation or later IB-split sibling than its out-of-flow (as michael@0: // is the case in some of our existing unit tests). So for now, in that michael@0: // case, we'll warn instead of asserting. michael@0: bool isInContinuationOrIBSplit = false; michael@0: nsIFrame* ancestor = this; michael@0: while ((ancestor = ancestor->GetParent())) { michael@0: if (ancestor->GetPrevContinuation() || michael@0: ancestor->Properties().Get(IBSplitPrevSibling())) { michael@0: isInContinuationOrIBSplit = true; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (isInContinuationOrIBSplit) { michael@0: NS_WARNING("Out-of-flow frame got reflowed before its placeholder"); michael@0: } else { michael@0: NS_ERROR("Out-of-flow frame got reflowed before its placeholder"); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: DO_GLOBAL_REFLOW_COUNT("nsPlaceholderFrame"); michael@0: DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); michael@0: aDesiredSize.Width() = 0; michael@0: aDesiredSize.Height() = 0; michael@0: michael@0: aStatus = NS_FRAME_COMPLETE; michael@0: NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsPlaceholderFrame::DestroyFrom(nsIFrame* aDestructRoot) michael@0: { michael@0: nsIFrame* oof = mOutOfFlowFrame; michael@0: if (oof) { michael@0: // Unregister out-of-flow frame michael@0: nsFrameManager* fm = PresContext()->GetPresShell()->FrameManager(); michael@0: fm->UnregisterPlaceholderFrame(this); michael@0: mOutOfFlowFrame = nullptr; michael@0: // If aDestructRoot is not an ancestor of the out-of-flow frame, michael@0: // then call RemoveFrame on it here. michael@0: // Also destroy it here if it's a popup frame. (Bug 96291) michael@0: if ((GetStateBits() & PLACEHOLDER_FOR_POPUP) || michael@0: !nsLayoutUtils::IsProperAncestorFrame(aDestructRoot, oof)) { michael@0: ChildListID listId = nsLayoutUtils::GetChildListNameFor(oof); michael@0: fm->RemoveFrame(listId, oof); michael@0: } michael@0: // else oof will be destroyed by its parent michael@0: } michael@0: michael@0: nsFrame::DestroyFrom(aDestructRoot); michael@0: } michael@0: michael@0: nsIAtom* michael@0: nsPlaceholderFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::placeholderFrame; michael@0: } michael@0: michael@0: /* virtual */ bool michael@0: nsPlaceholderFrame::CanContinueTextRun() const michael@0: { michael@0: if (!mOutOfFlowFrame) { michael@0: return false; michael@0: } michael@0: // first-letter frames can continue text runs, and placeholders for floated michael@0: // first-letter frames can too michael@0: return mOutOfFlowFrame->CanContinueTextRun(); michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsPlaceholderFrame::GetParentStyleContextFrame() const michael@0: { michael@0: NS_PRECONDITION(GetParent(), "How can we not have a parent here?"); michael@0: michael@0: // Lie about our pseudo so we can step out of all anon boxes and michael@0: // pseudo-elements. The other option would be to reimplement the michael@0: // {ib} split gunk here. michael@0: return CorrectStyleParentFrame(GetParent(), nsGkAtoms::placeholderFrame); michael@0: } michael@0: michael@0: michael@0: #ifdef DEBUG michael@0: static void michael@0: PaintDebugPlaceholder(nsIFrame* aFrame, nsRenderingContext* aCtx, michael@0: const nsRect& aDirtyRect, nsPoint aPt) michael@0: { michael@0: aCtx->SetColor(NS_RGB(0, 255, 255)); michael@0: nscoord x = nsPresContext::CSSPixelsToAppUnits(-5); michael@0: aCtx->FillRect(aPt.x + x, aPt.y, michael@0: nsPresContext::CSSPixelsToAppUnits(13), michael@0: nsPresContext::CSSPixelsToAppUnits(3)); michael@0: nscoord y = nsPresContext::CSSPixelsToAppUnits(-10); michael@0: aCtx->FillRect(aPt.x, aPt.y + y, michael@0: nsPresContext::CSSPixelsToAppUnits(3), michael@0: nsPresContext::CSSPixelsToAppUnits(10)); michael@0: } michael@0: #endif // DEBUG michael@0: michael@0: #if defined(DEBUG) || (defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)) michael@0: michael@0: void michael@0: nsPlaceholderFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: DO_GLOBAL_REFLOW_COUNT_DSP("nsPlaceholderFrame"); michael@0: michael@0: #ifdef DEBUG michael@0: if (GetShowFrameBorders()) { michael@0: aLists.Outlines()->AppendNewToTop( michael@0: new (aBuilder) nsDisplayGeneric(aBuilder, this, PaintDebugPlaceholder, michael@0: "DebugPlaceholder", michael@0: nsDisplayItem::TYPE_DEBUG_PLACEHOLDER)); michael@0: } michael@0: #endif michael@0: } michael@0: #endif // DEBUG || (MOZ_REFLOW_PERF_DSP && MOZ_REFLOW_PERF) michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: nsresult michael@0: nsPlaceholderFrame::GetFrameName(nsAString& aResult) const michael@0: { michael@0: return MakeFrameName(NS_LITERAL_STRING("Placeholder"), aResult); michael@0: } michael@0: michael@0: void michael@0: nsPlaceholderFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const michael@0: { michael@0: nsCString str; michael@0: ListGeneric(str, aPrefix, aFlags); michael@0: michael@0: if (mOutOfFlowFrame) { michael@0: str += " outOfFlowFrame="; michael@0: nsFrame::ListTag(str, mOutOfFlowFrame); michael@0: } michael@0: fprintf_stderr(out, "%s\n", str.get()); michael@0: } michael@0: #endif