Skip to content

Workweek tables

Lars Bergstrom edited this page Feb 7, 2014 · 2 revisions

Background on layout's three passes

  • Bottom-up bubble widths computes minimum intrinsic widthes and preferred widths

In gecko, getMinAndPrefWidth

  • Top-down "assign widths"

In gecko: reflow

  • Bottom-up "assign heights" assigns heights and stores overflow

In gecko: FinishAndStoreOverflow

Does text layout and line breaking. Once you do that, you know how tall you are.

  • jack: what is store overflow?
  • pcwalton: once you assign height you know your dimensions. Assign heights also needs to compute how much of you "stuck out" of your box. Important because we need to know if the node is in the visible region or not. Roc told us that we need to be able to store our overflow in order to support enormous pages. Store overflow is done by union'ing all of your children and has been optimized.

Floats

  • pcwalton: Each node has floats_in and floats_out. You have boxes and they have floats next to them. Floats_in means the floats that came into this box (vertically). Floats_out is the ones that stuck out.
  • simonsapin: You mean the ones that intersect?
  • jack: We know the height of the float. When a box intersects with the float, we pass it as floats_in. And we know that when we finish laying out a box the floats that stick out.
  • pcwalton: One thing this implies is that the floats_in of box n+1 is equal to the floats_out of box n. During bottom-up traversal, (assign_heights) if there are any floats in the subtree, we wait until we get to the parent that owns it, and then sequentially handle all of the children in order.

Tables (widths)

  • pcwalton: how do you compute minimum preferred widths for a table?
  • simonsapin: Based on weasyprint, horizontal is hard, but in dbaron/bz's conversations, vertial is hard. Mainly because it's actually more complicated than the spec says. dbaron's spect (http://dbaron.org/css/intrinsic/ ) defines how to perform the intrinsic widths for the table. So, bubble widths should already work for tables. There is also an intrinsic sizing spec that helps define this behavior more: http://dev.w3.org/csswg/css-sizing/
  • pcwalton: for bubble-widths to work, it needs to be based only on the values of the children
  • simonsapin: Intrinsic width literally means that it is defined in terms of your children.
  • jack: We may need to bubble up the column width and pass it up. When we get to the top of the column, we can then pass the value back down. This will require an extra traversal through the tree. Or we could store it off the table at the top.
  • pcwalton: Can we draw this out? It sounds like the colspan affects the intrinsic and preferred widths.
  • jack: basically, the min width of a column of span n is the greater of the minimum widths of the columns with colspans less than n ...
  • simonsapin: for each colspan, you have a different minimum and preferred width.
  • pcwalton: Does that apply to all cells with that colspan?
  • simonsapin: So, the width of a column with a colspan of 4 is affected by all other cells in that column with colspan 4, but also all cells in that column with a smaller colspan.
  • pcwalton: does the minimum intrinsic width of a cell with colspan of 2 depend on the minimum intrinsic with of other cells in that column with smaller spans?
  • simonsapin: No. Only the preferred width.
  • pcwalton: So, minimum intrinsic widths of cells are just like blocks - standard algorithm applies.
  • simonsapin: for preferred, you have 2n results (where n is the width of the table)
  • pcwalton: Can you compute them solely with the information you have so far? So when we get to the table element (or tbody) we could compute the columns' preferred widths then?
  • simonsapin: We can be parallel over columns, but sequential over the colspans for that column.
  • pcwalton: Are they reflected in the DOM tree?
  • simonsapin: There are col and col group elements.
  • pcwalton: What if you mix tr and col?
  • simonsapin: In markup, they don't interact. Only in parsing when you determine how things get matched up.
  • pcwalton: I didn't realize you could specify tables in columns as well as rows...
  • simonsapin: In markup, they're specified in terms of rows. The column elements are not, but you can specify widths, colors, etc. on those.
  • pcwalton: So the column elements affect presentation, but structure of the table, right? So I can't use a col element to make something appear?
  • simonsapin: No, you cannot. Column has no children.
  • jack: Just controls the visual presentation.
  • pcwalton: So, what should our flow tree look like? Maybe TableFlow with TableRowFlows that contain BlockFlows.
  • simonsapin: Rows are not block containers. They contain cells; that is it. If you try to avoid that, you will get an anonymous cell that wraps it. The spec requires those be generated.
  • jack: Are those generated content like before and after?
  • pcwalton: More like {ib}-splits. They do not show up in the DOM, but appear. Webkit gets this wrong during incremental layout. Gecko does better because of the dirty bits, and we should be fine, too, since we use that approach. If you change display to table-cell, we will probably wipe the containing block and re-create it.
  • simonsapin: ideally, the whole tableflow construction should be based on the display property.
  • pcwalton: Correct. We're talking about javascript that dynamically changes the display of something to table-cell.
  • simonsapin: even if you do not specify them, you will always have RowGroups, which contain multiple TRs (for presentation purposes).
  • pcwalton: Can the RowGroup be a number on the TRs?
  • simonsapin: Yes, there's usually just one. THEAD is not common. Most tables are just table, tr, and td.
  • jack: In assign widths, it's top-down, so we know how wide the table is and how wide its container is. So when we calculate the width of the table, we have to compute the width of each column. Then we have to distribute the extra width to each column...
  • simonsapin: not exactly. intrinsic width is independent. Then, the used width has to be done separately and pushed to the appropriate columns
  • pcwalton: Does assign_widths depend on the actual width of the kids?
  • simonsapin: what do you mean?
  • pcwalton: given the minimum preferred widthds of the kids and the columns and the actual mount of available width, can I assign the widths of all the columns?
  • simonsapin: yes.
  • pcwalton: great, so that's parallel safe. Could we decide the minimum & preferred widths of the children, can we decide them once we get up to the table element? (TableFlow)
  • jack: but we might have several, since we have to do widths for each colspan.
  • pcwalton: maybe we could do it sequentially at first, but then use work-stealing to enque jobs for each of the columns...
  • simonsapin: We also need to figure out which column each cell belongs to.
  • pcwalton: I think we can do that at flow construction...
  • jack: don't know if there will be an anonymous cell before you. When you get to a row, you could have a
    ...
  • simonsapin: It depends on how many previous sibling cells you had and their colspan.
  • pcwalton: Aha, you do need a pass to determine which. In parallel?
  • simonsapin: Parallel by row, but not column.
  • pcwalton: Maybe we can do it during bubble_widths?
  • jack: Could just store a vector of the colspans.
  • pcwalton: Then we can compute the columns for the cells when we hit the TableRowFlow in bubble_widths.
  • simonsapin: in bubble_widths, can we have it sometimes be parallel and sometimes sequential? Because for table rows, we need to be sequential
  • pcwalton: Yes, you just no-op until you hit the node you want to do the sequential work and then do the work sequentially on your kids. There is freedom in that.
  • simonsapin: it could do the intrinsic widths in parallel and then do a sequential pass for the colspans.
  • pcwalton: That's what I was thinking, too.
  • pcwalton: so assign_widths is top-down and you know how much your parent has, so you can just figure it out for each of the columns and pass it down.
  • simonsapin: Yes, that is pretty straightforward. So you pass down a list of widths for different colspans.
  • pcwalton: Could also let the children look up (readonly) during this pass at their parents for information. They can just get them from their TableRowFlow.
  • simonsapin: how to assign widths with colspans is the tricky bit. There are multiple possible ways, according to dbaron.
  • pcwalton: does not affect parallelism, right?
  • simonsapin: yes, but there are multiple ways to do it. dbaron's only gives one. when we do it, we should ping him to see if he wants us to do what is in his document or something else.
  • Tables (heights)

    • simonsapin: my understanding is normal layout for every cell which gives you a content-height. The height of the row is the maximum of that.
    • pcwalton: That's great...
    • simonsapin: But then you apply vertial-align to each cell to align the baselines, in which case each of them have to be aligned together.
    • pcwalton: Does that change the height of the cell?
    • simonsapin: No, just a translate of the cell.
    • pcwalton: Doesn't affect the height of the cell, just the row?
    • simonsapin: Spec is unclear. If you paint a background, it's always as high as the cell. The intermediate height is the height of the content. vertical align is based on that height. But, because we paint backgrounds and borders in a special pass anyway...
    • pcwalton: how often is vertical-align set?
    • jack: quite often. center or top, often.
    • simonsapin: I think baseline is the default, so for all tables the align is set
    • pcwalton: may patch it for all the kids when you get to the table row
    • simonsapin: my understanding after that is that the height of the table is just the height of your rows. That's what we do in weasyprint. If you have a percentage height, we ignore it. dbaron said, that's what makes this difficult.
    • pcwalton: is that where webkit differs from IE?
    • larsberg: was this the percentage is based on the size of the table?
    • simonsapin: yes....
    • pcwalton: How does that even work? It's recursive...
    • simonsapin: yes. it only works if the containing block has a definite height.
    • pcwalton: maybe we could do that in cascade and have a "-servo-height" computed that is not exposed to script. it's evil to have these hidden properties, but it makes parallelism easier.
    • simonsapin: I don't know how that is supposed to work for real.

    dbaron enters

    • simonsapin: is the spec wrong for table-height?
    • dbaron: it's incomplete - it only says how vertical-align works
    • simonsapin: we've based our widths on your document...
    • dbaron: it's incomplete, so looking at gecko is probably safer. it's different where distribution of spanning cell width to the spanned cells. The end result is that we shared the code for width distribution within the table for final width as for spanning cell widths to the column widths for intrinsic column computation. There are a few small differences.
    • simonsapin: We discussed multiple algorithms previous - is there one that's best?
    • dbaron: for column-width in spanning cells? Yes, there's only one you need for the web.
    • pcwalton: (describes Servo's layout).
    • dbaron: assign_heights is where the recursive information goes in and out?
    • pcwalton: yes
    • dbaron: You probably need two nested things for table cells. Once you start doing vertical alignment, because you have a thing that you have to lay out and vertical align them relative to each other (and their baselines), you need to at least have figured out their top line before you know the height of the row.
    • dbaron: cells that have the same align (baseline is the default, but UA stylesheet makes them vertical-align:top instead for TD and TR). but if you put display:table-cell, then you get vertical-align:default. You should not copy from gecko that the height on a table cell influences the block. If yo uhave two cells of height 200 pixels and one has 30px text and one has 10px, because you alighn their baselines, they are at offsets relative to each other. In gecko, we make the row taller than 200px. The spec says the height of the cell only contributes to the height of the row, not the cell itself. Having the nested boxes seems like it would help for dealing with those.
    • simonsapin: should we use the UA stylesheet from gecko or take it from the spec?
    • dbaron: are they different? if they are, you should figure out what to do... there should be some of the attribute-mapping stuff different because it's in C++ in gecko. table cells default to an alignment, but the table cells have a different one, which is difficult to express in CSS
    • dbaron: in gecko, we have two frames for every cell. We have a CellFrame with a BlockFrame inside of it. Maybe you don't need that... whenever I worry about the size of the FrameTree, it turns out to be 2% of the memory used, so don't worry about that too much.
    • pcwalton: So, possibly we need to add TCF under TRF before the BlockFlow
    • dbaron: And you need to handle row-groups
    • pcwalton: We were going to handle them as indices in the TRF
    • dbaron: in gecko's height algorithm code, row-groups are significant. Maybe they shouldn't be.
    • simonsapin: the height of the cells is the max of the heights and the widths is the max of the widths. and then the percentage heights is the hard part?
    • dbaron: Yes, that's the hard part. Gecko's code cares about percentage heights and row groups. The fun part of percentage heights is that tCSS says percentages as part of an auto layout item are a percentage of it. That's not always true for table cells and has to do with how table heights were implemented...
    • simonsapin: what is the containing block of the table cell? it doesn't say in the spec... literally from the cell, it would be the closest block container, which would be the parent and is probably not what we want
    • dbaron: at a minimum, it's the table and that's probably a spec bug. So, you know how the width distribution works?
    • pcwalton: Yes, you have the widths for the kids and know the colspans at the TableRowFlows...
    • dbaron: you need to know the row spans, though. We just build up a huge array with where the cell 7,3 is in the table.k It's called the CellMap and tracks how all the row spans and that falls out. If we want to do semantic table column selectors, we need know that information as well.
    • simonsapin: is that the double-pipe thing?
    • dbaron: maybe that's the syntax, but I'm thinking of td:nth-column(3n+4) and td:column(.date) to select on the column number or the column element so that you can style tables based on their columns
    • simonsapin: that requires a CellMap
    • pcwalton: Yes, maybe we can make it during bubble_widthds
    • simonsapin: They need to be sequential but can be parallel with the rest of the bubble_widths path
    • pcwalton: Yes, maybe we'll see if it's a bottleneck or not
    • jack: so if you get to the TF, you can spawn a task that creates a new CellMap, as you may not need it done until you get there for assign_widths
    • dbaron: you can start it arbitrarily early
    • pcwalton: e.g., during flow tree construction
    • dbaron: did you distinguish between non-specified, specified, and percentage widths? minimum width is just a single number. the preferred width is more complicated for the column. you might have four words is 200px and the min width is 50px (each of 4 words is 50px). if the cell has a width of 100 on it, that blows away the preferred width. if any cell in a column has a percentage width, then any non-percentage width and any pref width is irrelevant. Otherwise, if the column has a cell with a specified width (not a percentage) then any pref width is irrelevant. Only if no cell has a specified width then the pref width is relevant. This is only important for the combining algorith for column widths.
    • simonsapin: isn't it just two numbers that are relevant?
    • dbaron: yes, but one is a float and one is a coord. if floats and lengths had the same unit, we would be fine...
    • simonsapin: Rust has enums, so you could probably conflate those...
    • dbaron: You should double-check pref-percentage always being relevant. An explicit 0% width on a cell might have interesting semantics... I think a specified 0 in non-percentage might also be interesting. This hierarchy comes up over and over again (percentage over non-percentage specified over preferred).
    • simonsapin: at the end of bubble_width, we can determine the used widths of each column and determine the size of the widths of the table (maybe at the beginning of assign_widths instead?). So there, you determine the widths of each column and therefore the width of the table. then you use the CellMap to determine the width of each each cell.
    • pcwalton: so then we just write it in to each of the kids, since we have computed it top-down and have the CellMap available for the rest of that.
    • dbaron: You should look at the gecko code for the distribution algorithm, as I have changed it subtly since I wrote that document.
    • pcwalton: for assign_heights without percentages, it seems like you come up with candidate heights and then when you get to the table, you do vertical_align, and then write the new heights into each cell.
    • dbaron: that's the gecko way. IE6 did it similar to widths and did distributions. There are two effects percentages have on the final table. The effect of percentage columns on the final table width is interesting... you can follow up on it, but it's pretty understandable. There are two effects: one on the assumption the column is small and one that it's large. If the width is 2%, then the implication is that the pref_width is at least 50x the times of the width of the stuff in the column. If it's 80%, that implies the table is at least 5x the width of the stuff that's in all the other columns. Except that there's a better way of doing that... so you just max two different numbers into a number and it all works (not too complicated).
    • simonsapin: Is it worth spending time to update your document on widths if we spend time reading the gecko code?
    • dbaron: Yes, it's all in table layout strategy (unless you're using fixed)
    • pcwalton: Can those be consolidated in Servo?
    • dbaron: They need to be totally different strategies.
    • pcwalton: probably not different flow types. Just check the style or have a boolean. Might need one of those stupid flags to push down to your kids so they know... a cascaded fake CSS value. The cells have to know if they're in fixed layout or normal layout, right? Maybe not, because bubble_widths will be the same for either one.

    Percentage heights

    • dbaron: No browsers agree on percentage heights. Several issues are messy. One is whether a particular configuration of percentage heights make the cell act as if it had a specified width for the purposes of e.g.,

      will that be honored? spec says only if the parent has a specifeid width
    • dherman: So there's a wide variety of unspecified behavior. like all 100%?
    • dbaron: Each gets 25%. Or maybe we stop at 100%. So if you have two 99% columns, the first is 99% and the second is 1%. Because you ran out of percents. I wrote this 8 years ago, though...
    • dbaron: The fun case is whether or not it counts as a specified. The other is how you specify the final height you get.
    • pcwalton: It seems recursive because you don't know the percent until you have the height of the table.
    • dbaron: ie6 lays out without the percentages, and then will use the percentages if you have an explicit height on the table, it will assign extra space. I think all browsers will do this - I wrote tests for it in 2006, but I'm not sure right now. Once you have extra height to distribute, the question is where does it go? Or what happens when you have a specified height in one cell and a percentage as well? IE6 does lots of the same things as width distribution. If you have table height="1000" and you have a bunch of stuff and extra 4px, then IE6 distributes the height based on those percentages.
    • pcwalton: So for percentage heights, you're basically talking about re-layout for some rows.
    • dbaron: Yes, though gecko does really strange things to do it.
    • pcwalton: How can we kick off the task?
    • larsberg: you will only have percentages for heights to deal with if the table has a specified height, otherwise we ignore it
    • pcwalton: aha, so it's safe for parallelism because we'll have a specified height on the table so we can lay out the rest and then fire off a fix-up task to assign the extra height
    • dbaron: I'm not sure that's true (that you only do something with the percentage when the height is specified). May be some cases where you honor it by making that TR bigger and expand the table. IE6 would expand it by expanding the tables to the point where the TR is 25%. Gecko would just make it 25% of the size it used to be.
    • pcwalton: all of this involves laying stuff out without them and then doing something to lay them out again

    vertical resizes

    • dbaron: but vertical resizes should be cheap
    • pcwalton: because you don't need to lay stuff out again?
    • dbaron: we have separate relayout for horizontal or vertical resizes. for vertical, you can usually show that lots of stuff does not need to change. We set a bit in a frame if the height does not change based on vertical resizes to avoid reflowing the children. You need to optimize for vetical resizes for things like infobars. Some of that UI might now overlay. Findbar I'm not sure if it overlays or not. Sometimes they shrink the height of the page...
    • jack: why not just overlay?
    • dbaron: to see what's under them without scrolling
    • simonsapin: and we want overlay scrollbars because otherwise we have to reflow things multiple times

    Tests

    • dbaron: my tests from 2006 are the URL field of 359481 (http://dbaron.org/css/test/2006/percent-height-in-tables). They work differently at the point where it says 25% height TR.
    • jack: chrome is quite different on the 8 cells in a row one than gecko. The trick will be to implement tables and then look at this test to see where we fit on these edge cases
    • dbaron: this is not exhaustive. There are more edge cases.

    border collapse

    • dbaron: cells that are zero width behave differently, but borders can get them out of that state. a column with absolutely nothing in it will in most cases not expand but having a border in the non-collapsing model gives it a pixel of border, so now it has two pixels of width.
Clone this wiki locally