Line data Source code
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 "gfxContext.h"
14 : #include "gfxUtils.h"
15 : #include "mozilla/gfx/2D.h"
16 : #include "nsDisplayList.h"
17 : #include "nsFrameManager.h"
18 : #include "nsLayoutUtils.h"
19 : #include "nsPresContext.h"
20 : #include "nsIFrameInlines.h"
21 : #include "nsIContentInlines.h"
22 :
23 : using namespace mozilla;
24 : using namespace mozilla::gfx;
25 :
26 : nsIFrame*
27 35 : NS_NewPlaceholderFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
28 : nsFrameState aTypeBit)
29 : {
30 35 : return new (aPresShell) nsPlaceholderFrame(aContext, aTypeBit);
31 : }
32 :
33 35 : NS_IMPL_FRAMEARENA_HELPERS(nsPlaceholderFrame)
34 :
35 : #ifdef DEBUG
36 174 : NS_QUERYFRAME_HEAD(nsPlaceholderFrame)
37 0 : NS_QUERYFRAME_ENTRY(nsPlaceholderFrame)
38 174 : NS_QUERYFRAME_TAIL_INHERITING(nsFrame)
39 : #endif
40 :
41 : /* virtual */ nsSize
42 262 : nsPlaceholderFrame::GetXULMinSize(nsBoxLayoutState& aBoxLayoutState)
43 : {
44 262 : nsSize size(0, 0);
45 524 : DISPLAY_MIN_SIZE(this, size);
46 524 : return size;
47 : }
48 :
49 : /* virtual */ nsSize
50 235 : nsPlaceholderFrame::GetXULPrefSize(nsBoxLayoutState& aBoxLayoutState)
51 : {
52 235 : nsSize size(0, 0);
53 470 : DISPLAY_PREF_SIZE(this, size);
54 470 : return size;
55 : }
56 :
57 : /* virtual */ nsSize
58 202 : nsPlaceholderFrame::GetXULMaxSize(nsBoxLayoutState& aBoxLayoutState)
59 : {
60 202 : nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
61 404 : DISPLAY_MAX_SIZE(this, size);
62 404 : return size;
63 : }
64 :
65 : /* virtual */ void
66 0 : nsPlaceholderFrame::AddInlineMinISize(gfxContext* aRenderingContext,
67 : nsIFrame::InlineMinISizeData* aData)
68 : {
69 : // Override AddInlineMinWith so that *nothing* happens. In
70 : // particular, we don't want to zero out |aData->mTrailingWhitespace|,
71 : // since nsLineLayout skips placeholders when trimming trailing
72 : // whitespace, and we don't want to set aData->mSkipWhitespace to
73 : // false.
74 :
75 : // ...but push floats onto the list
76 0 : if (mOutOfFlowFrame->IsFloating()) {
77 : nscoord floatWidth =
78 0 : nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
79 : mOutOfFlowFrame,
80 0 : nsLayoutUtils::MIN_ISIZE);
81 0 : aData->mFloats.AppendElement(
82 0 : InlineIntrinsicISizeData::FloatInfo(mOutOfFlowFrame, floatWidth));
83 : }
84 0 : }
85 :
86 : /* virtual */ void
87 0 : nsPlaceholderFrame::AddInlinePrefISize(gfxContext* aRenderingContext,
88 : nsIFrame::InlinePrefISizeData* aData)
89 : {
90 : // Override AddInlinePrefWith so that *nothing* happens. In
91 : // particular, we don't want to zero out |aData->mTrailingWhitespace|,
92 : // since nsLineLayout skips placeholders when trimming trailing
93 : // whitespace, and we don't want to set aData->mSkipWhitespace to
94 : // false.
95 :
96 : // ...but push floats onto the list
97 0 : if (mOutOfFlowFrame->IsFloating()) {
98 : nscoord floatWidth =
99 0 : nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
100 : mOutOfFlowFrame,
101 0 : nsLayoutUtils::PREF_ISIZE);
102 0 : aData->mFloats.AppendElement(
103 0 : InlineIntrinsicISizeData::FloatInfo(mOutOfFlowFrame, floatWidth));
104 : }
105 0 : }
106 :
107 : void
108 71 : nsPlaceholderFrame::Reflow(nsPresContext* aPresContext,
109 : ReflowOutput& aDesiredSize,
110 : const ReflowInput& aReflowInput,
111 : nsReflowStatus& aStatus)
112 : {
113 : // NOTE that the ReflowInput passed to this method is not fully initialized,
114 : // on the grounds that reflowing a placeholder is a rather trivial operation.
115 : // (See bug 1367711.)
116 :
117 : #ifdef DEBUG
118 : // We should be getting reflowed before our out-of-flow.
119 : // If this is our first reflow, and our out-of-flow has already received its
120 : // first reflow (before us), complain.
121 : // XXXdholbert This "look for a previous continuation or IB-split sibling"
122 : // code could use nsLayoutUtils::GetPrevContinuationOrIBSplitSibling(), if
123 : // we ever add a function like that. (We currently have a "Next" version.)
124 102 : if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) &&
125 31 : !(mOutOfFlowFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
126 :
127 : // Unfortunately, this can currently happen when the placeholder is in a
128 : // later continuation or later IB-split sibling than its out-of-flow (as
129 : // is the case in some of our existing unit tests). So for now, in that
130 : // case, we'll warn instead of asserting.
131 0 : bool isInContinuationOrIBSplit = false;
132 0 : nsIFrame* ancestor = this;
133 0 : while ((ancestor = ancestor->GetParent())) {
134 0 : if (ancestor->GetPrevContinuation() ||
135 0 : ancestor->GetProperty(IBSplitPrevSibling())) {
136 0 : isInContinuationOrIBSplit = true;
137 0 : break;
138 : }
139 : }
140 :
141 0 : if (isInContinuationOrIBSplit) {
142 0 : NS_WARNING("Out-of-flow frame got reflowed before its placeholder");
143 : } else {
144 0 : NS_ERROR("Out-of-flow frame got reflowed before its placeholder");
145 : }
146 : }
147 : #endif
148 :
149 71 : MarkInReflow();
150 71 : DO_GLOBAL_REFLOW_COUNT("nsPlaceholderFrame");
151 142 : DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
152 71 : aDesiredSize.ClearSize();
153 :
154 71 : aStatus.Reset();
155 71 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
156 71 : }
157 :
158 : void
159 8 : nsPlaceholderFrame::DestroyFrom(nsIFrame* aDestructRoot)
160 : {
161 8 : nsIFrame* oof = mOutOfFlowFrame;
162 8 : if (oof) {
163 8 : mOutOfFlowFrame = nullptr;
164 8 : oof->DeleteProperty(nsIFrame::PlaceholderFrameProperty());
165 : // If aDestructRoot is not an ancestor of the out-of-flow frame,
166 : // then call RemoveFrame on it here.
167 : // Also destroy it here if it's a popup frame. (Bug 96291)
168 10 : if ((GetStateBits() & PLACEHOLDER_FOR_POPUP) ||
169 2 : !nsLayoutUtils::IsProperAncestorFrame(aDestructRoot, oof)) {
170 8 : ChildListID listId = nsLayoutUtils::GetChildListNameFor(oof);
171 8 : nsFrameManager* fm = PresContext()->GetPresShell()->FrameManager();
172 8 : fm->RemoveFrame(listId, oof);
173 : }
174 : // else oof will be destroyed by its parent
175 : }
176 :
177 8 : nsFrame::DestroyFrom(aDestructRoot);
178 8 : }
179 :
180 : /* virtual */ bool
181 0 : nsPlaceholderFrame::CanContinueTextRun() const
182 : {
183 0 : if (!mOutOfFlowFrame) {
184 0 : return false;
185 : }
186 : // first-letter frames can continue text runs, and placeholders for floated
187 : // first-letter frames can too
188 0 : return mOutOfFlowFrame->CanContinueTextRun();
189 : }
190 :
191 : nsStyleContext*
192 127 : nsPlaceholderFrame::GetParentStyleContextForOutOfFlow(nsIFrame** aProviderFrame) const
193 : {
194 127 : NS_PRECONDITION(GetParent(), "How can we not have a parent here?");
195 :
196 127 : nsIContent* parentContent = mContent ? mContent->GetFlattenedTreeParent() : nullptr;
197 127 : if (parentContent) {
198 : nsStyleContext* sc =
199 127 : PresContext()->FrameManager()->GetDisplayContentsStyleFor(parentContent);
200 127 : if (sc) {
201 0 : *aProviderFrame = nullptr;
202 0 : return sc;
203 : }
204 : }
205 :
206 127 : nsIFrame* parentFrame = GetParent();
207 : // Placeholder of backdrop frame is a child of the corresponding top
208 : // layer frame, and its style context inherits from that frame. In
209 : // case of table, the top layer frame is the table wrapper frame.
210 : // However, it will be skipped in CorrectStyleParentFrame below, so
211 : // we need to handle it specially here.
212 127 : if ((GetStateBits() & PLACEHOLDER_FOR_TOPLAYER) &&
213 0 : parentFrame->IsTableWrapperFrame()) {
214 0 : MOZ_ASSERT(mOutOfFlowFrame->IsBackdropFrame(),
215 : "Only placeholder of backdrop frame can be put inside "
216 : "a table wrapper frame");
217 0 : *aProviderFrame = parentFrame;
218 0 : return parentFrame->StyleContext();
219 : }
220 :
221 : // Lie about our pseudo so we can step out of all anon boxes and
222 : // pseudo-elements. The other option would be to reimplement the
223 : // {ib} split gunk here.
224 127 : *aProviderFrame = CorrectStyleParentFrame(parentFrame,
225 : nsGkAtoms::placeholderFrame);
226 127 : return *aProviderFrame ? (*aProviderFrame)->StyleContext() : nullptr;
227 : }
228 :
229 :
230 : #ifdef DEBUG
231 : static void
232 0 : PaintDebugPlaceholder(nsIFrame* aFrame, DrawTarget* aDrawTarget,
233 : const nsRect& aDirtyRect, nsPoint aPt)
234 : {
235 0 : ColorPattern cyan(ToDeviceColor(Color(0.f, 1.f, 1.f, 1.f)));
236 0 : int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
237 :
238 0 : nscoord x = nsPresContext::CSSPixelsToAppUnits(-5);
239 0 : nsRect r(aPt.x + x, aPt.y,
240 : nsPresContext::CSSPixelsToAppUnits(13),
241 0 : nsPresContext::CSSPixelsToAppUnits(3));
242 0 : aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), cyan);
243 :
244 0 : nscoord y = nsPresContext::CSSPixelsToAppUnits(-10);
245 0 : r = nsRect(aPt.x, aPt.y + y,
246 : nsPresContext::CSSPixelsToAppUnits(3),
247 : nsPresContext::CSSPixelsToAppUnits(10));
248 0 : aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), cyan);
249 0 : }
250 : #endif // DEBUG
251 :
252 : #if defined(DEBUG) || (defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF))
253 :
254 : void
255 0 : nsPlaceholderFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
256 : const nsRect& aDirtyRect,
257 : const nsDisplayListSet& aLists)
258 : {
259 0 : DO_GLOBAL_REFLOW_COUNT_DSP("nsPlaceholderFrame");
260 :
261 : #ifdef DEBUG
262 0 : if (GetShowFrameBorders()) {
263 0 : aLists.Outlines()->AppendNewToTop(
264 : new (aBuilder) nsDisplayGeneric(aBuilder, this, PaintDebugPlaceholder,
265 : "DebugPlaceholder",
266 0 : nsDisplayItem::TYPE_DEBUG_PLACEHOLDER));
267 : }
268 : #endif
269 0 : }
270 : #endif // DEBUG || (MOZ_REFLOW_PERF_DSP && MOZ_REFLOW_PERF)
271 :
272 : #ifdef DEBUG_FRAME_DUMP
273 : nsresult
274 0 : nsPlaceholderFrame::GetFrameName(nsAString& aResult) const
275 : {
276 0 : return MakeFrameName(NS_LITERAL_STRING("Placeholder"), aResult);
277 : }
278 :
279 : void
280 0 : nsPlaceholderFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
281 : {
282 0 : nsCString str;
283 0 : ListGeneric(str, aPrefix, aFlags);
284 :
285 0 : if (mOutOfFlowFrame) {
286 0 : str += " outOfFlowFrame=";
287 0 : nsFrame::ListTag(str, mOutOfFlowFrame);
288 : }
289 0 : fprintf_stderr(out, "%s\n", str.get());
290 0 : }
291 : #endif
|