Closed Bug 10209 Opened 25 years ago Closed 13 years ago

problems with absolute or fixed position on table [ABS POS] [FIX POS]

Categories

(Core :: Layout: Tables, defect, P3)

x86
Linux
defect

Tracking

()

RESOLVED FIXED
mozilla10
Tracking Status
firefox6 + unaffected
firefox7 - ---

People

(Reporter: dbaron, Assigned: ehsan.akhgari)

References

(Blocks 4 open bugs, )

Details

(Keywords: css2, dev-doc-complete, testcase)

Attachments

(10 files, 21 obsolete files)

6.57 KB, patch
Details | Diff | Splinter Review
16.55 KB, patch
Details | Diff | Splinter Review
93.30 KB, patch
mounir
: checkin+
Details | Diff | Splinter Review
43.86 KB, patch
Details | Diff | Splinter Review
84.74 KB, patch
Details | Diff | Splinter Review
875 bytes, patch
roc
: review+
Details | Diff | Splinter Review
250.01 KB, text/plain
Details
10.10 KB, text/plain
Details
271.35 KB, text/plain
Details
1.22 KB, patch
roc
: review+
Details | Diff | Splinter Review
The above URL ( http://www.fas.harvard.edu/~dbaron/css/test/sec1704b ) shouldn't
work completely until bug 2479 is fixed.  However, there may be some other
problems here, too, so I'm filing a separate bug.

The page involves fixed position on a table (same thing happens for absolute).
What should happen is:

1) fixed/absolute positioned table gets display: block
2) anonymous element with display table is put around the table (bug 2479)
3) you get a fixed positioned table

The problem in (2) is usually that the content is deleted rather than making an
anonymous box.  So this makes me think (1) isn't happening correctly, or
something else weird is happening.  You may want to wait on this bug until 2479
is fixed, though.

Right now, the table is being absolutely positioned above and to the left of its
natural position.

The above URL also gives the following assertions when I load the page:
Assertion: "bad parent" (parent == aParent) at file nsFrameList.cpp, line 342
Assertion: "no placeholder frame" (nsnull != placeholderFrame) at file
nsHTMLReflowState.cpp, line 408
Assertion: "no placeholder frame" (nsnull != placeholderFrame) at file
nsHTMLReflowState.cpp, line 408
Assignee: troy → karnaze
Component: Layout → HTMLTables
Chris, it's been a while since we talked about absolute/fixed position tables.
We used to hit an assert, what's the current status?
Status: NEW → ASSIGNED
Target Milestone: M12
QA Contact: petersen → chrisd
Summary: problems with absolute or fixed position on table → {css2} problems with absolute or fixed position on table
The page doesn't assert. This bug has not gotten much attention, in favor of
html4 and css 1 issues.
mass move to m14.
Keywords: css2
Migrating from {css2} to css2 keyword. The {css1}, {css2}, {css3} and {css-moz}
radars should now be considered deprecated in favour of keywords.
I am *really* sorry about the spam...
Target Milestone: M14 → M16
Summary: {css2} problems with absolute or fixed position on table → problems with absolute or fixed position on table
Moving to M17.
Target Milestone: M16 → M17
Keywords: testcase
This bug has been marked "future" because we have determined that it is not 
critical for netscape 6.0. If you feel this is an error, or if it blocks your 
work in some way -- please attach your concern to the bug for reconsideration.
Target Milestone: M17 → Future
If CSS2 positioning is indeed to be back-burnered, I must highly encourage the
complete commenting of all CSS2 positioning code that isn't also in CSS1.

IMO, we MUST NOT allow Mozilla's positioning bugs to render correctly-designed,
by-the-spec CSS2 documents brokenly. We must either support these features per
the spec, or turn them off.

Netscape 4 makes the mistake of rendering CSS1 with severe bugs, making pages
with correct CSS completely unreadable. This ruins the point of abstracting
structure from presentation. I don't think we should allow this in Mozilla.

On the other hand, if we can reach a concensus that we can get CSS2 positioning
working correctly in time for a stable public release (like netscape's?) I would
of course LOVE to have that support included.
I have to agree with sungod@atdot.org. If we can't get CSS2 specific positioning 
right per the spec, I'd rather it be turned off until such a time that it can be 
done right. A broken implementation would only be a burden.
We should check there are no showstoppers if we are going to release it like 
this. We certainly don't want to kill absolute positioning.

I'll do this in a few weeks. No need to panic. I have a feeling IE gets in 
wrong anyway. Unfortunately David's test case is not yet complete so this will
need some thought.
Whiteboard: (py8ieh:work out impact)
You do get a fixed position table now. But it appears to be in the wrong size 
and position. The caption spans the page and the table itself is touching the 
right edge of the page (and overlaps the scrollbar, but that's another issue).
Summary: problems with absolute or fixed position on table → problems with absolute or fixed position on table [ABS POS] [FIX POS]
is bug 60051 a dup of this?
*** Bug 60051 has been marked as a duplicate of this bug. ***
QA contact update
QA Contact: chrisd → amar
Whiteboard: (py8ieh:work out impact) → [awd:tbl](py8ieh:work out impact)
The scrollbar overlapping problem has a patch in bug 94009
So what specific issues is this bug about pertaining to the testcase?
Keywords: qawanted
Depends on: 53663
mass reassign to default owner
Assignee: karnaze → table
Status: ASSIGNED → NEW
QA Contact: amar → madhur
Target Milestone: Future → ---
Target Milestone: --- → Future
Flags: blocking1.6b?
Flags: blocking1.6b? → blocking1.6b-
QA Contact: madhur → ian
is bug 236077 related?
Is this bug still around/active?  If so, shouldn't the qawanted tag be removed
from the keywords?
  Reporter, is this bug still active in newer builds?

  You have 7-10 days to respond, after which time it would be OK for the bug to
be marked INVALID.  You can respond via the link you will recieve in the
bugzilla Email or via the comment blank.
mozillamonks@yahoo.ca: for lack of common sense you've lost editbugs. please
take this moment to think about your actions.
What the f..., is going on here? If David files a bug it is a bug period. Please
stop immeadetely to spam bugs if you have no clue. I asked to adjust your
privilegs to your knowledge of bugzilla and mozilla in general. Broad hint:
David is the owner of the layout component. 
(In reply to comment #20)
> mozillamonks@yahoo.ca: for lack of common sense you've lost editbugs. please
> take this moment to think about your actions.

  I'm very sorry, I didn't know David owned that
component.  I figured when he didn't respond after a
few weeks that he was just another bug reporter who
didn't show up again.
  I followed the guidelines for message templates
saying that one could say a time for a reporter to
respond and the if s/he was using an alternate system
that one could still say it, just not carry it out.
  I ask that you reconsider you decision.  I will take
time to study the names of the component owners so I
don't offend.  Could you suggest some other farther
reading I should read up on?
The spec has changed significantly since this bug was filed, and someone should
probably figure out how we deviate from the current spec, file appropriate bugs,
and close this one.

However, using a template like the one you used may make sense for a bug you
can't reproduce, but doesn't make sense for a bug you simply don't understand.
Assignee: layout.tables → nobody
QA Contact: ian → layout.tables
Blocks: 439258
I have a patch which refactors nearly all of the abs-pos child handling stuff to nsFrame, so that all of our frame types can use this implementation.  There is one last thing which I don't know how to do with the minimum level of code change, and that is reflowing the absolute children.

The reflowing should be independent of the reflowing of the frame itself, so at first I thought that a neat trick is to reflow the abs-pos children in nsFrame::DidReflow.  But it seems like that may be too late in the reflow process.  For example, we don't have an nsReflowStatus to modify any more.

Does anybody have any suggestions on how to solve this problem?  The last thing that I want to do is to modify all of our Reflow functions to take this into account...
I think this first part is good enough (it might need minor corrections).  It adds a bunch of APIs to nsIFrame to maintain the absolute children info on every frame type.
This part is not finished yet.  The main things which are missing from it is the reflow piece, removing all of the old code to maintain abs-pos children in nsViewportFrame, nsCanvasFrame, nsBlockFrame and removing nsPositionedInlineFrame entirely.

Oh, and of course, testing!  So far all of the testing that I've been able to do is to make sure that the changes compile...
This part of the patch doesn't even compile, let alone being correct in any way.  This patch reflects what I was trying in comment 25.
At this point I'm mostly stuck with comment 25.  Once the reflow stuff is done, I think I can just remove the stuff I mentioned in comment 27 and start testing the code.
(In reply to comment #25)
> The reflowing should be independent of the reflowing of the frame itself, so at
> first I thought that a neat trick is to reflow the abs-pos children in
> nsFrame::DidReflow.  But it seems like that may be too late in the reflow
> process.  For example, we don't have an nsReflowStatus to modify any more.
> 
> Does anybody have any suggestions on how to solve this problem?  The last thing
> that I want to do is to modify all of our Reflow functions to take this into
> account...

Reflowing the absolute children needs to be done near the end of each Reflow implementation, because it has to affect the overflow area stored in nsHTMLReflowMetrics before Reflow returns. We do need to modify all the Reflow methods.

So we'll want a function that abstracts the code in nsPositionedInlineFrame and nsCanvasFrame (I think the code in nsPositionedInlineFrame should work). Instead of adding a new function call in every Reflow method, I think you should add a method FinishReflowWithAbsoluteFrames which does your absolute stuff and also calls nsFrame::FinishAndStoreOverflow, and change existing FinishAndStoreOverflow calls to call FinishReflowWithAbsoluteFrames. Those changes can be done incrementally. (You probably do not want to convert nsBlockFrame since that does various optimizations we don't need in general.)
Although we probably should convert the block code from using its mAbsoluteContainer to your new property-based code.
 virtual nsIAtom* GetAbsoluteListName() const { return nsGkAtoms::fixedList; }

Should return nsGkAtoms::absoluteList
nsFrame::GetAdditionalChildListName needs to return null if aIndex > 0.

NS_CONTAINER_FRAME_OVERFLOW_LIST_INDEX and the other list indexes next to it need to change.

Instead of calling nsFrame::BuildDisplayListForAbsoluteChildren everywhere, you can probably just call it from nsFrame::BuildDisplayListForStackingContext and nsFrame::BuildDisplayListForChild.
(In reply to comment #32)
>  virtual nsIAtom* GetAbsoluteListName() const { return nsGkAtoms::fixedList; }
> 
> Should return nsGkAtoms::absoluteList

Are you sure?  That method is defined in nsViewportFrame, as that is the only type of frame which does abs-pos child processing with postition:fixed children (that's what the current code does, and I think that's what we want it to do).  If we should return absoluteList for nsViewportFrame as well, then I can probably remove GetAbsoluteListName altogether, since this difference is the only reason I had to add it.
Ah right.

We probably shouldn't make nsViewportFrame use the common mechanism you're building here. It should manage its fixed-pos frames itself.
I take back comment #35. Go ahead with GetAbsoluteListName().
(In reply to comment #33)
> nsFrame::GetAdditionalChildListName needs to return null if aIndex > 0.
> 
> NS_CONTAINER_FRAME_OVERFLOW_LIST_INDEX and the other list indexes next to it
> need to change.

Done.

May I pause for a moment to express my discontent at the way GetAdditionalChildListName is designed?  The way that it works now, it's impossible to figure out what needs to happen in order to change something here without looking at all of the definitions of this function.  :(  Why can't each implementation just return an array of child list names it supports?

> Instead of calling nsFrame::BuildDisplayListForAbsoluteChildren everywhere, you
> can probably just call it from nsFrame::BuildDisplayListForStackingContext and
> nsFrame::BuildDisplayListForChild.

Done.  I wish I knew this before, this makes the patch a lot smaller!  :-)
(In reply to comment #36)
> I take back comment #35. Go ahead with GetAbsoluteListName().

That makes sense, since all of the other handling should be exactly the same.
Newer version.
Attachment #528971 - Attachment is obsolete: true
Attachment #528973 - Attachment is obsolete: true
Assignee: nobody → ehsan
Status: NEW → ASSIGNED
Keywords: qawanted
Whiteboard: [awd:tbl](py8ieh:work out impact)
(In reply to comment #37)
> May I pause for a moment to express my discontent at the way
> GetAdditionalChildListName is designed?  The way that it works now, it's
> impossible to figure out what needs to happen in order to change something here
> without looking at all of the definitions of this function.  :(  Why can't each
> implementation just return an array of child list names it supports?

Yes.

Really I think the whole GetAdditionalChildListName dance should be replaced with something simpler and faster. Right now traversing all the child frame lists requires two virtual calls per child list the frame supports, which is way more than necessary.

E.g. we could have
  struct ChildList {
    nsIAtom* mName;
    nsIFrame* mFirstChild;
  };
  /** Appends one ChildList element for every non-empty child list to aLists */
  virtual void GetChildLists(nsTArray<ChildList>* aLists);

then you could write
  nsAutoTArray<ChildList,4> childLists;
  frame->GetChildLists(&childLists);
  for (PRUint32 i = 0; i < childLists.Length(); ++i) {
    for (nsIFrame* f = childLists[i].mFirstChild; f = f->GetNextSibling()) {
      ...
    }
  }

The implementors could write
  void nsXYZFrame::GetChildLists(nsTArray<ChildList>* aLists)
  {
    nsSuperClassFrame::GetChildLists(aLists);
    if (!mMyExtraList.IsEmpty()) {
      aLists->AppendElement(ChildList(nsGkAtoms::myExtraList, mMyExtraList.FirstFrame());
    }
  }
we could even add a helper function nsFrameList::AppendIfNonEmpty(nsTArray<ChildList>* aLists, nsIAtom* aListName).
Filed bug 653649 for comment 40.
Note to myself: http://pastebin.mozilla.org/1215193
This won't make sense to anyone, it's a random irc log which I didn't have time to clean up! :)
Actually, nsAbsoluteContainingBlock doesn't support reflowing on frames not derived from nsContainerFrame for now...
This new version should be roughly complete as far as simulating the current behavior is concerned.  It needs a lot more testing though.
Attachment #529036 - Attachment is obsolete: true
Blocks: 536692
This version of the patch passes the entire crashtest suite for me locally (yay!!!!!)
Attachment #529229 - Attachment is obsolete: true
This version passes all of the reftests for me locally as well.  I've submitted it to the try server.
Attachment #529545 - Attachment is obsolete: true
Comment on attachment 528970 [details] [diff] [review]
Part 1: Add an API for absolute container support for all frame types

This is ready for review.
Attachment #528970 - Flags: review?(roc)
Comment on attachment 528970 [details] [diff] [review]
Part 1: Add an API for absolute container support for all frame types

Review of attachment 528970 [details] [diff] [review]:

::: layout/generic/nsFrame.cpp
@@ +268,5 @@
+NS_DECLARE_FRAME_PROPERTY(AbsoluteContainingBlockProperty, DestroyAbsoluteContainingBlock)
+
+PRBool
+nsIFrame::HasAbsolutelyPositionedChildren() const {
+  return IsAbsoluteContainer() ? GetAbsoluteContainingBlock()->HasAbsoluteFrames() : PR_FALSE;

IsAbsoluteContainer() && GetAbsoluteContainingBlock()->HasAbsoluteFrames()

If PR_TRUE or PR_FALSE appears in a ?: expression, you're doing something wrong :-).
Attachment #528970 - Flags: review?(roc) → review+
(In reply to comment #48)
> +PRBool
> +nsIFrame::HasAbsolutelyPositionedChildren() const {
> +  return IsAbsoluteContainer() ?
> GetAbsoluteContainingBlock()->HasAbsoluteFrames() : PR_FALSE;
> 
> IsAbsoluteContainer() && GetAbsoluteContainingBlock()->HasAbsoluteFrames()
> 
> If PR_TRUE or PR_FALSE appears in a ?: expression, you're doing something wrong
> :-).

FWIW, I wrote this on purpose, because I think the "if"-resembling of ?: sometimes helps readability, but I have no objection to change it.  ;-)
With review comments addressed.
Attachment #528970 - Attachment is obsolete: true
Comment on attachment 529613 [details] [diff] [review]
Part 2: Implement the absolute positioning support for all frames

This should be ready for review.

These two patches shouldn't have any change on the current behavior.  I'd like to land these parts sooner than the rest of the patches in this bug (which I have yet to write).
Attachment #529613 - Attachment description: [WIP] Part 2: Implement the absolute positioning support for all frames → Part 2: Implement the absolute positioning support for all frames
Attachment #529613 - Flags: review?(roc)
Comment on attachment 529613 [details] [diff] [review]
Part 2: Implement the absolute positioning support for all frames

Review of attachment 529613 [details] [diff] [review]:

I like this very much!

::: layout/base/nsCSSFrameConstructor.cpp
@@ +1225,5 @@
+    // absolute containing block
+    if (aChildListName == containingBlock->GetAbsoluteListName()) {
+      if (!containingBlock->IsAbsoluteContainer()) {
+        containingBlock->MarkAsAbsoluteContainingBlock();
+      }

Why is this necessary? Seems to me that containingBlock should already have been marked as an absolute container.

@@ +1230,5 @@
+      rv = containingBlock->GetAbsoluteContainingBlock()->
+           SetInitialChildList(containingBlock, aChildListName, aFrameItems);
+    } else {
+      rv = containingBlock->SetInitialChildList(aChildListName, aFrameItems);
+    }

An alternative would be to have SetInitialChildList handle this case itself. I can see that would probably end up being more code, though.

Anyway, nsIFrame::SetInitialChildList should be documented to say that it doesn't handle GetAbsoluteListName().

@@ +1246,5 @@
+    // correctly.
+    if (aChildListName == containingBlock->GetAbsoluteListName() &&
+        !containingBlock->IsAbsoluteContainer()) {
+      containingBlock->MarkAsAbsoluteContainingBlock();
+    }

Again I would have expected containingBlock to already be one.

::: layout/base/nsFrameManager.cpp
@@ +535,5 @@
+      aListName == parentFrame->GetAbsoluteListName()) {
+    parentFrame->GetAbsoluteContainingBlock()->
+      RemoveFrame(parentFrame, aListName, aOldFrame);
+  } else {
+    rv = aOldFrame->GetParent()->RemoveFrame(aListName, aOldFrame);

use parentFrame on this line

::: layout/generic/nsAbsoluteContainingBlock.cpp
@@ +426,5 @@
 
   PRBool constrainHeight = (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE)
     && aConstrainHeight
        // Don't split if told not to (e.g. for fixed frames)
+    && !(aDelegatingFrame->GetType() == nsGkAtoms::inlineFrame && aDelegatingFrame->IsAbsoluteContainer())

We shouldn't check IsAbsoluteContainer here --- if it's not an absolute container we shouldn't be calling into nsAbsoluteContainingBlock!

If you want, you could assert aDelegatingFrame->IsAbsoluteContainer somewhere.

::: layout/generic/nsBlockFrame.cpp
@@ -304,5 @@
 
 void
 nsBlockFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
-  mAbsoluteContainer.DestroyFrames(this, aDestructRoot);

This is actually a little bit scary. We don't want to mess with frame destruction order. We should continue to tear down the absolute frames before we destroy their placeholders.

@@ +6238,5 @@
     }
   }
 
   aBuilder->MarkFramesForDisplayList(this, mFloats, aDirtyRect);
+  BuildDisplayListForAbsoluteChildren(aBuilder, aDirtyRect);

Is this needed, given you've added the call to BuildDisplayListForStackingContext/Child?

::: layout/generic/nsCanvasFrame.cpp
@@ +276,5 @@
   if (GetPrevInFlow()) {
     DisplayOverflowContainers(aBuilder, aDirtyRect, aLists);
   }
 
+  BuildDisplayListForAbsoluteChildren(aBuilder, aDirtyRect);

Is this needed, given you've added the call to BuildDisplayListForStackingContext/Child?

::: layout/generic/nsFrame.cpp
@@ +1854,5 @@
   return NS_OK;
 }
 
 void
+nsIFrame::BuildDisplayListForAbsoluteChildren(nsDisplayListBuilder* aBuilder,

This should be called something else since we're not actually building display items here, just ensuring that they *will* be built when placeholders are traversed.

How about just MarkAbsoluteFramesForDisplayList?

@@ +3637,5 @@
+nsFrame::FinishReflowWithAbsoluteFrames(nsPresContext*           aPresContext,
+                                        nsHTMLReflowMetrics&     aDesiredSize,
+                                        const nsHTMLReflowState& aReflowState,
+                                        nsReflowStatus&          aStatus,
+                                        PRBool                   aSkipFinish)

Instead of aSkipFinish, I think it's worth having a separate method ReflowAbsoluteFrames that does everything except the FinishAndStoreOverflow.

@@ +3646,5 @@
+    // Let the absolutely positioned container reflow any absolutely positioned
+    // child frames that need to be reflowed
+    // We want to do this under either of two conditions:
+    //  1. If we didn't do the incremental reflow above.
+    //  2. If our size changed.

But we're not checking those here, so this comment needs to be fixed.

@@ +3666,5 @@
+
+    // Factor the absolutely positioned child bounds into the overflow area
+    // Don't include this frame's bounds, nor its inline descendants' bounds,
+    // and don't store the overflow property.
+    // That will all be done by nsLineLayout::RelativePositionFrames.

This comment is inappropriate here.

::: layout/generic/nsInlineFrame.cpp
@@ +184,5 @@
 nsInlineFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists)
 {
+  BuildDisplayListForAbsoluteChildren(aBuilder, aDirtyRect);

Shouldn't be needed, as above.

::: layout/generic/nsPageContentFrame.cpp
@@ +142,2 @@
   nsReflowStatus fixedStatus = NS_FRAME_COMPLETE;
+  FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, fixedStatus);

You've effectively moved FinishAndStoreOverflow up here, but that's wrong since FinishAndStoreOverflow needs to run after we've set aDesiredSize.width/height.

::: layout/generic/nsViewportFrame.cpp
@@ +92,5 @@
   // We don't need any special painting or event handling. We just need to
   // mark our visible out-of-flow frames (i.e., the fixed position frames) so
   // that display list construction is guaranteed to recurse into their
   // ancestors.
+  BuildDisplayListForAbsoluteChildren(aBuilder, aDirtyRect);

Shouldn't be needed.

@@ +255,2 @@
 #ifdef DEBUG
+  if (IsAbsoluteContainer()) {

I think the case when this is not an absolute container is a page in print preview, right? You might want to mention that in a comment.
(In reply to comment #52)
> Comment on attachment 529613 [details] [diff] [review] [review]
> Part 2: Implement the absolute positioning support for all frames
> 
> Review of attachment 529613 [details] [diff] [review] [review]:
> 
> I like this very much!
> 
> ::: layout/base/nsCSSFrameConstructor.cpp
> @@ +1225,5 @@
> +    // absolute containing block
> +    if (aChildListName == containingBlock->GetAbsoluteListName()) {
> +      if (!containingBlock->IsAbsoluteContainer()) {
> +        containingBlock->MarkAsAbsoluteContainingBlock();
> +      }
> 
> Why is this necessary? Seems to me that containingBlock should already have
> been marked as an absolute container.

Not if the element in question has its style changed dynamically, for example by setting elem.style.position.

> @@ +1230,5 @@
> +      rv = containingBlock->GetAbsoluteContainingBlock()->
> +           SetInitialChildList(containingBlock, aChildListName, aFrameItems);
> +    } else {
> +      rv = containingBlock->SetInitialChildList(aChildListName, aFrameItems);
> +    }
> 
> An alternative would be to have SetInitialChildList handle this case itself. I
> can see that would probably end up being more code, though.
> 
> Anyway, nsIFrame::SetInitialChildList should be documented to say that it
> doesn't handle GetAbsoluteListName().

Did the latter.

> @@ +1246,5 @@
> +    // correctly.
> +    if (aChildListName == containingBlock->GetAbsoluteListName() &&
> +        !containingBlock->IsAbsoluteContainer()) {
> +      containingBlock->MarkAsAbsoluteContainingBlock();
> +    }
> 
> Again I would have expected containingBlock to already be one.

See above.

> ::: layout/base/nsFrameManager.cpp
> @@ +535,5 @@
> +      aListName == parentFrame->GetAbsoluteListName()) {
> +    parentFrame->GetAbsoluteContainingBlock()->
> +      RemoveFrame(parentFrame, aListName, aOldFrame);
> +  } else {
> +    rv = aOldFrame->GetParent()->RemoveFrame(aListName, aOldFrame);
> 
> use parentFrame on this line

Done.

> ::: layout/generic/nsAbsoluteContainingBlock.cpp
> @@ +426,5 @@
> 
>    PRBool constrainHeight = (aReflowState.availableHeight !=
> NS_UNCONSTRAINEDSIZE)
>      && aConstrainHeight
>         // Don't split if told not to (e.g. for fixed frames)
> +    && !(aDelegatingFrame->GetType() == nsGkAtoms::inlineFrame &&
> aDelegatingFrame->IsAbsoluteContainer())
> 
> We shouldn't check IsAbsoluteContainer here --- if it's not an absolute
> container we shouldn't be calling into nsAbsoluteContainingBlock!

Hmm, right!

> If you want, you could assert aDelegatingFrame->IsAbsoluteContainer somewhere.

GetAbsoluteContainingBlock() already asserts this.  No point in asserting twice I guess.

> ::: layout/generic/nsBlockFrame.cpp
> @@ -304,5 @@
> 
>  void
>  nsBlockFrame::DestroyFrom(nsIFrame* aDestructRoot)
>  {
> -  mAbsoluteContainer.DestroyFrames(this, aDestructRoot);
> 
> This is actually a little bit scary. We don't want to mess with frame
> destruction order. We should continue to tear down the absolute frames before
> we destroy their placeholders.

Hmm, even if the current code is not depending on this order?

I'd like to avoid modifying all of the DestroyFrom implementations if possible.  If this really scares you, I guess I'll make DestroyFrom protected, make existing callers to call Destroy instead, and modify nsIFrame::Destroy to handle the anon-pos children destruction...

> @@ +6238,5 @@
>      }
>    }
> 
>    aBuilder->MarkFramesForDisplayList(this, mFloats, aDirtyRect);
> +  BuildDisplayListForAbsoluteChildren(aBuilder, aDirtyRect);
> 
> Is this needed, given you've added the call to
> BuildDisplayListForStackingContext/Child?

Yeah, I remember some reftest somewhere failing without this, which caused me to add it back...

> ::: layout/generic/nsCanvasFrame.cpp
> @@ +276,5 @@
>    if (GetPrevInFlow()) {
>      DisplayOverflowContainers(aBuilder, aDirtyRect, aLists);
>    }
> 
> +  BuildDisplayListForAbsoluteChildren(aBuilder, aDirtyRect);
> 
> Is this needed, given you've added the call to
> BuildDisplayListForStackingContext/Child?

Yeah, see above.

> ::: layout/generic/nsFrame.cpp
> @@ +1854,5 @@
>    return NS_OK;
>  }
> 
>  void
> +nsIFrame::BuildDisplayListForAbsoluteChildren(nsDisplayListBuilder* aBuilder,
> 
> This should be called something else since we're not actually building display
> items here, just ensuring that they *will* be built when placeholders are
> traversed.
> 
> How about just MarkAbsoluteFramesForDisplayList?

Done.

> @@ +3637,5 @@
> +nsFrame::FinishReflowWithAbsoluteFrames(nsPresContext*           aPresContext,
> +                                        nsHTMLReflowMetrics&     aDesiredSize,
> +                                        const nsHTMLReflowState& aReflowState,
> +                                        nsReflowStatus&          aStatus,
> +                                        PRBool                   aSkipFinish)
> 
> Instead of aSkipFinish, I think it's worth having a separate method
> ReflowAbsoluteFrames that does everything except the FinishAndStoreOverflow.

Done.

While addressing the rest of the comments, FinishReflowWithAbsoluteFrames is now called only once.  Is it worth keeping it around, or should I just remove it?

> @@ +3646,5 @@
> +    // Let the absolutely positioned container reflow any absolutely
> positioned
> +    // child frames that need to be reflowed
> +    // We want to do this under either of two conditions:
> +    //  1. If we didn't do the incremental reflow above.
> +    //  2. If our size changed.
> 
> But we're not checking those here, so this comment needs to be fixed.
> 
> @@ +3666,5 @@
> +
> +    // Factor the absolutely positioned child bounds into the overflow area
> +    // Don't include this frame's bounds, nor its inline descendants' bounds,
> +    // and don't store the overflow property.
> +    // That will all be done by nsLineLayout::RelativePositionFrames.
> 
> This comment is inappropriate here.

Sorry, I had just moved the comments from nsPositionedInlineFrame, and forgot to fix them.

> ::: layout/generic/nsInlineFrame.cpp
> @@ +184,5 @@
>  nsInlineFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
>                                  const nsRect&           aDirtyRect,
>                                  const nsDisplayListSet& aLists)
>  {
> +  BuildDisplayListForAbsoluteChildren(aBuilder, aDirtyRect);
> 
> Shouldn't be needed, as above.

See above.

> ::: layout/generic/nsPageContentFrame.cpp
> @@ +142,2 @@
>    nsReflowStatus fixedStatus = NS_FRAME_COMPLETE;
> +  FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState,
> fixedStatus);
> 
> You've effectively moved FinishAndStoreOverflow up here, but that's wrong since
> FinishAndStoreOverflow needs to run after we've set aDesiredSize.width/height.

Fixed.

> ::: layout/generic/nsViewportFrame.cpp
> @@ +92,5 @@
>    // We don't need any special painting or event handling. We just need to
>    // mark our visible out-of-flow frames (i.e., the fixed position frames) so
>    // that display list construction is guaranteed to recurse into their
>    // ancestors.
> +  BuildDisplayListForAbsoluteChildren(aBuilder, aDirtyRect);
> 
> Shouldn't be needed.

See above.

> @@ +255,2 @@
>  #ifdef DEBUG
> +  if (IsAbsoluteContainer()) {
> 
> I think the case when this is not an absolute container is a page in print
> preview, right? You might want to mention that in a comment.

Hmm, no, it should also happen if we don't have any fixed-pos elements, right?  I mean, the code before this tested for the fixed container list being empty, but now instead of the container list being empty, it may not exist at all.
Attachment #529613 - Attachment is obsolete: true
Attachment #529888 - Flags: review?(roc)
Attachment #529613 - Flags: review?(roc)
(In reply to comment #53)
> Not if the element in question has its style changed dynamically, for example
> by setting elem.style.position.

Changing the position style will force frame reconstruction.

> > ::: layout/generic/nsBlockFrame.cpp
> > @@ -304,5 @@
> > 
> >  void
> >  nsBlockFrame::DestroyFrom(nsIFrame* aDestructRoot)
> >  {
> > -  mAbsoluteContainer.DestroyFrames(this, aDestructRoot);
> > 
> > This is actually a little bit scary. We don't want to mess with frame
> > destruction order. We should continue to tear down the absolute frames before
> > we destroy their placeholders.
> 
> Hmm, even if the current code is not depending on this order?

I believe it is. Maybe our tests are inadequate.

> I'd like to avoid modifying all of the DestroyFrom implementations if possible.
>  If this really scares you, I guess I'll make DestroyFrom protected, make
> existing callers to call Destroy instead, and modify nsIFrame::Destroy to
> handle the anon-pos children destruction...

You can't just do that since you need to pass the aDestructRoot.

I think for now just throw in calls to a new helper function like DestroyAbsoluteFrames(). If the order really doesn't matter, then I suggest changing the order in a separate patch. That will reduce risk.

> > Is this needed, given you've added the call to
> > BuildDisplayListForStackingContext/Child?
> 
> Yeah, I remember some reftest somewhere failing without this, which caused me
> to add it back...

I would like to understand why it's needed. Maybe we're fixing that failure the wrong way.

> While addressing the rest of the comments, FinishReflowWithAbsoluteFrames is
> now called only once.  Is it worth keeping it around, or should I just remove
> it?

Probably keep it around, since it will be the right thing for nsTableFrame::Reflow to call.

> > @@ +255,2 @@
> >  #ifdef DEBUG
> > +  if (IsAbsoluteContainer()) {
> > 
> > I think the case when this is not an absolute container is a page in print
> > preview, right? You might want to mention that in a comment.
> 
> Hmm, no, it should also happen if we don't have any fixed-pos elements, right? 

Whether a ViewportFrame is an absolute container should not depend on whether it has any fixed-pos children.
(In reply to comment #54)
> (In reply to comment #53)
> > Not if the element in question has its style changed dynamically, for example
> > by setting elem.style.position.
> 
> Changing the position style will force frame reconstruction.

Well, I was wrong about the reason why this is needed.  This code is necessary for fixed position stuff.  It's how we make the viewport frame as an absolute container.  (I found it by taking that code out and running the reftests, which makes us crash on <http://mxr.mozilla.org/mozilla-central/source/layout/reftests/bugs/76331-1.html?force=1>, which has a fixed position element.)  I _could_ probably get rid of this code by making the viewport frame always be an absolute containing block, but I think the current code is cleaner (also, see below).

> > I'd like to avoid modifying all of the DestroyFrom implementations if possible.
> >  If this really scares you, I guess I'll make DestroyFrom protected, make
> > existing callers to call Destroy instead, and modify nsIFrame::Destroy to
> > handle the anon-pos children destruction...
> 
> You can't just do that since you need to pass the aDestructRoot.
> 
> I think for now just throw in calls to a new helper function like
> DestroyAbsoluteFrames(). If the order really doesn't matter, then I suggest
> changing the order in a separate patch. That will reduce risk.

OK, I'll do that for now.

> > > Is this needed, given you've added the call to
> > > BuildDisplayListForStackingContext/Child?
> > 
> > Yeah, I remember some reftest somewhere failing without this, which caused me
> > to add it back...
> 
> I would like to understand why it's needed. Maybe we're fixing that failure the
> wrong way.

Without this for nsBlockFrame, the following tests fail (possibly among others):

http://mxr.mozilla.org/mozilla-central/source/layout/reftests/bugs/243519-9f.html?force=1
http://mxr.mozilla.org/mozilla-central/source/layout/reftests/bugs/372037-1.html?force=1
http://mxr.mozilla.org/mozilla-central/source/layout/reftests/bugs/387876-2.html?force=1

Basically they all have an abs-pos element which is the child of a block frame.

I think the only frame type which doesn't need this in ViewportFrame, which calls BuildDisplayListForStatckingChild unconditionally.  Do you agree?

> > > @@ +255,2 @@
> > >  #ifdef DEBUG
> > > +  if (IsAbsoluteContainer()) {
> > > 
> > > I think the case when this is not an absolute container is a page in print
> > > preview, right? You might want to mention that in a comment.
> > 
> > Hmm, no, it should also happen if we don't have any fixed-pos elements, right? 
> 
> Whether a ViewportFrame is an absolute container should not depend on whether
> it has any fixed-pos children.

Hmm, but this is not how the code currently behaves.  We mark the viewport frame as an absolute container in the code in question in the first paragraph of my comment.
> I think the only frame type which doesn't need this in ViewportFrame, which
> calls BuildDisplayListForStatckingChild unconditionally.  Do you agree?

Aha! The problem is in your change to BuildDisplayListForChild:
+  // Mark the display list items for absolutely positioned children
+  MarkAbsoluteFramesForDisplayList(aBuilder, aDirtyRect);
You actually should be calling MarkAbsoluteFramesForDisplayList on the *child*, not on the parent.

Basically, the only two callers of nsIFrame::BuildDisplayList should be BuildDisplayListForChild (which calls it on the child) and BuildDisplayListForStackingContext (which calls it on 'this'). So for something like this where we need to do something for every BuildDisplayList, you should be able to do it in just those two callers.

> Hmm, but this is not how the code currently behaves.  We mark the viewport
> frame as an absolute container in the code in question in the first paragraph
> of my comment.

OK, well, that's wrong. The viewport frame should probably be marked as an absolute container when we assign mFixedContainingBlock. Either that, or ViewportFrame::Init does it.
Basically I think it's a good idea to have the invariant that if a frame is pushed as some kind of absolute container during frame construction, then it has been set up as an absolute container.

Setting up the absolute container lazily might be slightly more efficient, but I think it would be more confusing. Certainly we have to be consistent and either always do it lazily or never do it lazily.
(In reply to comment #56)
> > I think the only frame type which doesn't need this in ViewportFrame, which
> > calls BuildDisplayListForStatckingChild unconditionally.  Do you agree?
> 
> Aha! The problem is in your change to BuildDisplayListForChild:
> +  // Mark the display list items for absolutely positioned children
> +  MarkAbsoluteFramesForDisplayList(aBuilder, aDirtyRect);
> You actually should be calling MarkAbsoluteFramesForDisplayList on the *child*,
> not on the parent.
> 
> Basically, the only two callers of nsIFrame::BuildDisplayList should be
> BuildDisplayListForChild (which calls it on the child) and
> BuildDisplayListForStackingContext (which calls it on 'this'). So for something
> like this where we need to do something for every BuildDisplayList, you should
> be able to do it in just those two callers.

It all makes sense now, and I have a patch which does the above!
(In reply to comment #57)
> Basically I think it's a good idea to have the invariant that if a frame is
> pushed as some kind of absolute container during frame construction, then it
> has been set up as an absolute container.
> 
> Setting up the absolute container lazily might be slightly more efficient, but
> I think it would be more confusing. Certainly we have to be consistent and
> either always do it lazily or never do it lazily.

OK, makes sense.  Done.
Attachment #529888 - Attachment is obsolete: true
Attachment #530198 - Flags: review?(roc)
Attachment #529888 - Flags: review?(roc)
Comment on attachment 530198 [details] [diff] [review]
Part 2: Implement the absolute positioning support for all frames

Review of attachment 530198 [details] [diff] [review]:

::: layout/generic/nsBlockFrame.cpp
@@ +6239,5 @@
     }
   }
 
   aBuilder->MarkFramesForDisplayList(this, mFloats, aDirtyRect);
+  MarkAbsoluteFramesForDisplayList(aBuilder, aDirtyRect);

We should not need this line.

::: layout/generic/nsFrame.cpp
@@ +1682,5 @@
     pseudoStackingContext = PR_TRUE;
+  }
+
+  // Mark the display list items for absolutely positioned children
+  aChild->MarkAbsoluteFramesForDisplayList(aBuilder, aDirtyRect);

I think you need to pass 'dirty' instead of aDirtyRect, since the rect needs to be relative to the child.

::: layout/generic/nsIFrame.h
@@ +2717,5 @@
   virtual nsIAtom* GetAbsoluteListName() const { return nsGkAtoms::absoluteList; }
 
+  /**
+   * This should be called by BuildDisplayList implementations to support adding
+   * the frames for absolute containing blocks to the display list builder.

Fix comment. BuildDisplayList implementations should not need to call this.
(In reply to comment #60)
> ::: layout/generic/nsBlockFrame.cpp
> @@ +6239,5 @@
>      }
>    }
> 
>    aBuilder->MarkFramesForDisplayList(this, mFloats, aDirtyRect);
> +  MarkAbsoluteFramesForDisplayList(aBuilder, aDirtyRect);
> 
> We should not need this line.
> 
> ::: layout/generic/nsFrame.cpp
> @@ +1682,5 @@
>      pseudoStackingContext = PR_TRUE;
> +  }
> +
> +  // Mark the display list items for absolutely positioned children
> +  aChild->MarkAbsoluteFramesForDisplayList(aBuilder, aDirtyRect);
> 
> I think you need to pass 'dirty' instead of aDirtyRect, since the rect needs to
> be relative to the child.

Ah, of course.  I missed these yesterday.  I fixed them, and there are now a bunch of reftests failures that I need to investigate again.  :(
OK, fixed the code and except for fixing the comment, just made MarkAbsoluteFramesForDisplayList private (because nobody else needs to call it!)
Attachment #530198 - Attachment is obsolete: true
Attachment #530448 - Flags: review?(roc)
Attachment #530198 - Flags: review?(roc)
Actually make MarkAbsoluteFramesForDisplayList private!
Attachment #530448 - Attachment is obsolete: true
Attachment #530450 - Flags: review?(roc)
Attachment #530448 - Flags: review?(roc)
Comment on attachment 530450 [details] [diff] [review]
Part 2: Implement the absolute positioning support for all frames

Review of attachment 530450 [details] [diff] [review]:
Attachment #530450 - Flags: review?(roc) → review+
Landed the first two patches in the bug:

http://hg.mozilla.org/mozilla-central/rev/b5c0b85194d6
http://hg.mozilla.org/mozilla-central/rev/14fe8a6cfd45

The rest of the work will follow.
This patch does most of what is needed to make tables abs-pos containing blocks.  It has some bugs though.  From the tests that I've run so far, we assert on <http://mxr.mozilla.org/mozilla-central/source/layout/generic/crashtests/323493-1.html?force=1> several times with a stack like this:

###!!! ASSERTION: containing block height must be constrained: 'containingBlockHeight != NS_AUTOHEIGHT', file ../../../layout/generic/nsHTMLReflowState.cpp, line 1135

#0	0x10002137f in NS_DebugBreak_P at nsDebugImpl.cpp:274
#1	0x10044b10b in nsHTMLReflowState::InitAbsoluteConstraints at nsHTMLReflowState.cpp:1134
#2	0x10044ccf7 in nsHTMLReflowState::InitConstraints at nsHTMLReflowState.cpp:1845
#3	0x10044d1e4 in nsHTMLReflowState::Init at nsHTMLReflowState.cpp:282
#4	0x1005cf43c in nsTableOuterFrame::InitChildReflowState at nsTableOuterFrame.cpp:451
#5	0x1005cf67c in nsTableOuterFrame::OuterBeginReflowChild at nsTableOuterFrame.cpp:920
#6	0x1005cf9e5 in nsTableOuterFrame::Reflow at nsTableOuterFrame.cpp:1021
#7	0x1003e351e in nsAbsoluteContainingBlock::ReflowAbsoluteFrame at nsAbsoluteContainingBlock.cpp:444
#8	0x1003e4108 in nsAbsoluteContainingBlock::Reflow at nsAbsoluteContainingBlock.cpp:158
#9	0x100416ffa in nsFrame::ReflowAbsoluteFrames at nsFrame.cpp:3681
#10	0x1005cffe9 in nsTableOuterFrame::Reflow at nsTableOuterFrame.cpp:1129
#11	0x1003e351e in nsAbsoluteContainingBlock::ReflowAbsoluteFrame at nsAbsoluteContainingBlock.cpp:444
#12	0x1003e4108 in nsAbsoluteContainingBlock::Reflow at nsAbsoluteContainingBlock.cpp:158
#13	0x100416ffa in nsFrame::ReflowAbsoluteFrames at nsFrame.cpp:3681
#14	0x100425400 in nsFrame::FinishReflowWithAbsoluteFrames at nsFrame.cpp:3642
#15	0x10044761d in nsCanvasFrame::Reflow at nsCanvasFrame.cpp:557
#16	0x100409c5c in nsContainerFrame::ReflowChild at nsContainerFrame.cpp:959
#17	0x10043b776 in nsHTMLScrollFrame::ReflowScrolledFrame at nsGfxScrollFrame.cpp:546
#18	0x10043c064 in nsHTMLScrollFrame::ReflowContents at nsGfxScrollFrame.cpp:638
#19	0x10043f66f in nsHTMLScrollFrame::Reflow at nsGfxScrollFrame.cpp:879
#20	0x100409c5c in nsContainerFrame::ReflowChild at nsContainerFrame.cpp:959
#21	0x1004bc78a in ViewportFrame::Reflow at nsViewportFrame.cpp:225

I think this is because we are either reflowing the absolute frames at the wrong time (since I'm reflowing them after the overflow dimensions is calculated -- but reflowing them before this breaks the rendering completely), or I need to change something so that the ascent member of the reflow metrics isn't NS_AUTOHEIGHT (but I have no idea how to do that yet).
Your reflow change looks right to me.

We shouldn't be calling InitAbsoluteConstraints for an nsTableFrame, which seems to be what's happening here. It happens on trunk too, but seems to be harmless on trunk for some reason. I think nsHTMLReflowState::InitFrameType should not return NS_CSS_FRAME_TYPE_ABSOLUTE for an nsTableFrame, I think where we test frame->GetType() == nsGkAtoms::tableFrame we should just return NS_CSS_FRAME_TYPE_UNKNOWN if so. nsHTMLReflowState::InitAbsoluteConstraints can assert frame->GetType() != nsGkAtoms::tableFrame instead of checking that.
Blocks: 512749
This worked perfectly, thanks!

Now, there's this reftest: http://mxr.mozilla.org/mozilla-central/source/layout/reftests/bugs/503364-1a.html?force=1 on which I'm getting an incorrect rendering.  Here's the resulting frame tree: http://pastebin.mozilla.org/1221780

Note how the frame for the div ends up under the absolute list of the viewport frame, except for the absolute list of the table frame.  I'm trying to figure out why this happens...
It seems to me that because the parent table doesn't have the NODE_DESCENDANTS_NEED_FRAMES flag set, we don't encounter it while creating the frame for the div, so the canvas frame ends up being the absolute container (because it was the last thing which called PushAbsoluteContainingBlock), which makes the reftest fail.  I'm not sure how dynamic changes like this are supposed to be handled.  Should we make sure that we reframe the table too?
I think I figured this out.  The code in nsCSSFrameConstructor::GetAbsoluteContainingBlock actively ignored table related frames, and I fixed that.
This is nearly finished.  The only remaining problem is the reftest failure with <http://mxr.mozilla.org/mozilla-central/source/layout/reftests/bugs/371561-1.html?force=1>.  In this page, the table is rendered too wide (NS_MAXSIZE), and lots of warnings like this are printed to the console:

WARNING: have unconstrained width; this should only result from very large sizes, not attempts at intrinsic width calculation: 'aContainingBlockWidth != NS_UNCONSTRAINEDSIZE', file ../../../layout/base/nsLayoutUtils.cpp, line 2424
WARNING: cell content 0x10643d7b8 has large width 1063004416 

I experimented with a fix like this one: http://pastebin.mozilla.org/1221886 but this patch breaks parts of this test <http://mxr.mozilla.org/mozilla-central/source/layout/reftests/bugs/427129-table.html?force=1> (the tables with a percent width).  I've run out of ideas on how to fix this...
Attachment #530762 - Attachment is obsolete: true
Analyzing the callstack from that first warning would be useful. Assuming nsTableFrame's reflow state is firing the warning, its available width should have been set in nsTableOuterFrame::OuterBeginReflowChild, to aAvailWidth, so why isn't that set to something reasonable?
This is a call stack:

#0	0x100376240 in nsLayoutUtils::ComputeWidthValue at nsLayoutUtils.cpp:2425
#1	0x10044df89 in nsCSSOffsetState::ComputeWidthValue at nsHTMLReflowState.cpp:189
#2	0x10044a688 in nsCSSOffsetState::ComputeWidthValue at nsHTMLReflowState.cpp:210
#3	0x10044a6d6 in nsHTMLReflowState::ComputeMinMaxValues at nsHTMLReflowState.cpp:2284
#4	0x10044c94a in nsHTMLReflowState::InitConstraints at nsHTMLReflowState.cpp:1786
#5	0x10044d194 in nsHTMLReflowState::Init at nsHTMLReflowState.cpp:282
#6	0x1005cf3ec in nsTableOuterFrame::InitChildReflowState at nsTableOuterFrame.cpp:451
#7	0x1005cf62c in nsTableOuterFrame::OuterBeginReflowChild at nsTableOuterFrame.cpp:920
#8	0x1005cf995 in nsTableOuterFrame::Reflow at nsTableOuterFrame.cpp:1021
#9	0x1003e34ce in nsAbsoluteContainingBlock::ReflowAbsoluteFrame at nsAbsoluteContainingBlock.cpp:444
#10	0x1003e40b8 in nsAbsoluteContainingBlock::Reflow at nsAbsoluteContainingBlock.cpp:158
#11	0x100416faa in nsFrame::ReflowAbsoluteFrames at nsFrame.cpp:3681
#12	0x10045c8be in nsInlineFrame::Reflow at nsInlineFrame.cpp:417
#13	0x1004649a7 in nsLineLayout::ReflowFrame at nsLineLayout.cpp:852
#14	0x1003eddf1 in nsBlockFrame::ReflowInlineFrame at nsBlockFrame.cpp:3826
#15	0x1003f43ea in nsBlockFrame::DoReflowInlineFrames at nsBlockFrame.cpp:3622
#16	0x1003f4f1e in nsBlockFrame::ReflowInlineFrames at nsBlockFrame.cpp:3481
#17	0x1003f6b6c in nsBlockFrame::ReflowLine at nsBlockFrame.cpp:2557
#18	0x1003f767b in nsBlockFrame::ReflowDirtyLines at nsBlockFrame.cpp:1995
#19	0x1003f9526 in nsBlockFrame::Reflow at nsBlockFrame.cpp:1075
#20	0x1003fda25 in nsBlockReflowContext::ReflowBlock at nsBlockReflowContext.cpp:296
#21	0x1003f5a1c in nsBlockFrame::ReflowBlockFrame at nsBlockFrame.cpp:3199
#22	0x1003f68f5 in nsBlockFrame::ReflowLine at nsBlockFrame.cpp:2501
#23	0x1003f767b in nsBlockFrame::ReflowDirtyLines at nsBlockFrame.cpp:1995
#24	0x1003f9526 in nsBlockFrame::Reflow at nsBlockFrame.cpp:1075
#25	0x100409c0c in nsContainerFrame::ReflowChild at nsContainerFrame.cpp:959
#26	0x100446fca in nsCanvasFrame::Reflow at nsCanvasFrame.cpp:454
#27	0x100409c0c in nsContainerFrame::ReflowChild at nsContainerFrame.cpp:959
#28	0x10043b726 in nsHTMLScrollFrame::ReflowScrolledFrame at nsGfxScrollFrame.cpp:546
#29	0x10043c014 in nsHTMLScrollFrame::ReflowContents at nsGfxScrollFrame.cpp:638
#30	0x10043f61f in nsHTMLScrollFrame::Reflow at nsGfxScrollFrame.cpp:879
#31	0x100409c0c in nsContainerFrame::ReflowChild at nsContainerFrame.cpp:959
#32	0x1004bc73a in ViewportFrame::Reflow at nsViewportFrame.cpp:225
#33	0x1003a022f in PresShell::DoReflow at nsPresShell.cpp:7735

The problem is that OuterBeginReflowChild does not pass the available width to InitChildReflowState, and the reflow state ends up getting initialized with avail width being set to -1, which triggers this code: <http://mxr.mozilla.org/mozilla-central/source/layout/generic/nsHTMLReflowState.cpp#1703>.  ComputeContainingBlockRectangle relies on mFrameType being ABSOLUTE <http://mxr.mozilla.org/mozilla-central/source/layout/generic/nsHTMLReflowState.cpp#1573>, which causes the incorrect available with to be calculated here.

The obvious patch <http://pastebin.mozilla.org/1222183> fixes this, but I have no idea if that's acceptable...
So, this patch <http://pastebin.mozilla.org/1222364> makes that reftest pass, but it also breaks a number of table layout reftests, such as <http://mxr.mozilla.org/mozilla-central/source/layout/reftests/bugs/370525-1-notref.html>, which is now rendered identical to <http://mxr.mozilla.org/mozilla-central/source/layout/reftests/bugs/370525-1.html>.
I think this happens because nsTableOuterFrame::IsContainingBlock now returns true, which changes the way nsHTMLReflowState::InitResizeFlags behaves (or maybe because of the change to mCBReflowState)...
Yeah, I think IsContainingBlock is bogus. In CSS, "containing block" is a binary relation, not a unary relation. So I think we should replace nsIFrame::IsContainingBlock with nsIFrame::GetContainingBlock() which returns the containing block for the given frame.

I don't know if that's stricly necessary to do here, but it will probably help. There aren't many users of IsContainingBlock, and some of them are just implementing GetContainingBlock() themselves.
I tried to do this, and I _think_ I implemented the CSS algorithm correctly <http://pastebin.mozilla.org/1222989>, but this patch doesn't fix the reftest in comment 71, and it _also_ breaks the reftest in comment 74...
Depends on: 656130
Your GetContainingBlock implementation looks right but it could be improved:

Drop GetNearestBlockContainer and just use nsLayoutUtils::FindNearestBlockAncestor.

In GetContainingBlock, don't bother checking for viewportFrame/pageContentFrame. For the STATIC case, calling FindNearestBlockAncestor(this) will return null if this has no ancestor block.

For ABSOLUTE and FIXED, you can just return GetParent(). An absolutely (for fixed) positioned frame's containing block is always its parent.
Shouldn't GetAdditionalChildListName only return the absolute list if the frame is an absolute container?
Blocks: 203225
Blocks: 239310
Blocks: 371452
(In reply to comment #79)
> Shouldn't GetAdditionalChildListName only return the absolute list if the
> frame is an absolute container?

No, why would it?  What prevents absolute containers to have other child list types?
Oh, gah.  We interpret a null as "no more lists", right.  That sucks.  :(
(In reply to comment #78)
> Drop GetNearestBlockContainer and just use
> nsLayoutUtils::FindNearestBlockAncestor.

It turns out that this is not really the right thing to do, since these two functions are not equivalent.

For example, nsRootBoxFrame is not an nsBlockFrame, but it's display is set to block.  This leads to a startup crash because we would incorrectly set the reflow state's mCBReflowState to null.
(In reply to comment #81)
> Oh, gah.  We interpret a null as "no more lists", right.  That sucks.  :(

Indeed!  See bug 653649.  ;-)
This is what I have so far.  It doesn't crash on startup, but it probably has lots of other bugs.  I'm going to sleep now, and will continue to work on this tomorrow.
(In reply to comment #82)
> (In reply to comment #78)
> > Drop GetNearestBlockContainer and just use
> > nsLayoutUtils::FindNearestBlockAncestor.
> 
> It turns out that this is not really the right thing to do, since these two
> functions are not equivalent.
> 
> For example, nsRootBoxFrame is not an nsBlockFrame, but it's display is set
> to block.  This leads to a startup crash because we would incorrectly set
> the reflow state's mCBReflowState to null.

Hmm yeah.

Maybe the thing to do for the static/relative case is what nsCSSRendering does near the comment "// Find the containing block frame": use the parent, unless the frame is eLineParticipant, in which case use the nearest ancestor that isn't eLineParticipant.
Depends on: 656875
Depends on: 657182
(In reply to comment #85)
> (In reply to comment #82)
> > (In reply to comment #78)
> > > Drop GetNearestBlockContainer and just use
> > > nsLayoutUtils::FindNearestBlockAncestor.
> > 
> > It turns out that this is not really the right thing to do, since these two
> > functions are not equivalent.
> > 
> > For example, nsRootBoxFrame is not an nsBlockFrame, but it's display is set
> > to block.  This leads to a startup crash because we would incorrectly set
> > the reflow state's mCBReflowState to null.
> 
> Hmm yeah.
> 
> Maybe the thing to do for the static/relative case is what nsCSSRendering
> does near the comment "// Find the containing block frame": use the parent,
> unless the frame is eLineParticipant, in which case use the nearest ancestor
> that isn't eLineParticipant.

Do you mean that should be done in nsLayoutUtils::FindNearestBlockAncestor?
We need to back this out from Firefox 6 Aurora if the work in this bug has not been finished yet.
(In reply to comment #86)
> Do you mean that should be done in nsLayoutUtils::FindNearestBlockAncestor?

No. Do it in a new function.
Attachment #531848 - Attachment is obsolete: true
Only 1 reftest fails with this patch now: layout/reftests/bugs/371561-1.html.  I'm going to fix that and submit the final version for review.
Reverting the mFrameType changes here <http://hg.mozilla.org/try/rev/19113c44bbca> fixes the reftest, but causes a bunch of assertions like this at the beginning of InitAbsoluteConstraints for reftests like this <http://mxr.mozilla.org/mozilla-central/source/layout/reftests/bugs/490176-1.html?force=1>:

###!!! ASSERTION: containing block height must be constrained: 'containingBlockHeight != NS_AUTOHEIGHT', file /Users/ehsanakhgari/moz/mozilla-central/layout/generic/nsHTMLReflowState.cpp, line 1120

This is because when computing the containing block rectangle (with the stack below), the CB RS is now the parent table outer frame, not the canvas frame, and that table outer frame has an unconstrained height:

#0	0x10061b1dd in nsHTMLReflowState::ComputeContainingBlockRectangle at nsHTMLReflowState.cpp:1558
#1	0x100617f2b in nsHTMLReflowState::InitConstraints at nsHTMLReflowState.cpp:1688
#2	0x1006164b0 in nsHTMLReflowState::Init at nsHTMLReflowState.cpp:282
#3	0x1008187b2 in nsTableOuterFrame::InitChildReflowState at nsTableOuterFrame.cpp:451
#4	0x10081a38f in nsTableOuterFrame::OuterBeginReflowChild at nsTableOuterFrame.cpp:920
#5	0x10081a9d0 in nsTableOuterFrame::Reflow at nsTableOuterFrame.cpp:1021
#6	0x1005911b2 in nsAbsoluteContainingBlock::ReflowAbsoluteFrame at nsAbsoluteContainingBlock.cpp:444
#7	0x1005903a1 in nsAbsoluteContainingBlock::Reflow at nsAbsoluteContainingBlock.cpp:155
#8	0x1005d611c in nsFrame::ReflowAbsoluteFrames at nsFrame.cpp:3677
#9	0x1005d5f7d in nsFrame::FinishReflowWithAbsoluteFrames at nsFrame.cpp:3641
#10	0x10081b2fd in nsTableOuterFrame::Reflow at nsTableOuterFrame.cpp:1130
#11	0x1005911b2 in nsAbsoluteContainingBlock::ReflowAbsoluteFrame at nsAbsoluteContainingBlock.cpp:444
#12	0x1005903a1 in nsAbsoluteContainingBlock::Reflow at nsAbsoluteContainingBlock.cpp:155
#13	0x1005d611c in nsFrame::ReflowAbsoluteFrames at nsFrame.cpp:3677
#14	0x1005d5f7d in nsFrame::FinishReflowWithAbsoluteFrames at nsFrame.cpp:3641
#15	0x10061516b in nsCanvasFrame::Reflow at nsCanvasFrame.cpp:557
#16	0x1005c1b7d in nsContainerFrame::ReflowChild at nsContainerFrame.cpp:958
#17	0x1005fdc72 in nsHTMLScrollFrame::ReflowScrolledFrame at nsGfxScrollFrame.cpp:545
#18	0x1005fe493 in nsHTMLScrollFrame::ReflowContents at nsGfxScrollFrame.cpp:639
#19	0x1005ff5cd in nsHTMLScrollFrame::Reflow at nsGfxScrollFrame.cpp:880
#20	0x1005c1b7d in nsContainerFrame::ReflowChild at nsContainerFrame.cpp:958
#21	0x1006accf7 in ViewportFrame::Reflow at nsViewportFrame.cpp:224

This causes ComputeContainingBlockRectangle to set aContainingBlockHeight to NS_MAXSIZE, which causes InitAbsoluteConstraints called later in InitConstraints to assert.

Boris, do you have any idea how this can be solved?
Why are we calling InitAbsoluteConstraints for the inner table?
(In reply to comment #92)
> Why are we calling InitAbsoluteConstraints for the inner table?

Because the table frame's mFrameType is NS_CSS_FRAME_TYPE_ABSOLUTE.  <http://mxr.mozilla.org/mozilla-central/source/layout/generic/nsHTMLReflowState.cpp#1842>
Wait.  I thought we were going to leave mFrameType as BLOCK for the inner table but take out the hackery in InitCBContainingBlock so the containing block reflow state for the inner table is the outer table....
(In reply to comment #94)
> Wait.  I thought we were going to leave mFrameType as BLOCK for the inner
> table but take out the hackery in InitCBContainingBlock so the containing
> block reflow state for the inner table is the outer table....

Hmm, IIRC what we discussed last week was trying to revert the changes I had made to InitFrameType, and also try removing the InitCBReflowState hack to make the CB reflow state for inner table frames the outer table on top of that.

Only doing the first thing seems to help things a lot (it fixes the rendering problems) except for these assertions: <http://tbpl.mozilla.org/?tree=Try&rev=19113c44bbca> (effectively this patch on top of my other ones: <http://hg.mozilla.org/try/rev/19113c44bbca>).  Note that with this patches, *both* the inner and outer table frames are NS_CSS_FRAME_TYPE_ABSOLUTE, and the inner table frame's CB reflow state points to the outer table frame's CB.

Removing the InitCBReflowState hackery for inner tables breaks rendering <http://tbpl.mozilla.org/?tree=Try&rev=ce676985fd3b> (effectively this patch on top of my other ones: <http://hg.mozilla.org/try/rev/ce676985fd3b>).  Note that with this patch, *both* the inner and outer table frames are NS_CSS_FRAME_TYPE_ABSOLUTE, and the inner table frame's CB reflow state points to the outer table frame.

Now, making the mFrameType for the inner table frame BLOCK will fix this assertion that I'm struggling with now, *but* it will regress the reftest in comment 91.  I'm not sure what you're suggesting...
I thought what I suggested was to leave your InitFrameType changes but take out the InitCBReflowState hack.  The comment 91 reftest was failing for type == BLOCK because the cb reflow state was the inline, which had the wrong computed dimensions, no?
Is this completely landed or does it need a backout?
(In reply to comment #96)
> I thought what I suggested was to leave your InitFrameType changes but take
> out the InitCBReflowState hack.  The comment 91 reftest was failing for type
> == BLOCK because the cb reflow state was the inline, which had the wrong
> computed dimensions, no?

Leaving the InitFrameType changes, and removing the InitCBReflowState hacks for dynamically positioned table frames seems to fix everything!  Bug I wouldn't believe it until I get try results.  ;-)
(In reply to comment #97)
> Is this completely landed or does it need a backout?

It does need a backout, I will attach a patch soon.
Attached patch Aurora backoutSplinter Review
Attachment #534949 - Flags: approval-mozilla-aurora?
Attachment #532973 - Attachment is obsolete: true
Attachment #533627 - Attachment is obsolete: true
Attachment #534950 - Flags: review?(bzbarsky)
Comment on attachment 534950 [details] [diff] [review]
[WIP] part 3.1: Replace IsContainingBlock with GetContainingBlock

roc, do you want to review this too?
Attachment #534950 - Flags: review?(roc)
Comment on attachment 534950 [details] [diff] [review]
[WIP] part 3.1: Replace IsContainingBlock with GetContainingBlock

bz can do this.
Attachment #534950 - Flags: review?(roc)
So, I was looking at why the test case in bug 63895 still doesn't render correctly, and I figured out that it's because the outer table frame (which is what we position abs-pos kids relative to) is getting a width equal to that of its containing block (the body element in this case).

After a while of poking through the specs, and talking to dbaron in IRC, I came to the conclusion that this is the right behavior, and it's Opera and WebKit which have got this wrong.

We can still make something like the rendering that the author of that test case intended work by making the row groups also an abs-pos container, so that the author can make the tbody element positioned, but that can happen in another bug.

So, the set of patches attached here, with the addition of the patch in bug 656130 complete the work of making tables abs-pos containers.  :-)
(In reply to comment #104)
> After a while of poking through the specs, and talking to dbaron in IRC, I
> came to the conclusion that this is the right behavior, and it's Opera and
> WebKit which have got this wrong.

I disagree. See discussion in bug 659828.
Depends on: 655462
Whiteboard: needs a back out for 6. approval pending.
Comment on attachment 534949 [details] [diff] [review]
Aurora backout

approved to back out from 5 Aurora
Attachment #534949 - Flags: approval-mozilla-aurora? → approval-mozilla-aurora+
Whiteboard: needs a back out for 6. approval pending.
Attachment #534949 - Flags: checked-in+ → checkin+
Depends on: 659828
Blocks: 653739
Comment on attachment 534950 [details] [diff] [review]
[WIP] part 3.1: Replace IsContainingBlock with GetContainingBlock

>+++ b/layout/generic/nsFrame.cpp
>+PRBool
>+nsIFrame::IsBlockWrapper()
>+{
>+  if (nsLayoutUtils::GetAsBlock(this)) {

I don't think we need this check; just check the pseudo type directly.  Unless this is meant to be an optimization?  I'm not sure which is faster...

>+GetNearestBlockContainer(nsIFrame* frame)

Can you make this iterative instead of recursing?  Should be simple, and better to not recurse when we can avoid it.

>+nsIFrame::GetContainingBlock()

1) Would it make sense to test IsAbsolutelyPositioned() instead of using the switch?
2) Even if IsAbsolutelyPositioned(), we probably need to check for the out-of-flow bit before deciding the containing block is our parent.  I hate MathML's messing with positioning.  :(

>+++ b/layout/generic/nsHTMLReflowState.cpp
>+  // Absolutely positioned frames should always be kids of the frames that
>+  // determine their containing block, except for inner table frames.

I'm not sure I follow this comment, given the code below.  In particular, for positioned inner table frames you're setting mCBReflowState = parentReflowState!  I think this comment should just go away.

>+  if (parentReflowState->frame == frame->GetContainingBlock() ||
>       (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE)) {

Isn't that second test redundant?  That is, can mFrameType be ABSOLUTE when parentReflowState->frame != frame->GetContainingBlock()?  Used to be that we needed the mFrameType test to handle the case when parentReflowState->frame was a rel pos inline, but with your changes that will test as frame->GetContainingBlock(), right?

>     // an absolutely positioned inner table needs to use the parent of
>+    // the outer table.

This comment doesn't seem to match reality.

>+    if (frame->GetType() == nsGkAtoms::tableFrame &&
>+        !parentReflowState->frame->GetStyleDisplay()->IsAbsolutelyPositioned()) {
>+      mCBReflowState = parentReflowState->mCBReflowState;

As I said on IRC, this doesn't quite make sense to me.  Why do we use the containing block of the _outer_ table for non-positioned inner tables only?  Shouldn't we just use the outer table as the inner table's containing block in all cases?

And yes, I realize that we had a whole comment about this situation that you deleted that claimed that we need to use the mCBReflowState of the outer table as the CBReflowState for the inner.  Maybe that's all that's going on here, but if so we should documet that.  And I'd love to understand why that's so....

>+  } else if (frame->GetType() == nsGkAtoms::tableColFrame ||
>+             frame->GetType() == nsGkAtoms::tableRowFrame ||
>+             frame->IsBlockWrapper()) {
>+    mCBReflowState = parentReflowState;

Why is this clause needed?  That is, if frame->IsBlockWrapper(), shouldn't we have fallen into the first clause of the |if|?  And similar for colframe and rowframe?

>+    // Make sure that we don't set mCBReflowState to null for edge cases
>+    // such as root frames.

Hmm.  What used to happen for those before?

>@@ -368,17 +365,18 @@ nsHTMLReflowState::InitResizeFlags(nsPre
>+  } else if (mCBReflowState &&
>+             !(nsLayoutUtils::GetAsBlock(frame) && !frame->IsBlockWrapper())) {

I _think_ this is OK, but I think that would be more readable as:

   } else if (mCBReflowState &&
              (!nsLayoutUtils::GetAsBlock(frame) || frame->IsBlockWrapper())) {

(less in the way of double-negatives).

>@@ -412,17 +410,18 @@ nsHTMLReflowState::InitResizeFlags(nsPre
>     // but only on containing blocks if this frame is not a suitable block
>-    dependsOnCBHeight |= !frame->IsContainingBlock();
>+    dependsOnCBHeight |= !(mCBReflowState &&
>+                           frame->GetContainingBlock() == mCBReflowState->frame);

This I'm pretty sure is wrong.  The test here needs to match the test in nsHTMLReflowState::CalcLineHeight(void), which I think is actually correct.

>+nsHTMLReflowState::GetContainingBlockFor(nsIFrame* aFrame)

This whole method seems redundant.  Indeed, if aFrame->GetStyleDisplay()->IsAbsolutelyPositioned(), then aFrame->GetContainingBlock() == aFrame->GetParent(), right?  So this method always returns aFrame->GetContainingBlock(), as far as I can tell.

If I'm right, then we should just make it return aFrame->GetContainingBlock() and file a bug to nix this method altogether.

>+++ b/layout/generic/nsIFrame.h
>+  PRBool IsBlockWrapper();

This can be a const method, right?

>+  nsIFrame* GetContainingBlock();

Likewise.

>+++ b/layout/style/nsComputedDOMStyle.cpp
>+    if (nsLayoutUtils::GetAsBlock(mInnerFrame) && !mInnerFrame->IsBlockWrapper()) {

This pattern pops up a lot in this patch.  Can we just factor it out into a helper method on nsLayoutUtils or something?
(In reply to comment #108)
> Comment on attachment 534950 [details] [diff] [review] [review]
> [WIP] part 3.1: Replace IsContainingBlock with GetContainingBlock
> 
> >+++ b/layout/generic/nsFrame.cpp
> >+PRBool
> >+nsIFrame::IsBlockWrapper()
> >+{
> >+  if (nsLayoutUtils::GetAsBlock(this)) {
> 
> I don't think we need this check; just check the pseudo type directly. 
> Unless this is meant to be an optimization?  I'm not sure which is faster...

No, this wasn't meant to be an optimization (and if it were, it would've been a premature one!).  I'll fix this.

> >+GetNearestBlockContainer(nsIFrame* frame)
> 
> Can you make this iterative instead of recursing?  Should be simple, and
> better to not recurse when we can avoid it.

Sure.

> >+nsIFrame::GetContainingBlock()
> 
> 1) Would it make sense to test IsAbsolutelyPositioned() instead of using the
> switch?

I guess so.

> 2) Even if IsAbsolutelyPositioned(), we probably need to check for the
> out-of-flow bit before deciding the containing block is our parent.  I hate
> MathML's messing with positioning.  :(

I don't really know about how MathML messes with positioning, so I'm not really sure what you're suggesting here...

> >+++ b/layout/generic/nsHTMLReflowState.cpp
> >+  // Absolutely positioned frames should always be kids of the frames that
> >+  // determine their containing block, except for inner table frames.
> 
> I'm not sure I follow this comment, given the code below.  In particular,
> for positioned inner table frames you're setting mCBReflowState =
> parentReflowState!  I think this comment should just go away.

Agreed.  This was stupid.  :-)

> >+  if (parentReflowState->frame == frame->GetContainingBlock() ||
> >       (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE)) {
> 
> Isn't that second test redundant?  That is, can mFrameType be ABSOLUTE when
> parentReflowState->frame != frame->GetContainingBlock()?  Used to be that we
> needed the mFrameType test to handle the case when parentReflowState->frame
> was a rel pos inline, but with your changes that will test as
> frame->GetContainingBlock(), right?

You're right, I've removed it.

> >     // an absolutely positioned inner table needs to use the parent of
> >+    // the outer table.
> 
> This comment doesn't seem to match reality.

Right, fixed.

> >+    if (frame->GetType() == nsGkAtoms::tableFrame &&
> >+        !parentReflowState->frame->GetStyleDisplay()->IsAbsolutelyPositioned()) {
> >+      mCBReflowState = parentReflowState->mCBReflowState;
> 
> As I said on IRC, this doesn't quite make sense to me.  Why do we use the
> containing block of the _outer_ table for non-positioned inner tables only? 
> Shouldn't we just use the outer table as the inner table's containing block
> in all cases?

OK, so I tried really hard to remember why I've done this, to no avail.  So I removed it and ran the reftests, and not including this check breaks this test: <http://mxr.mozilla.org/mozilla-central/source/layout/reftests/bugs/371561-1.html?force=1>.  In this test, we have an abs-pos table as a child of a positioned inline.  I guess in that case, the parent's CB is not suitable as the CB for the inner table frame.  Maybe we need to change this to check for the positioned inline case?  I really don't know what's a better way to deal with this situation.

> >+  } else if (frame->GetType() == nsGkAtoms::tableColFrame ||
> >+             frame->GetType() == nsGkAtoms::tableRowFrame ||
> >+             frame->IsBlockWrapper()) {
> >+    mCBReflowState = parentReflowState;
> 
> Why is this clause needed?  That is, if frame->IsBlockWrapper(), shouldn't
> we have fallen into the first clause of the |if|?  And similar for colframe
> and rowframe?

Hmm, right.  I had probably added this while I was in the middle of working on this, and forgot to take it out.

> >+    // Make sure that we don't set mCBReflowState to null for edge cases
> >+    // such as root frames.
> 
> Hmm.  What used to happen for those before?

Oops, s/root/canvas/.  nsCanvasFrame::IsContainingBlock returns true, so for canvas, we used to end up in the first if branch.  Basically, this preserves that behavior.

> >@@ -368,17 +365,18 @@ nsHTMLReflowState::InitResizeFlags(nsPre
> >+  } else if (mCBReflowState &&
> >+             !(nsLayoutUtils::GetAsBlock(frame) && !frame->IsBlockWrapper())) {
> 
> I _think_ this is OK, but I think that would be more readable as:
> 
>    } else if (mCBReflowState &&
>               (!nsLayoutUtils::GetAsBlock(frame) ||
> frame->IsBlockWrapper())) {
> 
> (less in the way of double-negatives).

Fixed.

> >@@ -412,17 +410,18 @@ nsHTMLReflowState::InitResizeFlags(nsPre
> >     // but only on containing blocks if this frame is not a suitable block
> >-    dependsOnCBHeight |= !frame->IsContainingBlock();
> >+    dependsOnCBHeight |= !(mCBReflowState &&
> >+                           frame->GetContainingBlock() == mCBReflowState->frame);
> 
> This I'm pretty sure is wrong.  The test here needs to match the test in
> nsHTMLReflowState::CalcLineHeight(void), which I think is actually correct.

Done.

> >+nsHTMLReflowState::GetContainingBlockFor(nsIFrame* aFrame)
> 
> This whole method seems redundant.  Indeed, if
> aFrame->GetStyleDisplay()->IsAbsolutelyPositioned(), then
> aFrame->GetContainingBlock() == aFrame->GetParent(), right?  So this method
> always returns aFrame->GetContainingBlock(), as far as I can tell.
> 
> If I'm right, then we should just make it return
> aFrame->GetContainingBlock() and file a bug to nix this method altogether.

Correct.  But not worth filing another bug, I just went ahead and did it.  :-)

> >+++ b/layout/generic/nsIFrame.h
> >+  PRBool IsBlockWrapper();
> 
> This can be a const method, right?
> 
> >+  nsIFrame* GetContainingBlock();
> 
> Likewise.

Done.

> >+++ b/layout/style/nsComputedDOMStyle.cpp
> >+    if (nsLayoutUtils::GetAsBlock(mInnerFrame) && !mInnerFrame->IsBlockWrapper()) {
> 
> This pattern pops up a lot in this patch.  Can we just factor it out into a
> helper method on nsLayoutUtils or something?

Done: nsLayoutUtils::IsNonWrapperBlock.
Attachment #534950 - Attachment is obsolete: true
Attachment #538623 - Flags: review?(bzbarsky)
Attachment #534950 - Flags: review?(bzbarsky)
Comment on attachment 538623 [details] [diff] [review]
part 3.1: Replace IsContainingBlock with GetContainingBlock

> I don't really know about how MathML messes with positioning

It disallows positioning inside it, so the style can be NS_STYLE_POSITION_ABSOLUTE but the frame is still in-flow and behaves like a statically-positioned frame...  So I really think we need to test IsAbsolutelyPositioned() && out-of-flow.

>+++ b/layout/generic/nsHTMLReflowState.cpp
>+    // Inner table frames need to use the containing block of the outer
>+    // table frame, unless if they're absolutely positioned.

s/unless if/unless/

You can just check GetStyleDisplay() on frame, instead of checking it on parentReflowState->frame, right?

> I guess in that case, the parent's CB is not suitable as the CB for
> the inner table frame.

Well, sure.  But the question is why it _is_ for non-positioned cases.  I guess percentage heights or something?

We should revisit this code after bug 659828, perhaps.  In my ideal world the comment here would describe _why_ the containing block is what it is.  In my even more ideal world, GetContainingBlock would return the right thing on inner tables to start with!

Can we file a followup bug to sort this out?

> Oops, s/root/canvas/.  nsCanvasFrame::IsContainingBlock returns true,
> so for canvas, we used to end up in the first if branch.  Basically,
> this preserves that behavior.

But frame->GetContainingBlock() should be the canvas for kids of the canvas, no?  So shouldn't we still end up in the first if branch?  Or is this trying to deal with the case when |frame| is the canvas or something?  But what's parentReflowState->frame in that case?  I'd really like the comments to clearly explain which case we're dealing with, at least.

The rest looks fine.
OK, this should address all of the review comments.  I finally figured out that special casing non-abspos inner table frames is a mistake; special casing should happen in ComputeContainingBlockRectangle.  This fails the reftest we talked about on IRC.  This is the fix: <http://hg.mozilla.org/users/ehsan.akhgari_gmail.com/mq/file/b879492c5fc7/trial>, and it should be the only significant change in this version of the patch (aside from the reftests added).
Attachment #538623 - Attachment is obsolete: true
Attachment #540345 - Flags: review?(bzbarsky)
Attachment #538623 - Flags: review?(bzbarsky)
We need to back this out from Aurora if the rest of the pieces don't land by the next Aurora migration (which is pretty unlikely I think!)
Blocks: 665853
Comment on attachment 540345 [details] [diff] [review]
part 3.1: Replace IsContainingBlock with GetContainingBlock

r=me with two nits that don't need another patch:

1)  Please file that followup bug about sorting out GetContainingBlock() behavior for inner tables.

2)  The new check added to ComputeContainingBlockRectangle should probably also check that frame->GetParent() is out of flow.
Attachment #540345 - Flags: review?(bzbarsky) → review+
(In reply to comment #113)
> Comment on attachment 540345 [details] [diff] [review] [review]
> part 3.1: Replace IsContainingBlock with GetContainingBlock
> 
> r=me with two nits that don't need another patch:
> 
> 1)  Please file that followup bug about sorting out GetContainingBlock()
> behavior for inner tables.

Filed bug 666447.

> 2)  The new check added to ComputeContainingBlockRectangle should probably
> also check that frame->GetParent() is out of flow.

Done.

Also, added a new reftest for the case you asked me to test.  But there's one problem with it: the test asserts here:

http://mxr.mozilla.org/mozilla-central/source/layout/generic/nsHTMLReflowState.cpp#1584

The assertion happens because aContainingBlockRS->mComputedBorderPadding.right is 60 (because of the right-border style, while aContainingBlockRS->frame->GetRect() is empty (because the inline frame's reflow is not finished yet.)  It seems like the comment there about nsInlineFrame::Reflow passing the CB dimensions is just lies, and the same assertions happen without my patches too.

Should I file a bug about that and annotate the reftest file?
Attachment #540345 - Attachment is obsolete: true
The comment about nsInlineFrame::Reflow passing the CB dimensions is true for the kids of that inline frame; the _outer_ table frame gets the right CB size passed to it.  ;)

Please do file a bug and annotate the reftest, yes.
Filed bug 666606 for the assertion.
Attachment #541248 - Attachment is obsolete: true
Switched the reftests to use the Ahem font for precise measurements, and skipped them because of bug 667079.
Attachment #541446 - Attachment is obsolete: true
>We need to back this out from Aurora if the rest of the pieces don't land by the >next Aurora migration (which is pretty unlikely I think!)

I'd prefer if you backed it out of mozilla-central. This divergence between what we're testing on mozilla-central and shipping in 5,6,7 is increasingly problematic. So is not being able to fuzz properly (see bug 656130).

Did this ever get backed out from Aurora-6? The bug is marked as "status-firefox6:fixed" :(
Depends on: 660588
(In reply to comment #118)
> >We need to back this out from Aurora if the rest of the pieces don't land by the >next Aurora migration (which is pretty unlikely I think!)
> 
> I'd prefer if you backed it out of mozilla-central. This divergence between
> what we're testing on mozilla-central and shipping in 5,6,7 is increasingly
> problematic. So is not being able to fuzz properly (see bug 656130).

OK, fair enough.  Backed out the patch from mozilla-central: <http://hg.mozilla.org/mozilla-central/rev/a10dd1a539db>

> Did this ever get backed out from Aurora-6? The bug is marked as
> "status-firefox6:fixed" :(

It has been backed out (comment 107), which is why I set the status to fixed.  Is that the wrong value?
Probably more appropriate as unaffected, as the backout made Firefox 6 unaffected by this problem.
This part is rewritten on top of the new frame childlist APIs.
Attachment #530450 - Attachment is obsolete: true
Attachment #561910 - Flags: review?(roc)
Comment on attachment 561910 [details] [diff] [review]
Part 2: Implement the absolute positioning support for all frames

Review of attachment 561910 [details] [diff] [review]:
-----------------------------------------------------------------

::: layout/tables/nsTableOuterFrame.cpp
@@ +220,5 @@
>        return mFrames;
>      case kCaptionList:
>        return mCaptionFrames;
> +    case kAbsoluteList:
> +      return nsHTMLContainerFrame::GetChildList(aListID);

Why don't we just make this the default case?

In fact, can't nsTableOuterFrame::GetChildList() just check for kCaptionList and pass all other cases to nsHTMLContainerFrame::GetChildList?
Attachment #561910 - Flags: review?(roc) → review+
(In reply to Robert O'Callahan (:roc) (Mozilla Corporation) from comment #123)
> Comment on attachment 561910 [details] [diff] [review]
> Part 2: Implement the absolute positioning support for all frames
> 
> Review of attachment 561910 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> ::: layout/tables/nsTableOuterFrame.cpp
> @@ +220,5 @@
> >        return mFrames;
> >      case kCaptionList:
> >        return mCaptionFrames;
> > +    case kAbsoluteList:
> > +      return nsHTMLContainerFrame::GetChildList(aListID);
> 
> Why don't we just make this the default case?

The default case is returning an empty list right now.  I assumed that's for a good reason?

> In fact, can't nsTableOuterFrame::GetChildList() just check for kCaptionList
> and pass all other cases to nsHTMLContainerFrame::GetChildList?

If that modification to the default case is acceptable, then we can make this change.
(In reply to Ehsan Akhgari [:ehsan] from comment #124)
> The default case is returning an empty list right now.  I assumed that's for
> a good reason?

I can't think what that reason might be.
Addressed the review comment.
Attachment #561910 - Attachment is obsolete: true
These two additional assertions happening here are the same as bug 548836, only now they happen twice for the placeholder frames of the abs-pos frame.
Attachment #562556 - Flags: review?(roc)
Attachment #562556 - Attachment description: Part 2.1: → Part 2.1: Adjust the assertion count on the test case for bug 348729
Try run for 3d1d60d61b12 is complete.
Detailed breakdown of the results available here:
    https://tbpl.mozilla.org/?tree=Try&rev=3d1d60d61b12
Results (out of 3 total builds):
    success: 2
    warnings: 1
Builds available at http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/eakhgari@mozilla.com-3d1d60d61b12
Try run for 5de952be2d5a is complete.
Detailed breakdown of the results available here:
    https://tbpl.mozilla.org/?tree=Try&rev=5de952be2d5a
Results (out of 3 total builds):
    success: 2
    warnings: 1
Builds available at http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/eakhgari@mozilla.com-5de952be2d5a
Try run for a0d0a3161bcf is complete.
Detailed breakdown of the results available here:
    https://tbpl.mozilla.org/?tree=Try&rev=a0d0a3161bcf
Results (out of 3 total builds):
    success: 1
    warnings: 2
Builds available at http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/eakhgari@mozilla.com-a0d0a3161bcf
Try run for ccf050fdea2f is complete.
Detailed breakdown of the results available here:
    https://tbpl.mozilla.org/?tree=Try&rev=ccf050fdea2f
Results (out of 3 total builds):
    success: 1
    warnings: 2
Builds available at http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/eakhgari@mozilla.com-ccf050fdea2f
Try run for 0266bcb23550 is complete.
Detailed breakdown of the results available here:
    https://tbpl.mozilla.org/?tree=Try&rev=0266bcb23550
Results (out of 3 total builds):
    success: 1
    warnings: 2
Builds available at http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/eakhgari@mozilla.com-0266bcb23550
Try run for 682e26fccd71 is complete.
Detailed breakdown of the results available here:
    https://tbpl.mozilla.org/?tree=Try&rev=682e26fccd71
Results (out of 3 total builds):
    success: 1
    warnings: 2
Builds available at http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/eakhgari@mozilla.com-682e26fccd71
Try run for 9abe603e8e15 is complete.
Detailed breakdown of the results available here:
    https://tbpl.mozilla.org/?tree=Try&rev=9abe603e8e15
Results (out of 3 total builds):
    success: 1
    warnings: 2
Builds available at http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/eakhgari@mozilla.com-9abe603e8e15
Try run for 36ab7e228dd2 is complete.
Detailed breakdown of the results available here:
    https://tbpl.mozilla.org/?tree=Try&rev=36ab7e228dd2
Results (out of 3 total builds):
    success: 2
    warnings: 1
Builds available at http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/eakhgari@mozilla.com-36ab7e228dd2
Try run for 8533d2324679 is complete.
Detailed breakdown of the results available here:
    https://tbpl.mozilla.org/?tree=Try&rev=8533d2324679
Results (out of 3 total builds):
    success: 2
    warnings: 1
Builds available at http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/eakhgari@mozilla.com-8533d2324679
So, I'm facing one last reftest failure that I'm not sure how to debug.  This is the failure: https://tbpl.mozilla.org/?tree=Try&rev=8533d2324679.  The failure only happens on Windows 7 (and not Windows XP).  The failure is basically the #d1 div appearing below the #d2 div as a horizontal 1-pixel tall line at the bottom of #d2.

It happens with the first two parts of the patch applied.  The reftest in question <http://mxr.mozilla.org/mozilla-central/source/layout/reftests/bugs/637597-1.html?force=1> is using transforms, but I'm not sure how they're implemented, and how they would interact with absolute positioning.

I tried reproducing the reftest locally.  When I run the entire reftest suite, I can reproduce it.  When I run part of it (such as all of the tests in layout/reftests/bugs), I can't reproduce.  I tried adding some debug code which would print the frame tree before and after the MozReftestInvalidate event, but with that code in place, the failure doesn't happen any more (and the frame trees look identical).

I'm not sure how to proceed with debugging this.  Do you guys have any ideas?
You mean #b1 and #b2 right?

I think you're misinterpreting the results. The difference is entirely in the rendering of the #b1 div, the #b2 div is white and nowhere near the #b1 div.

That is a little weird since clearly parts 1 and 2 should not affect behavior of this testcase.

Dumping the layer trees and comparing them might be helpful.
Roc was right about me misinterpreting the results.  I'm too confused!

Timothy suggested that I should increase the timer in <http://mxr.mozilla.org/mozilla-central/source/layout/generic/nsFrame.cpp#4153>.  I increased it to 500ms, and then I reran the reftest suite with MOZ_DUMP_PAINT_LIST, but now the failure doesn't happen any more.  Any other ideas?
Getting a layer tree seems like a good idea.

One question is whether you're actually regressing bug 637597. You might want to run the testcase(s) in that bug.

Breaking up patch 2 into as many independent patches as possible might help narrow down the problem.
(In reply to Robert O'Callahan (:roc) (Mozilla Corporation) from comment #140)
> One question is whether you're actually regressing bug 637597. You might
> want to run the testcase(s) in that bug.

Interesting!  Seems like that bug was never quite fixed!  I loaded https://bug637597.bugzilla.mozilla.org/attachment.cgi?id=516245 on Mac.  On a trunk build, when the green box moves, its width is increased by 1 pixel on the right side.  With a patched build with my patches, the green box's width is increased in the same way, but its height shrinks up by 1 pixel on the down side too.

On Windows, I don't see this on either a patched or non-patched build.

Is it possible that bug 637597 is not completely fixed, and my patch is triggering it on Windows 7 somehow?
Yes it is.

The thing is, I don't know why your patch would change anything here. It's supposed to be just a refactoring. If you can reproduce the different on Mac at will, maybe you can track down what's happening differently?
OK, I'll see if I can track this down on Mac.

Another interesting data point is that back when I was working on this bug in April and May, I used to push this to the try server all the time, and I never got that reftest failure even though the test was living in the tree back then.
This is the log representing when the test case is loaded on Mac with a patched build, until the move happens and the bug shows up.
This is the same log, only the layer tree parts, for easier viewing.
I noticed another weird thing.  How many pixels the green box changes in dimensions seems to depend on the Window size.  I was testing the trunk build in a maximized window and my patched build in a non-maximized window.  When I built a non-patched version and tried this, it shows the same behavior as trunk in a maximized window, and it also shows the same behavior as my patched build in the non-maximized window.

I'm not sure how to make sense of this log, or if the current direction in which I'm headed is going to help at all.
(In reply to Ehsan Akhgari [:ehsan] from comment #143)
> Another interesting data point is that back when I was working on this bug
> in April and May, I used to push this to the try server all the time, and I
> never got that reftest failure even though the test was living in the tree
> back then.

Hmm, I was actually wrong here.  Bug 637597 was marked as fixed by bug 637852, which was landed on 2011-06-22.  So this test probably did not exist in the tree back then.
(In reply to Ehsan Akhgari [:ehsan] from comment #147)
> (In reply to Ehsan Akhgari [:ehsan] from comment #143)
> > Another interesting data point is that back when I was working on this bug
> > in April and May, I used to push this to the try server all the time, and I
> > never got that reftest failure even though the test was living in the tree
> > back then.
> 
> Hmm, I was actually wrong here.  Bug 637597 was marked as fixed by bug
> 637852, which was landed on 2011-06-22.  So this test probably did not exist
> in the tree back then.

I just downloaded the nightly from 2011-06-23, and I can confirm that the same bug exists in that nightly too!
Try run for af10abe5e706 is complete.
Detailed breakdown of the results available here:
    https://tbpl.mozilla.org/?tree=Try&rev=af10abe5e706
Results (out of 3 total builds):
    success: 1
    warnings: 2
Builds available at http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/eakhgari@mozilla.com-af10abe5e706
Try run for 3df652952e6c is complete.
Detailed breakdown of the results available here:
    https://tbpl.mozilla.org/?tree=Try&rev=3df652952e6c
Results (out of 3 total builds):
    success: 1
    warnings: 2
Builds available at http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/eakhgari@mozilla.com-3df652952e6c
It's pretty easy for me to reproduce the problem in bug 637597 on a plain nightly on Linux (with basic layers). Seems like Ehsan is just getting unlucky here.
I think the best option for us at this point is to mark that reftest as random (I've already reopened that bug), and add it back to our reftest arsenal when we actually fix that bug.
Attachment #563455 - Flags: review?(roc)
Flags: in-testsuite+
Target Milestone: Future → mozilla10
Try run for f17b29c28af5 is complete.
Detailed breakdown of the results available here:
    https://tbpl.mozilla.org/?tree=Try&rev=f17b29c28af5
Results (out of 230 total builds):
    exception: 1
    success: 216
    warnings: 12
    failure: 1
Builds available at http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/eakhgari@mozilla.com-f17b29c28af5
Depends on: 691087
Depends on: 691166
Keywords: dev-doc-needed
Blocks: 437722
Depends on: 691628
Depends on: 691591
Depends on: 691824
Depends on: 691118
Depends on: 691210
Depends on: 693107
Depends on: 693262
Depends on: 696739
Change documented:

https://developer.mozilla.org/en/CSS/position#Gecko_notes

And linked to from Firefox 10 for developers.
The positioning issue seems to be fixed, but offsetParent on the element still returns the outer block element, rather than the table element. Isn't this wrong behavior?
Yep.  Please file a separate bug?  The behavior of offsetParent is actually completely independent of positioning behavior, and it has a special-case for tables that might need removing now...
Added under bug 717819.
Depends on: 729143
Depends on: 729114
Depends on: 729337
Depends on: 735579
Depends on: 729597
Depends on: 780510
Depends on: 708206
You need to log in before you can comment on or make changes to this bug.