Closed
Bug 138057
Opened 22 years ago
Closed 22 years ago
nsBlockFrame::RememberFloaterDamage doesn't note changes to a float's width
Categories
(Core :: Layout, defect, P2)
Core
Layout
Tracking
()
RESOLVED
FIXED
mozilla1.0.1
People
(Reporter: waterson, Assigned: waterson)
References
Details
Attachments
(3 files, 3 obsolete files)
1.56 KB,
text/html
|
Details | |
5.26 KB,
patch
|
dbaron
:
review+
|
Details | Diff | Splinter Review |
10.67 KB,
patch
|
dbaron
:
review+
attinasi
:
superreview+
|
Details | Diff | Splinter Review |
nsBlockFrame::RememberFloaterDamage doesn't note damage in the space manager when a floater's width changes in such a way as to not affect the line's combined area.
Assignee | ||
Updated•22 years ago
|
Blocks: 129115
Status: NEW → ASSIGNED
OS: Linux → All
Priority: -- → P2
Hardware: PC → All
Target Milestone: --- → mozilla1.0.1
http://bugzilla.mozilla.org/showattachment.cgi?attach_id=79566 is a testcase, once bug 129115 lands.
Also note that this should allow us to remove the PrepareResizeReflow hack when we get an incremental reflow through a floater.
Assignee | ||
Comment 3•22 years ago
|
||
This is a sketch of a patch; intended for expository purposes only! The basic idea here is to save the line's floater cache before reflowing the line, and then use this in RememberReflowDamage to determine if any of the floaters have changed horizontally. This fixes the problem that dbaron points out in bug 129115 comment 116: it allows us to notice that the floater has changed horizontally (even though the line's combined area hasn't changed), and so we add the floater's area to the space manager. So here's what needs more thought: - This is obviously inefficient, so we need to either prove that `n' will stay small, or we need to come up with a better way to compare the old and new floater caches; e.g., - Perhaps the two floater caches will stay in sync -- or at least in sync enough that we can walk both lists in parallel. For example, is it possible for a floater to be added to or removed from the new floater cache? (Almost certainly, content could be added or removed, or the line may wrap differently.) Is it possible for the new floater cache to be re-arranged? (Probably not, as this would entail content being shuffled; but in that case, the frames would be re-created.) - I used the floater cache's combined area. Is this right? (It looks like `mRegion' might be more appropriate.) Anyway, food for discussion.
Assignee | ||
Updated•22 years ago
|
Attachment #79767 -
Flags: needs-work+
Assignee | ||
Comment 4•22 years ago
|
||
Also, the above patch (once it's done right) will probably allow us to remove the call to nsBlockFrame::PrepareResizeReflow from PrepareChildIncrementalReflow.
I *think* the approach here is insufficient because it will only propagate damage that is within the block closest to the floater. In other words, I think it would handle a horizontal expansion of: <div> <tall-float /> text that must wrap </div> but it wouldn't work for: <div> <div> <tall-float /> </div> text that must wrap </div> since the only floater cache that points to "tall-float" (if my understanding is correct) is the one associated with the line of the inner div.
Assignee | ||
Comment 7•22 years ago
|
||
Here's a more radical solution, which eliminates RememberFloatDamage altogether. Specifically, floater damage is recorded in the space manager during nsBlockReflowContext::FlowAndPlaceFloater.
Assignee | ||
Comment 8•22 years ago
|
||
Well, the above patch makes it through the regression tests as well as a couple simple DHTML float resizing animation tests that I've concocted (including the one dbaron cites in comment #6).
Assignee | ||
Updated•22 years ago
|
Attachment #79767 -
Attachment is obsolete: true
Assignee | ||
Comment 9•22 years ago
|
||
So, what's going on here? When an incremental reflow occurs, a floater's size may change. If a floater's size changes, we need to note (in the space manager) the veritical `band' that has been affected by the change. ReflowDirtyLines uses this information to know when a line that is otherwise `clean' (i.e., not directly impacted by the reflow) must be reflowed. dbaron's first implementation of this used a routine called RememberFloaterDamage that would note that the vertical size of a linebox had changed. The delta was recorded in the space manager. The drawback of this approach is that horizontal changes to the floater (e.g., the float gains or loses width, but not height) go unnoticed. The fix is to eliminate RememberFloaterDamage altogether, and `directly' note that a floater's size has changed during FlowAndPlaceFloater. Specifically, we snapshot the floater's size before reflowing the float, and then compare this size with the float's size after reflow. If the floater's dimensions change, we record the information in the space manager. This additionally allows us to remove the hack in PrepareChildIncrementalReflow that `punts' any incremental reflow targeted at line's float to a full resize reflow. (This was done because the PrepareResizeReflow code marks each line that is impacted by a floater as requiring reflow: extremely conservative.)
Comment on attachment 80450 [details] [diff] [review] more radical approach >Index: nsBlockReflowState.cpp >+ // XXXwaterson cast! you know it's a block frame, don't you? It could be an outer table frame, right? >+ // XXXwaterson don't you only need to recur if NS_BLOCK_SPACEMGR is set? No, since the floater cache is associated only with the line immediately containing the placeholder. (I think this is the case because only lines that are |IsInline| can have a floater cache. I didn't trace through all the code.) >@@ -846,6 +848,12 @@ > // for it to live. > nsRect region; > >+ // The floater's old region, so we can propagate damage. >+ nsRect oldRegion; >+ floater->GetRect(region); >+ region.width += aFloaterCache->mMargins.left + aFloaterCache->mMargins.right; >+ region.height += aFloaterCache->mMargins.top + aFloaterCache->mMargins.bottom; Don't you want to be filling in |oldRegion| here rather than |region|? >+ > // Advance mY to mLastFloaterY (if it's not past it already) to > // enforce 9.5.1 rule [2]; i.e., make sure that a float isn't > // ``above'' another float that preceded it in the flow. >@@ -1001,6 +1009,17 @@ > #endif > mSpaceManager->AddRectRegion(floater, region); > NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "bad floater placement"); >+ } >+ >+ // If the floater's dimensions have changed, note the damage in the >+ // space manager. >+ // XXXwaterson conservative: we could probably get away with noting >+ // less damage; e.g., if only height has changed, then only note the >+ // area into which the float has grown or from which the float has >+ // shrunk. >+ if (oldRegion.height != region.height || oldRegion.width != region.width) { >+ nscoord height = NS_MAX(oldRegion.height, region.height); >+ mSpaceManager->IncludeInDamage(region.y, region.y + height); > } > What happens if the horizontal position of the floater changes, for example, because its margin changes? It seems to me that you want to track x and y as well, so that the first chunk of code would also have: oldRegion.x -= aFloaterCache->mMargins.left; oldRegion.y -= aFloaterCache->mMargins.top; Then the second part could be a little less conservative and say: if (oldRegion.width != newRegion.width) { nscoord top = NS_MIN(oldRegion.y, region.y); nscoord bottom = NS_MAX(oldRegion.YMost(), region.YMost()); mSpaceManager->IncludeInDamage(top, bottom); } else { if (oldRegion.y != newRegion.y) { /* maybe refactor into |inline void * SortPair(nscoord a, nscoord b, nscoord &smaller, nscoord &larger)| */ nscoord top, bottom; if (oldRegion.y > newRegion.y) { top = newRegion.y; bottom = oldRegion.y; } else { top = oldRegion.y; bottom = newRegion.y; } mSpaceManager->IncludeInDamage(top, bottom); } /* ... and the same for YMost() as for y ... */ } I like the patch in general, though.
Attachment #80450 -
Flags: needs-work+
Comment on attachment 80450 [details] [diff] [review] more radical approach >+ // The floater's old region, so we can propagate damage. >+ nsRect oldRegion; >+ floater->GetRect(region); >+ region.width += aFloaterCache->mMargins.left + aFloaterCache->mMargins.right; >+ region.height += aFloaterCache->mMargins.top + aFloaterCache->mMargins.bottom; Or better yet: nsRect oldRegion; floater->GetRect(oldRegion); oldRegion.Inflate(aFloaterCache->mMargins);
> It could be an outer table frame, right?
Or an image (or object) frame with 'display: block', I would think. And
probably a few other things as well. (Maybe nsBoxFrame?)
Assignee | ||
Comment 13•22 years ago
|
||
Whew! I'm glad to report that the patch still seems to work when we properly
initialize `oldRegion'. I've updated the patch to fix that, as well as use the
tidier nsRect::Inflate method. I've removed my `XXX' confusion: that is
unrelated to this bug.
dbaron wrote:
> What happens if the horizontal position of the floater changes, for example,
> because its margin changes? It seems to me that you want to track x and y
> as well...
It seems to me that we only care if there is a change to the size of the region
which the float occupies. If someone twiddles the margins, either they'll do so
in a way that alters the region that the float occupies (e.g., changing only
margin-left), or they won't (e.g., adding some amount margin-left and removing
the same amount from margin-right). In the former case, we'll note a change in
the region, and dirty the region in the space manager. In the latter case, the
region won't have changed, so there is no need to dirty the lines that flow
around the float. Right?
Attachment #80450 -
Attachment is obsolete: true
Assignee | ||
Comment 14•22 years ago
|
||
(IOW, I can't think of a way that region.x or region.y would change without the width or height changing, too...)
Assignee | ||
Comment 15•22 years ago
|
||
Here's a silly test case: <http://www.maubi.net/~waterson/mozilla/bugs/float-animated-margin.html> With paint-flashing on (and this patch, along with the patch from 129115) you'll see we only invalidate the float's area, (Ironically, we're off by a fair bit -- the invalidation rect is too high. I ought to track that down at some point).
I guess comment 13 makes sense, although you're still damaging too much for a height change (and for that you'll want the old x).
Comment 17•22 years ago
|
||
Comment on attachment 80871 [details] [diff] [review] clean up in response to dbaron's comments Looks OK to me - sr=attinasi
Attachment #80871 -
Flags: superreview+
This testcase shows why you need to check if the position of the float changed in addition to whether the size changed. The float can change position due to a change in size of another float. See comment 10 / comment 11.
Attachment #80871 -
Flags: needs-work+
(And, FWIW, in a current build (without the patch on this bug, when the damage is propagated correctly), we have some repainting issues on that testcase.)
Attachment #81061 -
Attachment description: testcase showing why you need to check position in addition to size → testcase showing why you need to check origin in addition to size
Assignee | ||
Comment 20•22 years ago
|
||
dbaron: good, good. :-)
Assignee | ||
Comment 21•22 years ago
|
||
kin also mentioned a (currently broken) case in editor where floats are dyanmically changed from `float: left' to `float: right'. Maybe this will help there, too.
Assignee | ||
Updated•22 years ago
|
Attachment #80871 -
Attachment is obsolete: true
Assignee | ||
Comment 22•22 years ago
|
||
I should note that dbaron's test case (attachment 81061 [details]) illustrates some
invalidation problems: if you set the window's width to ~725px, and then run the
test, you'll see some paint artifacts when the second <div> resizes. Forcing an
expose clears up the problem, so somewhere we're bungling an invalidate. (N.B.
that this happens with or without the patch: I'll file a bug if there isn't one
on file already...)
Comment on attachment 81079 [details] [diff] [review] address dbaron's test case r=dbaron
Attachment #81079 -
Flags: review+
Assignee | ||
Comment 24•22 years ago
|
||
This patch goes even more nuts: - There's no reason to dirty _all_ of the lines in the block if the child specified to ReflowDirtyChild is a float. This removes that logic. - Once that's been simplified, it's possible to pare down FindLineFor to simply return a line_iterator. We have to publicly expose nsBlockFrame::end_lines because FindLineFor is used from nsHTMLReflowState. I've placed a couple of test cases that explicitly test _dirty_ reflows (as opposed to the style-changed reflows we've been testing to date) at <http://www.maubi.net/mozilla/bugs/index.html#floater-incremental-reflow> and they seem to work. dbaron, it's getting hot in here. Are you still with me? :-)
Comment on attachment 81096 [details] [diff] [review] let's go crazy with the cheese whiz. r=dbaron I suspect some (or all?) of the callers of nsBlockFrame::FindLineFor really only need nsLineBox::FindLineContaining, but it may not be easy to tell.
Attachment #81096 -
Flags: review+
Comment 26•22 years ago
|
||
Comment on attachment 81096 [details] [diff] [review] let's go crazy with the cheese whiz. sr=attinasi
Attachment #81096 -
Flags: superreview+
Assignee | ||
Comment 27•22 years ago
|
||
Changes checked in, thanks.
Status: ASSIGNED → RESOLVED
Closed: 22 years ago
Resolution: --- → FIXED
Assignee | ||
Comment 28•22 years ago
|
||
Filed bug 142029 on the invalidation issue in comment 22.
You need to log in
before you can comment on or make changes to this bug.
Description
•