|
1 /* -*- Mode: C++; tab-width: 8; 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 // Eric Vaughan |
|
8 // Netscape Communications |
|
9 // |
|
10 // See documentation in associated header file |
|
11 // |
|
12 |
|
13 #include "nsLeafBoxFrame.h" |
|
14 #include "nsBoxFrame.h" |
|
15 #include "nsCOMPtr.h" |
|
16 #include "nsGkAtoms.h" |
|
17 #include "nsPresContext.h" |
|
18 #include "nsStyleContext.h" |
|
19 #include "nsIContent.h" |
|
20 #include "nsNameSpaceManager.h" |
|
21 #include "nsBoxLayoutState.h" |
|
22 #include "nsWidgetsCID.h" |
|
23 #include "nsViewManager.h" |
|
24 #include "nsContainerFrame.h" |
|
25 #include "nsDisplayList.h" |
|
26 #include <algorithm> |
|
27 |
|
28 // |
|
29 // NS_NewLeafBoxFrame |
|
30 // |
|
31 // Creates a new Toolbar frame and returns it |
|
32 // |
|
33 nsIFrame* |
|
34 NS_NewLeafBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) |
|
35 { |
|
36 return new (aPresShell) nsLeafBoxFrame(aPresShell, aContext); |
|
37 } |
|
38 |
|
39 NS_IMPL_FRAMEARENA_HELPERS(nsLeafBoxFrame) |
|
40 |
|
41 nsLeafBoxFrame::nsLeafBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext) |
|
42 : nsLeafFrame(aContext) |
|
43 { |
|
44 } |
|
45 |
|
46 #ifdef DEBUG_LAYOUT |
|
47 void |
|
48 nsLeafBoxFrame::GetBoxName(nsAutoString& aName) |
|
49 { |
|
50 GetFrameName(aName); |
|
51 } |
|
52 #endif |
|
53 |
|
54 |
|
55 /** |
|
56 * Initialize us. This is a good time to get the alignment of the box |
|
57 */ |
|
58 void |
|
59 nsLeafBoxFrame::Init( |
|
60 nsIContent* aContent, |
|
61 nsIFrame* aParent, |
|
62 nsIFrame* aPrevInFlow) |
|
63 { |
|
64 nsLeafFrame::Init(aContent, aParent, aPrevInFlow); |
|
65 |
|
66 if (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER) { |
|
67 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT); |
|
68 } |
|
69 |
|
70 UpdateMouseThrough(); |
|
71 } |
|
72 |
|
73 nsresult |
|
74 nsLeafBoxFrame::AttributeChanged(int32_t aNameSpaceID, |
|
75 nsIAtom* aAttribute, |
|
76 int32_t aModType) |
|
77 { |
|
78 nsresult rv = nsLeafFrame::AttributeChanged(aNameSpaceID, aAttribute, |
|
79 aModType); |
|
80 |
|
81 if (aAttribute == nsGkAtoms::mousethrough) |
|
82 UpdateMouseThrough(); |
|
83 |
|
84 return rv; |
|
85 } |
|
86 |
|
87 void nsLeafBoxFrame::UpdateMouseThrough() |
|
88 { |
|
89 if (mContent) { |
|
90 static nsIContent::AttrValuesArray strings[] = |
|
91 {&nsGkAtoms::never, &nsGkAtoms::always, nullptr}; |
|
92 switch (mContent->FindAttrValueIn(kNameSpaceID_None, |
|
93 nsGkAtoms::mousethrough, |
|
94 strings, eCaseMatters)) { |
|
95 case 0: AddStateBits(NS_FRAME_MOUSE_THROUGH_NEVER); break; |
|
96 case 1: AddStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS); break; |
|
97 case 2: { |
|
98 RemoveStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS); |
|
99 RemoveStateBits(NS_FRAME_MOUSE_THROUGH_NEVER); |
|
100 break; |
|
101 } |
|
102 } |
|
103 } |
|
104 } |
|
105 |
|
106 void |
|
107 nsLeafBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
|
108 const nsRect& aDirtyRect, |
|
109 const nsDisplayListSet& aLists) |
|
110 { |
|
111 // REVIEW: GetFrameForPoint used to not report events for the background |
|
112 // layer, whereas this code will put an event receiver for this frame in the |
|
113 // BlockBorderBackground() list. But I don't see any need to preserve |
|
114 // that anomalous behaviour. The important thing I'm preserving is that |
|
115 // leaf boxes continue to receive events in the foreground layer. |
|
116 DisplayBorderBackgroundOutline(aBuilder, aLists); |
|
117 |
|
118 if (!aBuilder->IsForEventDelivery() || !IsVisibleForPainting(aBuilder)) |
|
119 return; |
|
120 |
|
121 aLists.Content()->AppendNewToTop(new (aBuilder) |
|
122 nsDisplayEventReceiver(aBuilder, this)); |
|
123 } |
|
124 |
|
125 /* virtual */ nscoord |
|
126 nsLeafBoxFrame::GetMinWidth(nsRenderingContext *aRenderingContext) |
|
127 { |
|
128 nscoord result; |
|
129 DISPLAY_MIN_WIDTH(this, result); |
|
130 nsBoxLayoutState state(PresContext(), aRenderingContext); |
|
131 nsSize minSize = GetMinSize(state); |
|
132 |
|
133 // GetMinSize returns border-box width, and we want to return content |
|
134 // width. Since Reflow uses the reflow state's border and padding, we |
|
135 // actually just want to subtract what GetMinSize added, which is the |
|
136 // result of GetBorderAndPadding. |
|
137 nsMargin bp; |
|
138 GetBorderAndPadding(bp); |
|
139 |
|
140 result = minSize.width - bp.LeftRight(); |
|
141 |
|
142 return result; |
|
143 } |
|
144 |
|
145 /* virtual */ nscoord |
|
146 nsLeafBoxFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) |
|
147 { |
|
148 nscoord result; |
|
149 DISPLAY_PREF_WIDTH(this, result); |
|
150 nsBoxLayoutState state(PresContext(), aRenderingContext); |
|
151 nsSize prefSize = GetPrefSize(state); |
|
152 |
|
153 // GetPrefSize returns border-box width, and we want to return content |
|
154 // width. Since Reflow uses the reflow state's border and padding, we |
|
155 // actually just want to subtract what GetPrefSize added, which is the |
|
156 // result of GetBorderAndPadding. |
|
157 nsMargin bp; |
|
158 GetBorderAndPadding(bp); |
|
159 |
|
160 result = prefSize.width - bp.LeftRight(); |
|
161 |
|
162 return result; |
|
163 } |
|
164 |
|
165 nscoord |
|
166 nsLeafBoxFrame::GetIntrinsicWidth() |
|
167 { |
|
168 // No intrinsic width |
|
169 return 0; |
|
170 } |
|
171 |
|
172 nsSize |
|
173 nsLeafBoxFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, |
|
174 nsSize aCBSize, nscoord aAvailableWidth, |
|
175 nsSize aMargin, nsSize aBorder, |
|
176 nsSize aPadding, bool aShrinkWrap) |
|
177 { |
|
178 // Important: NOT calling our direct superclass here! |
|
179 return nsFrame::ComputeAutoSize(aRenderingContext, aCBSize, aAvailableWidth, |
|
180 aMargin, aBorder, aPadding, aShrinkWrap); |
|
181 } |
|
182 |
|
183 nsresult |
|
184 nsLeafBoxFrame::Reflow(nsPresContext* aPresContext, |
|
185 nsHTMLReflowMetrics& aDesiredSize, |
|
186 const nsHTMLReflowState& aReflowState, |
|
187 nsReflowStatus& aStatus) |
|
188 { |
|
189 // This is mostly a copy of nsBoxFrame::Reflow(). |
|
190 // We aren't able to share an implementation because of the frame |
|
191 // class hierarchy. If you make changes here, please keep |
|
192 // nsBoxFrame::Reflow in sync. |
|
193 |
|
194 DO_GLOBAL_REFLOW_COUNT("nsLeafBoxFrame"); |
|
195 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); |
|
196 |
|
197 NS_ASSERTION(aReflowState.ComputedWidth() >=0 && |
|
198 aReflowState.ComputedHeight() >= 0, "Computed Size < 0"); |
|
199 |
|
200 #ifdef DO_NOISY_REFLOW |
|
201 printf("\n-------------Starting LeafBoxFrame Reflow ----------------------------\n"); |
|
202 printf("%p ** nsLBF::Reflow %d R: ", this, myCounter++); |
|
203 switch (aReflowState.reason) { |
|
204 case eReflowReason_Initial: |
|
205 printf("Ini");break; |
|
206 case eReflowReason_Incremental: |
|
207 printf("Inc");break; |
|
208 case eReflowReason_Resize: |
|
209 printf("Rsz");break; |
|
210 case eReflowReason_StyleChange: |
|
211 printf("Sty");break; |
|
212 case eReflowReason_Dirty: |
|
213 printf("Drt "); |
|
214 break; |
|
215 default:printf("<unknown>%d", aReflowState.reason);break; |
|
216 } |
|
217 |
|
218 printSize("AW", aReflowState.AvailableWidth()); |
|
219 printSize("AH", aReflowState.AvailableHeight()); |
|
220 printSize("CW", aReflowState.ComputedWidth()); |
|
221 printSize("CH", aReflowState.ComputedHeight()); |
|
222 |
|
223 printf(" *\n"); |
|
224 |
|
225 #endif |
|
226 |
|
227 aStatus = NS_FRAME_COMPLETE; |
|
228 |
|
229 // create the layout state |
|
230 nsBoxLayoutState state(aPresContext, aReflowState.rendContext); |
|
231 |
|
232 nsSize computedSize(aReflowState.ComputedWidth(),aReflowState.ComputedHeight()); |
|
233 |
|
234 nsMargin m; |
|
235 m = aReflowState.ComputedPhysicalBorderPadding(); |
|
236 |
|
237 //GetBorderAndPadding(m); |
|
238 |
|
239 // this happens sometimes. So lets handle it gracefully. |
|
240 if (aReflowState.ComputedHeight() == 0) { |
|
241 nsSize minSize = GetMinSize(state); |
|
242 computedSize.height = minSize.height - m.top - m.bottom; |
|
243 } |
|
244 |
|
245 nsSize prefSize(0,0); |
|
246 |
|
247 // if we are told to layout intrinic then get our preferred size. |
|
248 if (computedSize.width == NS_INTRINSICSIZE || computedSize.height == NS_INTRINSICSIZE) { |
|
249 prefSize = GetPrefSize(state); |
|
250 nsSize minSize = GetMinSize(state); |
|
251 nsSize maxSize = GetMaxSize(state); |
|
252 prefSize = BoundsCheck(minSize, prefSize, maxSize); |
|
253 } |
|
254 |
|
255 // get our desiredSize |
|
256 if (aReflowState.ComputedWidth() == NS_INTRINSICSIZE) { |
|
257 computedSize.width = prefSize.width; |
|
258 } else { |
|
259 computedSize.width += m.left + m.right; |
|
260 } |
|
261 |
|
262 if (aReflowState.ComputedHeight() == NS_INTRINSICSIZE) { |
|
263 computedSize.height = prefSize.height; |
|
264 } else { |
|
265 computedSize.height += m.top + m.bottom; |
|
266 } |
|
267 |
|
268 // handle reflow state min and max sizes |
|
269 // XXXbz the width handling here seems to be wrong, since |
|
270 // mComputedMin/MaxWidth is a content-box size, whole |
|
271 // computedSize.width is a border-box size... |
|
272 if (computedSize.width > aReflowState.ComputedMaxWidth()) |
|
273 computedSize.width = aReflowState.ComputedMaxWidth(); |
|
274 |
|
275 if (computedSize.width < aReflowState.ComputedMinWidth()) |
|
276 computedSize.width = aReflowState.ComputedMinWidth(); |
|
277 |
|
278 // Now adjust computedSize.height for our min and max computed |
|
279 // height. The only problem is that those are content-box sizes, |
|
280 // while computedSize.height is a border-box size. So subtract off |
|
281 // m.TopBottom() before adjusting, then readd it. |
|
282 computedSize.height = std::max(0, computedSize.height - m.TopBottom()); |
|
283 computedSize.height = NS_CSS_MINMAX(computedSize.height, |
|
284 aReflowState.ComputedMinHeight(), |
|
285 aReflowState.ComputedMaxHeight()); |
|
286 computedSize.height += m.TopBottom(); |
|
287 |
|
288 nsRect r(mRect.x, mRect.y, computedSize.width, computedSize.height); |
|
289 |
|
290 SetBounds(state, r); |
|
291 |
|
292 // layout our children |
|
293 Layout(state); |
|
294 |
|
295 // ok our child could have gotten bigger. So lets get its bounds |
|
296 aDesiredSize.Width() = mRect.width; |
|
297 aDesiredSize.Height() = mRect.height; |
|
298 aDesiredSize.SetTopAscent(GetBoxAscent(state)); |
|
299 |
|
300 // the overflow rect is set in SetBounds() above |
|
301 aDesiredSize.mOverflowAreas = GetOverflowAreas(); |
|
302 |
|
303 #ifdef DO_NOISY_REFLOW |
|
304 { |
|
305 printf("%p ** nsLBF(done) W:%d H:%d ", this, aDesiredSize.Width(), aDesiredSize.Height()); |
|
306 |
|
307 if (maxElementWidth) { |
|
308 printf("MW:%d\n", *maxElementWidth); |
|
309 } else { |
|
310 printf("MW:?\n"); |
|
311 } |
|
312 |
|
313 } |
|
314 #endif |
|
315 |
|
316 return NS_OK; |
|
317 } |
|
318 |
|
319 #ifdef DEBUG_FRAME_DUMP |
|
320 nsresult |
|
321 nsLeafBoxFrame::GetFrameName(nsAString& aResult) const |
|
322 { |
|
323 return MakeFrameName(NS_LITERAL_STRING("LeafBox"), aResult); |
|
324 } |
|
325 #endif |
|
326 |
|
327 nsIAtom* |
|
328 nsLeafBoxFrame::GetType() const |
|
329 { |
|
330 return nsGkAtoms::leafBoxFrame; |
|
331 } |
|
332 |
|
333 nsresult |
|
334 nsLeafBoxFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo) |
|
335 { |
|
336 MarkIntrinsicWidthsDirty(); |
|
337 return nsLeafFrame::CharacterDataChanged(aInfo); |
|
338 } |
|
339 |
|
340 /* virtual */ nsSize |
|
341 nsLeafBoxFrame::GetPrefSize(nsBoxLayoutState& aState) |
|
342 { |
|
343 return nsBox::GetPrefSize(aState); |
|
344 } |
|
345 |
|
346 /* virtual */ nsSize |
|
347 nsLeafBoxFrame::GetMinSize(nsBoxLayoutState& aState) |
|
348 { |
|
349 return nsBox::GetMinSize(aState); |
|
350 } |
|
351 |
|
352 /* virtual */ nsSize |
|
353 nsLeafBoxFrame::GetMaxSize(nsBoxLayoutState& aState) |
|
354 { |
|
355 return nsBox::GetMaxSize(aState); |
|
356 } |
|
357 |
|
358 /* virtual */ nscoord |
|
359 nsLeafBoxFrame::GetFlex(nsBoxLayoutState& aState) |
|
360 { |
|
361 return nsBox::GetFlex(aState); |
|
362 } |
|
363 |
|
364 /* virtual */ nscoord |
|
365 nsLeafBoxFrame::GetBoxAscent(nsBoxLayoutState& aState) |
|
366 { |
|
367 return nsBox::GetBoxAscent(aState); |
|
368 } |
|
369 |
|
370 /* virtual */ void |
|
371 nsLeafBoxFrame::MarkIntrinsicWidthsDirty() |
|
372 { |
|
373 // Don't call base class method, since everything it does is within an |
|
374 // IsBoxWrapped check. |
|
375 } |
|
376 |
|
377 NS_IMETHODIMP |
|
378 nsLeafBoxFrame::DoLayout(nsBoxLayoutState& aState) |
|
379 { |
|
380 return nsBox::DoLayout(aState); |
|
381 } |