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 /*
13  * Destruction of a placeholder and its out-of-flow must observe the
14  * following constraints:
15  *
16  * - The mapping from the out-of-flow to the placeholder must be
17  *   removed from the frame manager before the placeholder is destroyed.
18  * - The mapping from the out-of-flow to the placeholder must be
19  *   removed from the frame manager before the out-of-flow is destroyed.
20  * - The placeholder must be removed from the frame tree, or have the
21  *   mapping from it to its out-of-flow cleared, before the out-of-flow
22  *   is destroyed (so that the placeholder will not point to a destroyed
23  *   frame while it's in the frame tree).
24  *
25  * Furthermore, some code assumes that placeholders point to something
26  * useful, so placeholders without an associated out-of-flow should not
27  * remain in the tree.
28  *
29  * The placeholder's Destroy() implementation handles the destruction of
30  * the placeholder and its out-of-flow. To avoid crashes, frame removal
31  * and destruction code that works with placeholders must not assume
32  * that the placeholder points to its out-of-flow.
33  */
34 
35 #ifndef nsPlaceholderFrame_h___
36 #define nsPlaceholderFrame_h___
37 
38 #include "mozilla/Attributes.h"
39 #include "nsIFrame.h"
40 #include "nsGkAtoms.h"
41 
42 namespace mozilla {
43 class PresShell;
44 }  // namespace mozilla
45 
46 class nsPlaceholderFrame;
47 nsPlaceholderFrame* NS_NewPlaceholderFrame(mozilla::PresShell* aPresShell,
48                                            mozilla::ComputedStyle* aStyle,
49                                            nsFrameState aTypeBits);
50 
51 #define PLACEHOLDER_TYPE_MASK                                                  \
52   (PLACEHOLDER_FOR_FLOAT | PLACEHOLDER_FOR_ABSPOS | PLACEHOLDER_FOR_FIXEDPOS | \
53    PLACEHOLDER_FOR_POPUP | PLACEHOLDER_FOR_TOPLAYER)
54 
55 /**
56  * Implementation of a frame that's used as a placeholder for a frame that
57  * has been moved out of the flow.
58  */
59 class nsPlaceholderFrame final : public nsIFrame {
60  public:
61   NS_DECL_FRAMEARENA_HELPERS(nsPlaceholderFrame)
62 #ifdef DEBUG
63   NS_DECL_QUERYFRAME
64 #endif
65 
66   /**
67    * Create a new placeholder frame.  aTypeBit must be one of the
68    * PLACEHOLDER_FOR_* constants above.
69    */
70   friend nsPlaceholderFrame* NS_NewPlaceholderFrame(
71       mozilla::PresShell* aPresShell, ComputedStyle* aStyle,
72       nsFrameState aTypeBits);
73 
nsPlaceholderFrame(ComputedStyle * aStyle,nsPresContext * aPresContext,nsFrameState aTypeBits)74   nsPlaceholderFrame(ComputedStyle* aStyle, nsPresContext* aPresContext,
75                      nsFrameState aTypeBits)
76       : nsIFrame(aStyle, aPresContext, kClassID), mOutOfFlowFrame(nullptr) {
77     MOZ_ASSERT(
78         aTypeBits == PLACEHOLDER_FOR_FLOAT ||
79             aTypeBits == PLACEHOLDER_FOR_ABSPOS ||
80             aTypeBits == PLACEHOLDER_FOR_FIXEDPOS ||
81             aTypeBits == PLACEHOLDER_FOR_POPUP ||
82             aTypeBits == (PLACEHOLDER_FOR_TOPLAYER | PLACEHOLDER_FOR_ABSPOS) ||
83             aTypeBits == (PLACEHOLDER_FOR_TOPLAYER | PLACEHOLDER_FOR_FIXEDPOS),
84         "Unexpected type bit");
85     AddStateBits(aTypeBits);
86   }
87 
88   // Get/Set the associated out of flow frame
GetOutOfFlowFrame()89   nsIFrame* GetOutOfFlowFrame() const { return mOutOfFlowFrame; }
SetOutOfFlowFrame(nsIFrame * aFrame)90   void SetOutOfFlowFrame(nsIFrame* aFrame) {
91     NS_ASSERTION(!aFrame || !aFrame->GetPrevContinuation(),
92                  "OOF must be first continuation");
93     mOutOfFlowFrame = aFrame;
94   }
95 
96   // nsIFrame overrides
97   // We need to override GetXULMinSize and GetXULPrefSize because XUL uses
98   // placeholders not within lines.
99   virtual void AddInlineMinISize(gfxContext* aRenderingContext,
100                                  InlineMinISizeData* aData) override;
101   virtual void AddInlinePrefISize(gfxContext* aRenderingContext,
102                                   InlinePrefISizeData* aData) override;
103   virtual nsSize GetXULMinSize(nsBoxLayoutState& aBoxLayoutState) override;
104   virtual nsSize GetXULPrefSize(nsBoxLayoutState& aBoxLayoutState) override;
105   virtual nsSize GetXULMaxSize(nsBoxLayoutState& aBoxLayoutState) override;
106 
107   virtual void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
108                       const ReflowInput& aReflowInput,
109                       nsReflowStatus& aStatus) override;
110 
111   virtual void DestroyFrom(nsIFrame* aDestructRoot,
112                            PostDestroyData& aPostDestroyData) override;
113 
114 #if defined(DEBUG) || (defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF))
115   virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
116                                 const nsDisplayListSet& aLists) override;
117 #endif  // DEBUG || (MOZ_REFLOW_PERF_DSP && MOZ_REFLOW_PERF)
118 
119 #ifdef DEBUG_FRAME_DUMP
120   void List(FILE* out = stderr, const char* aPrefix = "",
121             ListFlags aFlags = ListFlags()) const override;
122   virtual nsresult GetFrameName(nsAString& aResult) const override;
123 #endif  // DEBUG
124 
IsEmpty()125   virtual bool IsEmpty() override { return true; }
IsSelfEmpty()126   virtual bool IsSelfEmpty() override { return true; }
127 
128   virtual bool CanContinueTextRun() const override;
129 
SetLineIsEmptySoFar(bool aValue)130   void SetLineIsEmptySoFar(bool aValue) {
131     AddOrRemoveStateBits(PLACEHOLDER_LINE_IS_EMPTY_SO_FAR, aValue);
132     AddStateBits(PLACEHOLDER_HAVE_LINE_IS_EMPTY_SO_FAR);
133   }
GetLineIsEmptySoFar(bool * aResult)134   bool GetLineIsEmptySoFar(bool* aResult) const {
135     bool haveValue = HasAnyStateBits(PLACEHOLDER_HAVE_LINE_IS_EMPTY_SO_FAR);
136     if (haveValue) {
137       *aResult = HasAnyStateBits(PLACEHOLDER_LINE_IS_EMPTY_SO_FAR);
138     }
139     return haveValue;
140   }
ForgetLineIsEmptySoFar()141   void ForgetLineIsEmptySoFar() {
142     RemoveStateBits(PLACEHOLDER_HAVE_LINE_IS_EMPTY_SO_FAR);
143   }
144 
145 #ifdef ACCESSIBILITY
AccessibleType()146   virtual mozilla::a11y::AccType AccessibleType() override {
147     nsIFrame* realFrame = GetRealFrameForPlaceholder(this);
148     return realFrame ? realFrame->AccessibleType() : nsIFrame::AccessibleType();
149   }
150 #endif
151 
152   ComputedStyle* GetParentComputedStyleForOutOfFlow(
153       nsIFrame** aProviderFrame) const;
154 
155   // Like GetParentComputedStyleForOutOfFlow, but ignores display:contents bits.
156   ComputedStyle* GetLayoutParentStyleForOutOfFlow(
157       nsIFrame** aProviderFrame) const;
158 
RenumberFrameAndDescendants(int32_t * aOrdinal,int32_t aDepth,int32_t aIncrement,bool aForCounting)159   bool RenumberFrameAndDescendants(int32_t* aOrdinal, int32_t aDepth,
160                                    int32_t aIncrement,
161                                    bool aForCounting) override {
162     return mOutOfFlowFrame->RenumberFrameAndDescendants(
163         aOrdinal, aDepth, aIncrement, aForCounting);
164   }
165 
166   /**
167    * @return the out-of-flow for aFrame if aFrame is a placeholder; otherwise
168    * aFrame
169    */
GetRealFrameFor(nsIFrame * aFrame)170   static nsIFrame* GetRealFrameFor(nsIFrame* aFrame) {
171     MOZ_ASSERT(aFrame, "Must have a frame to work with");
172     if (aFrame->IsPlaceholderFrame()) {
173       return GetRealFrameForPlaceholder(aFrame);
174     }
175     return aFrame;
176   }
177 
178   /**
179    * @return the out-of-flow for aFrame, which is known to be a placeholder
180    */
GetRealFrameForPlaceholder(nsIFrame * aFrame)181   static nsIFrame* GetRealFrameForPlaceholder(nsIFrame* aFrame) {
182     MOZ_ASSERT(aFrame->IsPlaceholderFrame(),
183                "Must have placeholder frame as input");
184     nsIFrame* outOfFlow =
185         static_cast<nsPlaceholderFrame*>(aFrame)->GetOutOfFlowFrame();
186     NS_ASSERTION(outOfFlow, "Null out-of-flow for placeholder?");
187     return outOfFlow;
188   }
189 
190  protected:
191   nsIFrame* mOutOfFlowFrame;
192 };
193 
194 #endif /* nsPlaceholderFrame_h___ */
195