Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
6 /*
7 * code for managing absolutely positioned children of a rendering
8 * object that is a containing block for them
9 */
11 #include "nsAbsoluteContainingBlock.h"
13 #include "nsContainerFrame.h"
14 #include "nsGkAtoms.h"
15 #include "nsIPresShell.h"
16 #include "nsHTMLReflowState.h"
17 #include "nsPresContext.h"
18 #include "nsCSSFrameConstructor.h"
20 #ifdef DEBUG
21 #include "nsBlockFrame.h"
23 static void PrettyUC(nscoord aSize, char* aBuf)
24 {
25 if (NS_UNCONSTRAINEDSIZE == aSize) {
26 strcpy(aBuf, "UC");
27 } else {
28 if((int32_t)0xdeadbeef == aSize) {
29 strcpy(aBuf, "deadbeef");
30 } else {
31 sprintf(aBuf, "%d", aSize);
32 }
33 }
34 }
35 #endif
37 nsresult
38 nsAbsoluteContainingBlock::SetInitialChildList(nsIFrame* aDelegatingFrame,
39 ChildListID aListID,
40 nsFrameList& aChildList)
41 {
42 NS_PRECONDITION(mChildListID == aListID, "unexpected child list name");
43 #ifdef DEBUG
44 nsFrame::VerifyDirtyBitSet(aChildList);
45 #endif
46 mAbsoluteFrames.SetFrames(aChildList);
47 return NS_OK;
48 }
50 nsresult
51 nsAbsoluteContainingBlock::AppendFrames(nsIFrame* aDelegatingFrame,
52 ChildListID aListID,
53 nsFrameList& aFrameList)
54 {
55 NS_ASSERTION(mChildListID == aListID, "unexpected child list");
57 // Append the frames to our list of absolutely positioned frames
58 #ifdef DEBUG
59 nsFrame::VerifyDirtyBitSet(aFrameList);
60 #endif
61 mAbsoluteFrames.AppendFrames(nullptr, aFrameList);
63 // no damage to intrinsic widths, since absolutely positioned frames can't
64 // change them
65 aDelegatingFrame->PresContext()->PresShell()->
66 FrameNeedsReflow(aDelegatingFrame, nsIPresShell::eResize,
67 NS_FRAME_HAS_DIRTY_CHILDREN);
69 return NS_OK;
70 }
72 nsresult
73 nsAbsoluteContainingBlock::InsertFrames(nsIFrame* aDelegatingFrame,
74 ChildListID aListID,
75 nsIFrame* aPrevFrame,
76 nsFrameList& aFrameList)
77 {
78 NS_ASSERTION(mChildListID == aListID, "unexpected child list");
79 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == aDelegatingFrame,
80 "inserting after sibling frame with different parent");
82 #ifdef DEBUG
83 nsFrame::VerifyDirtyBitSet(aFrameList);
84 #endif
85 mAbsoluteFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
87 // no damage to intrinsic widths, since absolutely positioned frames can't
88 // change them
89 aDelegatingFrame->PresContext()->PresShell()->
90 FrameNeedsReflow(aDelegatingFrame, nsIPresShell::eResize,
91 NS_FRAME_HAS_DIRTY_CHILDREN);
93 return NS_OK;
94 }
96 void
97 nsAbsoluteContainingBlock::RemoveFrame(nsIFrame* aDelegatingFrame,
98 ChildListID aListID,
99 nsIFrame* aOldFrame)
100 {
101 NS_ASSERTION(mChildListID == aListID, "unexpected child list");
102 nsIFrame* nif = aOldFrame->GetNextInFlow();
103 if (nif) {
104 static_cast<nsContainerFrame*>(nif->GetParent())
105 ->DeleteNextInFlowChild(nif, false);
106 }
108 mAbsoluteFrames.DestroyFrame(aOldFrame);
109 }
111 nsresult
112 nsAbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame,
113 nsPresContext* aPresContext,
114 const nsHTMLReflowState& aReflowState,
115 nsReflowStatus& aReflowStatus,
116 const nsRect& aContainingBlock,
117 bool aConstrainHeight,
118 bool aCBWidthChanged,
119 bool aCBHeightChanged,
120 nsOverflowAreas* aOverflowAreas)
121 {
122 nsReflowStatus reflowStatus = NS_FRAME_COMPLETE;
124 bool reflowAll = aReflowState.ShouldReflowAllKids();
126 nsIFrame* kidFrame;
127 nsOverflowContinuationTracker tracker(aDelegatingFrame, true);
128 for (kidFrame = mAbsoluteFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) {
129 bool kidNeedsReflow = reflowAll || NS_SUBTREE_DIRTY(kidFrame) ||
130 FrameDependsOnContainer(kidFrame, aCBWidthChanged, aCBHeightChanged);
131 if (kidNeedsReflow && !aPresContext->HasPendingInterrupt()) {
132 // Reflow the frame
133 nsReflowStatus kidStatus = NS_FRAME_COMPLETE;
134 ReflowAbsoluteFrame(aDelegatingFrame, aPresContext, aReflowState,
135 aContainingBlock,
136 aConstrainHeight, kidFrame, kidStatus,
137 aOverflowAreas);
138 nsIFrame* nextFrame = kidFrame->GetNextInFlow();
139 if (!NS_FRAME_IS_FULLY_COMPLETE(kidStatus)) {
140 // Need a continuation
141 if (!nextFrame) {
142 nextFrame =
143 aPresContext->PresShell()->FrameConstructor()->
144 CreateContinuingFrame(aPresContext, kidFrame, aDelegatingFrame);
145 }
146 // Add it as an overflow container.
147 //XXXfr This is a hack to fix some of our printing dataloss.
148 // See bug 154892. Not sure how to do it "right" yet; probably want
149 // to keep continuations within an nsAbsoluteContainingBlock eventually.
150 tracker.Insert(nextFrame, kidStatus);
151 NS_MergeReflowStatusInto(&reflowStatus, kidStatus);
152 }
153 else {
154 // Delete any continuations
155 if (nextFrame) {
156 nsOverflowContinuationTracker::AutoFinish fini(&tracker, kidFrame);
157 static_cast<nsContainerFrame*>(nextFrame->GetParent())
158 ->DeleteNextInFlowChild(nextFrame, true);
159 }
160 }
161 }
162 else {
163 tracker.Skip(kidFrame, reflowStatus);
164 if (aOverflowAreas) {
165 aDelegatingFrame->ConsiderChildOverflow(*aOverflowAreas, kidFrame);
166 }
167 }
169 // Make a CheckForInterrupt call, here, not just HasPendingInterrupt. That
170 // will make sure that we end up reflowing aDelegatingFrame in cases when
171 // one of our kids interrupted. Otherwise we'd set the dirty or
172 // dirty-children bit on the kid in the condition below, and then when
173 // reflow completes and we go to mark dirty bits on all ancestors of that
174 // kid we'll immediately bail out, because the kid already has a dirty bit.
175 // In particular, we won't set any dirty bits on aDelegatingFrame, so when
176 // the following reflow happens we won't reflow the kid in question. This
177 // might be slightly suboptimal in cases where |kidFrame| itself did not
178 // interrupt, since we'll trigger a reflow of it too when it's not strictly
179 // needed. But the logic to not do that is enough more complicated, and
180 // the case enough of an edge case, that this is probably better.
181 if (kidNeedsReflow && aPresContext->CheckForInterrupt(aDelegatingFrame)) {
182 if (aDelegatingFrame->GetStateBits() & NS_FRAME_IS_DIRTY) {
183 kidFrame->AddStateBits(NS_FRAME_IS_DIRTY);
184 } else {
185 kidFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
186 }
187 }
188 }
190 // Abspos frames can't cause their parent to be incomplete,
191 // only overflow incomplete.
192 if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus))
193 NS_FRAME_SET_OVERFLOW_INCOMPLETE(reflowStatus);
195 NS_MergeReflowStatusInto(&aReflowStatus, reflowStatus);
196 return NS_OK;
197 }
199 static inline bool IsFixedPaddingSize(const nsStyleCoord& aCoord)
200 { return aCoord.ConvertsToLength(); }
201 static inline bool IsFixedMarginSize(const nsStyleCoord& aCoord)
202 { return aCoord.ConvertsToLength(); }
203 static inline bool IsFixedOffset(const nsStyleCoord& aCoord)
204 { return aCoord.ConvertsToLength(); }
206 bool
207 nsAbsoluteContainingBlock::FrameDependsOnContainer(nsIFrame* f,
208 bool aCBWidthChanged,
209 bool aCBHeightChanged)
210 {
211 const nsStylePosition* pos = f->StylePosition();
212 // See if f's position might have changed because it depends on a
213 // placeholder's position
214 // This can happen in the following cases:
215 // 1) Vertical positioning. "top" must be auto and "bottom" must be auto
216 // (otherwise the vertical position is completely determined by
217 // whichever of them is not auto and the height).
218 // 2) Horizontal positioning. "left" must be auto and "right" must be auto
219 // (otherwise the horizontal position is completely determined by
220 // whichever of them is not auto and the width).
221 // See nsHTMLReflowState::InitAbsoluteConstraints -- these are the
222 // only cases when we call CalculateHypotheticalBox().
223 if ((pos->mOffset.GetTopUnit() == eStyleUnit_Auto &&
224 pos->mOffset.GetBottomUnit() == eStyleUnit_Auto) ||
225 (pos->mOffset.GetLeftUnit() == eStyleUnit_Auto &&
226 pos->mOffset.GetRightUnit() == eStyleUnit_Auto)) {
227 return true;
228 }
229 if (!aCBWidthChanged && !aCBHeightChanged) {
230 // skip getting style data
231 return false;
232 }
233 const nsStylePadding* padding = f->StylePadding();
234 const nsStyleMargin* margin = f->StyleMargin();
235 if (aCBWidthChanged) {
236 // See if f's width might have changed.
237 // If border-left, border-right, padding-left, padding-right,
238 // width, min-width, and max-width are all lengths, 'none', or enumerated,
239 // then our frame width does not depend on the parent width.
240 // Note that borders never depend on the parent width
241 // XXX All of the enumerated values except -moz-available are ok too.
242 if (pos->WidthDependsOnContainer() ||
243 pos->MinWidthDependsOnContainer() ||
244 pos->MaxWidthDependsOnContainer() ||
245 !IsFixedPaddingSize(padding->mPadding.GetLeft()) ||
246 !IsFixedPaddingSize(padding->mPadding.GetRight())) {
247 return true;
248 }
250 // See if f's position might have changed. If we're RTL then the
251 // rules are slightly different. We'll assume percentage or auto
252 // margins will always induce a dependency on the size
253 if (!IsFixedMarginSize(margin->mMargin.GetLeft()) ||
254 !IsFixedMarginSize(margin->mMargin.GetRight())) {
255 return true;
256 }
257 if (f->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
258 // Note that even if 'left' is a length, our position can
259 // still depend on the containing block width, because if
260 // 'right' is also a length we will discard 'left' and be
261 // positioned relative to the containing block right edge.
262 // 'left' length and 'right' auto is the only combination
263 // we can be sure of.
264 if (!IsFixedOffset(pos->mOffset.GetLeft()) ||
265 pos->mOffset.GetRightUnit() != eStyleUnit_Auto) {
266 return true;
267 }
268 } else {
269 if (!IsFixedOffset(pos->mOffset.GetLeft())) {
270 return true;
271 }
272 }
273 }
274 if (aCBHeightChanged) {
275 // See if f's height might have changed.
276 // If border-top, border-bottom, padding-top, padding-bottom,
277 // min-height, and max-height are all lengths or 'none',
278 // and height is a length or height and bottom are auto and top is not auto,
279 // then our frame height does not depend on the parent height.
280 // Note that borders never depend on the parent height
281 if ((pos->HeightDependsOnContainer() &&
282 !(pos->mHeight.GetUnit() == eStyleUnit_Auto &&
283 pos->mOffset.GetBottomUnit() == eStyleUnit_Auto &&
284 pos->mOffset.GetTopUnit() != eStyleUnit_Auto)) ||
285 pos->MinHeightDependsOnContainer() ||
286 pos->MaxHeightDependsOnContainer() ||
287 !IsFixedPaddingSize(padding->mPadding.GetTop()) ||
288 !IsFixedPaddingSize(padding->mPadding.GetBottom())) {
289 return true;
290 }
292 // See if f's position might have changed.
293 if (!IsFixedMarginSize(margin->mMargin.GetTop()) ||
294 !IsFixedMarginSize(margin->mMargin.GetBottom())) {
295 return true;
296 }
297 if (!IsFixedOffset(pos->mOffset.GetTop())) {
298 return true;
299 }
300 }
301 return false;
302 }
304 void
305 nsAbsoluteContainingBlock::DestroyFrames(nsIFrame* aDelegatingFrame,
306 nsIFrame* aDestructRoot)
307 {
308 mAbsoluteFrames.DestroyFramesFrom(aDestructRoot);
309 }
311 void
312 nsAbsoluteContainingBlock::MarkSizeDependentFramesDirty()
313 {
314 DoMarkFramesDirty(false);
315 }
317 void
318 nsAbsoluteContainingBlock::MarkAllFramesDirty()
319 {
320 DoMarkFramesDirty(true);
321 }
323 void
324 nsAbsoluteContainingBlock::DoMarkFramesDirty(bool aMarkAllDirty)
325 {
326 for (nsIFrame* kidFrame = mAbsoluteFrames.FirstChild();
327 kidFrame;
328 kidFrame = kidFrame->GetNextSibling()) {
329 if (aMarkAllDirty) {
330 kidFrame->AddStateBits(NS_FRAME_IS_DIRTY);
331 } else if (FrameDependsOnContainer(kidFrame, true, true)) {
332 // Add the weakest flags that will make sure we reflow this frame later
333 kidFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
334 }
335 }
336 }
338 // XXX Optimize the case where it's a resize reflow and the absolutely
339 // positioned child has the exact same size and position and skip the
340 // reflow...
342 // When bug 154892 is checked in, make sure that when
343 // mChildListID == kFixedList, the height is unconstrained.
344 // since we don't allow replicated frames to split.
346 nsresult
347 nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegatingFrame,
348 nsPresContext* aPresContext,
349 const nsHTMLReflowState& aReflowState,
350 const nsRect& aContainingBlock,
351 bool aConstrainHeight,
352 nsIFrame* aKidFrame,
353 nsReflowStatus& aStatus,
354 nsOverflowAreas* aOverflowAreas)
355 {
356 #ifdef DEBUG
357 if (nsBlockFrame::gNoisyReflow) {
358 nsFrame::IndentBy(stdout,nsBlockFrame::gNoiseIndent);
359 printf("abs pos ");
360 if (aKidFrame) {
361 nsAutoString name;
362 aKidFrame->GetFrameName(name);
363 printf("%s ", NS_LossyConvertUTF16toASCII(name).get());
364 }
366 char width[16];
367 char height[16];
368 PrettyUC(aReflowState.AvailableWidth(), width);
369 PrettyUC(aReflowState.AvailableHeight(), height);
370 printf(" a=%s,%s ", width, height);
371 PrettyUC(aReflowState.ComputedWidth(), width);
372 PrettyUC(aReflowState.ComputedHeight(), height);
373 printf("c=%s,%s \n", width, height);
374 }
375 AutoNoisyIndenter indent(nsBlockFrame::gNoisy);
376 #endif // DEBUG
378 nscoord availWidth = aContainingBlock.width;
379 if (availWidth == -1) {
380 NS_ASSERTION(aReflowState.ComputedWidth() != NS_UNCONSTRAINEDSIZE,
381 "Must have a useful width _somewhere_");
382 availWidth =
383 aReflowState.ComputedWidth() + aReflowState.ComputedPhysicalPadding().LeftRight();
384 }
386 nsHTMLReflowMetrics kidDesiredSize(aReflowState);
387 nsHTMLReflowState kidReflowState(aPresContext, aReflowState, aKidFrame,
388 nsSize(availWidth, NS_UNCONSTRAINEDSIZE),
389 aContainingBlock.width,
390 aContainingBlock.height);
392 // Send the WillReflow() notification and position the frame
393 aKidFrame->WillReflow(aPresContext);
395 // Get the border values
396 const nsMargin& border = aReflowState.mStyleBorder->GetComputedBorder();
398 bool constrainHeight = (aReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE)
399 && aConstrainHeight
400 // Don't split if told not to (e.g. for fixed frames)
401 && (aDelegatingFrame->GetType() != nsGkAtoms::inlineFrame)
402 //XXX we don't handle splitting frames for inline absolute containing blocks yet
403 && (aKidFrame->GetRect().y <= aReflowState.AvailableHeight());
404 // Don't split things below the fold. (Ideally we shouldn't *have*
405 // anything totally below the fold, but we can't position frames
406 // across next-in-flow breaks yet.
407 if (constrainHeight) {
408 kidReflowState.AvailableHeight() = aReflowState.AvailableHeight() - border.top
409 - kidReflowState.ComputedPhysicalMargin().top;
410 if (NS_AUTOOFFSET != kidReflowState.ComputedPhysicalOffsets().top)
411 kidReflowState.AvailableHeight() -= kidReflowState.ComputedPhysicalOffsets().top;
412 }
414 // Do the reflow
415 nsresult rv = aKidFrame->Reflow(aPresContext, kidDesiredSize, kidReflowState, aStatus);
417 // If we're solving for 'left' or 'top', then compute it now that we know the
418 // width/height
419 if ((NS_AUTOOFFSET == kidReflowState.ComputedPhysicalOffsets().left) ||
420 (NS_AUTOOFFSET == kidReflowState.ComputedPhysicalOffsets().top)) {
421 nscoord aContainingBlockWidth = aContainingBlock.width;
422 nscoord aContainingBlockHeight = aContainingBlock.height;
424 if (-1 == aContainingBlockWidth) {
425 // Get the containing block width/height
426 kidReflowState.ComputeContainingBlockRectangle(aPresContext,
427 &aReflowState,
428 aContainingBlockWidth,
429 aContainingBlockHeight);
430 }
432 if (NS_AUTOOFFSET == kidReflowState.ComputedPhysicalOffsets().left) {
433 NS_ASSERTION(NS_AUTOOFFSET != kidReflowState.ComputedPhysicalOffsets().right,
434 "Can't solve for both left and right");
435 kidReflowState.ComputedPhysicalOffsets().left = aContainingBlockWidth -
436 kidReflowState.ComputedPhysicalOffsets().right -
437 kidReflowState.ComputedPhysicalMargin().right -
438 kidDesiredSize.Width() -
439 kidReflowState.ComputedPhysicalMargin().left;
440 }
441 if (NS_AUTOOFFSET == kidReflowState.ComputedPhysicalOffsets().top) {
442 kidReflowState.ComputedPhysicalOffsets().top = aContainingBlockHeight -
443 kidReflowState.ComputedPhysicalOffsets().bottom -
444 kidReflowState.ComputedPhysicalMargin().bottom -
445 kidDesiredSize.Height() -
446 kidReflowState.ComputedPhysicalMargin().top;
447 }
448 }
450 // Position the child relative to our padding edge
451 nsRect rect(border.left + kidReflowState.ComputedPhysicalOffsets().left + kidReflowState.ComputedPhysicalMargin().left,
452 border.top + kidReflowState.ComputedPhysicalOffsets().top + kidReflowState.ComputedPhysicalMargin().top,
453 kidDesiredSize.Width(), kidDesiredSize.Height());
455 // Offset the frame rect by the given origin of the absolute containing block.
456 // If the frame is auto-positioned on both sides of an axis, it will be
457 // positioned based on its containing block and we don't need to offset.
458 if (aContainingBlock.TopLeft() != nsPoint(0, 0)) {
459 if (!(kidReflowState.mStylePosition->mOffset.GetLeftUnit() == eStyleUnit_Auto &&
460 kidReflowState.mStylePosition->mOffset.GetRightUnit() == eStyleUnit_Auto)) {
461 rect.x += aContainingBlock.x;
462 }
463 if (!(kidReflowState.mStylePosition->mOffset.GetTopUnit() == eStyleUnit_Auto &&
464 kidReflowState.mStylePosition->mOffset.GetBottomUnit() == eStyleUnit_Auto)) {
465 rect.y += aContainingBlock.y;
466 }
467 }
469 aKidFrame->SetRect(rect);
471 nsView* view = aKidFrame->GetView();
472 if (view) {
473 // Size and position the view and set its opacity, visibility, content
474 // transparency, and clip
475 nsContainerFrame::SyncFrameViewAfterReflow(aPresContext, aKidFrame, view,
476 kidDesiredSize.VisualOverflow());
477 } else {
478 nsContainerFrame::PositionChildViews(aKidFrame);
479 }
481 aKidFrame->DidReflow(aPresContext, &kidReflowState, nsDidReflowStatus::FINISHED);
483 #ifdef DEBUG
484 if (nsBlockFrame::gNoisyReflow) {
485 nsFrame::IndentBy(stdout,nsBlockFrame::gNoiseIndent - 1);
486 printf("abs pos ");
487 if (aKidFrame) {
488 nsAutoString name;
489 aKidFrame->GetFrameName(name);
490 printf("%s ", NS_LossyConvertUTF16toASCII(name).get());
491 }
492 printf("%p rect=%d,%d,%d,%d\n", static_cast<void*>(aKidFrame),
493 rect.x, rect.y, rect.width, rect.height);
494 }
495 #endif
497 if (aOverflowAreas) {
498 aOverflowAreas->UnionWith(kidDesiredSize.mOverflowAreas + rect.TopLeft());
499 }
501 return rv;
502 }