Bug 22891 - Scrolling in Windows Cairo Broken if no background color set.
Summary: Scrolling in Windows Cairo Broken if no background color set.
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebKit Misc. (show other bugs)
Version: 523.x (Safari 3)
Hardware: PC Windows XP
: P2 Normal
Assignee: Nobody
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2008-12-16 15:25 PST by Brent Fulgham
Modified: 2009-06-09 11:32 PDT (History)
3 users (show)

See Also:


Attachments
Simple test case showing the scroll not working (6.58 KB, text/html)
2008-12-16 15:25 PST, Brent Fulgham
no flags Details
Simple test case showing the scroll functioning properly (6.59 KB, text/html)
2008-12-16 15:26 PST, Brent Fulgham
no flags Details
Image of 'bad' scroll (51.10 KB, image/jpeg)
2008-12-16 15:26 PST, Brent Fulgham
no flags Details
Image of 'bad' scroll in the reduced test case (55.47 KB, image/jpeg)
2008-12-16 15:26 PST, Brent Fulgham
no flags Details
Image of 'good' scroll in reduced test case (55.89 KB, image/jpeg)
2008-12-16 15:27 PST, Brent Fulgham
no flags Details
Corrects HDC/cairo_t* Context Synchronization (14.40 KB, patch)
2009-06-08 15:08 PDT, Brent Fulgham
eric: review-
Details | Formatted Diff | Diff
Revision based on Eric's review comments. (14.33 KB, patch)
2009-06-09 10:32 PDT, Brent Fulgham
eric: review+
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Brent Fulgham 2008-12-16 15:25:19 PST
Attempting to view the http://planet.webkit.org page with the WinLauncher executable on the Cairo-backend exhibits a scrolling bug (see attached image "bad_scroll_planet_webkit.jp").  As you scroll further down the page, the "bad" region at the top of the screen grows, until some inflection point at which the cycle seems to start over.

I managed to reduce the problem down to the point where the scroll works as expected if the page has a background color set.  Consider Bad.html (and corresponding "bad_scroll_reduced.jpg") where the scroll misbehaves, and the Good.html (and corresponding "good_scroll_reduced.jpg"), where things work properly.

bfulgham@bfulgham ~
$ diff Bad.html Good.html
6c6
< <body>
---
> <body bgcolor="#FFFFFF">
Comment 1 Brent Fulgham 2008-12-16 15:25:54 PST
Created attachment 26069 [details]
Simple test case showing the scroll not working
Comment 2 Brent Fulgham 2008-12-16 15:26:12 PST
Created attachment 26070 [details]
Simple test case showing the scroll functioning properly
Comment 3 Brent Fulgham 2008-12-16 15:26:34 PST
Created attachment 26071 [details]
Image of 'bad' scroll
Comment 4 Brent Fulgham 2008-12-16 15:26:53 PST
Created attachment 26072 [details]
Image of 'bad' scroll in the reduced test case
Comment 5 Brent Fulgham 2008-12-16 15:27:11 PST
Created attachment 26073 [details]
Image of 'good' scroll in reduced test case
Comment 6 Brent Fulgham 2008-12-16 15:34:50 PST
It appears that the bug persists if the CSS uses a background image, even if you set a background color.  If you do not set the background image (and do not set a background color) scroll breaks.  If you remove the background image and set a background color everything is fine.
Comment 7 Gustavo Noronha (kov) 2008-12-17 09:17:18 PST
Brent asked people to try if this happens with GTK+; I tested with GtkLauncher from TOT, and was unable to reproduce it with http://planet.webkit.org/.
Comment 8 Brent Fulgham 2009-02-05 14:59:45 PST
Marshall Cullpepper and the guys on the Titanium team seemed to find a workaround:

http://github.com/marshall/webkit_titanium/commit/aa1a8dce6e8df560c756701872b316736af9049d 
Comment 9 Brent Fulgham 2009-02-05 15:22:24 PST
Current workaround:

@@ -771,7 +771,9 @@ void WebView::scrollBackingStore(FrameView* frameView, int dx, int dy, const Int

     // Scroll the bitmap.
     RECT scrollRectWin(scrollViewRect);
     RECT clipRectWin(clipRect);
-    ::ScrollDC(bitmapDC, dx, dy, &scrollRectWin, &clipRectWin, updateRegion, 0);
+    // fix for repaint issue - http://jira.appcelerator.org/jira/browse/TI-58
+    ::ScrollWindowEx(m_viewWindow, dx, dy, &scrollRectWin, &clipRectWin, updateRegion, 0, 0);
+    //::ScrollDC(bitmapDC, dx, dy, &scrollRectWin, &clipRectWin, updateRegion, 0);
     RECT regionBox;
     ::GetRgnBox(updateRegion, &regionBox);

I'm worried this may not always do the right thing, but perhaps there is no difference in practice.  The positive Y direction in Cairo is inverted from Windows, which has caused several other bugs.  Maybe something similar is wrong with the handling of the bitmap region being scrolled.
Comment 10 Dave Hyatt 2009-02-05 15:27:08 PST
That does not do the right thing.  It scrolls the bits of the window itself rather than the backing store bits in the offscreen bitmap.
Comment 11 Dave Hyatt 2009-02-05 15:29:23 PST
This bug looks like there is a flipping problem.  Maybe look into how the DIB is made (we flip it) and make sure Cairo understands that there is a "flip" in effect and respects it.
Comment 12 Marshall Culpepper 2009-02-05 16:25:34 PST
FYI This was a pretty crude hack we made late last night in an effort to just get it working before our release. Dave/Brent can you suggest where in the code we look for mirroring the offscreen bitmap's "flip" in Cairo?
Comment 13 Brent Fulgham 2009-02-06 11:42:49 PST
Actually, I don't see how this could be caused by flipping of the context because when a background color is set, the scrolling problem goes away.  If it was purely a context orientation issue, I don't see how this simple difference would make the issue go away.

I played with flipping the cairo_matrix for the DC in GraphicsContextCairo, but this just created a flipped view of the web page, which still suffered from the tearing problem.  I did not try adjusting the bitmap region in the WebView code because I stopped thinking that this issue is related to the transform matrix since the presence of background color yields a correct scroll.
Comment 14 Brent Fulgham 2009-03-10 13:39:52 PDT
This bug seems to go away in Debug builds of the current WebKit tree (with Cairo 1.8.6).  Unfortunately, it still happens with Release builds.
Comment 15 Marshall Culpepper 2009-03-10 14:11:00 PDT
Interesting -- maybe a race condition?
Comment 16 Brent Fulgham 2009-03-26 22:48:38 PDT
For reference:  This is happening with Cairo 1.8.6 on Windows, though I also saw it with an earlier version (1.8.4 or 1.8.2) and may have existed far before that.
Comment 17 xfdbse 2009-03-26 23:20:03 PDT
(In reply to comment #16)
> For reference:  This is happening with Cairo 1.8.6 on Windows, though I also
> saw it with an earlier version (1.8.4 or 1.8.2) and may have existed far before
> that.
> 

yeah, also happen with cairo 1.4.14 with directfb+gtk+ platform.
Comment 18 Brent Fulgham 2009-06-08 15:00:59 PDT
It turns out that this bug is specific to the Cairo Windows port, though some of the behavior seems to be mimicked by the FrameBuffer implementation.

The Windows HDC was not being kept consistently in sync with the Cairo context, resulting in strange transformations and incorrect geometry computations when scrolling or otherwise interacting with screens containing mixtures of text, images, and paths.

The attached patch brings the Cairo implementation more in line with the corresponding CoreGraphics implementation, and keeps the HDC in sync with the corresponding cairo_t* context.
Comment 19 Brent Fulgham 2009-06-08 15:08:11 PDT
Created attachment 31065 [details]
Corrects HDC/cairo_t* Context Synchronization
Comment 20 Eric Seidel (no email) 2009-06-08 16:26:52 PDT
Comment on attachment 31065 [details]
Corrects HDC/cairo_t* Context Synchronization

In general this looks fine.  but I'm not really a windows guy.

Should follow "the create rule" and webkit naming style:
 39 static cairo_t* CairoContextWithHDC(HDC hdc, bool hasAlpha)

createCairoContextWithHDC(...)

Does:
 44     GetObject(bitmap, sizeof(info), &info);

need a later release call?

Extra spaces:
 53     cairo_t* context = cairo_create (image);
 54     cairo_surface_destroy (image);


This shoudl be abstracted into its own function:
90         // Create a bitmap DC in which to draw.
 91         BITMAPINFO bitmapInfo;
 92         bitmapInfo.bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
which returns the right BITMAPINFO.
BITMAPINFO bitmapInfo = bitmapInfoForSize(destRect.size());


Should be its own function too:
         if (supportAlphaBlend) {
 114             BITMAP bmpInfo;
 115             GetObject(bitmap, sizeof(bmpInfo), &bmpInfo);
 116             int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight;
 117             memset(bmpInfo.bmBits, 0, bufferSize);
 118         }
fillWithClearColor(bitmap);


Should again be another local static function:
123         // Apply a translation to our context so that the drawing done will be at (0,0) of the bitmap.
 124         XFORM xform;
 125         xform.eM11 = 1.0f;
 126         xform.eM12 = 0.0f;
 127         xform.eM21 = 0.0f;
 128         xform.eM22 = 1.0f;
 129         xform.eDx = -dstRect.x();
 130         xform.eDy = -dstRect.y();

XFORM xform = makeXFORM(1, 0, 0, 1, -x, -y);

Breaking it all out like that will make the original method much smaller, and hopefully make it easier to understand what you're actually trying to do.

Ugly (and error prone!) that we have to do this all manually:
177         // Delete all our junk.
 178         cairo_surface_destroy(image);
 179         ::DeleteDC(hdc);
 180         ::DeleteObject(bitmap);

This is confusing:
         return;
 183     }
 184 
 185     m_data->restore();

I think that restore shoudl be an early return, and the if block removed.

More extra spaces:
     cairo_t* targetRef = cairo_create (image);
 58     cairo_surface_destroy (image);
Comment 21 Brent Fulgham 2009-06-08 22:17:46 PDT
(In reply to comment #20)
> Ugly (and error prone!) that we have to do this all manually:
> 177         // Delete all our junk.
>  178         cairo_surface_destroy(image);
>  179         ::DeleteDC(hdc);
>  180         ::DeleteObject(bitmap);

I think this is a necessary evil.  After searching the existing source, it seems we don't have autopointers that support device context, bitmap, or Cairo types.  External libraries may provide this, but I don't think there's anything internal to WebKit that will clean this up (see the CoreGraphics version of this file for similar structure).
Comment 22 Brent Fulgham 2009-06-09 10:12:25 PDT
Thanks for the thorough review Eric.  A few comments:

(In reply to comment #20)
> Should follow "the create rule" and webkit naming style:
>  39 static cairo_t* CairoContextWithHDC(HDC hdc, bool hasAlpha)

Done
 
> Does:
>  44     GetObject(bitmap, sizeof(info), &info);
> 
> need a later release call?

No -- this just files in data in the 'info' autovariable.
 
> Extra spaces:
>  53     cairo_t* context = cairo_create (image);
>  54     cairo_surface_destroy (image);

Done

> This shoudl be abstracted into its own function:
 BITMAPINFO bitmapInfo = bitmapInfoForSize(destRect.size());

Done
 
> Should be its own function too:
> fillWithClearColor(bitmap);

Done
 
> Should again be another local static function:
> XFORM xform = makeXFORM(1, 0, 0, 1, -x, -y);

I agree with this idea, but looking over the other sources, the manual structure assignment is the standard way it's currently done.  I'd like to do a second pass to refactor this and hit the five or so other files with this same issue, rather than expand the scope of this bug to address this right now.
 
> Ugly (and error prone!) that we have to do this all manually:
> 177         // Delete all our junk.
>  178         cairo_surface_destroy(image);
>  179         ::DeleteDC(hdc);
>  180         ::DeleteObject(bitmap);

Unfortunately, this is a necessary evil at the moment -- there is no autopointer available to deal with this in the current WebKit infrastructure.
 
> This is confusing:
>          return;
>  183     }
>  184 
>  185     m_data->restore();
> 
> I think that restore shoudl be an early return, and the if block removed.

Done

> More extra spaces:
>      cairo_t* targetRef = cairo_create (image);
>  58     cairo_surface_destroy (image);

Done. 

The revised patch includes these changes.
Comment 23 Brent Fulgham 2009-06-09 10:32:33 PDT
Created attachment 31100 [details]
Revision based on Eric's review comments.
Comment 24 Eric Seidel (no email) 2009-06-09 11:14:22 PDT
Comment on attachment 31100 [details]
Revision based on Eric's review comments.

extra space:
 80 static void fillWithClearColor (HBITMAP bitmap)

and here:
 93         m_data->cr = createCairoContextWithHDC (dc, hasAlpha);

I really don't know what this all does.  But it seems sane enough.  Ideally someone with Cairo knowledge would review this, but I'm not sure we have any of those... at least any who would have time.

Looks sane enough.  Please fix the nits when landing.
Comment 25 Peter Kasting 2009-06-09 11:21:21 PDT
Taking myself off, I'm utterly ignorant of Cairo, low-level Windows graphics, etc. :)

BTW, if you really want, you could write a little local helper autopointer class for these types.  A dozen lines of C++ would do it.  It might be overkill for just this patch but it'd provide an implementation that could later be moved somewhere where everyone could use it.
Comment 26 Brent Fulgham 2009-06-09 11:29:18 PDT
(In reply to comment #24)
> (From update of attachment 31100 [details] [review])
> extra space:
>  80 static void fillWithClearColor (HBITMAP bitmap)
> 
> and here:
>  93         m_data->cr = createCairoContextWithHDC (dc, hasAlpha);

Fixed.  I have *got* to stop using my work style here.
Comment 27 Brent Fulgham 2009-06-09 11:29:59 PDT
(In reply to comment #25)
> BTW, if you really want, you could write a little local helper autopointer
> class for these types.  A dozen lines of C++ would do it.  It might be overkill
> for just this patch but it'd provide an implementation that could later be
> moved somewhere where everyone could use it.

That's a good idea.  I'm preparing a refactoring patch which should include that. 

Comment 28 Brent Fulgham 2009-06-09 11:32:30 PDT
Landed in @r44539.