|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 /* |
|
7 * rendering object for the point that anchors out-of-flow rendering |
|
8 * objects such as floats and absolutely positioned elements |
|
9 */ |
|
10 |
|
11 #include "nsPlaceholderFrame.h" |
|
12 |
|
13 #include "nsDisplayList.h" |
|
14 #include "nsFrameManager.h" |
|
15 #include "nsLayoutUtils.h" |
|
16 #include "nsPresContext.h" |
|
17 #include "nsRenderingContext.h" |
|
18 #include "nsIFrameInlines.h" |
|
19 |
|
20 nsIFrame* |
|
21 NS_NewPlaceholderFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, |
|
22 nsFrameState aTypeBit) |
|
23 { |
|
24 return new (aPresShell) nsPlaceholderFrame(aContext, aTypeBit); |
|
25 } |
|
26 |
|
27 NS_IMPL_FRAMEARENA_HELPERS(nsPlaceholderFrame) |
|
28 |
|
29 #ifdef DEBUG |
|
30 NS_QUERYFRAME_HEAD(nsPlaceholderFrame) |
|
31 NS_QUERYFRAME_ENTRY(nsPlaceholderFrame) |
|
32 NS_QUERYFRAME_TAIL_INHERITING(nsFrame) |
|
33 #endif |
|
34 |
|
35 /* virtual */ nsSize |
|
36 nsPlaceholderFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState) |
|
37 { |
|
38 nsSize size(0, 0); |
|
39 DISPLAY_MIN_SIZE(this, size); |
|
40 return size; |
|
41 } |
|
42 |
|
43 /* virtual */ nsSize |
|
44 nsPlaceholderFrame::GetPrefSize(nsBoxLayoutState& aBoxLayoutState) |
|
45 { |
|
46 nsSize size(0, 0); |
|
47 DISPLAY_PREF_SIZE(this, size); |
|
48 return size; |
|
49 } |
|
50 |
|
51 /* virtual */ nsSize |
|
52 nsPlaceholderFrame::GetMaxSize(nsBoxLayoutState& aBoxLayoutState) |
|
53 { |
|
54 nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE); |
|
55 DISPLAY_MAX_SIZE(this, size); |
|
56 return size; |
|
57 } |
|
58 |
|
59 /* virtual */ void |
|
60 nsPlaceholderFrame::AddInlineMinWidth(nsRenderingContext* aRenderingContext, |
|
61 nsIFrame::InlineMinWidthData* aData) |
|
62 { |
|
63 // Override AddInlineMinWith so that *nothing* happens. In |
|
64 // particular, we don't want to zero out |aData->trailingWhitespace|, |
|
65 // since nsLineLayout skips placeholders when trimming trailing |
|
66 // whitespace, and we don't want to set aData->skipWhitespace to |
|
67 // false. |
|
68 |
|
69 // ...but push floats onto the list |
|
70 if (mOutOfFlowFrame->IsFloating()) { |
|
71 nscoord floatWidth = |
|
72 nsLayoutUtils::IntrinsicForContainer(aRenderingContext, |
|
73 mOutOfFlowFrame, |
|
74 nsLayoutUtils::MIN_WIDTH); |
|
75 aData->floats.AppendElement( |
|
76 InlineIntrinsicWidthData::FloatInfo(mOutOfFlowFrame, floatWidth)); |
|
77 } |
|
78 } |
|
79 |
|
80 /* virtual */ void |
|
81 nsPlaceholderFrame::AddInlinePrefWidth(nsRenderingContext* aRenderingContext, |
|
82 nsIFrame::InlinePrefWidthData* aData) |
|
83 { |
|
84 // Override AddInlinePrefWith so that *nothing* happens. In |
|
85 // particular, we don't want to zero out |aData->trailingWhitespace|, |
|
86 // since nsLineLayout skips placeholders when trimming trailing |
|
87 // whitespace, and we don't want to set aData->skipWhitespace to |
|
88 // false. |
|
89 |
|
90 // ...but push floats onto the list |
|
91 if (mOutOfFlowFrame->IsFloating()) { |
|
92 nscoord floatWidth = |
|
93 nsLayoutUtils::IntrinsicForContainer(aRenderingContext, |
|
94 mOutOfFlowFrame, |
|
95 nsLayoutUtils::PREF_WIDTH); |
|
96 aData->floats.AppendElement( |
|
97 InlineIntrinsicWidthData::FloatInfo(mOutOfFlowFrame, floatWidth)); |
|
98 } |
|
99 } |
|
100 |
|
101 nsresult |
|
102 nsPlaceholderFrame::Reflow(nsPresContext* aPresContext, |
|
103 nsHTMLReflowMetrics& aDesiredSize, |
|
104 const nsHTMLReflowState& aReflowState, |
|
105 nsReflowStatus& aStatus) |
|
106 { |
|
107 #ifdef DEBUG |
|
108 // We should be getting reflowed before our out-of-flow. |
|
109 // If this is our first reflow, and our out-of-flow has already received its |
|
110 // first reflow (before us), complain. |
|
111 // XXXdholbert This "look for a previous continuation or IB-split sibling" |
|
112 // code could use nsLayoutUtils::GetPrevContinuationOrIBSplitSibling(), if |
|
113 // we ever add a function like that. (We currently have a "Next" version.) |
|
114 if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && |
|
115 !(mOutOfFlowFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { |
|
116 |
|
117 // Unfortunately, this can currently happen when the placeholder is in a |
|
118 // later continuation or later IB-split sibling than its out-of-flow (as |
|
119 // is the case in some of our existing unit tests). So for now, in that |
|
120 // case, we'll warn instead of asserting. |
|
121 bool isInContinuationOrIBSplit = false; |
|
122 nsIFrame* ancestor = this; |
|
123 while ((ancestor = ancestor->GetParent())) { |
|
124 if (ancestor->GetPrevContinuation() || |
|
125 ancestor->Properties().Get(IBSplitPrevSibling())) { |
|
126 isInContinuationOrIBSplit = true; |
|
127 break; |
|
128 } |
|
129 } |
|
130 |
|
131 if (isInContinuationOrIBSplit) { |
|
132 NS_WARNING("Out-of-flow frame got reflowed before its placeholder"); |
|
133 } else { |
|
134 NS_ERROR("Out-of-flow frame got reflowed before its placeholder"); |
|
135 } |
|
136 } |
|
137 #endif |
|
138 |
|
139 DO_GLOBAL_REFLOW_COUNT("nsPlaceholderFrame"); |
|
140 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); |
|
141 aDesiredSize.Width() = 0; |
|
142 aDesiredSize.Height() = 0; |
|
143 |
|
144 aStatus = NS_FRAME_COMPLETE; |
|
145 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); |
|
146 return NS_OK; |
|
147 } |
|
148 |
|
149 void |
|
150 nsPlaceholderFrame::DestroyFrom(nsIFrame* aDestructRoot) |
|
151 { |
|
152 nsIFrame* oof = mOutOfFlowFrame; |
|
153 if (oof) { |
|
154 // Unregister out-of-flow frame |
|
155 nsFrameManager* fm = PresContext()->GetPresShell()->FrameManager(); |
|
156 fm->UnregisterPlaceholderFrame(this); |
|
157 mOutOfFlowFrame = nullptr; |
|
158 // If aDestructRoot is not an ancestor of the out-of-flow frame, |
|
159 // then call RemoveFrame on it here. |
|
160 // Also destroy it here if it's a popup frame. (Bug 96291) |
|
161 if ((GetStateBits() & PLACEHOLDER_FOR_POPUP) || |
|
162 !nsLayoutUtils::IsProperAncestorFrame(aDestructRoot, oof)) { |
|
163 ChildListID listId = nsLayoutUtils::GetChildListNameFor(oof); |
|
164 fm->RemoveFrame(listId, oof); |
|
165 } |
|
166 // else oof will be destroyed by its parent |
|
167 } |
|
168 |
|
169 nsFrame::DestroyFrom(aDestructRoot); |
|
170 } |
|
171 |
|
172 nsIAtom* |
|
173 nsPlaceholderFrame::GetType() const |
|
174 { |
|
175 return nsGkAtoms::placeholderFrame; |
|
176 } |
|
177 |
|
178 /* virtual */ bool |
|
179 nsPlaceholderFrame::CanContinueTextRun() const |
|
180 { |
|
181 if (!mOutOfFlowFrame) { |
|
182 return false; |
|
183 } |
|
184 // first-letter frames can continue text runs, and placeholders for floated |
|
185 // first-letter frames can too |
|
186 return mOutOfFlowFrame->CanContinueTextRun(); |
|
187 } |
|
188 |
|
189 nsIFrame* |
|
190 nsPlaceholderFrame::GetParentStyleContextFrame() const |
|
191 { |
|
192 NS_PRECONDITION(GetParent(), "How can we not have a parent here?"); |
|
193 |
|
194 // Lie about our pseudo so we can step out of all anon boxes and |
|
195 // pseudo-elements. The other option would be to reimplement the |
|
196 // {ib} split gunk here. |
|
197 return CorrectStyleParentFrame(GetParent(), nsGkAtoms::placeholderFrame); |
|
198 } |
|
199 |
|
200 |
|
201 #ifdef DEBUG |
|
202 static void |
|
203 PaintDebugPlaceholder(nsIFrame* aFrame, nsRenderingContext* aCtx, |
|
204 const nsRect& aDirtyRect, nsPoint aPt) |
|
205 { |
|
206 aCtx->SetColor(NS_RGB(0, 255, 255)); |
|
207 nscoord x = nsPresContext::CSSPixelsToAppUnits(-5); |
|
208 aCtx->FillRect(aPt.x + x, aPt.y, |
|
209 nsPresContext::CSSPixelsToAppUnits(13), |
|
210 nsPresContext::CSSPixelsToAppUnits(3)); |
|
211 nscoord y = nsPresContext::CSSPixelsToAppUnits(-10); |
|
212 aCtx->FillRect(aPt.x, aPt.y + y, |
|
213 nsPresContext::CSSPixelsToAppUnits(3), |
|
214 nsPresContext::CSSPixelsToAppUnits(10)); |
|
215 } |
|
216 #endif // DEBUG |
|
217 |
|
218 #if defined(DEBUG) || (defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)) |
|
219 |
|
220 void |
|
221 nsPlaceholderFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
|
222 const nsRect& aDirtyRect, |
|
223 const nsDisplayListSet& aLists) |
|
224 { |
|
225 DO_GLOBAL_REFLOW_COUNT_DSP("nsPlaceholderFrame"); |
|
226 |
|
227 #ifdef DEBUG |
|
228 if (GetShowFrameBorders()) { |
|
229 aLists.Outlines()->AppendNewToTop( |
|
230 new (aBuilder) nsDisplayGeneric(aBuilder, this, PaintDebugPlaceholder, |
|
231 "DebugPlaceholder", |
|
232 nsDisplayItem::TYPE_DEBUG_PLACEHOLDER)); |
|
233 } |
|
234 #endif |
|
235 } |
|
236 #endif // DEBUG || (MOZ_REFLOW_PERF_DSP && MOZ_REFLOW_PERF) |
|
237 |
|
238 #ifdef DEBUG_FRAME_DUMP |
|
239 nsresult |
|
240 nsPlaceholderFrame::GetFrameName(nsAString& aResult) const |
|
241 { |
|
242 return MakeFrameName(NS_LITERAL_STRING("Placeholder"), aResult); |
|
243 } |
|
244 |
|
245 void |
|
246 nsPlaceholderFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const |
|
247 { |
|
248 nsCString str; |
|
249 ListGeneric(str, aPrefix, aFlags); |
|
250 |
|
251 if (mOutOfFlowFrame) { |
|
252 str += " outOfFlowFrame="; |
|
253 nsFrame::ListTag(str, mOutOfFlowFrame); |
|
254 } |
|
255 fprintf_stderr(out, "%s\n", str.get()); |
|
256 } |
|
257 #endif |