Closed Bug 312156 Opened 19 years ago Closed 13 years ago

implement text-overflow: ellipsis from CSS3 text

Categories

(Core :: CSS Parsing and Computation, enhancement)

enhancement
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla7
Tracking Status
blocking2.0 --- -

People

(Reporter: joeri_sebrechts, Assigned: MatsPalmgren_bugz)

References

(Depends on 3 open bugs, )

Details

(Keywords: css3, dev-doc-complete, Whiteboard: [parity-webkit][parity-IE][parity-opera][evang-wanted][CSS IS AWE]SOME)

Attachments

(35 files, 52 obsolete files)

(deleted), text/html
Details
(deleted), application/java-archive
Details
(deleted), application/x-zip-compressed
Details
(deleted), application/java-archive
Details
(deleted), text/html
Details
(deleted), patch
Details | Diff | Splinter Review
(deleted), text/html
Details
(deleted), text/html
Details
(deleted), text/html
Details
(deleted), text/html
Details
(deleted), text/html
Details
(deleted), patch
roc
: review+
Details | Diff | Splinter Review
(deleted), text/html
Details
(deleted), text/html
Details
(deleted), text/html
Details
(deleted), text/html
Details
(deleted), text/html
Details
(deleted), patch
dbaron
: review+
Details | Diff | Splinter Review
(deleted), patch
roc
: review+
Details | Diff | Splinter Review
(deleted), text/html
Details
(deleted), patch
roc
: review+
Details | Diff | Splinter Review
(deleted), patch
roc
: review+
Details | Diff | Splinter Review
(deleted), patch
MatsPalmgren_bugz
: review+
Details | Diff | Splinter Review
(deleted), patch
roc
: review+
Details | Diff | Splinter Review
(deleted), patch
MatsPalmgren_bugz
: review+
Details | Diff | Splinter Review
(deleted), patch
roc
: review+
Details | Diff | Splinter Review
(deleted), patch
roc
: review+
Details | Diff | Splinter Review
(deleted), text/html
Details
(deleted), patch
roc
: review+
Details | Diff | Splinter Review
(deleted), patch
Details | Diff | Splinter Review
(deleted), patch
roc
: review+
Details | Diff | Splinter Review
(deleted), patch
roc
: review+
Details | Diff | Splinter Review
(deleted), text/html
Details
(deleted), patch
MatsPalmgren_bugz
: review+
Details | Diff | Splinter Review
(deleted), text/html
Details
User-Agent:       Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.12) Gecko/20050915 Firefox/1.0.7
Build Identifier: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.12) Gecko/20050915 Firefox/1.0.7

Please implement support for the ellipsis value of the text-overflow property in
CSS3's text module.

Reproducible: Always

Steps to Reproduce:
1. Use text-overflow: ellipsis on any element in combination with overflow: hidden

Actual Results:  
Text is cut off correctly, but rendered without ending ellipsis.


Expected Results:  
Render the text cut off, but with ending ellipsis.
CSS3 Text module is still a working draft as far as I understand.

http://www.w3.org/TR/2003/CR-css3-text-20030514/#text-overflow

Note that the CSS3 Text Effects Module
W3C Working Draft 27 June 2005 says:
"Many sections intended for this module are not yet represented in this draft.
In particular, the 'text-justify-trim', 'text-indent', 'text-overflow' (...) and
related properties have not yet been evaulated."
http://www.w3.org/TR/css3-text/#changes
Keywords: css3
The reason I am submitting this is because IE already has support for it (I
don't know how, but probably CSS3 got its cues from IE for this), and we (the
programming team I am in) are finding it limiting in real life web app
development projects that firefox does not have it available.
My programming team too. Just voted for this bug.

I could probably accomplish the same effect with a hack like maybe .box {overflow:hidden;} .box:after {content:"...";} ?
This would really be great for Firefox 1.5.
This is not going to happen for Firefox 1.5 (that one is near final) and please keep comments out of this bug that do not help with fixing it. This is also not a support forum. Thanks.
Blocks: 366797
Safari supports this since 1.2, Konqueror since KDE 3.5.6.
Status: UNCONFIRMED → NEW
Ever confirmed: true
Whiteboard: [parity-safari] [parity-IE]
I think its first time when IE is better than Firefox, fight for your prestige guys and make some update for this...
It may not be an approved standard, but Firefox risks letting Microsoft remain the innovator if it waits for the long process of standards approval...
I'm not sure if this can be done until the new textframe code is in place (or it could work but maybe require adaptation in order to function with the new code).

I would also like to pointout that you should rad the Bugzilla Etiquette before replying to a bug (https://bugzilla.mozilla.org/page.cgi?id=etiquette.html).
Assignee: dbaron → nobody
QA Contact: ian → style-system
No longer blocks: 366797
Blocks: 391471
I guess a [parity-Opera] tag could now be added...
Hello all,

I tried the attachment 202163 [details] with MSIE 7, Opera 9.24 and Safari 3.0.4 and none of the 3 browsers rendered the testcase as expected. The CSS 3 Text module, May 14 2003 Working Draft
clearly says: "This will result in the content of the span to be partially visible and the ellipsis will be shown" 
http://www.w3.org/TR/2003/CR-css3-text-20030514/#text-overflow-mode
but there is no ellipsis rendered when trying attachment 202163 [details] with those 3 browsers.

----------

Another issue: the CSS Text Level 3, March 6th 2007 (latest) Working Draft says:

"Many sections intended for this module are not yet represented in this draft. In particular, the 'text-justify-trim', 'text-overflow', 'text-decoration', 'text-transformation', 'text-autospace', other properties have not yet been evaulated."
http://www.w3.org/TR/css3-text/#changes

Note that text-overflow-mode is not mentioned and no longer even mentioned as a property to evaluate.
Attached file Working testcase (IE, Safari, Opera) (deleted) —
The reason the attachment doesn't work is because it uses text-overflow-mode instead of text-overflow. "text-overflow: ellipsis" does work in IE6 and Safari 3.0.4. -o-text-overflow works in Opera 9.
This site has a testcase that should work with all major browsers except Gecko ones:
http://www.css3.info/preview/text-overflow/
I've run into this several times in development, particularly concerning displaying longer messages within tables where the message has an action (mouseover, click) that will display the full text as a tooltip or in a popup window.

I'd like to point out that Gecko already has this ability built into XUL, through crop="right" or crop="end" for XUL elements that serve a purpose identical to that of <xul:label>. (menus, bookmarks, tabs, etc.) I've tried to force this behavior upon HTML elements, via Mozilla-specific CSS selectors, with no success to date.

I'm not sure how accurately the behavior of @xul:crop follows that of the CSS3 text-overflow proposal. However, at least until a proper implementation can be developed, it could serve as a functional hack to fill in the gap, and I'm certain it would fulfill the needs of most developers who need a workaround for Gecko. And I don't think anyone could seriously complain, if it's implemented as -moz-text-overflow while CSS3-Text is still a Working Draft.

Could a developer who's more familiar with the inner workings of @xul:crop take a look and see if it'd be possible to directly transfer its behavior onto HTML elements with -moz-text-overflow: ellipsis? I'm sure you'd have a lot of grateful developers if this could be done and patched for Firefox 2. :)
So this is not working only in firefox :(
text-overflow offers a lot of usability for the end user, it is IMHO an important feature
I also vote for this.

Not being a standard is NOT a reason to not to implement. The innovator influence the standard, and we don't want a world where all innovation comes from Microsoft. If we don't do this, then MS's marketing slogan is actually correct, that "MS is the real innovator in all the time".

I think we did -moz-opacity; -moz-corner-radius and there is no reason not to have -moz-text-overflow. Besides, IE & Safari & Opera's move to support text-overflow makes W3 not possible to do it differently in the final CSS3, so even if it's draft, it's the part 'unchangable' in the draft.

There are other bugs that ONLY FIREFOX has and IE does it better *since 10 years ago*. See 41489 . the reason not to fix that one was the same, waiting for standard body. I even wouldn't expect a change in another 10 years...
Flags: wanted1.9.1?
Whiteboard: [parity-safari] [parity-IE] → [parity-safari] [parity-IE] [parity-opera]
The main thing we need here is a spec. Among other things, it needs to describe:
-- what style the ellipsis has (font, color, etc) ... does it come from the text or does it come from the element with text-overflow on it?
-- does text-overflow inherit by default or not?
-- how does it work with text-align:right, does the ellipsis go on the left?
-- how does it work with bidi text, e.g. a line of Hebrew? What about mixed bidi text e.g. English followed by Hebrew? Can bidi text make the ellipsis appear at the beginning of the line? I'm particuarly interested in the case of an LTR word followed by an RTL word that doesn't fit, e.g.

english WERBEH

where only "english HEB" fits, where should the ellipsis go?
-- what happens if there's replaced content near the end of the line, say an image? Do you get the ellipsis or does the image overflow? If an ellipsis, where does the ellipsis go?

And more things that I haven't thought of yet :-). This information needs to be gathered for different browsers and the most sensible common behaviour agreed on. It will help get text-overflow added to CSS3 too.

That's the most important thing someone can do to move this along.
For bidi I think it's reasonable to follow the natural bidi reordering of the ellipsed text, i.e. the ellipsis will always appear at the end of the line (right for left-to-right or left for right-to-left) just as end-of-paragraph punctuation does even when the last line ends in text in the other direction.

However, this won't necessarily Just Work if any string can be specified for the ellipsis, as in earlier versions of CSS3 Text [1].

Other issues with complex and international text that occur to me:

In shaped text, e.g. Arabic, if the last character that fits is joined to the first character that doesn't fit, should it appear in the joining form or the isolated form?

What happens in Indic languages where short vowels are reordered before the preceding consonant, e.g. "wikipedia", which in Hindi is "vikipIdiyA" (where "i" represents a short i and "I" represents a long i), and appears visually as ivikpIidyA. If either "ivi..." or "ivk..." fits but "ivik..." doesn't, what should appear?

And more things that I haven't thought of yet either :)

[1] http://www.w3.org/TR/2003/CR-css3-text-20030514/#text-overflow-ellipsis
Thanks Roc and Simon for your comments. It certainly points out that this isn't quite as simple as it sounds.

To answer your questions though - we can at least look at how Safari, Opera and IE have solved these issues?

They've already implemented it, so we could learn from their experience/mistakes.

The lack of this feature is holding back several of my own web site designs. I can use ellipsis for the other 3 main browsers, but users with Firefox get a poor user experience as I have to cut off boxes half-way through characters.
(In reply to comment #23)
> To answer your questions though - we can at least look at how Safari, Opera and
> IE have solved these issues?

We certainly should, because we need a spec that interoperates reasonably well with those browsers.

Anyone who wants to help could make a big contribution by writing some testcases covering those questions and anything else they can think of and documenting existing behaviour on those testcases. For bonus points, actually write a spec suggesting what the preferred behaviour should be.
Had I known test cases and specs were what held this back I'd have submitted them much earlier. I'm on the case. If anyone has any suggestions for additional test cases that are needed, or suggestions for how the spec should be structured, please add them to this bug and I'll take those comments into account too.
Thanks!

I think some previous versions of CSS3 Text had a draft specification for text-overflow, but it was pretty incomplete. Still worth taking into account if you can find it.

If you do come up with something reasonable you should post it to www-style since the CSS3 Text module needs this and I'm sure fantasai could use the help.
The old spec is at http://www.w3.org/TR/2003/CR-css3-text-20030514/#text-overflow-props but you're better off checking e.g. http://msdn.microsoft.com/en-us/library/ms531174(VS.85).aspx and running tests. I think for CSS3 Text we'll only want something like text-ellipsis: clip | ellipsis | <string>. I can add in the syntax definition to the spec very easily. The part I'm having trouble with is defining exactly how it works. When I ran tests several years ago Safari and IE had very different interpretations of which box the ellipses belonged to, but IIRC interoperability has improved since then.
I've done some tests, see the attachment for the results. IE 6 and 7 have identical renderings. I also tested with Safari 3 and Opera 9.5. A firefox 3 reference rendering is included.

I had to make some guesses with the bidi tests, because it's not really something I have any experience with, so if I messed that part up, please tell me. Also, if someone could point me to some educational materials that could get me up to speed on the topic, please do.

Behavior common across all browsers:
- Images never cause ellipsis (not even for replaced text)
- The color and style of the ellipsis come from the style of the overflowing element, not the element that causes overflow
- Text-overflow does not inherit automatically
- "text-align: right" renders the same as "text-align: left" (text is cut off at the right and rendered with a right-hand ellipsis)
- If the overflowing character is right after an ellipsis, that ellipsis is replaced just as if it were any other text to fit the overflow ellipsis

Differences:
- The ellipsis is rendered as three period characters in IE6, IE7 and Opera 9.5, and as an ellipsis entity in Safari 3
- The behavior of an overflow element with rtl direction is inconsistent:
  * Nonsensical in Opera (check out the screenshot)
  * Cut off at the left and rendered with a left-hand ellipsis in IE
  * Cut off at the left but rendered without an ellipsis in Safari
- The behavior of a ltr element with a rtl segment causing overflow is inconsistent:
  * Again nonsensical in Opera
  * Cut off at the right of the overflowing element in Safari, but rendered without ellipsis
  * Cut off at the right of the overflowing element in IE, and rendered with ellipsis

Generally the IE behavior seems sane to me, even for rtl, except that the ellipsis should be rendered using the correct entity, not as three dots.

If people agree, I'll try to spec that behavior (IE + ellipsis entity).
Sounds about right. The RTL in LTR (or LTR in RTL) case is tricky. You can either truncate the text visually and then insert the ellipsis, or truncate the text logically and then insert the ellipsis. I am not sure which is better. smontagu?

Wrt inheritance etc: what happens if the outer div has text-overflow and *it* is the one with a constrained width? (Need to check both auto width and large fixed width on the child for this one.)

The other thing to check is if the ellipsis is triggered when overflow != hidden.
Attachment #325795 - Attachment mime type: application/zip → application/java-archive
> You can either truncate the text visually and then insert the ellipsis

If you mean what IE7 does, I think that is far easier to implement. I think text-overflow can be implemented entirely at paint time, without affecting layout at all. But truncating the text logically would have to affect the layout.

Oh, one more thing that needs to be tested:

What happens if an overflowing line consists of text followed by image, where the left edge of the image is inside the area where the ellipsis would be? Do you get an ellipsis or not? It seems like either is possible/reasonable.

Thanks!
Hi. 2 cents from my understanding of Chinese/Japanese/Korean (I am native Chinese):

If eclipses was done logically, please note ideographs are not separated with space. They are not separated at all. So if the browser replace the overflowing space-separated "word" with eclipses, where "word" is defined as "space separated entity", it might have replaced the whole paragraph with eclipses, because there are usually only 0 space character in a Chinese paragraph.

Also note the eclipses in Chinese does not look like 3 dots or 4 dots, it is 6 dots. 10 years ago people would be surprised to see 3/4 dots used for eclipses but now it seems poor localized implemented systems are everywhere that nowadays Chinese people just tolerate 3/4 dot ellipses with no complain, although schools still teach 6-dot way. (It all starts from Windows which shows menu item that opens a dialog with padding 3 dots, now people think: "eclipses are 6 dots, but on computer it is 3 dots".) 
(In reply to comment #32)
> If eclipses was done logically, please note ideographs are not separated with
> space. They are not separated at all. So if the browser replace the overflowing
> space-separated "word" with eclipses, where "word" is defined as "space
> separated entity", it might have replaced the whole paragraph with eclipses,
> because there are usually only 0 space character in a Chinese paragraph.

text-overflow doesn't care about word boundaries so this issue will not arise.
(In reply to comment #33)
> (In reply to comment #32)
[...]
> text-overflow doesn't care about word boundaries so this issue will not arise.
> 

The 3-dots vs. 6-dots issue will arise, unless the overflow is localized according to (I suppose) the last character before the cut. (English text on a Chinese page, or vice-versa, should get the ellipsis corresponding to the text, not to the page, and I don't think we can rely on a lang= attribute unless, maybe, it is present at the very level where the overflow happens.)
A question: what happens if overflowing text hits a right floated element? e.g.

<div style="width: 8em; white-space: nowrap; border: 1px solid red; overflow: hidden; text-overflow:ellipsis">
<div style="float: right; width: 4em;">MM M</div>
m mm mm mm mm mm
</div>

Would be displayed as:
-----------------
m ... MM M
-----------------
Or as
-----------------
      MM M
m mm mm ...
-----------------

I checked currently Opera does it in the second way. I don't know IE since I don't have it.

I as a web developer think the 1st way is better because it provides more possibility and in many cases provide better layout (think about a news teaser that has a picture of the story floating on the right). How do you think?
> think about a news teaser that has a picture of the story floating on the right

Just in case I wasn't clear about this. Think of this:

<p class="news_teaser">
<img class="news_picture"/>
This is the teaser text of the news teaser. The web developer does not know how long this teaser text is, but he is clear each teaser should only have 5 rows and everything after that should be eclipsed. He also want: in case there are no news_picture together with the teaser, the teaser text should use all space, otherwise it should surround the news picture which floats to the right ...
</p>
(In reply to comment #33)
> (In reply to comment #32)
[...]
> text-overflow doesn't care about word boundaries so this issue will not arise.

So it is clear we are not going to implement "text-overflow: ellipsis-word;"
or not?

(In reply to comment #34)
> The 3-dots vs. 6-dots issue will arise, unless the overflow is localized
> according to (I suppose) the last character before the cut. (English text on a
> Chinese page, or vice-versa, should get the ellipsis corresponding to the text,
> not to the page, and I don't think we can rely on a lang= attribute unless,
> maybe, it is present at the very level where the overflow happens.)

Is there a Unicode character that displays 6-dots? The ELLIPSIS character displays only 3-dots in all the Chinese fonts on my Mac.

(In reply to comment #35)
For your example, the second way is the only way that's consistent with CSS, as far as I can tell.

If you put the float after the first character of the line, you might get the line to overlap the float. Firefox 3 would move the float to the next line but Firefox 3.1 (the nightly builds) behaves differently. However, text-overflow as specced so far is pretty clear that you only get an ellipsis when you overflow the edge of a container element that has text-overflow on it, so we wouldn't add an ellipsis just because it's overlapping a float.
(In reply to comment #37)
> So it is clear we are not going to implement "text-overflow: ellipsis-word;"
> or not?

That's fairly doable and we already have pretty good knowledge of word boundaries (e.g. for caret movement).
In Chinese we use two 3-dot-eclipsis. U+2026
(In reply to comment #34)
> The 3-dots vs. 6-dots issue will arise, unless the overflow is localized
> according to (I suppose) the last character before the cut. 

Difficult.

Problem 1:===========

1) in Chinese ellipsis is 6 dots;
2) in Japanese ellipsis is 3 dots OR 6 dots;
3) most (>80%) Chinese ideographs are also Japanese ideographs;

http://en.wikipedia.org/wiki/Ellipsis

So you don't know if it is Chinese by just looking at the character.

Solution:

Since in Chinese 6 dots is the standard way of ellipsis and in Japanese both 3-dot and 6-dot are acceptable, then let's use 6 dots when it comes to a Chinese/Japanese ideograph. OK for Chinese and acceptable for Japanese.

Problem 2:===========

1) In Chinese dots in ellipsis can be on the baseline or vertically centered. vertically centered is the standard way, baseline dots are acceptable;
2) In Japanese dots in ellipsis must be vertically centered;

Solution: We vertically center the dots when it comes to a Japanese/Chinese ideograph, acceptable for both.
In reply to comment #40 and comment #41
Problem: U+2026 (used also for Latin, Cyrillic, etc.) is on the baseline -- acceptable for Chinese but not for Japanese.
Solution: Display the CJK ellipsis as the equivalent of <sup>&#x2026;&#x2026;</sup> or similar.
I think it is font related.

A Chinese font places the dots of U+2026 on vertical center.

Important is to tell Mozilla to use a Chinese font for the Chinese ellipsis
(In reply to comment #38)
> 
> Is there a Unicode character that displays 6-dots? The ELLIPSIS character
> displays only 3-dots in all the Chinese fonts on my Mac.
> 

Wikipedia says:

In Chinese, the ellipsis is six dots (in two groups of three dots, occupying the same horizontal space as two characters).
...
In Chinese and sometimes in Japanese, ellipsis characters are done by entering two consecutive horizontal ellipsis (U+2026).
A few more things I thought of that need to be tested and specified:

-- what happens to position:relative and position:absolute content? if you position text so that it overflows the text-overflow element to the right, does an ellipsis occur there? What if you position it to the left of the left edge of the element, does an ellipsis occur there?

-- what if you have a line of text that overflows to the right, causing an ellipsis, but beyond that text on the same line you have more text in a span which is relatively positioned with negative 'left' so it's actually visible? Do you get *another* ellipsis drawn? Or is the stuff after the ellipsized text not rendered at all?

-- if you click on an ellipsis, does the event go to the element with text-overflow or the text "underneath" the ellipsis?
Also, what text descendants are affected by text-overflow? Do the effects propagate to descendants inside overflow:scroll descendants? Positioned descendants? Float descendants? Descendants in tables? inline-block descendants?
Sorry this is a bit off-topic, but I was confused by this:

Robert O'Callahan (:roc) <roc@ocallahan.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
 Attachment #325795 [details]|application/zip             |application/java-archive
          mime type|                            |

Isn't it actually a ZIP file? When I download the attachment, it ends with .zip.

Also, when I click the links in comment 30, I get a Bugzilla message saying "Invalid Attachment ID" and e.g. "The attachment id 325795!/text-overflow tests/test1.html is invalid."
(In reply to comment #47)
> Also, when I click the links in comment 30, I get a Bugzilla message saying
> "Invalid Attachment ID" and e.g. "The attachment id 325795!/text-overflow
> tests/test1.html is invalid."

Your questions are related.  Copy the full line (including the jar: part) and you'll see the specific file from the archive in Firefox (won't work in other browsers.)  This is for convenience.

-[Unknown]
(In reply to comment #29)
> Sounds about right. The RTL in LTR (or LTR in RTL) case is tricky. You can
> either truncate the text visually and then insert the ellipsis, or truncate the
> text logically and then insert the ellipsis. I am not sure which is better.
> smontagu?

I never considered the possibility of truncating visually. It seems to me obvious that the text remaining after truncation must always be logically contiguous and therefore readable as the beginning of the original text.
(In reply to comment #28)
> I had to make some guesses with the bidi tests, because it's not really
> something I have any experience with, so if I messed that part up, please tell
> me. Also, if someone could point me to some educational materials that could
> get me up to speed on the topic, please do.

Richard Ishida's "What you need to know about the bidi algorithm and inline markup" is an excellent introduction: http://www.w3.org/International/articles/inline-bidi-markup/
(In reply to comment #49)
> I never considered the possibility of truncating visually. It seems to me
> obvious that the text remaining after truncation must always be logically
> contiguous and therefore readable as the beginning of the original text.

But we don't do that for overflowing text that's just clipped, so doing it for text-overflow would make text-overflow behave more differently from clipping than I think was intended. Not to mention that it would make text-overflow much much harder to implement.
(In reply to comment #48)
> (In reply to comment #47)
> > Also, when I click the links in comment 30, I get a Bugzilla message saying
> > "Invalid Attachment ID" and e.g. "The attachment id 325795!/text-overflow
> > tests/test1.html is invalid."
> 
> Your questions are related.  Copy the full line (including the jar: part) and
> you'll see the specific file from the archive in Firefox (won't work in other
> browsers.)  This is for convenience.
> 
> -[Unknown]

Ah, thanks for the tip; it works fine when I copy the jar: part as well :)
My understanding is that part of roc's interest in this bug is to unify XUL and HTML text frames. Now XUL text frames currently use the intl.ellipses localised preference to know how to ellipse text correctly. (I have it set to ... because my preferred UI font's bold ellipsis looks like an underscore.) Would this mean that the HTML ellipsing code would have to use the same preference?
For Japanese, at least, we need to support two kinds of ellipsization, one for "content" text and one for UI text. We can easily do that with a -moz-ui-ellipsis value which XUL would use.
Two quick things:
1. Does text-overflow:ellipsis automatically happen when specified on tables with table-layout:fixed? (but without overflow:hidden and white-space:nowrap specified?) I'm not sure how IE/Opera/Safari handle this.
2. What should happen when the cut-off text is selected? I know IE hides the ellipsis and shows the cut-off text under the ellipsis when the text is selected.

Thanks for the activity, everyone! I'm really looking forward to seeing this implemented!
(In reply to comment #55)
> 1. Does text-overflow:ellipsis automatically happen when specified on tables
> with table-layout:fixed? (but without overflow:hidden and white-space:nowrap
> specified?) I'm not sure how IE/Opera/Safari handle this.

That would be really strange and inconsistent with other css properties, wouldn't it?  Might be nice if it inherited from table to td/th though.... but I think those are both spec decisions?

-[Unknown]
(In reply to comment #56)
> (In reply to comment #55)
> > 1. Does text-overflow:ellipsis automatically happen when specified on tables
> > with table-layout:fixed? (but without overflow:hidden and white-space:nowrap
> > specified?) I'm not sure how IE/Opera/Safari handle this.
> 
> That would be really strange and inconsistent with other css properties,
> wouldn't it?  Might be nice if it inherited from table to td/th though.... but
> I think those are both spec decisions?

As I said, I don't actually know how it works/acts in the other browsers, nor how overflow and whitespace are specified for table-layout:fixed in HTML or CSS. This is more of an empirical observation that text-overflow:ellipsis requires overflow:hidden, white-space:nowrap, and a set width to be specified in order to be "triggered", and all three of those seem to automatically happen for text in TD's in a table with table-layout:fixed. Just a curious thought, but if it does happen in the other browsers, we might as well add it to the list of test cases. :)
(In reply to comment #57)
> all three of those seem to automatically happen
> for text in TD's in a table with table-layout:fixed.

Ah, sorry about that.  I had read what you said as "act as if text-overflow: ellipsis were specified with table-layout: fixed applied."

Anyway, from my tests:
IE6 requires all three properties to do the ellipsis (even overflow: hidden), otherwise it just clips to width.
IE7 behaves no differently.
Safari acts like IE except that it (like Firefox) does not clip a fixed table with no overflow: hidden.

I don't have anything newer than Opera 9.21 and can't upgrade now, but hope that helps nonetheless.

-[Unknown]
OK, I've done more tests, you can find screenshots in the attachment.

Some more conclusions:
- ellipsis only happens for runs of text. As soon as text-overflow is defined on an element, but it's blocks that cause the overflow to happen, no ellipsis is rendered.
- With the exception of opera, inline images don't cause ellipsis if they're the one triggering the ellipsis, even if they would be fully replaced by the ellipsis when rendered. Opera removes the text to fit the ellipsis, but not the image, so the ellipsis gets rendered on top of the image, which is clearly broken behavior.
- overflow auto causes ellipsis, but it is broken in all but IE, because scrolling the overflow element does not reveal more text (the ellipsis scrolls along, revealing empty space where text should have been).
- Chinese text gets an ellipsis with the same rules as english text (three dots, whole symbols are removed to make room for the ellipsis).
- There's no consistent behavior for a right floated element inside an overflowing element. I don't know the exact rules CSS specifies in this case (regardless of ellipsis). Safari does the intuitive thing by pushing back the text and rendering ellipsis right before the floated element. Opera and IE push the text down. Firefox renders the float on top of the text.
- Position relative/absolute of the overflowing content inside the overflowing element does odd things in IE, but generally I would say it doesn't cause ellipsis (because it turns the content that triggers overflow into blocks, and blocks don't trigger ellipsis).
- Tables don't trigger ellipsis if they have fixed layout, nor does the property inherit to the cells. I think this is quite acceptable behaviorally. I am in doubt about whether a fixed layout table should automatically set overflow to hidden for the cells, or leave it as visible. The correct behavior is unclear to me (someone with deep CSS knowledge who would like to comment?).
- When the overflow is caused by a child element, and you click the ellipsis, does the event get sent to the element that caused overflow, or the element that has the text-overflow style set? IE and Opera send it to the child, Safari to the parent.

Again, IE seems mostly consistent. There are a few things which are not correct, but it's a good baseline for the feature. I'll work on getting that spec, but I can already tell it's not going to be easy. I'm going to make a baseline spec just for text-overflow: ellipsis, but if someone wants to flesh it out for ellipsis-word later on, they're ofcourse welcome to.
Flags: wanted1.9.1? → wanted1.9.1+
Thanks for all this analysis!!!

(In reply to comment #59)
> - Position relative/absolute of the overflowing content inside the overflowing
> element does odd things in IE, but generally I would say it doesn't cause
> ellipsis (because it turns the content that triggers overflow into blocks, and
> blocks don't trigger ellipsis).

position:relative doesn't change anything into a block. position:absolute kind of does, but the text inside it remains inline...
I just want to get this implemented :)

I've posted an opening volley to www-style, in the hopes that we can get some kind of description for this feature worked out, but I don't know if it's really the right path, because the W3's task is more one of harmonization and documentation rather than one of prior specification.

http://lists.w3.org/Archives/Public/www-style/2008Aug/0006.html
You've made a lot of progress, but we still need a complete answer to comment #46 and the second item in comment #45 ... we really do need to specify what happens in positioned content. Note that normally, setting "position:relative" but leaving "top" and "left" as "auto" (or "0") will not change any rendering, so it would be nice if text-overflow preserved that invariant.
Blocks: 438517
"the W3's task is more one of harmonization and documentation rather than one of prior specification."

That's not true. W3C does both. (Ideally it's supposed to just do prior specification for everything, but that's not how reality works.)

I agree with roc that relative positioning should not change any rendering. I also don't think changing a relatively positioned element's offsets should change where the ellipsis shows up: I'm thinking of animated effects where a box e.g. slides into place. (In other words, I think the May 2003 spec is wrong in this aspect.

I'll add a skeleton for text-outline to the Editor's Draft, maybe you can start by proposing text to fill it in and we'll go from there.
Probably also need to define what happens when the 'clip' property takes effect.
Attached file More tests in response to #45 and #46 (deleted) —
I'm really getting the knack of this "testing" thing ;)

@Roc

In response to #45:
- position:absolute never renders ellipsis because it turns the element that triggers overflow into a block box (more or less, as you say)
- position:relative does in fact render ellipsis, unlike the wrong conclusion I took from my previous test, but it does it differently in every browser:
  * IE: ellipsis gets rendered, as far as I can tell, inside the relatively positioned element at the position that it would be rendered if the content of the relatively positioned element was placed directly inside the overflowing element. Yes, that is pretty weird, but do check out the screenshot.
  * Safari: space gets cleared in the relatively positioned element to make room for the ellipsis, as if the element wasn't offset, but then the ellipsis gets rendered at the end of the overflowing element anyway.
  * Opera: the ellipsis is simply offset with the rest of the positioned element.
- Positioning the element beyond the left edge does not cause an ellipsis at the left.
- Negatively positioned content "moving in" from beyond the overflow boundary does not cause a second ellipsis. In fact, it isn't even visible in opera and safari (just the border around the positioned element is), which I assume is due to the clearing of text that happens when ellipsis is drawn. In IE this causes the ellipsis to not be drawn at all.
- Relatively positioned content that wouldn't overflow if it wasn't positioned doesn't get an ellipsis.

And in response to #46:
- blocks across the overflow boundary prevent ellipsis, except in the case of IE, which renders ellipsis for divs inside overflow:auto elements for example (I don't quite get the pattern of when IE renders ellipsis). So, descendants that are block boxes don't cause ellipsis, while descendants that are inline boxes do cause ellipsis.

@Fantasai

I didn't mean to say that W3C does no prior specification of behavior, just that my view as an outsider is that the majority of the work they do is one of documenting what the standard is after it is created or modified de-facto. As a personal aside this is the role I prefer them in anyway.

The case of relative positioning is a tricky one:
- Either you consider the ellipsis part of the positioned element and you move it along. This is consistent and simple, but non-intuitive.
- Or you consider the ellipsis a hint of overflowing content, and render it at or near the overflow boundary, visually removing characters as needed. In this case changing positioning wouldn't affect the position the ellipsis is rendered much (except in that it must be rendered right after a character or block, so it might move a few pixels depending on the changes to the content). I prefer this approach because it is consistent and intuitive.
- Or you simply say that relatively positioned content doesn't trigger ellipsis. Simplest of all, but again non-intuitive.

Regarding clip, some quick testing showed that ellipsis is obscured along with the rest of the clipped content. This behavior makes sense as the border is obscured also, instead of moved to fit the clipped content.

I'd be more than willing to try to fill in the blanks in the spec, if you'll forgive me if my first attempts come up short :) Is this editor's draft you speak of the document at this location: http://dev.w3.org/csswg/css3-text/ ?
Attachment #332428 - Attachment mime type: application/zip → application/jar
Attachment #332428 - Attachment mime type: application/jar → application/java-archive
Thanks for plugging away at this!!

(In reply to comment #66)
> In response to #45:
> - position:absolute never renders ellipsis because it turns the element that
> triggers overflow into a block box (more or less, as you say)

I don't know if that's the right reasoning but the behaviour sounds acceptable.

> - position:relative does in fact render ellipsis, unlike the wrong conclusion
> I took from my previous test, but it does it differently in every browser:
>   * IE: ellipsis gets rendered, as far as I can tell, inside the relatively
> positioned element at the position that it would be rendered if the content of
> the relatively positioned element was placed directly inside the overflowing
> element. Yes, that is pretty weird, but do check out the screenshot.

Oops.

>   * Safari: space gets cleared in the relatively positioned element to make
> room for the ellipsis, as if the element wasn't offset, but then the ellipsis
> gets rendered at the end of the overflowing element anyway.

That doesn't seem right.

>   * Opera: the ellipsis is simply offset with the rest of the positioned
> element.

Of these options, I prefer this behaviour.

> - Positioning the element beyond the left edge does not cause an ellipsis at
> the left.

Sounds OK.

> - Negatively positioned content "moving in" from beyond the overflow boundary
> does not cause a second ellipsis. In fact, it isn't even visible in opera and
> safari (just the border around the positioned element is), which I assume is
> due to the clearing of text that happens when ellipsis is drawn. In IE this
> causes the ellipsis to not be drawn at all.

The Opera and Safari behaviour sounds best. Hmm, so in general, ellipsization clears the text following it in the line, but non-text stuff like borders is still drawn? What about images, inline-blocks, and other stuff you can place in a line, are they all still drawn if relative positioning brings them back into view?

> And in response to #46:
> - blocks across the overflow boundary prevent ellipsis, except in the case of
> IE, which renders ellipsis for divs inside overflow:auto elements for example
> (I don't quite get the pattern of when IE renders ellipsis). So, descendants
> that are block boxes don't cause ellipsis, while descendants that are inline
> boxes do cause ellipsis.

The question is, do inline descendants which happen to be in a float descendant get ellipsized? What about inline descendants in a table, block or inline-block descendant? (You've already answered for inline descendants in an absolutely-positioned descendant --- "no").

> The case of relative positioning is a tricky one:
> - Either you consider the ellipsis part of the positioned element and you move
> it along. This is consistent and simple, but non-intuitive.
> - Or you consider the ellipsis a hint of overflowing content, and render it at
> or near the overflow boundary, visually removing characters as needed. In this
> case changing positioning wouldn't affect the position the ellipsis is rendered
> much (except in that it must be rendered right after a character or block, so
> it might move a few pixels depending on the changes to the content). I prefer
> this approach because it is consistent and intuitive.
> - Or you simply say that relatively positioned content doesn't trigger
> ellipsis. Simplest of all, but again non-intuitive.

I don't think your last option is acceptable, because it breaks the invariant that just setting "position:relative" on an element doesn't change rendering. I'm not sure which of the first two options I prefer, but I lean towards preferring option #2.

Thanks again!
Yep. I just checked in the skeleton of a text-overflow definition: just the syntax, no prose. <http://dev.w3.org/csswg/css3-text/#text-overflow> Post your proposed text to www-style.
I just tested the situation with an element with text-overflow containing a block descendant (float, table, inline-block), in turn containing an inline descendant (span). The block causes the inline descendants not to get ellipsis. There must be an ancestral path of only inline descendants to the overflowing element in order for ellipsis to be applied.

The question is ofcourse: is this the behavior we want? I don't doubt that it's easier to implement that way, but is it what authors want? I can imagine arguments in favor of applying ellipsis to block descendants and their children, as long as there is text at the overflow boundary.

But perhaps this is a case of "the enemy of the good is the perfect", and the existing cross-browser behavior is "good enough".
Depends on: 452667
I've posted a suggested spec wording to www-style. Anyone interested in getting this feature specified and implemented should discuss it there.

http://lists.w3.org/Archives/Public/www-style/2008Aug/0271.html
No longer blocks: 438517
Assignee: nobody → smontagu
wow its over 3 years for this bug, all other major browsers support this normally already. 

before recent FF release (3.0.9) we could use XUL approach, but referencing pages to "outside" makes it invalid because of security patch:

http://www.mozilla.org/security/announce/2009/mfsa2009-18.html

(vote for fix)
(In reply to comment #71)

You're supposed to vote via the "Vote" link, not by commenting. By doing it the way you did, you've just sent an email to somewhere around 150 people...

Also, the bug is already marked [parity-safari], [parity-IE] and [parity-opera].
The following CSS still works in Fx 3.09 Mac for me.

div#subMenu h2 {
    -moz-binding:url(ellipsis.xml#ellipsis);
    overflow:hidden;
    white-space:nowrap;
    width:150px;
}
Has the implementation of this begun?
Yes, I am working on it.
How close ru (maybe in years) :) Do we have to wait for Fx 4.00 before we see it?
Apparently no other UA supports text-overflow: <string>.

I also used attachment 387396 [details] to test behaviour when selecting text by dragging the mouse as far as the right-hand border of the <div> and copying.

IE8 (Windows XP): the text appears selected including the first two dots of the ellipsis. The text that appears before the ellipsis is copied.

Opera 9.64 (Windows XP): while dragging, the text hidden by the ellipsis reappear and the text appears selected up to the border. The text copied is the same as what appears selected.

Safari 4.0 (OS X 10.4.11): the text appears selected including the whole ellipsis. The text copied includes the text hidden by the ellipsis.

Opera's behaviour seems the most logical to me, and is also what is described at http://msdn.microsoft.com/en-us/library/ms531174%28VS.85%29.aspx:

"The hidden text can be selected by selecting the ellipses. When selected, the ellipses will disappear and be replaced by the text to the extent of the layout area."
Opera's behavior is consistent with ellipsis being a visual hint only, that does not affect structure or behavior.
Flags: wanted1.9.2?
Flags: blocking1.9.2?
(In reply to comment #77)
Would it be useful to add a soft hyphen (SHY character) in the test case?

While trying to figure out a solution for the lacking ellipsis-support for Gecko-based browsers, I used an XBL binding to produce a work-around, but it has issues with both text selection and with soft hyphens, as described at:
http://ernstdehaan.blogspot.com/2008/10/ellipsis-in-all-modern-browsers.html

I do realize that the solution being implemented may be completely unrelated to the issues I ran into. If there may be a relation, though, then adding a single soft hyphen character in each list-item may be useful.
Flags: wanted1.9.2?
Flags: wanted1.9.2-
Flags: blocking1.9.2?
Flags: blocking1.9.2-
Is the implementation-in-progress going to require overflow:hidden, as IE's does and as documented at https://developer.mozilla.org/En/CSS/Text-overflow ?

If so, should bug 491549 be a dependency? The combination of inline-block and overflow-hidden triggers a nasty vertical misalignment which makes it almost unusable in a baseline-aligned inline context.
Dave Hyatt wanted to remove the dependency on overflow:hidden, so if the rest of the WG agrees we can do that. I'm going to ask about putting text-overflow on the CSSWG agenda for the TPAC, as there's a lot of issues here.
Attached patch WIP v0.1 (obsolete) (deleted) — Splinter Review
too early version.  I need to add some property to cache due to performance and fix selection bug.
That's a good try, but unfortunately you can't do this just in the text frame. For example you need to be able to hide overflowing non-text content, and in an overflow:scroll context you need to be able to repaint the text as you scroll because the ellipsis is moving.

See https://wiki.mozilla.org/Gecko:TextOverflow
A better example of where doing this just in the text frame won't work is content like

hello<b>kitty</b>

where the 'k' overflows the boundary. We need to add ellipsis after the "hello", so you can't just implement it in the textframe that overflows (which is "kitty" in this case).

Another issue is that the ellipsis needs to use the text-style of the block that set 'text-overflow', not the style of the text itself, according to what other browsers have implemented.
I hate to say this because I have never had a problem with Firefox being the one falling behind. But every other major browser has support for this. what doesn't Firefox. It just doesn't make any since. Its usually the only browser that I generally don't have to write little hacks for. I mean even IE 6.0 supports it.
Whiteboard: [parity-safari] [parity-IE] [parity-opera] → [parity-safari] [parity-IE] [parity-opera] [evang-wanted]
This is not the only one issue that Firefox has and others don't. As a user I could just name a few without evening googling. For example, http authentication not working for multi-byte user name (frequent case in database driven web forums), jpeg2000, and so like. My favorites are two CSS limitations: inter-ideograph text align justification which is necessary to correctly display ideograph/latin mixed text line, and writing-mode which is necessary to render vertical Chinese text (widely used in Taiwan and Japan) correctly. Both are there since IE6, and both break Chinese typography so much that use of PDF for printing being not replaceable.

As a stupid luser when I found something is not working on Firefox, and question that, usually I was given a long explanation why it should not work and a list of technical reference plus a kind warning message that posting anything without fully understanding the former would not be constructive nor helping, as you should be in any opensource project. Meanwhile all other browsers offer it just fine without explanation nor discussion. Some say dictatorship occassionally are proficient in software development. I say they have to offer features without such discussion because many are not opensource thus there is no open place users can discuss ;( There is even a third funny theory that since opensource is celebration instead of competition, it effectively removed the need to compete for features nor the need to advance ;)

This is not a complaint, just facts. The next commenter, he would pointing out all I said above doesn't belong here, it belongs to newsgroups and mailing lists where developers don't listen as much as here. The saddest part is, he /is/ right.
This it not a spectacular feature but one of a must-have item for modern Web applications. Why not raise the priority and get fixed in Firefox 3.7? I don't want to use <xul:description crop="end" value="Foo" /> hack anymore.
Attached patch WIP v0.2 (deleted) — Splinter Review
Attachment #415598 - Attachment is obsolete: true
Just bumping to make sure that the WIP patch isn't forgotten. How are you doing, Makoto?
This patch is not the right approachh, for the reasons mentioned in comment #85.

Mats is working on a real fix.
Assignee: smontagu → matspal
Whiteboard: [parity-safari] [parity-IE] [parity-opera] [evang-wanted] → [parity-webkit] [parity-IE] [parity-opera] [evang-wanted]
blocking2.0: --- → ?
As remote XUL is going away (bug 546857), the <xul:description> hack probably won't work anymore.
Yeah, I can confirm that the xul hack doesn't work anymore.  Is anyone working on this?
Renominating in light of comment 93.
blocking2.0: - → ?
This is something only Firefox doesn't support among modern browsers. (See the whiteboard.) The priority should be higher...
"Duke Nukem Forever" gets a release date, but this bug is still open after five years?  Ouch!
I agree, this is one of the biggest pains I got in daily web development...
Guys, if you're not actively helping to fix this bug, *don't comment*.
https://bugzilla.mozilla.org/page.cgi?id=etiquette.html
Adding Tantek, since we need a spec here, and CSS3 UI seems like a more appropriate place for overflow control than CSS3 Text, despite the property name.
Blocks: 596472
Blocks: 569993
This issue should be considered a regression and be prioritised accordingly. 

The XUL workaround was very widely used, and now with the XUL avenue blocked, this feature has been lost.

Could someone at mozilla please bring this regression to the powers-that-be for consideration as a Firefox 4 blocker. I can really see this causing issues for many thousands of websites that would have implemented the XUL workaround.
This bug is blocking the "Firefox 4 Web Product Tracking Bug" (bug 569993), but not blocking2.0.

Mats, roc, any update?
It is way too late in the game to be adding new Web features to FF4, especially features that aren't nearly done yet.
Comment 92 reads:
> Mats is working on a real fix.

What's going on??
With FF4 wrapping up, is there a chance we might see a fix land in a post-FF4 early build?
Whiteboard: [parity-webkit] [parity-IE] [parity-opera] [evang-wanted] → [parity-webkit][parity-IE][parity-opera][evang-wanted][wanted2.1?]
I've written a draft specification of text-overflow:ellipsis based on previous work, above comments, current implementation (claims) and incorporated it into a CSS3-UI editor's draft. I believe I've provided sufficient details for at least a rough implementation (including answers to many/most of RoC's spec questions above). Please feel free to send me feedback directly or leave it in comments here.

http://dev.w3.org/csswg/css3-ui/#text-overflow

I believe this specifies what is already interoperably implemented among WebKit, Opera, IE and thus will be taking it directly (back) to last call along with the rest of CSS3-UI.
Thank you for moving the definition from css3-text to css3-ui. The definition there is, however, very vague (often uses examples instead of specific, normative requirements) and does not address many of the concerns we have (such as behavior in the presence of bidi). What is your intention to address these concerns? Do you want to address these issues in the current spec? Or leave the definition vague and underspecified for CSS3 and make it more precise in CSS4?
“The ellipsis should be styled consistent with the text being ellipsed” doesn't specify the style when the ellipsed text has several styles, e.g. when “textABC” is ellipsed to “text…” and were it not for the insertion of the ellipsis A would be completely visible, B would be partially clipped, and C would not be visible, is the ellipsis given the style of A, B, C, or their common attributes?
@fantasai - I greatly expanded the definition in css3-ui as compared to css3-text, please take another look.

> The definition there is, however, very vague (often uses examples instead of specific, normative requirements) and does not address many of the concerns we have (such as behavior in the presence of bidi).

Actually it does address exactly those concerns, and does so consistent with working group resolutions.

Please provide either test cases that illustrate what specific cases you think are vague and/or restate your concerns in the form of specific questions (e.g. "What happens when ... ").

> Do you want to address these issues in the current spec? Or leave the
definition vague and underspecified for CSS3 and make it more precise in CSS4?

In short, any behavior detail that is already interoperably implemented I'm willing to include in CSS3-UI. Anything that isn't gets punted to CSS4-UI.


@steele - that's a good question. To make sure I understand precisely what case you mean, could you provide a test case with HTML+CSS that exemplifies your question?
Attached file testcase (color of ellipsis) (obsolete) (deleted) —
Ellipsis is always black in current implementations.
I suggest we move discussion of the spec details to www-style@w3.org where they belong, since this bug is about implementing it, not speccing it. Tantek, I will send my commentary there. steele has already pointed out one of the issues.
Blocks: 631042
No longer blocks: 569993
Whiteboard: [parity-webkit][parity-IE][parity-opera][evang-wanted][wanted2.1?] → [parity-webkit][parity-IE][parity-opera][evang-wanted][wanted2.1?][CSS IS AWE]SOME
Any updates?  I find hard to believe that FF4 a modern browser doesn't support a basic text-ellipsis.  Literally all browsers (yes even IE) and FF is the only one not doing so...

It'd be appreciated.
In my opinion this bug has to be solved immediatly since FF4 dropped XUL binding (https://bugzilla.mozilla.org/show_bug.cgi?id=546857).
Please implement it, perhaps like -moz-text-overflow for now if you have problems with missing specifications.
At this moment I have to suggest using Google Chrome - or maybe IE - Browsers, because this single missing feature in FF4 can't be workarounded any more. 
Using a js-function to render it manually would be a poor solution in a (very big) table for example.
I agree that if ellipses is vital for a very important application of yours Firefox is not the browser you should be using.

What I expect from the dev team is an implementation of ellipses that is consistent with what the other browsers do should be good enough. They should create test cases for every scenario they think is not well specified, check what the other browsers do and code those behaviors into FF. W3C might even appreciate those test cases and use them to refine their spec.

What I don't understand is how a bug like this one can be still open (actually: new) after 5 years and 6 months. I bet this part of CSS is not in any ACID test.
It is not vital for this application, but it looks ugly and all I can do is to add a known issue with "use another browser" or something like this as workaround description. 

I agree, a Bug opened for over 5 years is not acceptable! It seems that all the dev team can do is just discussing on standards and specifications.

An ugly solution would be still a solution!
Once again, folks, this is not a place to complain about how a bug isn't getting fixed fast enough. No one responds well to comments such as those.

The age of the bug has nothing to do with it. Obviously, people in the know have concerns with specifications and how to implement them. An ugly solution is not, in fact, a solution at all, and would not be a good way to go.

If you have anything useful to offer (patches, testcases, etc.), then feel free to contribute them. Otherwise, please take your unconstructive comments elsewhere.
(In reply to comment #117)
Sorry, I didn't want to bother someone, it's just hard for me to understand that this bug is not solved now since almost all other browsers out there have this feature, regardless if this was fully specified or not.
This may be useful to anyone who lands on the page and needs a solid workaround.

http://www.bramstein.com/projects/text-overflow/
I should look at that. For Firebug, we had to create out own crop function, though it is based on a fixed character count, not the width of a container like the CSS property does.
Sorry to keep it off topic but I've added it to https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills . Non-jQuery version would be nice too. :)
New ETA? Spec in shape? Twittersphere wants to know.

/be
Spec for text-overflow has been in sufficient shape to implement for some time:

http://dev.w3.org/csswg/css3-ui/#text-overflow

(status: that's an editor's draft update of what is currently a CR, so it'll go back for a quick last call and then back to CR)

Just looking at maybe adding a few figures for scrolling + text-overflow edgecases.

Please report any issues with the spec at www-style@w3.org (rather than this bug). Thanks. -t
The status is that I'm currently working on it.  The ETA is Firefox 6.
Target Milestone: --- → mozilla6
(In reply to comment #124)
> The status is that I'm currently working on it.  The ETA is Firefox 6.

FF6? Is that a joke?
Firefox 5 will come by the end of June, so I guess Firefox 6 will come by the end of the year.
Firefox 6 will be released 6 weeks after Firefox 5 which will be released around June 21st.
No...  The development cycle is 6 weeks but the release cycle is 18 weeks.  That puts Firefox 6 around late October.
Gandalf is correct - Firefox 6 is targeted for release 6 weeks after Firefox 5.

Aaron: there will be 18 weeks between when a branch splits off of mozilla central until that branch is released, but *not* 18 weeks *between releases*.  The plan is to start a new branch every 6 weeks and have a release ready every 6 weeks.

(Please let this be the last comment here about release cycles...? :))
Attached patch Proposed patch, part 1: Parsing. (obsolete) (deleted) — Splinter Review
In between Rust compiles I figured I'd take a stab at this. No guarantees I'll finish before Mats gets around to it, but I figured I'd post WIPs. Feedback would be much appreciated!
Attached patch part 1, style system (obsolete) (deleted) — Splinter Review
Thanks for trying to help, but I already have the style system part working.
Attachment #525811 - Attachment is obsolete: true
No longer depends on: 452667
Attached file Test 1, basic (deleted) —
Attached file Test 2, form elements (deleted) —
Blocks: 589132
Attachment #202163 - Attachment is obsolete: true
Attached file Test 5, simple mixed-bidi cases (deleted) —
Attached patch part 1, style system (obsolete) (deleted) — Splinter Review
text-overflow: clip | ellipsis | <string>
Attachment #529429 - Flags: review?(dbaron)
Move some text frame color stuff into nsLayoutUtils::GetTextColor()
Attachment #525909 - Attachment is obsolete: true
Attachment #529430 - Flags: review?(roc)
Attached patch part 3, nsDisplayText changes (obsolete) (deleted) — Splinter Review
Move nsDisplayText to nsDisplayList.h/cpp and add mLeftEdge/mRightEdge
members that suppress painting some characters during PaintText().
Attachment #529431 - Flags: review?(roc)
Attached patch part 4, layout changes (obsolete) (deleted) — Splinter Review
This implements both left and right markers.  Text is clipped at character
granularity at a marker. The marker itself is clipped at pixel granularity
with the other marker or at the block's edge.  The marker is placed at the
block's edge (as requested), using the block's color, font and background,
on the baseline of the line.  If the block's font doesn't have an ellipsis
character, three ASCII periods are used (I'm not doing any font checks for
text-overflow:<string>). Block-ish inlines, like images, inline-blocks and
replaced elements like <button> etc, are removed when they would be clipped
by a marker.  Relative positioned inlines are not counted as block-ish, they
are handled as any other inline - any text inside it is treated as text that
generate a marker when overflowing the containing block.  

Test builds:
http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/mpalmgren@mozilla.com-5001c34b024f/

BUGS

1. Characters in (non-trivial) mixed-bidi cases are dropped in the
wrong order.  I do handle RTL text that's clipped at the right edge
by dropping characters from the end of that text run.  (Which seems
to be what Opera and Google Chrome does too.)

2. Text in a <textarea> appears pixel-clipped by the marker.  AFAICT,
I'm calculating the number of characters that fit correctly, but it
seems the background hasn't been repainted by the block?

3. text-shadow/decorations for the marker text isn't implemented yet.

4. When text inside an inline-block overflows its inline-block and the
overflowing text clips a marker, only the text is hidden; not the
whole inline-block as intended.

5. The separate path for painting text in a selection hasn't been
updated yet.  Markers would be displayed etc, but for clipped RTL text
the wrong part of the text would be painted in some cases.


QUESTIONS

Should a marker affect ScrollIntoView or a caret in any way?
ScrollIntoView: scroll a bit extra to come clear of a marker?
if a caret moves in under a marker should the marker be inhibited?
Attachment #529432 - Flags: review?(roc)
Comment on attachment 529432 [details] [diff] [review]
part 4, layout changes

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

::: layout/generic/nsBlockFrame.cpp
@@ +101,5 @@
 static const int MIN_LINES_NEEDING_CURSOR = 20;
+static const PRUnichar kEllipsisChar[] = { 0x2026, 0x0 };
+static const nsDependentString kEllipsis(kEllipsisChar, 1);
+static const PRUnichar kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 };
+static const nsDependentString kASCIIPeriods(kASCIIPeriodsChar, 3);

I don't think we should have static nsDependentStrings, since they require destructors to run.

@@ +6209,5 @@
+  // scroll port area.
+  nsDisplayTextOverflowMarker(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
+                              const nsRect& aRect)
+    : nsDisplayItem(aBuilder, aFrame), mRect(aRect),
+      mString(kEllipsis), mAscent(nscoord_MAX) {

I don't really like this dummy item approach. I haven't figured out exactly why we need a dummy item yet, but if we do need one it should probably be its own display item type.

@@ +6261,5 @@
+  while (!cb->IsContainingBlock()) {
+    cb = cb->GetParent();
+  }
+  return cb;
+}

nsHTMLReflowState contains GetNearestContainingBlock, which is about the same as this. Please share them somehow.

@@ +6264,5 @@
+  return cb;
+}
+
+static bool
+IsClusteredItem(nsDisplayItem* aItem, nsIFrame* aBlock)

It's not really clear what IsClusteredItem means.

@@ +6650,5 @@
 
+  // Setup marker and clip rect for text-overflow processing.
+  nsAutoPtr<TextOverflowData> textOverflowData;
+  bool hasTextOverflow =
+    GetStyleTextReset()->mTextOverflow.mType != NS_STYLE_TEXT_OVERFLOW_CLIP &&

I think you might need to check the style of the primary frame for the block's content. E.g. if you have a table cell <td style="text-overflow:ellipsis">, the block will not have the text-overflow style set since it's not inherited. You worked around this for column-set frames with 'text-overflow:inherit' in ua.css, but I think it's probably better to work around it for all wrapped block frames here.

@@ +6653,5 @@
+  bool hasTextOverflow =
+    GetStyleTextReset()->mTextOverflow.mType != NS_STYLE_TEXT_OVERFLOW_CLIP &&
+    ::HasHiddenHorizontalOverflow(this) &&
+    !aBuilder->IsForEventDelivery();
+  if (hasTextOverflow) {

Please move as much of this setup code as you can to a separate method.

I also think TextOverflowData should probably move to a separate file if we can do that, to keep nsBlockFrame from growing much bigger.

::: layout/generic/nsFrame.cpp
@@ +1585,5 @@
+  nsIFrame* cb = aFrame;
+  do {
+    cb = cb->GetParent();
+  } while (!cb->IsContainingBlock());
+  return cb;

Please use the copy you're adding earlier in this patch!

@@ +1685,5 @@
+        }
+        // It's a text or inline frame in a 'text-overflow' containing block.
+        // Fall through to generate a display item (it'll be removed later
+        // but we need it to know if there is displayable text beyond an
+        // image, inline-block, etc that is on the block's clip edge).

Can you explain this situation further?

Are we dealing with a situation where, for example, there's an image on the line which extends beyond the clip edge, followed by text, and you want to drop the image but show a marker?

In that situation I think we probably shouldn't show a marker. I think we should only show a marker when an nsDisplayText intersects the clip edge. If we do it that way, you don't need this code.

::: layout/generic/nsTextFrameThebes.cpp
@@ +4987,5 @@
+  gfxPoint textBaselinePt(rtl ? gfxFloat(aPt.x + frameWidth) : framePt.x,
+                          GetSnappedBaselineY(ctx, aPt.y));
+  PRUint32 startOffset = provider.GetStart().GetSkippedOffset();
+  PRUint32 maxLength = ComputeTransformedLength(provider);
+  if (aLeftEdge > 0 || aRightEdge != 0) {

I'd like to move most of this code out to TextOverflowData to avoid complicating PaintText. Maybe PaintText could take an optional TextOverflowData*, and if that's not null then we call a method on the TextOverflowData to adjust our baseline point and character offset/length? Then nsDisplayText would have an mTextOverflowData pointer which is usually null but can be set when TextOverflowData processes the line.
Attachment #529449 - Attachment description: Test 5, simple color font decorations shadow background test → Test 6, simple color font decorations shadow background test
(In reply to comment #140)
> This implements both left and right markers.

Other browsers only display markers at one side of the block, determined by the block direction. Do you feel strongly that markers at both ends are desirable?

Allowing markers in both direction is OK by me, and would be nice for scrolling, but the "string" value would need to be extend to specify separate strings for "left" and "right" (e.g. you might want "<<<" and ">>>" or something like that), or else we would have to automatically mirror the string (see gfxUnicodeProperties::GetMirroredChar).

> The marker is placed at the block's edge (as requested),

As discussed in email, I think we should not align the marker with the block edge, but place it at the end of the text instead, as other browsers do.

> 1. Characters in (non-trivial) mixed-bidi cases are dropped in the
> wrong order.  I do handle RTL text that's clipped at the right edge
> by dropping characters from the end of that text run.  (Which seems
> to be what Opera and Google Chrome does too.)

Because all the other browsers have serious bugs that show they're not really moving elements around on the line, I think we should go with purely visual clipping: dropping text clusters beyond the clip edge/ellipsis edge.

> 2. Text in a <textarea> appears pixel-clipped by the marker.  AFAICT,
> I'm calculating the number of characters that fit correctly, but it
> seems the background hasn't been repainted by the block?

Not sure what you mean.

> 3. text-shadow/decorations for the marker text isn't implemented yet.

Other browsers don't apply text-decorations, which makes sense to me. Opera and Chrome do apply text-shadow, so we should too.

> 4. When text inside an inline-block overflows its inline-block and the
> overflowing text clips a marker, only the text is hidden; not the
> whole inline-block as intended.

Chrome and Opera don't seem to drop inline-blocks. Instead, they don't show an ellipsis if it would intersect an inline-block. IE does hide some inline-blocks sometimes... but not consistently :-(. This sucks!

Hmm. I think we actually shouldn't hide "large" inline-blocks, replaced elements, or images. Maybe it's better to never hide content other than text. We definitely don't want the marker to overlap content, though. In that case, we could use this policy:
-- Show a marker for the line if the line has text beyond the clipping edge of the block, AND the line has no CSS boxes, other than text and inline boxes, whose border-box would horizontally intersect the marker. Note that if we do it this way we would have to scan the frame tree, not the display item tree ... I think elements which don't paint anything (e.g. they have a width and height but no border, background or rendered children) should still affect whether a marker is displayed. I'm not 100% sure about that though.
-- When we show a marker, remove all glyph clusters whose trailing x-advance is beyond the leading x-origin of the marker if the trailing edge of the marker was aligned with the block clipping edge.
-- Then position the marker at the x-origin of the leading removed glyph cluster before the clip edge, or if there isn't one, align the trailing edge of the marker with the block clipping edge.

> Should a marker affect ScrollIntoView or a caret in any way?

I think we should not show the marker if the caret or the selection intersects the marker. (I.e. include a check for the caret or selection in the first step above.) But getting the invalidation for this right could be tricky; it will be easier when we have display-list-based invalidation. Maybe we should defer this to a followup patch.

> ScrollIntoView: scroll a bit extra to come clear of a marker?

Yes, I think for usability we should bring the content into further into view by the width of the marker. This shouldn't be too hard to do, but it should probably be a separate patch.
This probably isn't the right place for this, but I don't know where else to post this. Apologies in advance.

Will it be possible to explicitly style the ellipsis using CSS? Example:

#mydiv {overflow: ellipsis;}

#mydiv:-moz-ellipsis {content: "....."; color: green;}

If so, it could help resolve browser parity issues and standards ambiguities with regards to styling (text-shadow, text-decoration, textual content, positioning, etc...) by enabling web developers to provide their own desired style overrides. Reasonable defaults would still need to be provided, obviously.

My suggestion above just uses a -moz-prefixed pseudo-selector for the ellipsis, which would basically work similarly to the ":after" pseudo-selector. I think such an extension to the standards would be appropriate, non-invasive and quite powerful.
Attachment #506179 - Attachment is obsolete: true
Comment on attachment 529429 [details] [diff] [review]
part 1, style system

I think you probably want 'text-overflow: inherit' everywhere in
ua.css and forms.css that we have unicode-bidi: inherit -- which means
you're missing 2 places in ua.css.  (We should probably reorganize
that at some point, but do NOT do it in this patch.)

In nsRuleNode.cpp, please set text->mTextOverflow.mString in all
three branches, not just the third:  in the first two, call
text->mTextOverflow.mString.Truncate();

r=dbaron with those changes
Attachment #529429 - Flags: review?(dbaron) → review+
However, I'm not convinced that NS_STYLE_HINT_VISUAL is sufficient -- shouldn't NS_STYLE_HINT_REFLOW be required?  Or does our implementation differ from the spec enough (why?) that it isn't?
(In reply to comment #144)
> #mydiv:-moz-ellipsis {content: "....."; color: green;}

That's harder to implement, so I wouldn't bother unless there's a real need for it.

(In reply to comment #146)
> However, I'm not convinced that NS_STYLE_HINT_VISUAL is sufficient -- shouldn't
> NS_STYLE_HINT_REFLOW be required?  Or does our implementation differ from the
> spec enough (why?) that it isn't?

I don't think reflow is required. Our design here hinges on making the presence or absence of a marker not affect layout (or overflow).
(In reply to comment #141)
> +        // Fall through to generate a display item (it'll be removed later
> +        // but we need it to know if there is displayable text beyond an
> +        // image, inline-block, etc that is on the block's clip edge).
> 
> Are we dealing with a situation where, for example, there's an image on the
> line which extends beyond the clip edge, followed by text, and you want
> to drop the image but show a marker?

Yes, this code normally optimizes that situation by not creating an item.
I need the item to see if it's an "image at the end of the line" (no marker)
or "image at the end of the line followed by text" (marker).

> In that situation I think we probably shouldn't show a marker. I think we
> should only show a marker when an nsDisplayText intersects the clip edge.

See below for my proposal...


(In reply to comment #143)
> Other browsers only display markers at one side of the block...
> Do you feel strongly that markers at both ends are desirable?

Personally, I can see the value in having two markers, especially in
scroll views.  This is a spec issue though.  I don't feel strongly
whether the single value form should generate both markers, but I do
think the spec should have support for two markers in some form.

> or else we would have to automatically mirror the string
> (see gfxUnicodeProperties::GetMirroredChar).

I haven't tried it, but it doesn't seem very attractive to me.
Separate left/right properties would be much better in that case.
I'll look into how much work that would be.

> > 2. Text in a <textarea> appears pixel-clipped by the marker.  AFAICT,
> > I'm calculating the number of characters that fit correctly, but it
> > seems the background hasn't been repainted by the block?
> 
> Not sure what you mean.

See the <textarea> examples in "Test 2, form elements" using a test build.

> Other browsers don't apply text-decorations, which makes sense to me.

OK

> Opera and Chrome do apply text-shadow, so we should too.

Definitely, but we shouldn't block on that, IMO.

> Hmm. I think we actually shouldn't hide "large" inline-blocks, replaced
> elements, or images.

How about if we clip them normally (and not show a marker) if their visible
width is larger than the width of the marker?  That will look nice when
scrolling - as the inline-block is scrolled out of view it's removed when
the ellipsis would "connect" with the text before it; this avoids having
the marker hide text when it suddenly is shown again after the inline-block
disappears out of view.  It would also completely suppress "small" items
as they go over the clip edge in favor of showing a marker.

That means I would still need the text items that are otherwise not
generated.  I don't think that's big deal though, since it's limited to
text-overflow cases and I'm removing those items later.
> Are we dealing with a situation where, for example, there's an image on the
> line which extends beyond the clip edge, followed by text, and you want
> to drop the image but show a marker?

Actually, it's not just the example I mentioned above.  We need those items
for basic correctness (knowing if there is text outside the clip edge).
(In reply to comment #148)
> How about if we clip them normally (and not show a marker) if their visible
> width is larger than the width of the marker?  That will look nice when
> scrolling - as the inline-block is scrolled out of view it's removed when
> the ellipsis would "connect" with the text before it; this avoids having
> the marker hide text when it suddenly is shown again after the inline-block
> disappears out of view.  It would also completely suppress "small" items
> as they go over the clip edge in favor of showing a marker.

It might be a little bit better but it adds complexity for not much gain IMHO. Not many replaced elements and inline-blocks will be narrower than the ellipsis.
Attached file Test 6, marker alignment tests (deleted) —
I have now implemented a text-aligned marker model to match the spec
and other UAs, instead of the block-aligned model in the first build.

The clipping behavior is now purely visual clipping.

I have now implemented whole-character clipping for text-decorations
and text-shadows (which was missing in the last build).

I changed the behavior to match the spec for the case where two
markers are needed but only one fits.

I'm not removing atomic inlines to make room for a marker as I did
earlier.  Except in one case: when an atomic inline is clipped on
the block's edge, if the *visible* part of it inside the view is less
than the marker width, then remove it.  This looks better visually
when scrolling over a line with mixed text and atomic elements -
as they "go out of view" the effect is that the marker shows up
before it would clip any text on the other side of the atomic element.

The marker is [pixel] clipped when there is insufficient space to
render it fully.  I clip the right marker on the right side (i.e. at
the right block edge), and the left marker on the left side.
FWIW, I actually implemented the opposite as well and compared them.
Clipping on the "outside" looks better when scrolling; as you scroll
and the marker bumps into an atomic element it "stops" there and that
feels like the marker "belongs with the text" as it goes out of view.
Clipping on the inside gave me the feeling that the marker "slides
under" the atomic element which felt weird.

I'm now inhibiting the markers when there's a caret in the block,
i.e. the behavior is text-overflow:clip.

BUGS

1. Text in a <textarea> appears pixel-clipped by the marker.
   (as described in comment 140)

2. text-shadow for the marker text isn't implemented yet.

3. Text shows up under the marker when there's a selection.
   With visual clipping this feels like less of a problem though
   (feature?) :-) since it doesn't move glyphs around as it did
   with RTL text in the last build.
   (hmm, is this what causes the <textarea> problem? does it take the
    selection paint path always?)


Builds for testing are available:
http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/mpalmgren@mozilla.com-0f9891448a5d/
Attached patch part 1, style system (obsolete) (deleted) — Splinter Review
I removed all the 'text-overflow: inherit' I added in the last patch
and use the style of the primary frame instead, as roc suggested.
See TextOverflow::GetTextOverflowStyleFor() in the layout part for
details.

> In nsRuleNode.cpp, please set text->mTextOverflow.mString in all
> three branches, not just the third:  in the first two, call
> text->mTextOverflow.mString.Truncate();

Thanks!  I fixed the first branch as you suggested.  The 'inherit'
branch should be OK as is I think:

+    text->mTextOverflow = parentText->mTextOverflow;

as that will do memberwise assignment and thus copy the string from
'parentText', and that's what we want right?
Attachment #529429 - Attachment is obsolete: true
Attachment #530811 - Flags: review?(dbaron)
Attached patch part 3, layout changes (obsolete) (deleted) — Splinter Review
I've add new files layout/generic/TextOverflow.h/cpp for the
bulk of the code that was in nsBlockFrame.

I moved the PaintText stuff into a separate method
nsTextFrame::MeasureCharClippedText()
I discovered one problem in it - when applying the char-snapping
on edges that are already snapped the result should be the same.
But BreakAndMeasureText returns an advance that doesn't seem to 
fulfill that.  I doesn't appear to be a rounding problem, but I'm
not sure.  I added an XXX comment in the code (with wallpaper):
  // XXX we need to ensure that the snapped values we return can be used
  // as input values and give the same result.  Subtracting 1 app unit seems
  // to fix it altough I was expecting that floor should be enough(?)
  // I tried TIGHT_INK_EXTENTS but that didn't make a difference...
  snappedStartEdge = NSToCoordFloor(advanceWidth) - 1;

I added a new display item nsDisplayVaryingRelativeToMovingFrame
for the dummy item needed in scroll frames.
I think I've addressed your other comments as well.
Attachment #529431 - Attachment is obsolete: true
Attachment #529432 - Attachment is obsolete: true
Attachment #529449 - Attachment is obsolete: true
Attachment #530813 - Flags: review?(roc)
Attachment #529431 - Flags: review?(roc)
Attachment #529432 - Flags: review?(roc)
(In reply to comment #153)
> I removed all the 'text-overflow: inherit' I added in the last patch
> and use the style of the primary frame instead, as roc suggested.
> See TextOverflow::GetTextOverflowStyleFor() in the layout part for
> details.

Why?  It seems quite unusual to do it that way.

> Thanks!  I fixed the first branch as you suggested.  The 'inherit'
> branch should be OK as is I think:
> 
> +    text->mTextOverflow = parentText->mTextOverflow;
> 
> as that will do memberwise assignment and thus copy the string from
> 'parentText', and that's what we want right?

yes
This adds support for having separate left/right text-overflow behavior.
It adds text-overflow-left/right properties and a two-value form:
text-overflow: <left-value> <right-value>
Attachment #530898 - Flags: review?(dbaron)
Separate left/right values was mostly prepared for already, but there
was one problem:  when one side has 'clip' we want that side to clip
normally by pixel, not clip whole characters.
I solved that by pretending the clip edge is at nscoord_MAX.
I'll look into if there's a less hacky way to solve this.
Builds that support text-overflow-left/right are available here:
http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/mpalmgren@mozilla.com-6760dd3d7115/
Attached file Test 8, jittery scrolling (deleted) —
Aligning the marker with the text looks jittery while scrolling, especially
when many lines have markers.  This testcase illustrates the problem.
One possible solution would be to align at the block edge while scrolling,
then align with the end of the text after a small delay.
This patch implements this proposal and it looks much smoother while
scrolling.  The delay after the scrolling stops is still too long in this
build though (3-4 seconds)...

Builds:
http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/mpalmgren@mozilla.com-7e5f03ee8393/
This patch fixes the delay problem by setting a shorter timeout (300-400ms)
in the expiration tracker in nsGfxScrollFrame when there's a possible
text-overflow marker in this scroll frame.  When I grab the scroll thumb
and scroll a bit and then release the button the marker snaps to the text
just about as I release the button (on a Linux desktop PC).

I would appreciate feedback on whether:
  A. scrolling feels smooth in Test 8
  B. aligning the marker to the text after scrolling stops
     feels fast enough

Builds:
http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/mpalmgren@mozilla.com-e6724ff7cb71/
(still building, should be ready in an hour or so)
(In reply to comment #155)
> Why?  It seems quite unusual to do it that way.

See roc's comment 141.

I agree that GetTextOverflowStyleFor() feels a bit hacky, and likely slower.
You should probably do what dbaron said in comment #145.
Comment on attachment 529430 [details] [diff] [review]
part 2, nsLayoutUtils::GetTextColor()

Review of attachment 529430 [details] [diff] [review]:
-----------------------------------------------------------------
Attachment #529430 - Flags: review?(roc) → review+
I think you should use namespace "css" instead of "layout".

Need comments for the methods of TextOverflow and the new methods of nsTextFrame. Also for the class nsCharClipDisplayItem.

Since we're still trying to figure out the behavior for some cases, let's split up the layout patch a bit more. The new nsDisplayList methods can go in one patch, another patch can have nsCharClipDisplayItem and the changes to existing display items that use it, another patch can have the nsTextFrameThebes changes to support clipping edges, another patch can add TextOverflow, and another patch can add the hooks from nsBlockFrame that use TextOverflow. One advantage of doing that is that if we need to disable text-overflow, we can just back out that last patch.
OK, will do.  FYI, there are a couple of bugs in the layout patch when
clipping decorations/shadows.  We should use GetVisualOverflowRect() when
analyzing and nsCharClipDisplayItem::Intersect is just plain wrong.
I'm looking into that, new patches coming soon.
Mats, I really don't think you should be implementing separate -left/-right properties. I can't imagine a reason for them to cascade independently. (And "left"/"right" doesn't make any sense for vertical text.)
Attachment #530898 - Attachment is obsolete: true
Attachment #530898 - Flags: review?(dbaron)
Attached file Test 9, Quirks mode test (deleted) —
Attached patch part 1, style system (deleted) — Splinter Review
This should address your comments regarding text-overflow:inherit
and the missing Truncate().
Attachment #530811 - Attachment is obsolete: true
Attachment #531423 - Flags: review?(dbaron)
Attachment #530811 - Flags: review?(dbaron)
This adds syntax to specify separate left and right values:
text-overflow: <left-value> <right-value>
where values can be clip|ellipsis|<string> but not inherit or initial.

FYI, this fails one of the style system mochitests:
layout/style/test/test_compute_data_with_start_struct.html

I haven't figured what the problem is yet; I'd appreciate if you could
take a quick look and tell me if you see any obvious problems.  Thanks.

It works for the most part though, for example with the testcases
attached on this bug.

(In reply to comment #167)
> I can't imagine a reason for them to cascade independently.

Good point, I removed the separate properties, but I'm keeping the
two-value form above.
Attachment #531429 - Flags: review?(dbaron)
Attached patch part 3, add nsDisplayList::Remove[All] (obsolete) (deleted) — Splinter Review
Attachment #530813 - Attachment is obsolete: true
Attachment #530899 - Attachment is obsolete: true
Attachment #530945 - Attachment is obsolete: true
Attachment #530946 - Attachment is obsolete: true
Attachment #531430 - Flags: review?(roc)
Attachment #530813 - Flags: review?(roc)
Attached patch part 4, add nsCharClipDisplayItem (obsolete) (deleted) — Splinter Review
Attachment #531435 - Flags: review?(roc)
Attachment #531440 - Flags: review?(roc)
Attached patch part 7, add mozilla::css::TextOverflow (obsolete) (deleted) — Splinter Review
Attachment #531442 - Flags: review?(roc)
I fixed the bugs with text-decoration/shadow in previous builds.
I found that quirks mode has it's own path for doing decorations
so I fixed that too.
I'm now always block-aligning the markers in scrolled views to
fix the problem in "Test 8".  Tracking active scrolling and snapping
to the line end when it stops doesn't seem worth the complexity.
Dropped the separate -left/-right properties, but the two-value
form is still supported.
I think I've addressed all the review comments so far.

Builds for testing (some platforms still pending):
http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/mpalmgren@mozilla.com-ced57a63dff1/

Regarding rel.pos. inlines/transforms - no change in this round,
transformed items are ignored (mostly by accident actually) and
rel.pos. inlines are treated like any other inline - at its *visual*
position.  I'm going to make a try server build which also ignores
rel.pos. inlines completely so you can try that out.  I've attached
"Test 10" for this purpose.  (BTW, the code change is trivial)
Comment on attachment 531430 [details] [diff] [review]
part 3, add nsDisplayList::Remove[All]

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

::: layout/base/nsDisplayList.h
@@ +960,5 @@
> +   * it is updated to point to the first item above its current value if
> +   * that item is removed.  If all items above it are removed it is set
> +   * to null.
> +   */
> +  void RemoveAll(nsIFrame* aFrame, nsDisplayList* aList, nsDisplayItem** aIterator);

Call it RemoveAllItemsForFrameSubtree?

For aIterator, I think you need to say "if *aIterator is removed, aIterator is updated to point to the next item that is still in the list, or null if there is no such item".
Attachment #531430 - Flags: review?(roc) → review+
Comment on attachment 531435 [details] [diff] [review]
part 4, add nsCharClipDisplayItem

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

::: layout/base/nsDisplayList.h
@@ +2199,5 @@
>  private:
>    nsDisplayWrapList mStoredList;
>  };
>  
> +class nsCharClipDisplayItem : public nsDisplayItem {

Need a comment explaining what this class is for.

@@ +2211,5 @@
> +  // The default value is 0,0 (paint all text).
> +  nscoord mLeftEdge;  // length from the left side of the frame
> +  nscoord mRightEdge; // length from the right side of the frame
> +
> +  void Intersect(const nscoord aFrameX, const nscoord aFrameWidth,

don't need const here

@@ +2222,5 @@
> +    aX = NS_MAX(aX, aFrameX + mLeftEdge);
> +    aWidth = NS_MAX(NS_MIN(xmost1, xmost2) - aX, 0);
> +  }
> +
> +  static bool InstanceOf(nsDisplayItem* aItem) {

How about making this
  static nsCharClipDisplayItem* CheckCast(nsDisplayItem* aItem)?
Seems like callers are going to want that cast.
Comment on attachment 531435 [details] [diff] [review]
part 4, add nsCharClipDisplayItem

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

::: layout/base/nsDisplayList.h
@@ +2212,5 @@
> +  nscoord mLeftEdge;  // length from the left side of the frame
> +  nscoord mRightEdge; // length from the right side of the frame
> +
> +  void Intersect(const nscoord aFrameX, const nscoord aFrameWidth,
> +                 nscoord& aX, nscoord& aWidth) {

I think aX and aWidth should be nscoord* so that in callers it's clear that these are out parameters.
Comment on attachment 531438 [details] [diff] [review]
part 5, do whole-character clipping between nsCharClipDisplayItem edges

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

::: layout/generic/nsBlockFrame.cpp
@@ +6093,5 @@
>    nscoord width = aLine->mBounds.width;
>  
>    AdjustForTextIndent(aLine, start, width);
> +  nscoord x = start + aPt.x + aItem.mLeftEdge;
> +  width -= aItem.mRightEdge + aItem.mLeftEdge;

This could go negative. Why not use aItem.Intersect() here?

::: layout/generic/nsHTMLContainerFrame.cpp
@@ +455,2 @@
>    nscoord innerWidth = mRect.width - bp.left - bp.right;
> +  aItem.Intersect(aPt.x, GetScrollableOverflowRect().width, x, innerWidth);

Why GetScrollableOverflowRect().width here to set the right edge?

I guess it's not clear in part 4 what "length from the right side of the frame" means for mRightEdge, or for that matter what the left side of the frame actually is.

I think the right thing to do might be to make mLeftEdge and mRightEdge actually relative to the display list reference frame.
Fixed the bug that caused the test failure.
Attachment #531429 - Attachment is obsolete: true
Attachment #531722 - Flags: review?(dbaron)
Attachment #531429 - Flags: review?(dbaron)
Fixed per your comments.
Attachment #531430 - Attachment is obsolete: true
Attachment #532058 - Flags: review?(roc)
Attached patch part 4, add nsCharClipDisplayItem (obsolete) (deleted) — Splinter Review
Fixed per your comments.
Attachment #531435 - Attachment is obsolete: true
Attachment #532060 - Flags: review?(roc)
Attachment #531435 - Flags: review?(roc)
Now using Intersect() also in nsBlockFrame.

The edges are in relation to GetScrollableOverflowRect(), that is
zero to GetScrollableOverflowRect().width.
I've updated the comment for the nsCharClipDisplayItem class to
make that clear, and using Intersect() consistently.
Attachment #531438 - Attachment is obsolete: true
Attachment #532062 - Flags: review?(roc)
Attachment #531438 - Flags: review?(roc)
Attached patch part 7, add mozilla::css::TextOverflow (obsolete) (deleted) — Splinter Review
Attachment #531442 - Attachment is obsolete: true
Attachment #532064 - Flags: review?(roc)
Attachment #531442 - Flags: review?(roc)
Comment on attachment 532058 [details] [diff] [review]
part 3, add nsDisplayList::Remove[AllItemsForFrameSubtree]

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

::: layout/base/nsDisplayList.cpp
@@ +635,5 @@
>    return item;
>  }
>  
> +void nsDisplayList::Remove(nsDisplayItem* aItem) {
> +  NS_PRECONDITION(aItem, "");

Needs a string

@@ +655,5 @@
> +                                             nsDisplayList* aList,
> +                                             nsDisplayItem** aIterator)
> +{
> +  NS_PRECONDITION(aFrame, "");
> +  NS_PRECONDITION(aList, "");

Need strings
Attachment #532058 - Flags: review?(roc) → review+
Comment on attachment 532060 [details] [diff] [review]
part 4, add nsCharClipDisplayItem

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

::: layout/base/nsDisplayList.h
@@ +2211,5 @@
> + * amount to suppress on each side.
> + *
> + * Setting none, both or only one edge is allowed.
> + * The values must be in the [0 .. width] range, where 'width' is the item's
> + * underlaying frame's GetScrollableOverflowRect().width.

I think you should explicitly say that the edge values are relative to the left and right edges of the frame's scrollable overflow rectangle.
Attachment #532060 - Flags: review?(roc) → review+
Comment on attachment 532060 [details] [diff] [review]
part 4, add nsCharClipDisplayItem

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

::: layout/base/nsDisplayList.h
@@ +2220,5 @@
> +  nsCharClipDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
> +    : nsDisplayItem(aBuilder, aFrame), mLeftEdge(0), mRightEdge(0) {}
> +
> +  void Intersect(nscoord aFrameX, nscoord aFrameWidth,
> +                 nscoord* aX, nscoord* aWidth) {

Actually I think it would make more sense to not have the aFrameX and aFrameWidth parameters. Instead aX and aWidth should be required to be relative to the frame's origin, and then you can get the scrollable overflow rectangle for the frame, inset by the edge amounts, and do the clipping.
Attachment #532060 - Flags: review+ → review-
Comment on attachment 531440 [details] [diff] [review]
part 6, add nsDisplayVaryingRelativeToMovingFrameItem

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

::: layout/base/nsDisplayList.cpp
@@ +2817,5 @@
> +
> +void nsDisplayVaryingRelativeToMovingFrameItem::Paint(nsDisplayListBuilder* aBuilder,
> +                                                  nsRenderingContext* aCtx) {
> +  // does not paint anything
> +}

You don't need to override HitTest and Paint.

::: layout/base/nsDisplayList.h
@@ +2230,5 @@
>    }
>  };
>  
> +
> +class nsDisplayVaryingRelativeToMovingFrameItem : public nsDisplayItem

I think it should be called nsDisplayForcePaintOnScroll.
Comment on attachment 531443 [details] [diff] [review]
part 8, integrate text-overflow processing into the rendering process

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

::: layout/generic/nsFrame.cpp
@@ +1707,5 @@
>      // to descend into it because its scrolled child may intersect the dirty
>      // area even if the scrollframe itself doesn't.
>      if (aChild != aBuilder->GetIgnoreScrollFrame()) {
>        nsRect childDirty;
> +      if (!childDirty.IntersectRect(dirty, aChild->GetVisualOverflowRect())) {

This is really horrible. Instead of doing it, can we just check the line's scrollable overflow area to see if it extends beyond the block edge?
Comment on attachment 532064 [details] [diff] [review]
part 7, add mozilla::css::TextOverflow

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

::: layout/generic/TextOverflow.cpp
@@ +58,5 @@
> +static nsIFrame*
> +FindContainingBlock(nsIFrame* aFrame)
> +{
> +  return nsLayoutUtils::GetAsBlock(aFrame) ? aFrame :
> +         nsLayoutUtils::FindNearestBlockAncestor(aFrame);

A frame can't be its own containing block, so you need to rename this function.

@@ +143,5 @@
> +      nsCOMPtr<nsIContent> content = do_QueryInterface(node);
> +      if (content && nsContentUtils::ContentIsDescendantOf(content,
> +                       aBlockFrame->GetContent())) {
> +        return false;
> +      }

So this means when the caret enters or leaves a block with text-overflow we need to invalidate the block. You don't seem to be doing that.

@@ +168,5 @@
> +  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) {
> +    return mRect;
> +  }
> +  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
> +                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {

You don't need to override this.

@@ +206,5 @@
> +  nsRefPtr<nsRenderingContext> rc =
> +    aBlockFrame->PresContext()->PresShell()->GetReferenceRenderingContext();
> +  nsLayoutUtils::SetFontFromStyle(rc, aBlockFrame->GetStyleContext());
> +
> +  nsAutoPtr<TextOverflow> textOverflow(new (std::nothrow) TextOverflow);

I don't think you need the nothrow here

@@ +235,5 @@
> +    style->mTextOverflow.mRight.mType == NS_STYLE_TEXT_OVERFLOW_ELLIPSIS ?
> +      ellipsis : style->mTextOverflow.mRight.mString;
> +
> +  textOverflow->mClipRect =
> +    nsRect(aBuilder->ToReferenceFrame(aBlockFrame), aBlockFrame->GetSize());

Hmm, check how text-overflow interacts with padding on the block.

@@ +241,5 @@
> +    nsLayoutUtils::GetScrollableFrameFor(aBlockFrame);
> +  textOverflow->mBlockIsScrolled = false;
> +  if (scroll) {
> +    nsMargin scrollbars = scroll->GetActualScrollbarSizes();
> +    textOverflow->mBlockIsScrolled = scrollbars.TopBottom() != 0;

OK, so you position the ellipsis at the text or at the block edge based on whether a horizontal scrollbar is showing? Why not just check the style, that would be simpler?
Attached file Test 11, block padding (obsolete) (deleted) —
Added assertion text.
Attachment #532058 - Attachment is obsolete: true
Attachment #532469 - Flags: review?(roc)
Attached patch part 4, add nsCharClipDisplayItem (obsolete) (deleted) — Splinter Review
Updated Intersect() params per your comments.
Attachment #532060 - Attachment is obsolete: true
Attachment #532470 - Flags: review?(roc)
Updated the calls to Intersect() accordingly.
Attachment #532062 - Attachment is obsolete: true
Attachment #532471 - Flags: review?(roc)
Attachment #532062 - Flags: review?(roc)
Attached patch part 6, add nsDisplayForcePaintOnScroll (obsolete) (deleted) — Splinter Review
Attachment #531440 - Attachment is obsolete: true
Attachment #532472 - Flags: review?(roc)
Attachment #531440 - Flags: review?(roc)
Attached patch part 7, add mozilla::css::TextOverflow (obsolete) (deleted) — Splinter Review
> FindContainingBlock ... you need to rename this function

Renamed it to GetSelfOrNearestBlock.
Removed the unnecessary Paint/HitTest methods.

> Hmm, check how text-overflow interacts with padding on the block.

Good catch.  I've changed this to use the content rect, which seems
to be what other UAs are doing.

> Why not just check the style, that would be simpler?

I didn't want to second guess what the scroll frame does, which is more
than just looking at the style, not sure what the difference is in
practice though.  I can change it to just do a simple style check if
you prefer.

Added TextOverflow::CanHaveTextOverflow() that also checks that there is
at least one line that overflows the content rect.

Explicitly clipping the markers, which is needed when the block has padding.
(see Test 11)
Attachment #532064 - Attachment is obsolete: true
Attachment #532473 - Flags: review?(roc)
Attachment #532064 - Flags: review?(roc)
> ... can we just check the line's
> scrollable overflow area to see if it extends beyond the block edge?

Using TextOverflow::CanHaveTextOverflow()
Attachment #531443 - Attachment is obsolete: true
Attachment #532474 - Flags: review?(roc)
Attachment #531443 - Flags: review?(roc)
> So this means when the caret enters or leaves a block with text-overflow
> we need to invalidate the block. You don't seem to be doing that.

This patch does that.
Attachment #532475 - Flags: review?(roc)
Attachment #532473 - Flags: review?(roc)
Attached patch part 7, add mozilla::css::TextOverflow (obsolete) (deleted) — Splinter Review
Regarding the issue with atomic inlines that would overlap the marker.
This is the version that implements what the CSS spec says - hide them.

Test builds:
http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/mpalmgren@mozilla.com-2a22d47a4918/
Attachment #532473 - Attachment is obsolete: true
Attachment #532527 - Flags: review?(roc)
I tried to #ifdef the whole thing, but it seems having a 'textOverflow'
attribute in nsIDOMCSS2Properties.idl requires that there is a
corresponding property defined in nsCSSPropList.h:

js/src/xpconnect/src/dom_quickstubs.cpp: In function 'JSBool nsIDOMCSS2Properties_GetTextOverflow(JSContext*, JSObject*, jsid, jsval*)':
js/src/xpconnect/src/dom_quickstubs.cpp:11436: error: 'QS_CSS_PROP_TextOverflow' was not declared in this scope

dbaron: how did you get around that for MozAnimationName etc?
Comment on attachment 532469 [details] [diff] [review]
part 3, add nsDisplayList::Remove[AllItemsForFrameSubtree]

Review of attachment 532469 [details] [diff] [review]:
-----------------------------------------------------------------
Attachment #532469 - Flags: review?(roc) → review+
Comment on attachment 532470 [details] [diff] [review]
part 4, add nsCharClipDisplayItem

Review of attachment 532470 [details] [diff] [review]:
-----------------------------------------------------------------
Attachment #532470 - Flags: review?(roc) → review+
I wonder whether we should just specify x/width or startX/endX coordinates instead of using left/right edges.. It seems to me it might lead to simpler code. What do you think?
I don't see any advantage with either of those, I think the current
edges fits quite well with what MeasureCharClippedText() wants
(available text advance width) and the analysis in TextOverflow
which deals with comparing overlaps.  Intersect() would be simpler
but that's just a few lines of code.
Comment on attachment 532471 [details] [diff] [review]
part 5, do whole-character clipping between nsCharClipDisplayItem edges

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

::: layout/generic/nsBlockFrame.cpp
@@ +6078,5 @@
>                                        PRUint8 aStyle,
>                                        gfxFloat aOffset, 
>                                        gfxFloat aAscent, 
>                                        gfxFloat aSize,
> +                                      nsCharClipDisplayItem& aItem,

const nsCharClipDisplayItem&

::: layout/generic/nsHTMLContainerFrame.h
@@ +172,5 @@
>     *                                text-decoration should be drawn,
>     *                                i.e. negative offsets draws *below*
>     *                                the baseline.
>     *    @param aSize              the thickness of the line
> +   *    @param aItem              clip edges

"clip edges based on this display item"

::: layout/generic/nsTextFrame.h
@@ +275,5 @@
>  
> +  /**
> +   * Calculate the length to the next grapheme cluster inside the given
> +   * left/right edges (which are positive lengths from the respective
> +   * frame edge).  If an input value is zero it is ignored and the result

I think it would be better to say
"Calculate the horizontal bounds of the grapheme clusters that fit entirely inside the given range".

@@ +278,5 @@
> +   * left/right edges (which are positive lengths from the respective
> +   * frame edge).  If an input value is zero it is ignored and the result
> +   * for that edge is zero.  Edge results are returned in aSnappedLeftEdge
> +   * and aSnappedRightEdge.
> +   * @return true if at least one whole grapheme cluster fit between the edges

Need to document what aSnappedLeftEdge/aSnappedRightEdge are set to if this returns fals.e

@@ +294,5 @@
> +                              PropertyProvider& aProvider,
> +                              nscoord aLeftEdge, nscoord aRightEdge,
> +                              PRUint32& aStartOffset, PRUint32& aMaxLength,
> +                              nscoord& aSnappedLeftEdge,
> +                              nscoord& aSnappedRightEdge);

I prefer the out parameters to be pointers so the caller can tell they're out-parameters.

::: layout/generic/nsTextFrameThebes.cpp
@@ +4751,5 @@
>    // This rect is the box which is equivalent to where the shadow will be painted.
>    // The origin of mBoundingBox is the text baseline left, so we must translate it by
>    // that much in order to make the origin the top-left corner of the text bounding box.
>    gfxRect shadowGfxRect = shadowMetrics.mBoundingBox +
> +     gfxPoint(aFramePt.x + aItem.mLeftEdge, aTextBaselinePt.y) + shadowOffset;

Is this change needed, or is it just an optimization? I think it doesn't make a lot of sense so if it's not needed for correctness, we should just drop it.

@@ +5096,5 @@
> +                                    nscoord& aSnappedRightEdge)
> +{
> +  aSnappedLeftEdge = aLeftEdge;
> +  aSnappedRightEdge = aRightEdge;
> +  if (aLeftEdge <= 0 && aRightEdge <= 0) {

This can move up to the beginning of the function.

@@ +5108,5 @@
> +  const nscoord frameWidth = GetSize().width;
> +  nscoord visibleWidth = frameWidth - (aLeftEdge + aRightEdge);
> +  if (visibleWidth <= 0) {
> +    return false; // Nothing will fit in zero width.
> +  }

Can we get rid of this "if"? Zero-width characters can fit in zero width (and might actually render something!). If we don't need this special case, we should just remove it.

@@ +5120,5 @@
> +    gfxBreakPriority breakPriority = eNoBreak;
> +    PRUint32 fit = mTextRun->BreakAndMeasureText(
> +      startOffset, maxLength, PR_FALSE, gfxFloat(startEdge), &aProvider,
> +      SUPPRESS_INITIAL_BREAK, nsnull, &metrics, gfxFont::LOOSE_INK_EXTENTS,
> +      aCtx, nsnull, nsnull, CAN_WORD_WRAP, &breakPriority);

I'd love to see a patch to BreakAndMeasureText that replaces all these boolean parameters with a flags word ... in another bug.

@@ +5144,5 @@
> +        startOffset + fit, 1, PR_FALSE, gfxFloat(frameWidth), &aProvider,
> +        SUPPRESS_INITIAL_BREAK, nsnull, &metrics, gfxFont::LOOSE_INK_EXTENTS,
> +        aCtx, nsnull, nsnull, CAN_WORD_WRAP, &breakPriority);
> +      advanceWidth += metrics.mAdvanceWidth;
> +    }

Why is this only if fit != 0? What if startEdge is one pixel, then we'd get here with fit == 0 but we should still add the width of the first cluster here.

@@ +5146,5 @@
> +        aCtx, nsnull, nsnull, CAN_WORD_WRAP, &breakPriority);
> +      advanceWidth += metrics.mAdvanceWidth;
> +    }
> +    maxLength -= fit + 1;
> +    startOffset += fit + 1;

I don't think this updates startOffset enough. If the cluster crossing startEdge has many characters, startOffset might need to move further ahead. I think you need to keep adding to the advance width until it's greater than or equal to the left edge.

I think perhaps it would make sense to not use BreakAndMeasureText here and instead have a loop that calls GetAdvanceWidth character by character until you've accumulated width of at least startEdge. It won't be super-fast, but it doesn't need to be (and it won't be super-slow either).

@@ +5152,5 @@
> +      nscoord& snappedStartEdge = rtl ? aSnappedRightEdge : aSnappedLeftEdge;
> +      snappedStartEdge = frameWidth;
> +      return false;
> +    }
> +    nscoord& snappedStartEdge = rtl ? aSnappedRightEdge : aSnappedLeftEdge;

Move this up above "if (maxLength == 0)"

@@ +5157,5 @@
> +    // XXX we need to ensure that the snapped values we return can be used
> +    // as input values and give the same result.  Subtracting 1 app unit seems
> +    // to fix it altough I was expecting that floor should be enough(?)
> +    // I tried TIGHT_INK_EXTENTS but that didn't make a difference...
> +    snappedStartEdge = NSToCoordFloor(advanceWidth) - 1;

Maybe using GetAdvanceWidth instead will fix this.

TIGHT_INK_EXTENTS only changes the result in metrics.mBoundingBox, which you don't care about. LOOSE_INK_EXTENTS is the best value to use since it's the fastest.

@@ +5170,5 @@
> +      SUPPRESS_INITIAL_BREAK, nsnull, &metrics, gfxFont::LOOSE_INK_EXTENTS,
> +      aCtx, nsnull, nsnull, CAN_WORD_WRAP, &breakPriority);
> +    nscoord& snappedEndEdge = rtl ? aSnappedLeftEdge : aSnappedRightEdge;
> +    if (maxLength == 1) {
> +      // We need to have SUPPRESS_INITIAL_BREAK but a side-effect of that is

I'm not sure why you need SUPPRESS_INITIAL_BREAK here. If no text fits in the range, why not return 0?

@@ +5175,5 @@
> +      // that it will return 1 even if it doesn't fit within the given width.
> +      if (metrics.mAdvanceWidth > gfxFloat(remainderToEndEdge)) {
> +        snappedEndEdge = endEdge + remainderToEndEdge;
> +        return false;
> +      }

I think this should just be unconditional.
Comment on attachment 532472 [details] [diff] [review]
part 6, add nsDisplayForcePaintOnScroll

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

::: layout/base/nsDisplayList.h
@@ +2249,5 @@
>  
> +
> +/**
> + * This is a dummy item that reports true for IsVaryingRelativeToMovingFrame.
> + * It is transparent to events and does not paint anything.

Add 
 * It forces the bounds of its frame to be repainted every time it is scrolled.
Attachment #532472 - Flags: review?(roc) → review+
Comment on attachment 532475 [details] [diff] [review]
part 9, invalidate the block when caret enters/leaves

Review of attachment 532475 [details] [diff] [review]:
-----------------------------------------------------------------
Attachment #532475 - Flags: review?(roc) → review+
Comment on attachment 532474 [details] [diff] [review]
part 8, integrate text-overflow processing into the rendering process

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

::: layout/generic/nsFrame.cpp
@@ +1725,5 @@
> +        // It's a text or inline frame in a 'text-overflow' containing block.
> +        // Fall through to generate a display item (it'll be removed later
> +        // but we need it to know if there is displayable text beyond an
> +        // image, inline-block, etc that is on the block's clip edge).
> +      }

What about comment #193?
Comment on attachment 532527 [details] [diff] [review]
part 7, add mozilla::css::TextOverflow

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

::: layout/generic/TextOverflow.cpp
@@ +63,5 @@
> +}
> +
> +// Return true if the item belongs to a block or atomic inline-level element.
> +static bool
> +IsAtomicItem(nsDisplayItem* aItem, nsIFrame* aBlock)

This should be called IsInAtomicInlineElement, I think.

I think it's slightly wrong for anonymous blocks. Anonymous blocks should not affect the behavior of text-overflow. So, for example, if you have a block-in-inline situation, we need to make sure that IsInAtomicInlineElement returns false for a text display item whose parent is an anonymous block whose parent is the block with text-overflow. This definitely needs to be reftested.
Attachment #532432 - Attachment is obsolete: true
> What about comment #193?

I thought I addressed that by making TextOverflow::CanHaveTextOverflow
check that there's a line with a scrollable overflow area that extends
beyond the clip edge.  Was that not your concern?
Amended the comment as you suggested.
Attachment #532472 - Attachment is obsolete: true
Attachment #533727 - Flags: review?(roc)
(In reply to comment #210)
> > +                                      nsCharClipDisplayItem& aItem,
> 
> const nsCharClipDisplayItem&

This requires that Intersect() becomes const, and since it use
GetUnderLyingFrame() and ToReferenceFrame() these have to become
const as well (unless you prefer const_cast).
Attachment #533730 - Flags: review?(roc)
Attached patch part 4, add nsCharClipDisplayItem (obsolete) (deleted) — Splinter Review
Make Intersect() const.
Attachment #532470 - Attachment is obsolete: true
Attachment #533731 - Flags: review?(roc)
(In reply to comment #210)
> "clip edges based on this display item"

Fixed

> "Calculate the horizontal bounds of the grapheme clusters that fit entirely
> inside the given range".

Fixed

> Need to document what aSnappedLeftEdge/aSnappedRightEdge are set to if this
> returns fals.e

Added "All out parameter values are undefined when the method returns false."
since none of my calls needs the values when no text fits.
We can change that in the future if there is a need for it.

> I prefer the out parameters to be pointers so the caller can tell they're
> out-parameters.

Fixed

> ::: layout/generic/nsTextFrameThebes.cpp
> >    gfxRect shadowGfxRect = shadowMetrics.mBoundingBox +
> > +     gfxPoint(aFramePt.x + aItem.mLeftEdge, aTextBaselinePt.y) + shadowOffset;
> 
> Is this change needed, or is it just an optimization?

It is needed because PaintOneShadow() calls MeasureText() using aOffset, aLength
which are the snapped values here.

> > +    maxLength -= fit + 1;
> > +    startOffset += fit + 1;
> 
> I don't think this updates startOffset enough. If the cluster crossing
> startEdge has many characters, startOffset might need to move further ahead.

Good catch.  Multi-character clusters were not handled correctly here.
I've rewritten MeasureCharClippedText to not use BreakAndMeasureText
which is troublesome not just becasue of its suppress-initial-break behavior
but also how it handles multicharacter clusters - it stops as soon as it sees a
*character* that doesn't fit regardless of that is a cluster boundary or not.
Trying to compensate for that just became too messy...

Accumulating GetAdvanceWidth() cluster by cluster is much simpler.

> > +    snappedStartEdge = NSToCoordFloor(advanceWidth) - 1;
> 
> Maybe using GetAdvanceWidth instead will fix this.

It didn't.
Attachment #532471 - Attachment is obsolete: true
Attachment #533737 - Flags: review?(roc)
Attachment #532471 - Flags: review?(roc)
Attached patch part 7, add mozilla::css::TextOverflow (obsolete) (deleted) — Splinter Review
(In reply to comment #214)
> > +// Return true if the item belongs to a block or atomic inline-level element.
> > +static bool
> > +IsAtomicItem(nsDisplayItem* aItem, nsIFrame* aBlock)
> 
> This should be called IsInAtomicInlineElement, I think.

I renamed it IsAtomicOrBlockItem to better match what it does.

I also did a small change to TextOverflow::ApplyMarkers - it used
to ignore zero-width frames, but I removed that so that a text item
with only a zero-width cluster is removed if it's not inside the markers.

> I think it's slightly wrong for anonymous blocks. Anonymous blocks should
> not affect the behavior of text-overflow.

Agreed, but I can't find an example where it would.
AFAICT, the block part of an IB-split always ends up on its own "block line"
and those were pruned already in DisplayLine:
+  if (aTextOverflow && lineInline) {
+    aTextOverflow->ApplyForLine(collection, aLine.get());
Attachment #532527 - Attachment is obsolete: true
Attachment #533743 - Flags: review?(roc)
Attachment #532527 - Flags: review?(roc)
(In reply to comment #193)
I guess I could remove this and instead walk the frame tree in
nsBlockFrame (for lines with overflow) to see if there's text beyond
the content edges.  I'll look into it...
Comment on attachment 533727 [details] [diff] [review]
part 6, add nsDisplayForcePaintOnScroll

Review of attachment 533727 [details] [diff] [review]:
-----------------------------------------------------------------
Attachment #533727 - Flags: review?(roc) → review+
(In reply to comment #223)
> (In reply to comment #193)
> I guess I could remove this and instead walk the frame tree in
> nsBlockFrame (for lines with overflow) to see if there's text beyond
> the content edges.  I'll look into it...

It seems to me you only need to check if the line box's scrollable overflow area extends beyond the content edges. The decision about whether to use markers can be entirely based on that.
Comment on attachment 531423 [details] [diff] [review]
part 1, style system

r=dbaron
Attachment #531423 - Flags: review?(dbaron) → review+
(In reply to comment #225)
> It seems to me you only need to check if the line box's scrollable overflow
> area extends beyond the content edges. The decision about whether to use
> markers can be entirely based on that.

No.  You're forgetting the case where an atomic inline element is at the
clip edge, there shouldn't be a marker if there is no text outside it.
The line has overflow in both cases.  Also, line overflow can be created
by visibility:hidden, we don't want markers in that case (we got that for
free when analyzing display items - there is no text items in this case).

I'm rewriting TextOverflow::AnalyzeEdges to walk the frame tree instead
(this removes the need to change nsFrame.cpp) so please wait with
reviewing that bit.
Comment on attachment 531722 [details] [diff] [review]
part 1b, style system - add support for two value syntax

Where's the spec for this two-value syntax?  Is there a reason it's
left-value right-value rather than start-value end-value?  

Do other browsers implement it?  I'm hesitant to implement things 
unprefixed if other browsers don't already.  (Speaking of which, do 
othre browsers implement the <string> value?)

For the parser changes, could you:
 * use CSS_PROPERTY_VALUE_PARSER_FUNCTION instead of 
   CSS_PROPERTY_PARSE_FUNCTION
 * avoid all use of ExpectEndProperty / CheckEndProperty in the parsing 
   function
(In reply to comment #227)
> (In reply to comment #225)
> > It seems to me you only need to check if the line box's scrollable overflow
> > area extends beyond the content edges. The decision about whether to use
> > markers can be entirely based on that.
> 
> No.  You're forgetting the case where an atomic inline element is at the
> clip edge, there shouldn't be a marker if there is no text outside it.

I'm not convinced that's a good idea. Why shouldn't we show the marker when non-text content is clipped?

> The line has overflow in both cases.  Also, line overflow can be created
> by visibility:hidden, we don't want markers in that case (we got that for
> free when analyzing display items - there is no text items in this case).

overflow:hidden can't apply to inline elements, but FWIW even if it did I'm not convinced that it should affect whether a marker is shown.
(In reply to comment #228)
> Do other browsers implement it?
> do othre browsers implement the <string> value?

No and no, as far as I know.

>  * use CSS_PROPERTY_VALUE_PARSER_FUNCTION instead of 
>    CSS_PROPERTY_PARSE_FUNCTION
>  * avoid all use of ExpectEndProperty / CheckEndProperty in the parsing 
>    function

Fixed
Attachment #531722 - Attachment is obsolete: true
Attachment #534500 - Flags: review?(dbaron)
Attachment #531722 - Flags: review?(dbaron)
Attached patch part 7, add mozilla::css::TextOverflow (obsolete) (deleted) — Splinter Review
(In reply to comment #229)
> Why shouldn't we show the marker when non-text content is clipped?

Ok, fixed.

> overflow:hidden can't apply to inline elements, ...

I said visibility:hidden, which takes up space in layout and can
cause line overflow.  I don't think they should trigger overflow
markers.

Changed the edge analysis to use the frame tree instead of
display items.
Attachment #533743 - Attachment is obsolete: true
Attachment #534502 - Flags: review?(roc)
Attachment #533743 - Flags: review?(roc)
Removed all changes to nsFrame.cpp
Attachment #532474 - Attachment is obsolete: true
Attachment #534503 - Flags: review?(roc)
Attachment #532474 - Flags: review?(roc)
(In reply to comment #231)
> (In reply to comment #229)
> > overflow:hidden can't apply to inline elements, ...
> 
> I said visibility:hidden, which takes up space in layout and can
> cause line overflow.  I don't think they should trigger overflow
> markers.

Oops. Why shouldn't visibility:hidden trigger overflow markers? visibility:hidden affects the presence of overflow:auto scrollbars and we make sure you can scroll such content into view. As a general principle doesn't it make sense that anything we would show a scrollbar for, we should also show a marker for?
To me, the ellipsis marker represents (is a replacement for) content
that cannot be displayed in the available space.  visibility:hidden
elements are not displayed at all, so they don't qualify.
This is another issue that we should call out in the spec and get feedback on. I'll try to like your frame tree code when I review it :-)
Comment on attachment 533730 [details] [diff] [review]
part 3b, constify GetUnderlyingFrame, ToReferenceFrame

Review of attachment 533730 [details] [diff] [review]:
-----------------------------------------------------------------
Attachment #533730 - Flags: review?(roc) → review+
Comment on attachment 533731 [details] [diff] [review]
part 4, add nsCharClipDisplayItem

Review of attachment 533731 [details] [diff] [review]:
-----------------------------------------------------------------
Attachment #533731 - Flags: review?(roc) → review+
Comment on attachment 533737 [details] [diff] [review]
part 5, do whole-character clipping between nsCharClipDisplayItem edges

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

::: layout/generic/nsTextFrameThebes.cpp
@@ +5131,5 @@
> +      advanceWidth +=
> +        mTextRun->GetAdvanceWidth(offset, clusterLength, &aProvider);
> +      maxLength -= clusterLength;
> +      offset += clusterLength;
> +      if (advanceWidth > maxAdvance) {

Shouldn't this be >=? We should stop if we've advanced exactly to startEdge.

@@ +5139,5 @@
> +    // XXX we need to ensure that the snapped values we return can be used
> +    // as input values and give the same result.  Subtracting 1 app unit seems
> +    // to fix it altough I was expecting that floor should be enough(?)
> +    nscoord* snappedStartEdge = rtl ? aSnappedRightEdge : aSnappedLeftEdge;
> +    *snappedStartEdge = NSToCoordFloor(advanceWidth) - 1;

Can you explain the problem here in more detail? This smells bad :-)

@@ +5191,5 @@
> +                          GetSnappedBaselineY(ctx, aPt.y));
> +  PRUint32 startOffset = provider.GetStart().GetSkippedOffset();
> +  PRUint32 maxLength = ComputeTransformedLength(provider);
> +  if (!MeasureCharClippedText(ctx, provider, aItem.mLeftEdge, aItem.mRightEdge,
> +         &startOffset, &maxLength, &aItem.mLeftEdge, &aItem.mRightEdge)) {

I'd actually prefer it if instead of overwriting the unsnapped mLeftEdge/mRightEdge, we just store the snapped values separately and pass them where they're needed. Right now it's confusing because mLeftEdge/mRightEdge changes from being unsnapped to snapped.
Comment on attachment 532469 [details] [diff] [review]
part 3, add nsDisplayList::Remove[AllItemsForFrameSubtree]

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

::: layout/base/nsDisplayList.cpp
@@ +698,5 @@
> +      aList->AppendToTop(above);
> +      continue;  // leave |prev| as is
> +    }
> +    prev = prev->mAbove;
> +  }

Hmm, this should probably call RemoveAllItemsForFrameSubtree recursively on items whose GetList() returns non-null
Comment on attachment 534502 [details] [diff] [review]
part 7, add mozilla::css::TextOverflow

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

I can't help feeling the processing of each line could be simplified, but I'm not completely sure how :-(.

What do you think of this:
1) Determined the presence or absence of markers just by checking the line box scrollable overflow area. If there shouldn't be markers, stop.
2) If there should be markers, compute their rectangles as if they're at the edge of the block clip rect. Also compute the usable area of the line, the rectangle between the markers.
3) Walk the frame tree and decide how to handle each frame.
  3a) If it needs to be thrown away (atomic inline not entirely inside the usable area), add it to a list of frames whose display items should be destroyed and move on to the next frame.
  3b) Otherwise, if the frame is an inline frame, move on to the next frame since it should be displayed and not affect marker placement
  3c) Otherwise, if the frame is a text frame and doesn't fit entirely inside the usable area, measure its clipped text bounds
  3d) Add the clipped text bounds, or the frame border-box if it's not a text frame, to an accumulated rectangle of what will be rendered on the line
4) Compute final marker positions, placing the markers next to the rectangle accumulated in part 3d if overflow-x:hidden
5) Walk the display list, destroying all items identified in part 3a) and updating all nsDisplayCharClip frames for the final marker positions

Maybe this isn't very different from what you've already done, but it seems like it would involve more manipulation of rectangles and less of edges, which would result in cleaner code.

It feels awkward that I think this can be simpler but I can't prove it without writing the code myself or making you do it :-(. Sorry...

::: layout/generic/TextOverflow.cpp
@@ +61,5 @@
> +  return nsLayoutUtils::GetAsBlock(aFrame) ? aFrame :
> +         nsLayoutUtils::FindNearestBlockAncestor(aFrame);
> +}
> +
> +// Return true if the item belongs to a block or atomic inline-level element.

This needs a comment that involves the aBlock parameter

@@ +74,5 @@
> +    }
> +    if (itemFrame->IsFrameOfType(nsIFrame::eLineParticipant)) {
> +      if (GetSelfOrNearestBlock(itemFrame) == aBlock) {
> +        return itemFrame->IsFrameOfType(nsIFrame::eReplaced) &&
> +               itemFrame->GetType() != nsGkAtoms::textFrame;

why is the second check necessary? A textFrame can't be eReplaced.

@@ +91,5 @@
> +IsAtomicInlineElement(nsIFrame* aFrame)
> +{
> +  return !aFrame->IsFrameOfType(nsIFrame::eLineParticipant) ||
> +         (aFrame->IsFrameOfType(nsIFrame::eReplaced) &&
> +            aFrame->GetType() != nsGkAtoms::textFrame) ||

Again, how can a textframe be eReplaced?

@@ +92,5 @@
> +{
> +  return !aFrame->IsFrameOfType(nsIFrame::eLineParticipant) ||
> +         (aFrame->IsFrameOfType(nsIFrame::eReplaced) &&
> +            aFrame->GetType() != nsGkAtoms::textFrame) ||
> +         nsLayoutUtils::GetAsBlock(aFrame);

This returns true for frames that aren't atomic inline-level elements, e.g. for elements that aren't inline-level at all.

Can we just check to see if it's an nsInlineFrame?

@@ +148,5 @@
> +      // is less than the marker width)
> +      aMarkerRect->width -= markerOverflow;
> +      aMarkerRect->x += markerOverflow;
> +      aMarker = new (aBuilder)
> +        nsDisplayClip(aBuilder, aFrame, aMarker, *aMarkerRect);

Prefer to assign the new item to a new local variable instead of overwriting a parameter.

@@ +222,5 @@
> +    gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
> +    gfxFont* firstFont = fontGroup->GetFontAt(0);
> +    ellipsis = firstFont && !firstFont->HasCharacter(kEllipsisChar[0])
> +      ? nsString(kEllipsisChar, NS_ARRAY_LENGTH(kEllipsisChar) - 1)
> +      : nsString(kASCIIPeriodsChar, NS_ARRAY_LENGTH(kASCIIPeriodsChar) - 1);

nsDependentString

@@ +238,5 @@
> +  nsIScrollableFrame* scroll =
> +    nsLayoutUtils::GetScrollableFrameFor(aBlockFrame);
> +  textOverflow->mBlockIsScrolled = false;
> +  if (scroll) {
> +    textOverflow->mBlockIsScrolled =

Should probably call this mCanHaveHorizontalScrollbar or something. overflow:hidden content can be scrolled.

@@ +256,5 @@
> +                                  textOverflow->mRight.mMarkerString.Length());
> +  textOverflow->mLeft.mClipStyle =
> +    style->mTextOverflow.mLeft.mType == NS_STYLE_TEXT_OVERFLOW_CLIP;
> +  textOverflow->mRight.mClipStyle =
> +    style->mTextOverflow.mRight.mType == NS_STYLE_TEXT_OVERFLOW_CLIP;

I think we could avoid duplicating some of this left/right code by calling a method on Marker to set up the marker string, passing in style->mTextOverflow.mLeft/mRight. You could just recompute the 'ellipsis' string for both sides as necessary. Actually I'd suggest having markers lazily set up their strings and width info when we hit the first line that has overflow in the required direction. Since most current text-overflow cases only overflow on one side (because they use overflow:hidden and don't scroll), we'd only ever have to set up one marker.

@@ +287,5 @@
> +                           nscoord aClipX, nscoord aClipXMost,
> +                           nscoord aLeftMarkerXMost, nscoord aRightMarkerX)
> +{
> +  if (!aFrame->GetStyleVisibility()->IsVisible()) {
> +    return;

This isn't quite right. A visibility:hidden element could have a descendant that overrides 'visibility', I guess you'd want markers for that case.

Do we want to draw markers when an inline frame with border or background but no visible text overflows?

I wish I could persuade you to only check the line scrollable overflow area :-)

@@ +294,5 @@
> +  nsRect childRect = aFrame->GetScrollableOverflowRect() +
> +                     aFrame->GetOffsetTo(mBlock);
> +  if (childRect.width != 0) {
> +    mLeft.CondOverflow(childRect.x < aClipX);
> +    mRight.CondOverflow(childRect.XMost() > aClipXMost);

CheckOverflow instead of CondOverflow?

@@ +317,5 @@
> +    while (childList == nsGkAtoms::floatList ||
> +           childList == nsGkAtoms::absoluteList ||
> +           childList == nsGkAtoms::fixedList ||
> +           childList == nsGkAtoms::pushedFloatsList ||
> +           childList == nsGkAtoms::overflowOutOfFlowList) {

What child lists are we iterating through other than the normal child list? Can we just use the normal child list only?

@@ +335,5 @@
> +  if (!isText && !isAtomicInline) {
> +    return;
> +  }
> +
> +  nscoord left = 0, right = 0;

leftOverlap, rightOverlap?

@@ +404,5 @@
> +    mRight.mClipStyle ? nscoord_MAX : clipRect.XMost() + scrollAdjust;
> +  const nscoord clipRectX =
> +    mLeft.mClipStyle ? nscoord_MIN : clipRect.x - scrollAdjust;
> +  nsRect lineRect = aLine->GetScrollableOverflowArea();
> +  if (lineRect.x >= clipRectX && lineRect.XMost() <= clipRectXMost) {

It's probably slightly simpler to write
  if ((!mLeft.mClipStyle && lineRect.x >= clipRect.x - scrollAdjust) ||
      (!mRight.mClipStyle && lineRect.XMost() <= clipRect.XMost() + scrollAdjust)

@@ +437,5 @@
> +  }
> +  bool needLeft = mLeft.IsNeeded();
> +  bool needRight = mRight.IsNeeded();
> +  if (!needLeft && !needRight) {
> +    return; // no markers needed on this line

Wouldn't LineHasOverflowText have already returned in this case?

@@ +476,5 @@
> +
> +
> +void
> +TextOverflow::ApplyMarkers(nsDisplayList* aList, nsLineBox* aLine,
> +                           const nscoord aLeftEdge, const nscoord aRightEdge)

So here, the right edge is not relative to the right side of the block, it's a horizontal coordinate? That's confusing.

@@ +482,5 @@
> +  nsDisplayItem* item = aList->GetBottom();
> +  while (item) {
> +    nsIFrame* itemFrame = item->GetUnderlyingFrame();
> +    if (!itemFrame) {
> +      item = item->GetAbove();

This should probably happen after we've checked item->GetList().

@@ +503,5 @@
> +    if (rect.XMost() > aRightEdge && rect.x < aRightEdge) {
> +      right = rect.XMost() - aRightEdge;
> +    }
> +
> +    enum DisplayItemOp { eIgnore, eOutside, eOutsideAtomic, eClippedNonAtomic,

These need to be documented

@@ +587,5 @@
> +      nsCOMPtr<nsIContent> content = do_QueryInterface(node);
> +      if (content && nsContentUtils::ContentIsDescendantOf(content,
> +                       aBlockFrame->GetContent())) {
> +        return false;
> +      }

Instead of all this, how about calling aBuilder->GetCarentFrame() and checking whether the result (if non-null) is a descendant of the block? That would be simpler and slightly more accurate.

Or, we could return true here regardless of the caret, and instead avoid drawing a marker on just the line that contains the caret.

Best of all, we could just avoid drawing the marker if it overlaps the caret. I don't think that would be too hard, call nsCaret::GetCaretRect.

::: layout/generic/TextOverflow.h
@@ +132,5 @@
> +  bool                   mBlockIsScrolled;
> +
> +  class Marker {
> +  public:
> +    void CondOverflow(bool aCondition) {

I think we need a better name here, and some documentation?

@@ +165,5 @@
> +    nscoord                mWidth;
> +    // The length to the nearest frame towards the block's center measured from
> +    // this marker's initial inner edge.  In case of a text frame it may
> +    // actually overlap; it can still be non-zero in that case due to a removed
> +    // partially visible cluster.

This comment is hard to understand...

@@ +173,5 @@
> +    // after all display items have been analyzed.
> +    nscoord                mFinalEdge;
> +    // The marker text.
> +    nsString               mMarkerString;
> +    // True if this side has 'clip' style.

You mean 'text-overflow:clip'?
> What do you think of this:
> 1) Determined the presence or absence of markers just by checking the line
> box scrollable overflow area. If there shouldn't be markers, stop.

That doesn't work.  A float can cause line overflow we're not interested in.
(possibly also visibility:hidden depending on what the spec wants)

>   3a) If it needs to be thrown away (atomic inline not entirely inside the
> usable area), add it to a list of frames whose display items should be
> destroyed and move on to the next frame.

I guess you mean "entirely inside the usable area *horizontally*".
Yeah, collecting these frames is another way to do it, I'll explore this
to see if it leads to simpler code...

> Maybe this isn't very different from what you've already done,

Apart from 3a, it's not.

> but it seems
> like it would involve more manipulation of rectangles and less of edges,

Agreed.

> which would result in cleaner code.

That's not evident to me, since we're only interested in the *horizontal*
dimension using rectangles seems like more work than using edges.
But sure, I'll rewrite it so we can judge it from the result...  ;-)
(In reply to comment #242)
> > What do you think of this:
> > 1) Determined the presence or absence of markers just by checking the line
> > box scrollable overflow area. If there shouldn't be markers, stop.
> 
> That doesn't work.  A float can cause line overflow we're not interested in.
> (possibly also visibility:hidden depending on what the spec wants)

True. However, you can avoid the float issue by checking the scrollable overflow area of each frame in the line instead (non-recursively).

> > but it seems
> > like it would involve more manipulation of rectangles and less of edges,
> 
> Agreed.
> 
> > which would result in cleaner code.
> 
> That's not evident to me, since we're only interested in the *horizontal*
> dimension using rectangles seems like more work than using edges.

Maybe so. But maybe working with the horizontal interval between the two markers instead of talking about the markers themselves --- setting the left edge of that interval to nscoord_MIN if there's no left marker, and the right edge to nscoord_MAX if there's no right marker --- will be simpler.

Also, in your current patch we have to classify frames in two places in two different passes: ApplyMarkers and AnalyzeMarkerEdges. My suggestion would reduce that to one place.

> But sure, I'll rewrite it so we can judge it from the result...  ;-)

Sorry! I apologize profusely in advance in case it doesn't end up simpler...
Removed nsDisplayList::Remove() - it's not needed anymore.
Attachment #532469 - Attachment is obsolete: true
Attachment #535675 - Flags: review?(roc)
Comment on attachment 535675 [details] [diff] [review]
part 3, add nsDisplayList::RemoveAllItemsForFrameSubtree

(In reply to comment #240)
> Hmm, this should probably call RemoveAllItemsForFrameSubtree recursively on
> items whose GetList() returns non-null

I think you're right - fixed.
Attached patch part 4, add nsCharClipDisplayItem (obsolete) (deleted) — Splinter Review
Adding a ClipEdges helper class...
Attachment #533731 - Attachment is obsolete: true
Attachment #535676 - Flags: review?(roc)
(In reply to comment #239)
> Shouldn't this be >=? We should stop if we've advanced exactly to startEdge.

Fixed, thanks!

> > +    *snappedStartEdge = NSToCoordFloor(advanceWidth) - 1;
> 
> Can you explain the problem here in more detail? This smells bad :-)

Fixing the >= bug above fixed this.

> I'd actually prefer it if instead of overwriting the unsnapped
> mLeftEdge/mRightEdge, we just store the snapped values separately and pass
> them where they're needed.

Ok, I'm creating a ClipEdges from the item and pass that around for
doing the actual clipping.  In the text frame case I'm using the
snapped values and the DisplayItem is unmodified (enforced with const).
Attachment #533737 - Attachment is obsolete: true
Attachment #535677 - Flags: review?(roc)
Attachment #533737 - Flags: review?(roc)
Attached patch part 7, add mozilla::css::TextOverflow (obsolete) (deleted) — Splinter Review
(In reply to comment #241)
> > +        return itemFrame->IsFrameOfType(nsIFrame::eReplaced) &&
> > +               itemFrame->GetType() != nsGkAtoms::textFrame;
> 
> why is the second check necessary? A textFrame can't be eReplaced.

You are mistaken, text frames are eReplaced.
http://mxr.mozilla.org/mozilla-central/source/layout/generic/nsTextFrame.h#138

> This returns true for frames that aren't atomic inline-level elements, e.g.
> for elements that aren't inline-level at all.

Yes. It's not supposed to be called for block frames since we never
process any block-level descendants for text-overflow.
I've added a comment and an assertion to make that clear.

> Can we just check to see if it's an nsInlineFrame?

Using IsFrameOfType seems more correct than assuming properties
for certain frame classes.

> Actually I'd suggest having markers
> lazily set up their strings and width info when we hit the first line that
> has overflow in the required direction.

Fixed

> This isn't quite right. A visibility:hidden element could have a descendant
> that overrides 'visibility', I guess you'd want markers for that case.

Fixed

> Do we want to draw markers when an inline frame with border or background
> but no visible text overflows?

The spec says so, if I understand it correctly.

> > +    while (childList == nsGkAtoms::floatList ||
> > +           childList == nsGkAtoms::absoluteList ||
> > +           childList == nsGkAtoms::fixedList ||
> > +           childList == nsGkAtoms::pushedFloatsList ||
> > +           childList == nsGkAtoms::overflowOutOfFlowList) {
> 
> What child lists are we iterating through other than the normal child list?
> Can we just use the normal child list only?

Ok, I've changed it to only process the principal child list.
We can add additional lists if we find there is a need for it.

> Instead of all this, how about calling aBuilder->GetCarentFrame() and
> checking whether the result (if non-null) is a descendant of the block?

That won't work.  aBuilder->GetCaretFrame() returns NULL in the
caret's off-cycle.

> Or, we could return true here regardless of the caret, and instead avoid
> drawing a marker on just the line that contains the caret.
> Best of all, we could just avoid drawing the marker if it overlaps the
> caret. I don't think that would be too hard, call nsCaret::GetCaretRect.

I like that suggestion, but let's do that in a follow-up bug?
Do you want me to remove the caret stuff for now then?

All your other nits: fixed.

Test builds:
http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/mpalmgren@mozilla.com-909ba359fc0f/
Attachment #534502 - Attachment is obsolete: true
Attachment #535678 - Flags: review?(roc)
Attachment #534502 - Flags: review?(roc)
(In reply to comment #243)
> > That doesn't work.  A float can cause line overflow we're not interested in.
> > (possibly also visibility:hidden depending on what the spec wants)
> 
> True. However, you can avoid the float issue by checking the scrollable
> overflow area of each frame in the line instead (non-recursively).

Nope.  Floats wrapped in any number of inlines can still overflow the
line without the inlines doing so.
Comment on attachment 535675 [details] [diff] [review]
part 3, add nsDisplayList::RemoveAllItemsForFrameSubtree

Review of attachment 535675 [details] [diff] [review]:
-----------------------------------------------------------------
Attachment #535675 - Flags: review?(roc) → review+
Comment on attachment 535676 [details] [diff] [review]
part 4, add nsCharClipDisplayItem

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

r+ with that.

::: layout/base/nsDisplayList.h
@@ +2217,5 @@
> +    nscoord mX;
> +    nscoord mXMost;
> +  };
> +
> +  operator ClipEdges () { return ClipEdges(*this, mLeftEdge, mRightEdge); }

An implicit conversion like this is not really a good idea, since a ClipEdges is in no way equivalent to an nsCharClipDisplayItem. Let's just have a getter method here, say Edges().
Attachment #535676 - Flags: review?(roc) → review+
Comment on attachment 535677 [details] [diff] [review]
part 5, do whole-character clipping between nsCharClipDisplayItem edges

Review of attachment 535677 [details] [diff] [review]:
-----------------------------------------------------------------
Attachment #535677 - Flags: review?(roc) → review+
Comment on attachment 535678 [details] [diff] [review]
part 7, add mozilla::css::TextOverflow

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

::: layout/generic/TextOverflow.cpp
@@ +88,5 @@
> +                  "unexpected block frame");
> +  return !aFrame->IsFrameOfType(nsIFrame::eLineParticipant) ||
> +         (aFrame->IsFrameOfType(nsIFrame::eReplaced) &&
> +            aFrame->GetType() != nsGkAtoms::textFrame) ||
> +         nsLayoutUtils::GetAsBlock(aFrame);

Block frames would return false for eLineParticipant, so why is this GetAsBlock check needed?

What eReplaced frames return true for eLineParticipant (other than text frames, which need to return false here)?

@@ +135,5 @@
> +    aMarkerRect->width -= markerOverflow;
> +    item = new (aBuilder)
> +      nsDisplayClip(aBuilder, aFrame, aMarker, *aMarkerRect);
> +  } else {
> +    nscoord markerOverflow = aContentArea.x - aMarkerRect->x;

Don't shadow the outer declaration of markerOverflow.

@@ +176,5 @@
> +  if (aResultRect->IsEmpty()) {
> +    *aResultRect = aRectToAdd;
> +  } else {
> +    aResultRect->UnionRect(*aResultRect, aRectToAdd);
> +  }

Why aren't you just calling UnionRect here?

That said, I'm not sure that ignoring empty rects is what you want to do here. Are you sure zero-width or zero-height content shouldn't affect the placement of the marker? I tend to think that just because a frame is zero-height shouldn't make it be ignored.

@@ +216,5 @@
> +  aCtx->SetColor(nsLayoutUtils::GetTextColor(mFrame));
> +  nsPoint baselinePt = mRect.TopLeft();
> +  baselinePt.y += mAscent;
> +  nsLayoutUtils::DrawString(mFrame, aCtx, mString.get(), mString.Length(),
> +                            baselinePt);

Are you going to draw text-shadow in a followup bug?

@@ +280,5 @@
> +  const bool isAtomic = IsAtomicElement(aFrame);
> +  if (aFrame->GetStyleVisibility()->IsVisible()) {
> +    nsRect childRect = aFrame->GetScrollableOverflowRect() +
> +                       aFrame->GetOffsetTo(mBlock);
> +    if (childRect.width != 0) {

I think we probably don't want this check here, as mentioned above.

@@ +341,5 @@
> +        }
> +        AccumulateRect(aAlignmentRect, snappedRect);
> +      }
> +    } else if (IsAtomicElement(aFrame)) {
> +      aFramesToHide->AppendElement(aFrame);

So if we have an atomic inline in the area where a marker might be, but we ultimately decide not to draw that marker because content doesn't overflow in that direction, are we still hiding that atomic inline? That seems wrong.

My proposal for a single pass over the frame tree assumed that you would compute which markers were present ahead of time by looking at the line's scrollable area, or the scrollable areas of the toplevel frames on the line. But you're not doing that so I suspect we have a problem here.

@@ +401,5 @@
> +  // Calculate the usable area between the potential markers aligned at the
> +  // block's edge.
> +  nsRect usableArea = mContentArea;
> +  InflateLeft(&usableArea, mLeft.mClipStyle, -leftMarkerWidth);
> +  InflateRight(&usableArea, mRight.mClipStyle, -rightMarkerWidth);

For consistency I think usableArea should be called insideMarkersArea.

@@ +408,5 @@
> +  // and at the edges of the usable area between the markers.
> +  PRInt32 n = aLine->GetChildCount();
> +  nsIFrame* child = aLine->mFirstChild;
> +  for (; n-- > 0; child = child->GetNextSibling()) {
> +    AnalyzeEdges(child, contentArea, usableArea, aFramesToHide, aAlignmentRect);

Let's call this ExamineFrameSubtree

@@ +414,5 @@
> +  return mLeft.IsNeeded() || mRight.IsNeeded();
> +}
> +
> +void
> +TextOverflow::ApplyForLine(const nsDisplayListSet& aLists,

Let's call this ProcessLine.

@@ +423,5 @@
> +  mLeft.Reset();
> +  mRight.Reset();
> +  nsTArray<nsIFrame*> framesToHide;
> +  nsRect alignmentRect = nsRect(0,0,0,0);
> +  if (!LineHasInlineOverflow(aLine, &framesToHide, &alignmentRect)) {

How about checking mLeft.IsNeeded || mRight.IsNeeded here ... that makes it clearer what's going on.

Then LineHasInlineOverflow can return void and we can rename it to something like ExamineLineFrames (because it really does a lot more than just checking for overflow).

@@ +455,5 @@
> +
> +  // Clip and remove display items as needed at the final marker edges.
> +  nsDisplayList* lists[] = { aLists.Content(), aLists.PositionedDescendants() };
> +  for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(lists); ++i) {
> +    ApplyMarkers(lists[i], framesToHide, insideMarkersArea);

Let's call this PruneDisplayListContents.

@@ +468,5 @@
> +{
> +  nsDisplayItem* item = aList->GetBottom();
> +  while (item) {
> +    nsIFrame* itemFrame = item->GetUnderlyingFrame();
> +    if (aFramesToHide.Contains(itemFrame)) {

aFramesToHide should probably be a hashtable of frame pointers, to ensure this is fast.

Actually we should probably just get rid of RemoveAllItemsForFrameSubtree because that makes this code O(N^2) as well if we have N sibling frames all of which need to have their display items removed ... we'd get N calls to RemoveAllItemsForFrameSubtree, each of which scans O(N) items. Instead we can just scan all the ancestors of itemFrame (up to the block whose line we're processing) to see if they're in aFramesToHide.

::: layout/generic/TextOverflow.h
@@ +85,5 @@
> +  TextOverflow() {}
> +
> +  /**
> +   * Checks if the line box is overflowing and if so calls AnalyzeEdges to look
> +   * for actual inline overflow.

"Examines frames on the line to determine whether we should draw a left and/or right marker, and if so, which frames should be completely hidden and the bounds of what will be displayed between the markers."

@@ +89,5 @@
> +   * for actual inline overflow.
> +   * @param aLine the line we're processing
> +   * @param aFramesToHide frames that should have their display items removed
> +   * @param aAlignmentRect the union of all text and inline-level atomic frames
> +   *                       inside the usable area between the markers

Let's call it aInsideMarkerRect.

@@ +186,5 @@
> +    nscoord                mWidth;
> +    // The marker text.
> +    nsString               mMarkerString;
> +    // True if this side has 'text-overflow:clip' style.
> +    bool                   mClipStyle;

Booleans kinda suck. Let's use the clip style here and have InflateLeft etc check for OVERFLOW_CLIP explicitly.

Either that or we use a better name here, like mCanHaveMarker (with reversed meaning).
(In reply to comment #250)
> (In reply to comment #243)
> > > That doesn't work.  A float can cause line overflow we're not interested in.
> > > (possibly also visibility:hidden depending on what the spec wants)
> > 
> > True. However, you can avoid the float issue by checking the scrollable
> > overflow area of each frame in the line instead (non-recursively).
> 
> Nope.  Floats wrapped in any number of inlines can still overflow the
> line without the inlines doing so.

Yes, but I'm not sure how that matters.

A bigger issue is that out-of-flow stuff like an abs-pos child of a rel-pos inline can cause line scrollable area overflow that we don't want to trigger text-overflow markers :-(.

So I think you do need two passes over the frame tree to see which markers should be displayed. Sorry :-(. (But I do think the new code is an improvement, and will still be an improvement even with that change.)
Comment on attachment 534503 [details] [diff] [review]
part 8, integrate text-overflow processing into the rendering process

Review of attachment 534503 [details] [diff] [review]:
-----------------------------------------------------------------
Attachment #534503 - Flags: review?(roc) → review+
If you do add that second pass, I recommend making it a new function, say ComputePresenceOfMarkers.
Attached patch part 3, add nsDisplayList::Remove (obsolete) (deleted) — Splinter Review
Remove RemoveAllItemsForFrameSubtree(), reintroduce Remove().
I think you may have reviewed this in an earlier patch set.
(there's no changes from the last version)
Attachment #535675 - Attachment is obsolete: true
Attachment #536379 - Flags: review?(roc)
Comment on attachment 534500 [details] [diff] [review]
part 1b, style system - add support for two value syntax

Put on ice until CSS4-UI adds it ;-)
http://wiki.csswg.org/spec/css4-ui#text-overflow-two-values
Attachment #534500 - Attachment is obsolete: true
Attachment #534500 - Flags: review?(dbaron)
Fixed as you suggested: s/operator ClipEdges()/Edges()/
Attachment #535676 - Attachment is obsolete: true
Attachment #536388 - Flags: review+
Use Edges() instead of *this
Attachment #535677 - Attachment is obsolete: true
Attachment #536389 - Flags: review?(roc)
Attached patch part 7, add mozilla::css::TextOverflow (obsolete) (deleted) — Splinter Review
> > +  return !aFrame->IsFrameOfType(nsIFrame::eLineParticipant) ||
> > +         (aFrame->IsFrameOfType(nsIFrame::eReplaced) &&
> > +            aFrame->GetType() != nsGkAtoms::textFrame) ||
> > +         nsLayoutUtils::GetAsBlock(aFrame);
> 
> Block frames would return false for eLineParticipant, so why is this
> GetAsBlock check needed?

inline-block

> What eReplaced frames return true for eLineParticipant (other than text
> frames, which need to return false here)?

Only BRFrame as far as I can tell.  I've changed it like so:

IsAtomicElement(nsIFrame* aFrame)
{
  if (aFrame->IsFrameOfType(nsIFrame::eReplaced)) {
    const nsIAtom* frameType = aFrame->GetType();
    if (frameType != nsGkAtoms::textFrame &&
        frameType != nsGkAtoms::brFrame) {
      return true;
    }
  }
  const nsStyleDisplay* display = aFrame->GetStyleDisplay();
  return display->mDisplay != NS_STYLE_DISPLAY_INLINE &&
         display->IsInlineOutside();
}

I dislike checking the style, but there is no suitable IsFrameOfType bit
to test.  I think it would be good to introduce one (in a separate bug).


> > +  if (aResultRect->IsEmpty()) {
> > +    *aResultRect = aRectToAdd;
> > +  } else {
> > +    aResultRect->UnionRect(*aResultRect, aRectToAdd);
> > +  }
> 
> Why aren't you just calling UnionRect here?

Because the initial value we pass in is nsRect(0,0,0,0),
we don't want to union the first frame we find with that.
I've now added "struct AccumulatedRect" to handle this.


> That said, I'm not sure that ignoring empty rects is what you want to do
> here. Are you sure zero-width or zero-height content shouldn't affect the
> placement of the marker? I tend to think that just because a frame is
> zero-height shouldn't make it be ignored.

I think we should only align the markers at positions where there is
visible content.  So, I've changed it to check if the frame has visual
overflow and adding the empty border box rect if there is.
Does that sound ok to you?

I also think that we should NOT trigger markers for frames with a
zero-width ScrollableOverflowRect because it would trigger just an
overflow marker and NOT a scrollbar, which I think would be confusing
to the user.


> Are you going to draw text-shadow in a followup bug?

Yes, I don't think that it should block.


> > +    } else if (IsAtomicElement(aFrame)) {
> > +      aFramesToHide->AppendElement(aFrame);
> 
> So if we have an atomic inline in the area where a marker might be, but we
> ultimately decide not to draw that marker because content doesn't overflow
> in that direction, are we still hiding that atomic inline? That seems wrong.

Good catch, fixed by doing a second pass when needed (should be rare).
Actually, it would be possible to do this in just one pass by collecting
separate aFramesToHide sets and alignment rects for each side, but it
doesn't seem worth penalizing the common case with this extra work.

> aFramesToHide should probably be a hashtable of frame pointers, to ensure
> this is fast.

Fixed

> Actually we should probably just get rid of RemoveAllItemsForFrameSubtree
> ... Instead we can just scan all the ancestors of itemFrame

Fixed

Addressed your other nits as suggested.
Attachment #535678 - Attachment is obsolete: true
Attachment #536397 - Flags: review?(roc)
Attachment #535678 - Flags: review?(roc)
s/ApplyForLine/ProcessLine/
Attachment #534503 - Attachment is obsolete: true
Attachment #536399 - Flags: review+
Attachment #536389 - Attachment description: art 5, do whole-character clipping between nsCharClipDisplayItem edges → part 5, do whole-character clipping between nsCharClipDisplayItem edges
(In reply to comment #255)
> A bigger issue is that out-of-flow stuff like an abs-pos child of a rel-pos
> inline can cause line scrollable area overflow that we don't want to trigger
> text-overflow markers :-(.

I'm pretty sure that what you're seeing is that the abs.pos. child
inflates its containing block's scrollable overflow rect to contain it.
So I think we handle this correctly - it may appear that it's the
abs.pos. that is overflowing, but it's both really.

Please attach a testcase if this isn't case you're thinking of and
I'll look into it.
Addendum to part 7.
Attachment #536452 - Flags: review?(roc)
Addendum to part 9.
Attachment #536453 - Flags: review?(roc)
Target Milestone: mozilla6 → mozilla7
(In reply to comment #262)
> Because the initial value we pass in is nsRect(0,0,0,0),
> we don't want to union the first frame we find with that.

UnionRect would ignore an empty rect so that shouldn't be a problem.

> > That said, I'm not sure that ignoring empty rects is what you want to do
> > here. Are you sure zero-width or zero-height content shouldn't affect the
> > placement of the marker? I tend to think that just because a frame is
> > zero-height shouldn't make it be ignored.
> 
> I think we should only align the markers at positions where there is
> visible content.  So, I've changed it to check if the frame has visual
> overflow and adding the empty border box rect if there is.
> Does that sound ok to you?

No, I really think we should not special-case zero-sized frames here. It's simpler, at least conceptually, and more predictable because a frame whose size changes from zero to something nonzero doesn't dramatically change the layout.

> I also think that we should NOT trigger markers for frames with a
> zero-width ScrollableOverflowRect because it would trigger just an
> overflow marker and NOT a scrollbar, which I think would be confusing
> to the user.

Actually it would still trigger a scrollbar. Zero-sized frames are allowed to contribute to the scrollable overflow area of their ancestors.

> Good catch, fixed by doing a second pass when needed (should be rare).
> Actually, it would be possible to do this in just one pass by collecting
> separate aFramesToHide sets and alignment rects for each side, but it
> doesn't seem worth penalizing the common case with this extra work.

Right.
Comment on attachment 536389 [details] [diff] [review]
part 5, do whole-character clipping between nsCharClipDisplayItem edges

Review of attachment 536389 [details] [diff] [review]:
-----------------------------------------------------------------
Attachment #536389 - Flags: review?(roc) → review+
Comment on attachment 536397 [details] [diff] [review]
part 7, add mozilla::css::TextOverflow

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

::: layout/generic/TextOverflow.cpp
@@ +95,5 @@
> +    }
> +  }
> +  const nsStyleDisplay* display = aFrame->GetStyleDisplay();
> +  return display->mDisplay != NS_STYLE_DISPLAY_INLINE &&
> +         display->IsInlineOutside();

OK, why do we need to check IsInlineOutside here?

@@ +203,5 @@
> +                       nsIFrame* aCommonAncestor)
> +{
> +  FrameDescendantOfData data = { aChild, aCommonAncestor, false };
> +  const_cast<TextOverflow::FrameHashtable&>(aSetOfFrames)
> +    .EnumerateEntries(&IsFrameDescendantOf, &data);

Why are you enumerating the entries here? If we just wanted to enumerate the entries we could use an array and not a hashtable. But this way requires one IsProperAncestorFrame check per entry in the hashtable, and we call it once per display item in the list!

A faster and simpler approach would be to walk the frame ancestors ourselves and check each one to see if it's in the hashtable.

@@ +309,5 @@
> +  if (aFrame->GetStyleVisibility()->IsVisible()) {
> +    nsRect childRect = aFrame->GetScrollableOverflowRect() +
> +                       aFrame->GetOffsetTo(mBlock);
> +    if (childRect.width != 0 ||
> +        !aFrame->GetVisualOverflowRect().IsEmpty()) {

As I said above, I really don't think we should be checking either of these.

Checking the VisualOverflowRect is especially problematic. Imagine that you're writing a spec for this behavior, how are you going to specify this?

@@ +535,5 @@
> +  while (item) {
> +    nsIFrame* itemFrame = item->GetUnderlyingFrame();
> +    if (IsFrameDescendantOfAny(itemFrame, aFramesToHide, mBlock)) {
> +      nsDisplayItem* above = item->GetAbove();
> +      aList->Remove(item);

Remove is currently O(N) because it walks the entire display list to find the previous item, which makes this potentially O(N^2). Since we're walking the display list here already, that is unnecessary, we can keep track of the previous item ourselves.

Perhaps the simplest thing to do is to have a temporary list and in our loop, keep calling aList->RemoveBottom() to get the next item and add it to the end of the temporary list if we decide to keep the item. Then at the end we can append the temporary list to the (empty) aList.
Comment on attachment 536379 [details] [diff] [review]
part 3, add nsDisplayList::Remove

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

I don't think we're actually going to need this if you use the approach I just suggested.
Since we probably will need to re-add separate left/right control, why not have mTextOverflow have two methods Left() and Right() which currently return the same data, but later we can have return different things?
Attached patch part 3, add nsDisplayList::Remove (obsolete) (deleted) — Splinter Review
Add 'aBelow' parameter for fast removal.
Attachment #536379 - Attachment is obsolete: true
Attachment #536982 - Flags: review?(roc)
Attachment #536379 - Flags: review?(roc)
Attached patch part 7, add mozilla::css::TextOverflow (obsolete) (deleted) — Splinter Review
(In reply to comment #269)
> OK, why do we need to check IsInlineOutside here?

You're right, it seems redundant - removed.

> Why are you enumerating the entries here?

My bad, sorry about that.

> > +    if (childRect.width != 0 ||
> > +        !aFrame->GetVisualOverflowRect().IsEmpty()) {
> 
> As I said above, I really don't think we should be checking either of these.

I've removed it.

> @@ +535,5 @@
> > +      aList->Remove(item);
> 
> Remove is currently O(N) 

Fixed

(In reply to comment #267)
> UnionRect would ignore an empty rect so that shouldn't be a problem.

Oops, right.  I changed it to use a local AlignmentEdges struct instead.
Perhaps we could fuse this with the ClipEdges in the other patch as the
start for a nsEdges struct to add in nsRect.h?  Seems like a useful
thing to have.

Test builds:
http://ftp.mozilla.org/pub/mozilla.org/firefox/try-builds/mpalmgren@mozilla.com-ecc8c5eb01e5/
Attachment #536397 - Attachment is obsolete: true
Attachment #536985 - Flags: review?(roc)
Attachment #536397 - Flags: review?(roc)
(In reply to comment #271)
> Since we probably will need to re-add separate left/right control, why not
> have mTextOverflow have two methods Left() and Right() which currently
> return the same data, but later we can have return different things?

But we still separate overflow state for left/right so we would have to
sometimes use mLeft instead of Left()...  I think it would be confusing.
Also, it's pretty trivial to revert this patch when the time comes so
it doesn't seem worth it.
Oh, and one more thing about 'part 7, add mozilla::css::TextOverflow'.
There was a bug introduced when we changed from analyzing display items
to analyzing frames.  A <br> frame triggers overflow markers.
See for example "Test 3", scroll the RTL block so it's not at an end
position -- there's a marker for line 2 and 3 that has nothing but a
<br>.  I couldn't think of a better way to avoid this than simply making
an early return in ExamineFrameSubtree for <br> frames.
Comment on attachment 536982 [details] [diff] [review]
part 3, add nsDisplayList::Remove

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

::: layout/base/nsDisplayList.cpp
@@ +656,5 @@
>    item->mAbove = nsnull;
>    return item;
>  }
>  
> +void nsDisplayList::Remove(nsDisplayItem* aItem, nsDisplayItem* aBelow) {

Why pass two parameters? You could just pass aBelow and that would be enough.

Although I still think your code would be simpler if you just did what I said at the end of comment #269 --- build a new list and move over only the items you want to keep.
Comment on attachment 536985 [details] [diff] [review]
part 7, add mozilla::css::TextOverflow

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

The rest looks good. Almost there!

::: layout/generic/TextOverflow.cpp
@@ +81,5 @@
> +// Return true if the frame is an atomic inline-level element.
> +// It's not supposed to be called for block frames since we never
> +// process block descendants for text-overflow.
> +static bool
> +IsAtomicElement(nsIFrame* aFrame)

To make this slightly more efficient, pass the frame type in as a parameter since the only caller already has the frame type.

@@ +179,5 @@
> +IsFrameDescendantOfAny(nsIFrame* aChild,
> +                       const TextOverflow::FrameHashtable& aSetOfFrames,
> +                       nsIFrame* aCommonAncestor)
> +{
> +  for (nsIFrame* f = aChild; f != aCommonAncestor; f = f->GetParent()) {

I think you should be using nsLayoutUtils::GetCrossDocParentFrame here.

And you'll want a test that all the contents of an IFRAME are removed when necessary.

@@ +540,5 @@
> +      }
> +    }
> +
> +    below = item;
> +    item = item->GetAbove();

Like I said, I still think this would be simpler if you just moved all the items you want to keep to a new list, delete the contents of the old list, and move the new list back to the old list.
Comment on attachment 536453 [details] [diff] [review]
part 9b, remove support for separate left/right property values in part 9.

Review of attachment 536453 [details] [diff] [review]:
-----------------------------------------------------------------
Attachment #536453 - Flags: review?(roc) → review+
(In reply to comment #276)
> Why pass two parameters? You could just pass aBelow and that would be enough.

In my case, yes, but it seems like a more versatile API to make aBelow
optional, for future consumers.

> Although I still think your code would be simpler if you just did what I
> said at the end of comment #269 --- build a new list and move over only the
> items you want to keep.

I think the code in TextOverflow is simpler with just a 'prev' pointer
rather than rebuilding the list.

Either way is fine with me though, I can drop the Remove method if
you think it wouldn't be useful to anyone else.
Comment on attachment 536452 [details] [diff] [review]
part 7b, remove support for separate left/right property values in part 7.

Review of attachment 536452 [details] [diff] [review]:
-----------------------------------------------------------------
Attachment #536452 - Flags: review?(roc) → review+
Attachment #536982 - Attachment is obsolete: true
Attachment #536982 - Flags: review?(roc)
Attached file Test 12, iframe (deleted) —
> > +IsAtomicElement(nsIFrame* aFrame)
> 
> To make this slightly more efficient, pass the frame type in as a parameter
> since the only caller already has the frame type.

All three callers fixed ;-)

> I think you should be using nsLayoutUtils::GetCrossDocParentFrame here.

Fixed

> Like I said, I still think this would be simpler if you just moved all the
> items you want to keep to a new list, delete the contents of the old list,
> and move the new list back to the old list.

Fixed
Attachment #536985 - Attachment is obsolete: true
Attachment #537032 - Flags: review?(roc)
Attachment #536985 - Flags: review?(roc)
Comment on attachment 537032 [details] [diff] [review]
part 7, add mozilla::css::TextOverflow

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

Excellent! Now we just need reftests :-)
Attachment #537032 - Flags: review?(roc) → review+
Attached patch part 10, tests (deleted) — Splinter Review
Attached patch part 8b, bug fix for part 8 (deleted) — Splinter Review
While writing the tests I discovered a bug.  There's an early return in
DisplayLine() when aLineArea.Intersects(aDirtyRect) is false.
We can't do that because the line might still need a marker.

Example testcase:
<div style="overflow:hidden; text-overflow:ellipsis">
  <span style="margin-left:-3em">x</span>
</div>
Attachment #537637 - Flags: review?(roc)
Comment on attachment 537637 [details] [diff] [review]
part 8b, bug fix for part 8

Review of attachment 537637 [details] [diff] [review]:
-----------------------------------------------------------------
Attachment #537637 - Flags: review?(roc) → review+
I found the problem with 'ellipsis' on non-Linux platforms...
It's a typo in GetEllipsis() which is testing !HasCharacter(ellipsis)
rather than HasCharacter(ellipsis).  So how could I miss something
major like that?  well, it turns out HasCharacter() always returns
false for Pango fonts...

Here's gfxFontEntry::HasCharacter():
   inline PRBool HasCharacter(PRUint32 ch) {
        if (mCharacterMap.test(ch))
            return PR_TRUE;

        return TestCharacterMap(ch);
    }

and
PRBool gfxFontEntry::TestCharacterMap(PRUint32 aCh)
{
    if (!mCmapInitialized) {
        ReadCMAP();
    }
    return mCharacterMap.test(aCh);
}

ReadCMAP() isn't implemented for Pango font entries, so we end up in
gfxFontEntry::ReadCMAP() which doesn't do anything.

So, I have two alternative solutions, the first one is to implement
(the virtual) gfxFcFontEntry::TestCharacterMap() to make HasCharacter()
work.  This would have the side-effect of also fixing GetHyphenTextRun():
http://mxr.mozilla.org/mozilla-central/source/layout/generic/nsTextFrameThebes.cpp#1678
(whether that is desirable or not, I do not know)
It also fixes gfxFontFamily::FindFontForChar(FontSearch*) which uses
HasCharacter() internally.
Attachment #538787 - Flags: review?(roc)
... an alternative is to not use HasCharacter() and instead use
gfxFontGroup::FindFontForChar() which seems to be implemented
correctly cross-platform.  Big fat WARNINGs on HasCharacter() and
anything that depends on it is warranted IMHO, it's a trap!
Attachment #538788 - Flags: review?(roc)
Attached patch part 10, tests (obsolete) (deleted) — Splinter Review
All tests now use text-overflow:ellipsis to make it easier to compare
the rendering against other UAs.  Except for "marker-string.html"
which is for testing text-overflow:<string>

Added a test for ellipsis fallback to "..." when the font doesn't have an ellipsis glyph.  Added tests for text-decorations/shadows in quirks and
standards mode.

All tests pass on Try server, with both "part 7c" alternatives.
Attachment #538790 - Flags: review?(roc)
(In reply to comment #287)
> So, I have two alternative solutions, the first one is to implement
> (the virtual) gfxFcFontEntry::TestCharacterMap() to make HasCharacter()
> work.  This would have the side-effect of also fixing GetHyphenTextRun():
> http://mxr.mozilla.org/mozilla-central/source/layout/generic/
> nsTextFrameThebes.cpp#1678
> (whether that is desirable or not, I do not know)
> It also fixes gfxFontFamily::FindFontForChar(FontSearch*) which uses
> HasCharacter() internally.

That doesn't matter because gfxPangoFontFamily overrides FindFontForChar.
Comment on attachment 538787 [details] [diff] [review]
part 7c, bug fix for GetEllipsis(), alternative 1

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

This looks good.
Attachment #538787 - Flags: review?(roc) → review+
Comment on attachment 538788 [details] [diff] [review]
part 7c, bug fix for GetEllipsis(), alternative 2

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

minusing on the grounds that it's better to make APIs work as advertised than leave them broken
Attachment #538788 - Flags: review?(roc) → review-
Comment on attachment 538790 [details] [diff] [review]
part 10, tests

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

Let's just call the reftest directory text-overflow.

::: layout/reftests/css-text-overflow/anonymous-block.html
@@ +55,5 @@
> +
> +s {position:absolute; background:lime; z-index:1; }
> +#mask1 {top:23em; left:31em; width:3em; height:3em; }
> +#mask2 {top:0em; left:10em; width:0.4em; height:6em; }
> +#mask3 {top:12em; left:4em; width:0.4em; height:6em; }

What are these masks for?
> Let's just call the reftest directory text-overflow.

ok

> What are these masks for?

All masks are for covering up anti-aliasing differences.
Why do we have these antialiasing differences? Are elements actually positioned in different locations? Is there another way to make them go away?
Attached file anti-aliasing bug (deleted) —
Here's an example of an anti-aliasing bug.  The two lines should look
identical.  This fails on Windows XP, but not on Linux or OSX.
Attachment #538788 - Attachment is obsolete: true
sigh... save the test file and replace url(attachment.cgi?id=340543) with
url(Ahem.ttf) and copy an Ahem.ttf file to the same directory...
Apparently one cannot refer to an attachment in a different bug in
Bugzilla anymore...
OK, that antialiasing issue is due to the fact that we do separate show_glyphs calls for the span vs the rest, and when multiple glyphs touch the same pixel we get different results depending on whether that happens in the same show_glyphs call or different show_glyphs calls.

But things should work if we do what we discussed earlier and use a free monospace font instead of Ahem. We have DejaVuSansMono.woff in layout/reftests/fonts, you should be able to use that. Use narrow characters like "l" to ensure glyphs don't overlap.
DejaVuSansMono.woff has an ellipsis character, fortunately.
Attached patch part 10, tests (obsolete) (deleted) — Splinter Review
* changed the font from Ahem to DejaVuSansMono
* removed all masks except in "scroll-rounding.html" which masks
  the uninteresting edge of the block that had some AA differences
* created a new font, TestEllipsisFallback.woff, that has glyphs only
  for '.', 'X' and ' ' for testing ellipsis fallback
* added a couple of tests

The remaining AA differences are:
  bidi-simple-scrolled.html  on Windows and OSX
  bidi-simple.html           on Android
where the dots of the ellipsis itself has AA differences.
I couldn't find way to work around that so I disabled those
tests on the platforms that failed.
Attachment #538790 - Attachment is obsolete: true
Attachment #540029 - Flags: review?(roc)
Attachment #538790 - Flags: review?(roc)
Comment on attachment 540029 [details] [diff] [review]
part 10, tests

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

I don't think you need to copy DejaVuSansMono.woff here. Make your tests HTTP(..) and then you should be able to access the font under fonts/.

It would be nice to find a way to make bidi-simple-scrolled pass across platforms, but I think we shouldn't let that stop this landing, so r+ with the above fixed. Do you know what causes the antialiasing difference in bidi-simple-scrolled.
Attachment #540029 - Flags: review?(roc) → review+
Attached patch part 10, tests (deleted) — Splinter Review
* added HTTP(..) to reftest.list and added ../fonts/ to the font-face urls
* removed the local Ahem.ttf and DejaVuSansMono.woff files

Switching to http caused an anti-aliasing difference in one of the
scrollbar buttons, so I tweaked that test to hide that scrollbar
(the scrollbar itself wasn't relevant for the test anyway).

> It would be nice to find a way to make bidi-simple-scrolled pass across
> platforms, but I think we shouldn't let that stop this landing, so r+ with
> the above fixed. Do you know what causes the antialiasing difference in
> bidi-simple-scrolled.

Agreed.  I wish I knew, but I have no clue to be honest.  At first I thought
it might be rounding or async rendering in the scroll frame, but I have
ruled out those two.  I'm pretty sure the layout is correct and that the
problem is graphics oriented.

If you compare the dots of the ellipsis (on Windows XP), you'll see that
the text-overflow ellipsis has AA pixels in grey colors, whereas the
reference ellipsis has light-blue/beige AA pixels like the nearby text.
(you can use the magnifier tool to see this clearly)

Generally, the DejaVuSansMono text looks ugly on Windows, it's fuzzy due to
the heavy anti-aliasing.  I can clearly see a blue-ish shadow on the right
side and a red-ish one on the left even without using a magnifier.
(kind of what you could see on old CRT monitors)
If I set the pref gfx.font_rendering.cleartype.use_for_downloadable_fonts
to false then the text looks crisp, and if I run the text-overflow reftests
with this setting it pass!
Attachment #540029 - Attachment is obsolete: true
Attachment #540653 - Flags: review+
Wow, I just discovered something really odd...  looking at the text-overflow
ellipsis using the magnifier, the dots of the ellipsis has grey-ish AA
pixels when I load the page, then after 4-5 seconds they get the same
colors as the AA pixels on the text.  Weird!
That's layer scrolling doing that, I guess. But it shouldn't be...
Attached file test case - background bleed (deleted) —
I can't find this test case in the list of test cases attached to this bug. The second case in the test seems wrong to me in chrome and IE. I don't have time to get the source to test this out, but would be interested in your feedback if you have time.

Basically 
If I have span

New Zealand

and I am cutting off from w onwards and inserting an ellipsis, I would not expect background colours from the cut off text to be applied to the parent span.

I have raised a related webkit bug at https://bugs.webkit.org/show_bug.cgi?id=63058
Attachment #540704 - Attachment mime type: text/plain → text/html
What's left to get this landed?  Can it land this week?
It's ready to land.  It should be on mozilla-inbound in a few hours.
(In reply to comment #305)
"Hide characters and atomic inline-level elements at the end edge of the line as necessary to fit the ellipsis."
http://dev.w3.org/csswg/css3-ui/#text-overflow

In your example, the red background belongs to a non-atomic inline,
which is still displayed even though its text is removed.

I don't see any error in IE9 or Chrome13 for your testcase, this is how
Firefox will render it as well.
hg.mozilla.org and tbpl.mozilla.org have connectivity problems for me.
I'll try to land tomorrow instead.
(In reply to comment #309)
> hg.mozilla.org and tbpl.mozilla.org have connectivity problems for me.

FWIW, if you land on mozilla-inbound, you wouldn't need to watch the tree (but you do need to be able to access hg.mozilla.org!).  :-)
http://hg.mozilla.org/integration/mozilla-inbound/rev/a24270f27843
http://hg.mozilla.org/integration/mozilla-inbound/rev/847fc92f36ee
http://hg.mozilla.org/integration/mozilla-inbound/rev/451c13333f14
http://hg.mozilla.org/integration/mozilla-inbound/rev/e6d61d90c72f
Flags: in-testsuite+
Whiteboard: [parity-webkit][parity-IE][parity-opera][evang-wanted][wanted2.1?][CSS IS AWE]SOME → [inbound][parity-webkit][parity-IE][parity-opera][evang-wanted][wanted2.1?][CSS IS AWE]SOME
am i the only one experiencing rendering / overlay weirdness when selecting and deselecting text around the ellipsis area? bug?
(In reply to comment #314)
> am i the only one experiencing rendering / overlay weirdness when selecting
> and deselecting text around the ellipsis area? bug?

No, I see that too on Mac OS X.

If I take sheppy's example in link in comment 313 and select some chars in
a document with that example stylesheet applied, I see the ellipsis ABOVE some
text when I resize the window.
I hit the same issue in the mac version of BlueGriffon I just built from
an updated tree.

I guess it deserves a bug spin-off since the layout is really horked in the
case detailed above.
Depends on: 666669
(In reply to comment #314)
> am i the only one experiencing rendering / overlay weirdness when selecting
> and deselecting text around the ellipsis area? bug?

This is a known problem that we decided wasn't a blocker.
I filed bug 666669 about it.

(I also filed bug 666362 on the text anti-aliasing problem when scrolling)
Depends on: 666689
Depends on: 666696
Depends on: 666751
You should also file a bug on making text-shadow work on the ellipsis text.
Oh you already did, sorry.

I think bug 666669 is the highest priority followup at the moment.
Whiteboard: [inbound][parity-webkit][parity-IE][parity-opera][evang-wanted][wanted2.1?][CSS IS AWE]SOME → [parity-webkit][parity-IE][parity-opera][evang-wanted][wanted2.1?][CSS IS AWE]SOME
Depends on: 667010
Whiteboard: [parity-webkit][parity-IE][parity-opera][evang-wanted][wanted2.1?][CSS IS AWE]SOME → [parity-webkit][parity-IE][parity-opera][evang-wanted][CSS IS AWE]SOME
Depends on: 669284
would it make sense to change the selection behavior to be a bit more clear?

http://www.quirksmode.org/css/textoverflow.html

1. Triple-clicking the text selects everything behind the ellipsis as well.
2. Drag-selecting will select text as far as the mouse is dragged, even beyond the ellipsis.

in both cases it is unclear that there is text selected beyond the ellipsis because there is no distinction from a selection which actually terminates at it. might be better at least to show the ellipsis itself as selected if anything beyond it is in the selection range.

must say it also feels a bit strange not to be able to select something that looks like text :\
(In reply to comment #319)
> must say it also feels a bit strange not to be able to select something that
> looks like text :\

That's the case in general with CSS generated content, not new.
(In reply to comment #319)
> 1. Triple-clicking the text selects everything behind the ellipsis as well.
> 2. Drag-selecting will select text as far as the mouse is dragged, even
> beyond the ellipsis.
> 
> in both cases it is unclear that there is text selected beyond the ellipsis
> because there is no distinction from a selection which actually terminates
> at it. might be better at least to show the ellipsis itself as selected if
> anything beyond it is in the selection range.

I think we should hide the marker if it overlaps the caret or any selected text. I thought we had a followup bug filed for that, but I can't find it.
Refining how selection/caret affects text-overflow is bug 668240.
(forgot to add it to the dependency list, sorry)
Depends on: 668240
Depends on: 669618
Depends on: 670564
Depends on: 680610
Depends on: 688878
Depends on: 690131
Depends on: 689897
Hi!
In some cases if the text overflows, FF7 only shows the ellipsis, nothing more. I don't know yet how to reproduce it on my own, I found this problem, while using Roundcube Webmail.
(In reply to Dénes Sebestyén from comment #323)
> In some cases if the text overflows, FF7 only shows the ellipsis, nothing
> more. I don't know yet how to reproduce it on my own, I found this problem,
> while using Roundcube Webmail.

It has been mentioned in the release notes and fixed in the product. See Bug 680610 for details.

http://www.mozilla.org/en-US/firefox/7.0/releasenotes/#issues
Depends on: 669579
Depends on: 690187
(In reply to Dénes Sebestyén from comment #323)
> Hi!
> In some cases if the text overflows, FF7 only shows the ellipsis, nothing
> more. I don't know yet how to reproduce it on my own, I found this problem,
> while using Roundcube Webmail.

Salut !
I have the same problem with FF8 using Roundcube Webmail.
For Roundcube Webmail issues, see bug 680610.
Depends on: 703360
Blocks: 775823
Depends on: 886716
Depends on: 1070742
Depends on: 1516965
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: