/src/mozilla-central/layout/generic/nsPlaceholderFrame.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | /* |
8 | | * rendering object for the point that anchors out-of-flow rendering |
9 | | * objects such as floats and absolutely positioned elements |
10 | | */ |
11 | | |
12 | | #include "nsPlaceholderFrame.h" |
13 | | |
14 | | #include "gfxContext.h" |
15 | | #include "gfxUtils.h" |
16 | | #include "mozilla/dom/ElementInlines.h" |
17 | | #include "mozilla/gfx/2D.h" |
18 | | #include "mozilla/ServoStyleSetInlines.h" |
19 | | #include "nsCSSFrameConstructor.h" |
20 | | #include "nsDisplayList.h" |
21 | | #include "nsLayoutUtils.h" |
22 | | #include "nsPresContext.h" |
23 | | #include "nsIFrameInlines.h" |
24 | | #include "nsIContentInlines.h" |
25 | | |
26 | | using namespace mozilla; |
27 | | using namespace mozilla::gfx; |
28 | | |
29 | | nsPlaceholderFrame* |
30 | | NS_NewPlaceholderFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle, |
31 | | nsFrameState aTypeBits) |
32 | 0 | { |
33 | 0 | return new (aPresShell) nsPlaceholderFrame(aStyle, aTypeBits); |
34 | 0 | } |
35 | | |
36 | | NS_IMPL_FRAMEARENA_HELPERS(nsPlaceholderFrame) |
37 | | |
38 | | #ifdef DEBUG |
39 | | NS_QUERYFRAME_HEAD(nsPlaceholderFrame) |
40 | | NS_QUERYFRAME_ENTRY(nsPlaceholderFrame) |
41 | | NS_QUERYFRAME_TAIL_INHERITING(nsFrame) |
42 | | #endif |
43 | | |
44 | | /* virtual */ nsSize |
45 | | nsPlaceholderFrame::GetXULMinSize(nsBoxLayoutState& aBoxLayoutState) |
46 | 0 | { |
47 | 0 | nsSize size(0, 0); |
48 | 0 | DISPLAY_MIN_SIZE(this, size); |
49 | 0 | return size; |
50 | 0 | } |
51 | | |
52 | | /* virtual */ nsSize |
53 | | nsPlaceholderFrame::GetXULPrefSize(nsBoxLayoutState& aBoxLayoutState) |
54 | 0 | { |
55 | 0 | nsSize size(0, 0); |
56 | 0 | DISPLAY_PREF_SIZE(this, size); |
57 | 0 | return size; |
58 | 0 | } |
59 | | |
60 | | /* virtual */ nsSize |
61 | | nsPlaceholderFrame::GetXULMaxSize(nsBoxLayoutState& aBoxLayoutState) |
62 | 0 | { |
63 | 0 | nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE); |
64 | 0 | DISPLAY_MAX_SIZE(this, size); |
65 | 0 | return size; |
66 | 0 | } |
67 | | |
68 | | /* virtual */ void |
69 | | nsPlaceholderFrame::AddInlineMinISize(gfxContext* aRenderingContext, |
70 | | nsIFrame::InlineMinISizeData* aData) |
71 | 0 | { |
72 | 0 | // Override AddInlineMinWith so that *nothing* happens. In |
73 | 0 | // particular, we don't want to zero out |aData->mTrailingWhitespace|, |
74 | 0 | // since nsLineLayout skips placeholders when trimming trailing |
75 | 0 | // whitespace, and we don't want to set aData->mSkipWhitespace to |
76 | 0 | // false. |
77 | 0 |
|
78 | 0 | // ...but push floats onto the list |
79 | 0 | if (mOutOfFlowFrame->IsFloating()) { |
80 | 0 | nscoord floatWidth = |
81 | 0 | nsLayoutUtils::IntrinsicForContainer(aRenderingContext, |
82 | 0 | mOutOfFlowFrame, |
83 | 0 | nsLayoutUtils::MIN_ISIZE); |
84 | 0 | aData->mFloats.AppendElement( |
85 | 0 | InlineIntrinsicISizeData::FloatInfo(mOutOfFlowFrame, floatWidth)); |
86 | 0 | } |
87 | 0 | } |
88 | | |
89 | | /* virtual */ void |
90 | | nsPlaceholderFrame::AddInlinePrefISize(gfxContext* aRenderingContext, |
91 | | nsIFrame::InlinePrefISizeData* aData) |
92 | 0 | { |
93 | 0 | // Override AddInlinePrefWith so that *nothing* happens. In |
94 | 0 | // particular, we don't want to zero out |aData->mTrailingWhitespace|, |
95 | 0 | // since nsLineLayout skips placeholders when trimming trailing |
96 | 0 | // whitespace, and we don't want to set aData->mSkipWhitespace to |
97 | 0 | // false. |
98 | 0 |
|
99 | 0 | // ...but push floats onto the list |
100 | 0 | if (mOutOfFlowFrame->IsFloating()) { |
101 | 0 | nscoord floatWidth = |
102 | 0 | nsLayoutUtils::IntrinsicForContainer(aRenderingContext, |
103 | 0 | mOutOfFlowFrame, |
104 | 0 | nsLayoutUtils::PREF_ISIZE); |
105 | 0 | aData->mFloats.AppendElement( |
106 | 0 | InlineIntrinsicISizeData::FloatInfo(mOutOfFlowFrame, floatWidth)); |
107 | 0 | } |
108 | 0 | } |
109 | | |
110 | | void |
111 | | nsPlaceholderFrame::Reflow(nsPresContext* aPresContext, |
112 | | ReflowOutput& aDesiredSize, |
113 | | const ReflowInput& aReflowInput, |
114 | | nsReflowStatus& aStatus) |
115 | 0 | { |
116 | 0 | // NOTE that the ReflowInput passed to this method is not fully initialized, |
117 | 0 | // on the grounds that reflowing a placeholder is a rather trivial operation. |
118 | 0 | // (See bug 1367711.) |
119 | 0 |
|
120 | | #ifdef DEBUG |
121 | | // We should be getting reflowed before our out-of-flow. |
122 | | // If this is our first reflow, and our out-of-flow has already received its |
123 | | // first reflow (before us), complain. |
124 | | // XXXdholbert This "look for a previous continuation or IB-split sibling" |
125 | | // code could use nsLayoutUtils::GetPrevContinuationOrIBSplitSibling(), if |
126 | | // we ever add a function like that. (We currently have a "Next" version.) |
127 | | if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && |
128 | | !(mOutOfFlowFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { |
129 | | |
130 | | // Unfortunately, this can currently happen when the placeholder is in a |
131 | | // later continuation or later IB-split sibling than its out-of-flow (as |
132 | | // is the case in some of our existing unit tests). So for now, in that |
133 | | // case, we'll warn instead of asserting. |
134 | | bool isInContinuationOrIBSplit = false; |
135 | | nsIFrame* ancestor = this; |
136 | | while ((ancestor = ancestor->GetParent())) { |
137 | | if (ancestor->GetPrevContinuation() || |
138 | | ancestor->GetProperty(IBSplitPrevSibling())) { |
139 | | isInContinuationOrIBSplit = true; |
140 | | break; |
141 | | } |
142 | | } |
143 | | |
144 | | if (isInContinuationOrIBSplit) { |
145 | | NS_WARNING("Out-of-flow frame got reflowed before its placeholder"); |
146 | | } else { |
147 | | NS_ERROR("Out-of-flow frame got reflowed before its placeholder"); |
148 | | } |
149 | | } |
150 | | #endif |
151 | |
|
152 | 0 | MarkInReflow(); |
153 | 0 | DO_GLOBAL_REFLOW_COUNT("nsPlaceholderFrame"); |
154 | 0 | DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); |
155 | 0 | MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); |
156 | 0 | aDesiredSize.ClearSize(); |
157 | 0 |
|
158 | 0 | NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); |
159 | 0 | } |
160 | | |
161 | | static nsIFrame::ChildListID |
162 | | ChildListIDForOutOfFlow(nsFrameState aPlaceholderState, const nsIFrame* aChild) |
163 | 0 | { |
164 | 0 | if (aPlaceholderState & PLACEHOLDER_FOR_FLOAT) { |
165 | 0 | return nsIFrame::kFloatList; |
166 | 0 | } |
167 | 0 | if (aPlaceholderState & PLACEHOLDER_FOR_POPUP) { |
168 | 0 | return nsIFrame::kPopupList; |
169 | 0 | } |
170 | 0 | if (aPlaceholderState & PLACEHOLDER_FOR_FIXEDPOS) { |
171 | 0 | return nsLayoutUtils::MayBeReallyFixedPos(aChild) |
172 | 0 | ? nsIFrame::kFixedList : nsIFrame::kAbsoluteList; |
173 | 0 | } |
174 | 0 | if (aPlaceholderState & PLACEHOLDER_FOR_ABSPOS) { |
175 | 0 | return nsIFrame::kAbsoluteList; |
176 | 0 | } |
177 | 0 | MOZ_DIAGNOSTIC_ASSERT(false, "unknown list"); |
178 | 0 | return nsIFrame::kFloatList; |
179 | 0 | } |
180 | | |
181 | | void |
182 | | nsPlaceholderFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData) |
183 | 0 | { |
184 | 0 | nsIFrame* oof = mOutOfFlowFrame; |
185 | 0 | if (oof) { |
186 | 0 | mOutOfFlowFrame = nullptr; |
187 | 0 | oof->DeleteProperty(nsIFrame::PlaceholderFrameProperty()); |
188 | 0 |
|
189 | 0 | // If aDestructRoot is not an ancestor of the out-of-flow frame, |
190 | 0 | // then call RemoveFrame on it here. |
191 | 0 | // Also destroy it here if it's a popup frame. (Bug 96291) |
192 | 0 | if ((GetStateBits() & PLACEHOLDER_FOR_POPUP) || |
193 | 0 | !nsLayoutUtils::IsProperAncestorFrame(aDestructRoot, oof)) { |
194 | 0 | ChildListID listId = ChildListIDForOutOfFlow(GetStateBits(), oof); |
195 | 0 | nsFrameManager* fm = PresContext()->FrameConstructor(); |
196 | 0 | fm->RemoveFrame(listId, oof); |
197 | 0 | } |
198 | 0 | // else oof will be destroyed by its parent |
199 | 0 | } |
200 | 0 |
|
201 | 0 | nsFrame::DestroyFrom(aDestructRoot, aPostDestroyData); |
202 | 0 | } |
203 | | |
204 | | /* virtual */ bool |
205 | | nsPlaceholderFrame::CanContinueTextRun() const |
206 | 0 | { |
207 | 0 | if (!mOutOfFlowFrame) { |
208 | 0 | return false; |
209 | 0 | } |
210 | 0 | // first-letter frames can continue text runs, and placeholders for floated |
211 | 0 | // first-letter frames can too |
212 | 0 | return mOutOfFlowFrame->CanContinueTextRun(); |
213 | 0 | } |
214 | | |
215 | | ComputedStyle* |
216 | | nsPlaceholderFrame::GetParentComputedStyleForOutOfFlow(nsIFrame** aProviderFrame) const |
217 | 0 | { |
218 | 0 | MOZ_ASSERT(GetParent(), "How can we not have a parent here?"); |
219 | 0 |
|
220 | 0 | Element* parentElement = |
221 | 0 | mContent ? mContent->GetFlattenedTreeParentElement() : nullptr; |
222 | 0 | if (parentElement && Servo_Element_IsDisplayContents(parentElement)) { |
223 | 0 | RefPtr<ComputedStyle> style = |
224 | 0 | PresShell()->StyleSet()->ResolveServoStyle(*parentElement); |
225 | 0 | *aProviderFrame = nullptr; |
226 | 0 | // See the comment in GetParentComputedStyle to see why returning this as a |
227 | 0 | // weak ref is fine. |
228 | 0 | return style; |
229 | 0 | } |
230 | 0 | |
231 | 0 | return GetLayoutParentStyleForOutOfFlow(aProviderFrame); |
232 | 0 | } |
233 | | |
234 | | ComputedStyle* |
235 | | nsPlaceholderFrame::GetLayoutParentStyleForOutOfFlow(nsIFrame** aProviderFrame) const |
236 | 0 | { |
237 | 0 | // Lie about our pseudo so we can step out of all anon boxes and |
238 | 0 | // pseudo-elements. The other option would be to reimplement the |
239 | 0 | // {ib} split gunk here. |
240 | 0 | *aProviderFrame = CorrectStyleParentFrame(GetParent(), |
241 | 0 | nsGkAtoms::placeholderFrame); |
242 | 0 | return *aProviderFrame ? (*aProviderFrame)->Style() : nullptr; |
243 | 0 | } |
244 | | |
245 | | |
246 | | #ifdef DEBUG |
247 | | static void |
248 | | PaintDebugPlaceholder(nsIFrame* aFrame, DrawTarget* aDrawTarget, |
249 | | const nsRect& aDirtyRect, nsPoint aPt) |
250 | | { |
251 | | ColorPattern cyan(ToDeviceColor(Color(0.f, 1.f, 1.f, 1.f))); |
252 | | int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel(); |
253 | | |
254 | | nscoord x = nsPresContext::CSSPixelsToAppUnits(-5); |
255 | | nsRect r(aPt.x + x, aPt.y, |
256 | | nsPresContext::CSSPixelsToAppUnits(13), |
257 | | nsPresContext::CSSPixelsToAppUnits(3)); |
258 | | aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), cyan); |
259 | | |
260 | | nscoord y = nsPresContext::CSSPixelsToAppUnits(-10); |
261 | | r = nsRect(aPt.x, aPt.y + y, |
262 | | nsPresContext::CSSPixelsToAppUnits(3), |
263 | | nsPresContext::CSSPixelsToAppUnits(10)); |
264 | | aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), cyan); |
265 | | } |
266 | | #endif // DEBUG |
267 | | |
268 | | #if defined(DEBUG) || (defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF)) |
269 | | |
270 | | void |
271 | | nsPlaceholderFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
272 | | const nsDisplayListSet& aLists) |
273 | | { |
274 | | DO_GLOBAL_REFLOW_COUNT_DSP("nsPlaceholderFrame"); |
275 | | |
276 | | #ifdef DEBUG |
277 | | if (GetShowFrameBorders()) { |
278 | | aLists.Outlines()->AppendToTop( |
279 | | MakeDisplayItem<nsDisplayGeneric>(aBuilder, this, PaintDebugPlaceholder, |
280 | | "DebugPlaceholder", |
281 | | DisplayItemType::TYPE_DEBUG_PLACEHOLDER)); |
282 | | } |
283 | | #endif |
284 | | } |
285 | | #endif // DEBUG || (MOZ_REFLOW_PERF_DSP && MOZ_REFLOW_PERF) |
286 | | |
287 | | #ifdef DEBUG_FRAME_DUMP |
288 | | nsresult |
289 | | nsPlaceholderFrame::GetFrameName(nsAString& aResult) const |
290 | | { |
291 | | return MakeFrameName(NS_LITERAL_STRING("Placeholder"), aResult); |
292 | | } |
293 | | |
294 | | void |
295 | | nsPlaceholderFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const |
296 | | { |
297 | | nsCString str; |
298 | | ListGeneric(str, aPrefix, aFlags); |
299 | | |
300 | | if (mOutOfFlowFrame) { |
301 | | str += " outOfFlowFrame="; |
302 | | nsFrame::ListTag(str, mOutOfFlowFrame); |
303 | | } |
304 | | fprintf_stderr(out, "%s\n", str.get()); |
305 | | } |
306 | | #endif |