Gecko:Border collapse
This page aims at discussing the move of BC borders painting from table to cells. [ Warning: It is long known that I am just a simple guy trying in vain to mimic geniuses and help them, so feel free to correct any blunder here. ]
Contents
Rationale
The current way Gecko handles border-collapse: collapse tables is to let the table paint all borders. Thus, the table paints its background, then tells its cells to repaint their background and content, but not their borders, and finally draws borders on top of cells.
This approach has several drawbacks:
- The borders of the table always are on top (for an example of problem, a relatively positionned cell with appropriate z-index to put it on top shouldn't have the collapsed borders over it)
- The borders do not scroll with the cells (see https://bugzilla.mozilla.org/show_bug.cgi?id=135236)
- Border painting must rely on ad-hoc, complicated code to decide which borders must be redrawn in a paint event
- visibility: collapse does not reflect on borders (see https://bugzilla.mozilla.org/show_bug.cgi?id=242997)
- Because successive borders with same style are grouped together in the border chunks cache, a local change (e.g. border color of hovered cell) must trigger a recalculation of the borders chunks
- Again, complicated optimisations (damage areas) obfuscate the code
- There are lots of missing updates scattered accross the code, source of a lot of bugs (see https://bugzilla.mozilla.org/show_bug.cgi?id=258377, but there are lots of them)
Overall, the code is complicated; it is hard to fight one's way through BCMapIterator logic, for instance.
The Right Way (TM)
Cells will paint their borders, so the current calculation of border chunks will disappear, but Gecko will still be calculating wining borders and store them in the cellmap during reflow: this calculation is too slow to be afforded during paint.
Positionned cells
Discussion about the rendering of positionned cells is put here because possible implementations aren't equally adapted to each choice here.
The same example table will be used for each mockup below: every td has a 4px solid black border and a silver background, except B3, B4, C3 which have a 10px solid red border, D2 which has border: none, and C2 which has a yellow background, a 6px green top border, the usual 4px black as right border, a border: none for bottom, and an hidden left border. Last, but not least, the second column is given a 20px solid blue right border.
expected rendering when C2 is not positionned:
When a cell is positionned, it can
- Leave adjacent borders (and corners) where they are, and move without them
- Move with its inner half of adjacent borders (and corners), leave the other half in place
- Take the whole adjacent borders (and corners) with it (may be too strange and unexpected by webmasters)
- Leave borders/corners, but draw a copy (or the inner half of the copy) of them around itself
- Behave as if it were alone in another table, that is in conflict resolution for the rest of the table, (asked for) borders of positionned cell are ignored, and the cell draws its (or the inner half of its) borders around itself
- ... ?
People in the know (e.g. roc, bernd, etc) seem to agree on those:
- position: absolute and position: fixed cells are taken out of flow, and thus have no longer influence on border conflict resolution. This is (5) above.
- There shouldn't be any difference of rendering of the table (apart from what depends on the cell being a positionned ancestror) when the cell is in position: static and when it is in position: relative without any offset, even if borders are antialiased/transparent. That seems to rule out (4) and (5) for relative positionning.
- Since when does rel. positioning of cells work? Abs. and fixed cells will be wrapped by pseudos. (Bernd)
- The goal of this section is discussing of how we should render relatively positionned cells (the abs/fixed case seem already decided) (frnchfrgg)
Winning corners
In the following, the weight of a border will be a couple (s, t) where s is the size of the border and t an integer corresponding to the type (solid, inset, etc...) in a way consistent with the CSS2.1 ordering of border styles for conflict resolution. In particular, border: none computed style leads to (0, 0), and non-existing borders (due to border of the table, or row/col-span) will be considered as border: none.
Let us denote by t, r, b, l the respective weights of the top, right, bottom, and left borders as determined by the CSS conflict resolution, and by 1, 2, 3, 4 those weights sorted lexicographically from greatest to lowest. Sometimes a bold number/character will abusively refer to the corresponding border instead of only the weight, when there is no ambiguity.
First of all, the witdh of the corner is the maximum of the size of t and b; its height is the max of the size of l and r.
Proposition: Winning by weight
- If 2 > 3, then there are two "weighty" borders (possibly of same weight), and two "light" ones (idem). Two possibilities arise :
- The two weighty borders are one in front of the other (e.g. 1 = t and 2 = b)
- If 1 and 2 have the same weight, there is no mean to decide which one should win, so just share the corner between the two:
- If 1 and 2 do not have the same size, sharing the corner is difficult to achieve in a slick an beautiful way, so just let 1 win:
- It 1 and 2 have the same size but not the same style (thus not the same weight), both sharing or not sharing can be argued as correct. IMHO we shouldn't share here (see notes about sharing).
- If 1 and 2 have the same weight, there is no mean to decide which one should win, so just share the corner between the two:
- 1 and 2 aren't in front of each other
- If they have the same weight, there is no mean to decide wich one should win, so just share the corner between the two:
- If not, the rendering is arguable. I'd lean towards sharing if and only if they have the same style, to enable people having shared corners between borders of different size, but also enable them to prevent sharing between borders of same size (see notes about sharing) [Edit: I wrongly thought there was at least two border different border styles that were rendered as solid in the BC model, so in fact there is no way to defeat the sharing between solid borders. I thus now think we should never share between borders of different widths]. Possible renderings:
shared ----- or 1 winning
- If they have the same weight, there is no mean to decide wich one should win, so just share the corner between the two:
- The two weighty borders are one in front of the other (e.g. 1 = t and 2 = b)
- Else, 1 >= 2 = 3 >= 4.
- If 1 = 2 = 3 = 4, there is no way to decide which one should win, so just share:
- If 1 = 2 = 3 > 4, there are three "weighty" and one "light" border. There are several rendering possibilities:
- The perpendicular one (that is the one in front of 4) looses, and the two other (2 and 3) share the corner:
- The perpendicular one wins the whole corner:
- First way of sharing between the three big borders:
- Second way:
This last rendering has some advantages:- The rendering is more consistent with the four concurent borders case
- It doesn't "cut" the border formed by the two in front of each other (vertical in the mockup) - like the "perpendicular looses" solution
- It gives a fair portion of the border to the perpendicular border, in fact the portion earned by each border is close to equal (not the same, but sufficiently close considering the other constraints).
- Most importantly, it allows double borders to render really beautifully:
- The perpendicular one (that is the one in front of 4) looses, and the two other (2 and 3) share the corner:
- Else, 1 > 2 = 3 >= 4
The idea is that since 2 = 3, there are at least two candidates at being the border which would share the corner with 1. Thus just let 1 win the whole corner.
or
The only exception is when there are exaclty two such candidates, that is 3 > 4, and when they are in front of each other (i.e. 4 is in front of 1). Then sharing could be considered (see notes about sharing below):
or
The last one is also really good with double bordering.
- If 1 = 2 = 3 = 4, there is no way to decide which one should win, so just share:
Notes:
- Border sharing: Several times above, sharing between two or three borders with different widths was proposed. I think it would be an interesting feature (especially in the case of two borders), but that we should provide a way to defeat it. The way I see it is that we should share the corner between 1 and the other candidate(s) if – and only if – it(they) has(have) the same style as 1. Then the web designer could be able to easily obtain what he wants, in a supposedly logical and non-akward way.
Edit: I just reread the spec and noticed that only inset & outset behave as synonyms in border collapse model, so this discussion is somewhat moot. I don't know why I was imagining that there were several border styles that resolved to solid, but with different priorities. --FrnchFrgg - An interesting case: when 3 or 4 borders have the same width, there is a way to determine a winner if there is a majority of borders of the same color. Then only between them the sharing could be done. That would go a bit further towards the idea of non-compromise --FrnchFrgg
Proposition: Winning by majority
An other algorithm was proposed by Lucent (see discussion), here is my take on it, corrections/ideas are (as everywhere else in my text) welcome.
- If 1 >= 2 = 3 >= 4.
- If 1 = 2 = 3 = 4, then just share the corner between the four borders.
- If 1 = 2 = 3 > 4, then share the corner between the three weighty borders.
- If 1 > 2 = 3 = 4, then share the corner between the three small borders. Several renderings are arguable, but I think all must verify this property: outside the part in front of the perpendicular border, the corner should have the color of the nearest small, non-perpendicular border. That is the final rendering should look as if the corner was the square at the intersection of the three thin borders (one could argue that then we could change the definition of the size of the "corner", but I am not sure it would be efficient)
- If 1 = 2 = 3 = 4, if 2 and 3 are in front of each other, then share the corner between them. If not, I didn't find a rendering that did please me. 1 could take every part of the corner outside the intersection square of 2 and 3, that would be shared between them.
- Else, 1 >= 2 > 3 >= 4.
- If 1 = 2 > 3 >= 4, share the corner between the two weighty borders.
- If 1 > 2 > 3 > 4, just let 1 win.
- If 1 > 2 > 3 = 4:
- if they are in front of each other, share between 3 and 4
- else, paint 1 on the whole corner, then paint 3 and 4 with their shared intersection square on top of 1. Note that if their size is 0 the rendering will be the same as 1 winning.
Discussion: No compromise on corners
Currently, corners are divided diagonally or horizontally among competing edges in a sort of compromise. I believe this is against the spirit of the collapsed border model, which is primarily different from the separate border model in that borders compete for edges. The separate model produces tables with borders that look like a compromise; if a red and orange edge meet, the border between them appears to be half red and half orange. The collapsed model exists in opposition to this, and if edges compete rather than compromise, so should corners. --Lucent
I think too that corners should compete rather than compromise, but when there is no correct way to decide which border wins the corner, we shouldn't arbitrarily give the corner to one of the candidates. Such behaviour leads to https://bugzilla.mozilla.org/show_bug.cgi?id=325074 or https://bugzilla.mozilla.org/show_bug.cgi?id=192031 which isn't expected. Yet we may share the corner between the fewest borders possible. I tried hard to find and present the most cases and possible rendering decisions possible, but we'll have to choose. --FrnchFrgg
Bug 325074 could be easily resolved with some simple rules. If three edges meet and two are the same, they take the corner. If four meet and three are the same, they take the corner. If two adjacent are the same and two other adjacent are the same, follow the normal rules of thickness gets the corner or "eye catching" gets it. Same for two and two opposite edges. If all four are different and the same width and style, then whatever, who cares? That's not going to happen in any sensibly designed table, and if it does, its designer deserves to get a random colored corner.
I think all this silliness with blending borders with diagonal lines is caused by those trying to formulate its workings not having any use for it personally. Not to be too abrupt, but if one is not actually using the collapsed border model and trying to take advantage of sensible conflict resolution in one's own work, one is just going to come up with a bunch of silly rules suggesting funny diagonal lines at intersection points. I find it absurd, especially when I see such nonsensical renderings as in 325074. It makes me wonder if anyone is actually using the collapsed border model at all.
I'm working on a page that's been begging for collapsed borders to come along for years. Now that they have, the corner resolution is completely irrational. Check out http://www.dayah.com/periodic/ around the bottom where the blue line wraps the rare earths. Why are the black corners jutting into the spanning blue lines? What insane algorithm would see three edges meet in the shape of a T, with two of them colinear, and decide the interloping vertical line should win the corner? --Lucent
Sharing between borders of same weight is IMHO critical for people who use double borders (or groove, ridge, etc...). Furthermore, when there is no mean to decide which border should win, instead of "punishing" the "bad designer" with an arbitrary winner, why not just do something useful (double borders, etc...) ? If the designer is not pleased by the rendering, he will change his table for a correct design. (Nota: I based some of my comments on a wrong assumption, see the edit of border sharing note above, so I now think that we definitively shouldn't share the corner between borders of different widths, because there is no mean to defeat this) --FrnchFrgg
I am really concerned about the discussion here. It does not answer the question of code bloat. Any change in the implementation should result in less code than we have now. I will resist any proposal that creates border segments with more than four edges. The question should be: what we need to do to become CSS 2.1 compliant, not what fancy corners one could create. There is http://www.w3.org/TR/css3-background to discuss this. --Bernd
The difficulty is that CSS 2.1 seem not to specify what to do for corners, and the current behaviour of Gecko, while purportedly compliant, is uneasy to the eyes to say the least. FrnchFrgg
Who paints what
In the collapsed borders model, borders are shared among cells; thus there are several possibilities to consider for painting:
- Each cell wholly paints each winning adjacent border/corner (unless doesn't want to, for example if positionned and proposition 1 is choosen). Either the whole corners are included, or only their part corresponding to borders adjacent to the cell. Problems of overlapping to solve, especially with transparent borders. Probably slow, too. Adapted to any proposition but (2). (3) needs more work, because if the adjacent cell is positionned the border shouldn't be drawn.
- Each cell paints its inner half of each winning adjacent border/corner. Clearly adapted to the (2)nth proposition above. Subject to the same difficulties as touching divs in an antialiased backend ?
- Each cell paints wholly the adjacent borders/corners for which it is the winner, that is has the winner of conflict resolution as its own (asked for) border. The borders painted include the (eventual) part of the corner that correspond to them. Adapted to any proposition but (2). Edge cases: when the winner belongs to a positionned cell and proposition 1 or 4 is choosen, same if winner does not belong to the positionned cell and 3 or 4 is choosen.
- ... ?