Bug 12423 - Mixing white-space:pre text with non white-space:pre text does not wrap properly
Summary: Mixing white-space:pre text with non white-space:pre text does not wrap properly
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Layout and Rendering (show other bugs)
Version: 420+
Hardware: Mac OS X 10.4
: P2 Normal
Assignee: Nobody
URL: http://validator.w3.org/
Keywords: HasReduction
Depends on:
Blocks:
 
Reported: 2007-01-26 12:08 PST by David Kilzer (:ddkilzer)
Modified: 2007-01-28 19:30 PST (History)
3 users (show)

See Also:


Attachments
Screenshot of overlapping text (203.71 KB, image/png)
2007-01-26 12:10 PST, David Kilzer (:ddkilzer)
no flags Details
Test file to validate (34.32 KB, text/html)
2007-01-26 12:16 PST, David Kilzer (:ddkilzer)
no flags Details
Real test file to validate (224 bytes, text/html)
2007-01-26 12:26 PST, David Kilzer (:ddkilzer)
no flags Details
Reduced test case (336 bytes, text/html)
2007-01-27 08:41 PST, David Kilzer (:ddkilzer)
no flags Details
Layout test case (1.42 KB, text/html)
2007-01-27 19:34 PST, David Kilzer (:ddkilzer)
no flags Details
Patch v1 (68.89 KB, patch)
2007-01-27 19:35 PST, David Kilzer (:ddkilzer)
darin: review+
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description David Kilzer (:ddkilzer) 2007-01-26 12:08:59 PST
Summary:

After validating an HTML document, some error descriptions contain a mix of <code> tags in them.  When the Safari window is narrowed horizontally, the text does not wrap properly.

Steps to reproduce:

1. Open Safari/WebKit.
2. Open URL: http://validator.w3.org/
3. Upload test file (will be attached shortly).
4. Narrow width of browser until text starts wrapping in Error #1.

Expected results:

Text with <code> tags should wrap properly.

Actual results:

Text with <code> tags does not wrap properly, but insteads overlaps.

Notes:

Tested with locally-built debug build of WebKit r19151 with Safari 2.0.4 (419.3) on Mac OS X 10.4.8 (8N1037).
Comment 1 David Kilzer (:ddkilzer) 2007-01-26 12:10:22 PST
Created attachment 12688 [details]
Screenshot of overlapping text
Comment 2 David Kilzer (:ddkilzer) 2007-01-26 12:16:41 PST
Created attachment 12689 [details]
Test file to validate

Use this file in Step 3 of "Steps to reproduce".
Comment 3 David Kilzer (:ddkilzer) 2007-01-26 12:25:05 PST
Comment on attachment 12689 [details]
Test file to validate

Oops!  This attachment was for Bug 12407, but it will show the issue as well if validated by URL:

http://bugs.webkit.org/attachment.cgi?id=12689

Scroll down to Error #8, then reduce the width of the browser window.
Comment 4 David Kilzer (:ddkilzer) 2007-01-26 12:26:29 PST
Created attachment 12691 [details]
Real test file to validate

Use this file in Step 3 of "Steps to reproduce", not Attachment 12688 [details].
Comment 5 David Kilzer (:ddkilzer) 2007-01-26 12:29:50 PST
(In reply to comment #0)
> Notes:
> 
> Tested with locally-built debug build of WebKit r19151 with Safari 2.0.4
> (419.3) on Mac OS X 10.4.8 (8N1037).

Shipping Safari 2.0.4 (419.3) on Mac OS X 10.4.8 (8N1037) does the same thing, although the effect isn't quite as bad (it wraps before overlapping as much as ToT).
Comment 6 David Kilzer (:ddkilzer) 2007-01-27 08:41:27 PST
Created attachment 12710 [details]
Reduced test case

Mixing text with white-space:pre and without white-space:pre within a text-align:justify block causes the text to render overlapped (depending on the width of the containing block).

Note that the "width: 372px;" style may be removed from <p>, but then the browser must be resized to view the bug.
Comment 7 David Kilzer (:ddkilzer) 2007-01-27 09:49:32 PST
I've tried tracing through the rendering code, and found that in RenderBlock::computeHorizontalPositionsForLine(), availableWidth = 372 while totWidth = 509 by the time the code reaches the "Armed with the total width of the line (without justification),..." comment.

It seems that the text with the white-space:pre style is lying about its width during layout somewhere.

Note that Opera 9.10, Firefox 2.0.0.1 and Mac MSIE 5.23 all render this without overlapping text.
Comment 8 mitz 2007-01-27 10:21:28 PST
I think the root cause is that line breaks are not computed correctly. See this (left-aligned) example:

<div style="width: 350px; background-color: #ffe;">
Typical for type are type="text/css" for &lt;style&gt; and <span style="white-space: pre;">type="text/javascript"</span> for &lt;script&gt;.
</div>

Comment 9 David Kilzer (:ddkilzer) 2007-01-27 11:02:30 PST
(In reply to comment #8)
> I think the root cause is that line breaks are not computed correctly. See this
> (left-aligned) example:

I'm not positive, but the text with the white-space:pre style may be "binding" to a space that precedes it instead of breaking.  I need to trace through RenderBlock::findNextLineBreak() some more to grok it.
Comment 10 David Kilzer (:ddkilzer) 2007-01-27 19:34:23 PST
Created attachment 12719 [details]
Layout test case
Comment 11 David Kilzer (:ddkilzer) 2007-01-27 19:35:27 PST
Created attachment 12720 [details]
Patch v1

Proposed patch.
Comment 12 David Kilzer (:ddkilzer) 2007-01-27 19:44:53 PST
(In reply to comment #10)
> Created an attachment (id=12719) [edit]
> Layout test case

This test case passes on Opera 9.10 and MSIE 6.  Two of the five cases (3rd and 5th) fail to wrap on Firefox 2.0.0.1.  Filed B.M.O. 368458:

https://bugzilla.mozilla.org/show_bug.cgi?id= 368458

Comment 13 David Kilzer (:ddkilzer) 2007-01-27 19:55:18 PST
(In reply to comment #12)
> This test case passes on Opera 9.10 and MSIE 6.  Two of the five cases (3rd and
> 5th) fail to wrap on Firefox 2.0.0.1.  Filed B.M.O. 368458:

https://bugzilla.mozilla.org/show_bug.cgi?id=368458
Comment 14 Dave Hyatt 2007-01-27 20:30:50 PST
Comment on attachment 12720 [details]
Patch v1

This looks suspicious to me.  I'll need to study it some more.
Comment 15 David Kilzer (:ddkilzer) 2007-01-27 21:18:50 PST
(In reply to comment #14)
> (From update of attachment 12720 [details] [edit])
> This looks suspicious to me.  I'll need to study it some more.

I wouldn't expect anything less from my first ever patch to bidi.cpp.  :)

Note that all layout tests passed with this change.

I started with something like this:

    if (!willFitOnLine && !autoWrap && last && last->isText() && last->style()->autoWrap())
        canPlaceOnLine = false;

Then I came up with some more test cases (see Attachment 12719 [details]) that required this concept to be generalized for any text run on the line.  Thus if any text run on the current line had autoWrap set to true, then this text run shouldn't be placed on the current line.   Instead, it should wrap.

Of course, I could be missing something since I'm not real familiar with this code.

Comment 16 Dave Hyatt 2007-01-28 14:32:33 PST
Why does this only happen when text-align is justify?
Comment 17 David Kilzer (:ddkilzer) 2007-01-28 16:54:56 PST
(In reply to comment #16)
> Why does this only happen when text-align is justify?

It doesn't just happen when text-align is justify.  That's how I noticed it in the first place (and is what caused the overlapping text).

Mitz's test cases show that in the default case (no text-align specified), the text is drawn outside the bounding box.  See "Layout test case" (Attachment 12719 [details]).

Comment 18 Darin Adler 2007-01-28 18:05:32 PST
Comment on attachment 12720 [details]
Patch v1

+        autoWrapWasEverTrueOnLine = autoWrapWasEverTrueOnLine || autoWrap;

You can write that as ||= if you like.

Looks fine, r=me
Comment 19 David Kilzer (:ddkilzer) 2007-01-28 18:35:41 PST
(In reply to comment #18)
> (From update of attachment 12720 [details] [edit])
> +        autoWrapWasEverTrueOnLine = autoWrapWasEverTrueOnLine || autoWrap;
> You can write that as ||= if you like.

I don't think C/C++/ObjC[++] supports the ||= logical assignment operator.  I know Perl does, though.

> Looks fine, r=me

Is Hyatt okay with this patch?  He expressed doubts in previous comments.
Comment 20 Dave Hyatt 2007-01-28 18:59:28 PST
Something is still bothering me about this... I feel like there's a scenario I could craft where the break point would be incorrect, but since I can't come up with it after thinking about it for a bit, r=me as well.

Comment 21 David Kilzer (:ddkilzer) 2007-01-28 19:20:24 PST
After looking at this expression again, I think it may be simplified, but I will NOT make this change when committing:

    bool canPlaceOnLine = willFitOnLine || !autoWrap;
    if (!willFitOnLine && !autoWrap && autoWrapWasEverTrueOnLine)
        canPlaceOnLine = false;

Could be simplified to (parens added for visual syntactic sugar):

    bool canPlaceOnLine = willFitOnLine || (!willFitOnLine && !autoWrapWasEverTrueOnLine);

Comment 22 David Kilzer (:ddkilzer) 2007-01-28 19:30:02 PST
Committed revision 19206.