Coverage Report

Created: 2018-09-25 14:53

/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