1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 * rendering object for the point that anchors out-of-flow rendering
8 * objects such as floats and absolutely positioned elements
11 #include "nsPlaceholderFrame.h"
14 #include "mozilla/gfx/2D.h"
15 #include "nsDisplayList.h"
16 #include "nsFrameManager.h"
17 #include "nsLayoutUtils.h"
18 #include "nsPresContext.h"
19 #include "nsRenderingContext.h"
20 #include "nsIFrameInlines.h"
22 using namespace mozilla
;
23 using namespace mozilla::gfx
;
26 NS_NewPlaceholderFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
,
27 nsFrameState aTypeBit
)
29 return new (aPresShell
) nsPlaceholderFrame(aContext
, aTypeBit
);
32 NS_IMPL_FRAMEARENA_HELPERS(nsPlaceholderFrame
)
35 NS_QUERYFRAME_HEAD(nsPlaceholderFrame
)
36 NS_QUERYFRAME_ENTRY(nsPlaceholderFrame
)
37 NS_QUERYFRAME_TAIL_INHERITING(nsFrame
)
41 nsPlaceholderFrame::GetMinSize(nsBoxLayoutState
& aBoxLayoutState
)
44 DISPLAY_MIN_SIZE(this, size
);
49 nsPlaceholderFrame::GetPrefSize(nsBoxLayoutState
& aBoxLayoutState
)
52 DISPLAY_PREF_SIZE(this, size
);
57 nsPlaceholderFrame::GetMaxSize(nsBoxLayoutState
& aBoxLayoutState
)
59 nsSize
size(NS_INTRINSICSIZE
, NS_INTRINSICSIZE
);
60 DISPLAY_MAX_SIZE(this, size
);
65 nsPlaceholderFrame::AddInlineMinISize(nsRenderingContext
* aRenderingContext
,
66 nsIFrame::InlineMinISizeData
* aData
)
68 // Override AddInlineMinWith so that *nothing* happens. In
69 // particular, we don't want to zero out |aData->trailingWhitespace|,
70 // since nsLineLayout skips placeholders when trimming trailing
71 // whitespace, and we don't want to set aData->skipWhitespace to
74 // ...but push floats onto the list
75 if (mOutOfFlowFrame
->IsFloating()) {
77 nsLayoutUtils::IntrinsicForContainer(aRenderingContext
,
79 nsLayoutUtils::MIN_ISIZE
);
80 aData
->floats
.AppendElement(
81 InlineIntrinsicISizeData::FloatInfo(mOutOfFlowFrame
, floatWidth
));
86 nsPlaceholderFrame::AddInlinePrefISize(nsRenderingContext
* aRenderingContext
,
87 nsIFrame::InlinePrefISizeData
* aData
)
89 // Override AddInlinePrefWith so that *nothing* happens. In
90 // particular, we don't want to zero out |aData->trailingWhitespace|,
91 // since nsLineLayout skips placeholders when trimming trailing
92 // whitespace, and we don't want to set aData->skipWhitespace to
95 // ...but push floats onto the list
96 if (mOutOfFlowFrame
->IsFloating()) {
98 nsLayoutUtils::IntrinsicForContainer(aRenderingContext
,
100 nsLayoutUtils::PREF_ISIZE
);
101 aData
->floats
.AppendElement(
102 InlineIntrinsicISizeData::FloatInfo(mOutOfFlowFrame
, floatWidth
));
107 nsPlaceholderFrame::Reflow(nsPresContext
* aPresContext
,
108 nsHTMLReflowMetrics
& aDesiredSize
,
109 const nsHTMLReflowState
& aReflowState
,
110 nsReflowStatus
& aStatus
)
113 // We should be getting reflowed before our out-of-flow.
114 // If this is our first reflow, and our out-of-flow has already received its
115 // first reflow (before us), complain.
116 // XXXdholbert This "look for a previous continuation or IB-split sibling"
117 // code could use nsLayoutUtils::GetPrevContinuationOrIBSplitSibling(), if
118 // we ever add a function like that. (We currently have a "Next" version.)
119 if ((GetStateBits() & NS_FRAME_FIRST_REFLOW
) &&
120 !(mOutOfFlowFrame
->GetStateBits() & NS_FRAME_FIRST_REFLOW
)) {
122 // Unfortunately, this can currently happen when the placeholder is in a
123 // later continuation or later IB-split sibling than its out-of-flow (as
124 // is the case in some of our existing unit tests). So for now, in that
125 // case, we'll warn instead of asserting.
126 bool isInContinuationOrIBSplit
= false;
127 nsIFrame
* ancestor
= this;
128 while ((ancestor
= ancestor
->GetParent())) {
129 if (ancestor
->GetPrevContinuation() ||
130 ancestor
->Properties().Get(IBSplitPrevSibling())) {
131 isInContinuationOrIBSplit
= true;
136 if (isInContinuationOrIBSplit
) {
137 NS_WARNING("Out-of-flow frame got reflowed before its placeholder");
139 NS_ERROR("Out-of-flow frame got reflowed before its placeholder");
144 DO_GLOBAL_REFLOW_COUNT("nsPlaceholderFrame");
145 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aDesiredSize
, aStatus
);
146 aDesiredSize
.ClearSize();
148 aStatus
= NS_FRAME_COMPLETE
;
149 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
153 nsPlaceholderFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
155 nsIFrame
* oof
= mOutOfFlowFrame
;
157 // Unregister out-of-flow frame
158 nsFrameManager
* fm
= PresContext()->GetPresShell()->FrameManager();
159 fm
->UnregisterPlaceholderFrame(this);
160 mOutOfFlowFrame
= nullptr;
161 // If aDestructRoot is not an ancestor of the out-of-flow frame,
162 // then call RemoveFrame on it here.
163 // Also destroy it here if it's a popup frame. (Bug 96291)
164 if ((GetStateBits() & PLACEHOLDER_FOR_POPUP
) ||
165 !nsLayoutUtils::IsProperAncestorFrame(aDestructRoot
, oof
)) {
166 ChildListID listId
= nsLayoutUtils::GetChildListNameFor(oof
);
167 fm
->RemoveFrame(listId
, oof
);
169 // else oof will be destroyed by its parent
172 nsFrame::DestroyFrom(aDestructRoot
);
176 nsPlaceholderFrame::GetType() const
178 return nsGkAtoms::placeholderFrame
;
182 nsPlaceholderFrame::CanContinueTextRun() const
184 if (!mOutOfFlowFrame
) {
187 // first-letter frames can continue text runs, and placeholders for floated
188 // first-letter frames can too
189 return mOutOfFlowFrame
->CanContinueTextRun();
193 nsPlaceholderFrame::GetParentStyleContext(nsIFrame
** aProviderFrame
) const
195 NS_PRECONDITION(GetParent(), "How can we not have a parent here?");
197 nsIContent
* parentContent
= mContent
? mContent
->GetFlattenedTreeParent() : nullptr;
200 PresContext()->FrameManager()->GetDisplayContentsStyleFor(parentContent
);
202 *aProviderFrame
= nullptr;
207 // Lie about our pseudo so we can step out of all anon boxes and
208 // pseudo-elements. The other option would be to reimplement the
209 // {ib} split gunk here.
210 *aProviderFrame
= CorrectStyleParentFrame(GetParent(), nsGkAtoms::placeholderFrame
);
211 return *aProviderFrame
? (*aProviderFrame
)->StyleContext() : nullptr;
217 PaintDebugPlaceholder(nsIFrame
* aFrame
, nsRenderingContext
* aCtx
,
218 const nsRect
& aDirtyRect
, nsPoint aPt
)
220 ColorPattern
cyan(ToDeviceColor(Color(0.f
, 1.f
, 1.f
, 1.f
)));
221 DrawTarget
* drawTarget
= aCtx
->GetDrawTarget();
222 int32_t appUnitsPerDevPixel
= aFrame
->PresContext()->AppUnitsPerDevPixel();
224 nscoord x
= nsPresContext::CSSPixelsToAppUnits(-5);
225 nsRect
r(aPt
.x
+ x
, aPt
.y
,
226 nsPresContext::CSSPixelsToAppUnits(13),
227 nsPresContext::CSSPixelsToAppUnits(3));
228 drawTarget
->FillRect(NSRectToRect(r
, appUnitsPerDevPixel
), cyan
);
230 nscoord y
= nsPresContext::CSSPixelsToAppUnits(-10);
231 r
= nsRect(aPt
.x
, aPt
.y
+ y
,
232 nsPresContext::CSSPixelsToAppUnits(3),
233 nsPresContext::CSSPixelsToAppUnits(10));
234 drawTarget
->FillRect(NSRectToRect(r
, appUnitsPerDevPixel
), cyan
);
238 #if defined(DEBUG) || (defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF))
241 nsPlaceholderFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
242 const nsRect
& aDirtyRect
,
243 const nsDisplayListSet
& aLists
)
245 DO_GLOBAL_REFLOW_COUNT_DSP("nsPlaceholderFrame");
248 if (GetShowFrameBorders()) {
249 aLists
.Outlines()->AppendNewToTop(
250 new (aBuilder
) nsDisplayGeneric(aBuilder
, this, PaintDebugPlaceholder
,
252 nsDisplayItem::TYPE_DEBUG_PLACEHOLDER
));
256 #endif // DEBUG || (MOZ_REFLOW_PERF_DSP && MOZ_REFLOW_PERF)
258 #ifdef DEBUG_FRAME_DUMP
260 nsPlaceholderFrame::GetFrameName(nsAString
& aResult
) const
262 return MakeFrameName(NS_LITERAL_STRING("Placeholder"), aResult
);
266 nsPlaceholderFrame::List(FILE* out
, const char* aPrefix
, uint32_t aFlags
) const
269 ListGeneric(str
, aPrefix
, aFlags
);
271 if (mOutOfFlowFrame
) {
272 str
+= " outOfFlowFrame=";
273 nsFrame::ListTag(str
, mOutOfFlowFrame
);
275 fprintf_stderr(out
, "%s\n", str
.get());