Skip to content

Instantly share code, notes, and snippets.

@MatsPalmgren
Last active July 26, 2020 04:23
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MatsPalmgren/8eb65464f08850597877ab155e856a0c to your computer and use it in GitHub Desktop.
Save MatsPalmgren/8eb65464f08850597877ab155e856a0c to your computer and use it in GitHub Desktop.
General fallback mechanism for fragmentation bugs

Consider the following testcase:

<!DOCTYPE HTML>
<style>
body { font-size: 32pt; margin:0; }
</style>

<div style="color:blue">
<div style="position:absolute; top:calc(100% - 20px); border:1px solid">Hello</div>
  <div style="break-after:always">1</div>
  <div style="">2</div>
</div>

In Print Preview you would get something like:

image

because of bug 267029.

I have an idea of how to fix bugs like this where we don't fragment a frame for some reason and it instead overflows the page content area and we clip it with "dataloss". This can be fixed in a general way by building a DisplayList for the frames on the first page with a clip area that corresponds to the second page (i.e. that starts exactly at the end edge of the first page and has the size of the second page) and include that in the DisplayList for the second page. Then you get the desired result:

image

I hacked up a proof-of-concept for the above using the same technique as we use here: https://searchfox.org/mozilla-central/rev/446160560bf32ebf4cb7c4e25d7386ee22667255/layout/generic/nsPageFrame.cpp#515-543

My DisplayList-fu is weak though, so I'm hoping that someone on the Web Painting team can investigate this idea further...

@MatsPalmgren
Copy link
Author

MatsPalmgren commented May 11, 2020

This would "fix" most of the high priority bugs we have on our list of bugs here. We still want to fix some of those properly of course, by improving the fragmentation, but the fallback mechanism described above would work as a pretty good interim solution (in most cases on par with other UAs). Also, it's pretty much exactly what the spec calls for in the remaining cases where we can't find a break opportunity for some reason. It says:

Finally, if there are no possible break points below the top of the fragmentainer, and not all the content fits, the UA may break anywhere in order to avoid losing content off the edge of the fragmentainer. In such cases, the UA may also fragment the contents of monolithic elements by slicing the element’s graphical representation.

(my emphasis)

@MatsPalmgren
Copy link
Author

MatsPalmgren commented May 12, 2020

BTW, this would also allow us to render scrollbars in print mode, compare for example:

<div style="height:70cm; overflow:scroll"></div>

when printed in Firefox and Chrome. We currently render such boxes as special non-scrollable blocks so that we can fragment them and avoid dataloss of their contents. (See NS_BLOCK_CLIP_PAGINATED_OVERFLOW and its uses.) But that also means we don't render scrollbars for most elements in print mode.

@MatsPalmgren
Copy link
Author

MatsPalmgren commented Jul 24, 2020

A variation on the Layout side would be to create continuations for all frames, even for "atomic" ones, if they overflow. The continuation would never have any children... instead we modify BuildDisplayList* so that they generate a clipped DL from its first-in-flow (or at least its first-in-flow's children) in a similar way as I suggested for the PageContentFrame above. This is a bit trickier and more work, but it has the advantage that we can fix the "reserve space" problem easily. This variation would still require support for multiple DL for the same frame on the Paint side though.

For the record, the "reserve space" problem is that the "overflow DL" of page 1 might paint on top of content on page 2, e.g. if we clip a line box then the line after it (in the next-in-flow) will start at the top of the next page - we should reserve some space there for the overflowed part of the clipped line.

(We could do this variation as a version 2 solution though, and do the whole page as suggested above as version 1 since that's faster to implement.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment