[GTK] Switch to a backing store approach for painting WebKitWebView
authormrobinson@webkit.org <mrobinson@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 30 Oct 2011 16:29:33 +0000 (16:29 +0000)
committermrobinson@webkit.org <mrobinson@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 30 Oct 2011 16:29:33 +0000 (16:29 +0000)
https://bugs.webkit.org/show_bug.cgi?id=70213

Reviewed by Gustavo Noronha Silva.

Source/WebCore:

No new tests. The changes to WebCore should not change
behavior. The changes in WebKit are covered by existing
tests.

* platform/cairo/WidgetBackingStore.h:
(WebCore::WidgetBackingStore::size): Added this getter for the size.
* platform/cairo/WidgetBackingStoreCairo.cpp:
(WebCore::WidgetBackingStore::WidgetBackingStore): Initialize size.
* platform/graphics/cairo/CairoUtilities.cpp:
(WebCore::copyRectFromCairoSurfaceToContext): Added this new helper.
(WebCore::copyRectFromOneSurfaceToAnother): Use the new helper.
* platform/graphics/cairo/CairoUtilities.h:
* platform/gtk/GtkWidgetBackingStoreX11.cpp:
(WebCore::WidgetBackingStore::WidgetBackingStore): Initialize the size.

Source/WebKit/gtk:

Paint the WebView into a backing store. This prevents expose events from
triggering a layout, making scrolling and resizing much smoother.

* WebCoreSupport/ChromeClientGtk.cpp:
(WebKit::ChromeClient::ChromeClient): Initialize new members.
(WebKit::repaintEverythingSoonTimeout): Added this helper which repaints
the WebView more quickly during resize events.
(WebKit::clipOutOldWidgetArea): Clips out the old widget area, so that we
can clear out invalid pixels when growing the widget into a pre-existing
backing store.
(WebKit::clearEverywhereInBackingStore): Clear out the entire backing store.
(WebKit::ChromeClient::widgetSizeChanged): Handle widget size changes by
intelligently resizing or reusing the existing backing store.
(WebKit::coalesceRectsIfPossible): Moved this method from webkitwebview.cpp.
(WebKit::paintWebView): Moved this method from webkitwebview.cpp.
(WebKit::ChromeClient::performAllPendingScrolls): If there are any pending
scrolling operations, perform them by scrolling the backing store.
(WebKit::ChromeClient::paint): Added this timer callback, which paints the
WebView after a short timeout.
(WebKit::ChromeClient::invalidateWindow): No need to do anything here any longer.
(WebKit::ChromeClient::invalidateContentsAndWindow): Now unite the rect with
the existing dirty region and queue the paint timeout.
(WebKit::ChromeClient::scroll): Now just queue a scroll event in the repaint timeout.
Intelligently calculate the area to move and the dirty area using code derived
from WebKit2.
* WebCoreSupport/ChromeClientGtk.h: Added new method definitions and also use
the WebCore namespace to avoid lots of uses of "WebCore::".
* webkit/webkitwebview.cpp:
(webkit_web_view_draw): Now just blit the backing store into the widget.
(webkit_web_view_expose): Ditto.
(webkit_web_view_size_allocate): Let the ChromeClient know the widget size changed.
(webkit_web_view_init): Turn off GDK double buffering as we have our own double
buffer.
* webkit/webkitwebviewprivate.h: Add the backing store member.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@98827 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/platform/cairo/WidgetBackingStore.h
Source/WebCore/platform/cairo/WidgetBackingStoreCairo.cpp
Source/WebCore/platform/graphics/cairo/CairoUtilities.cpp
Source/WebCore/platform/graphics/cairo/CairoUtilities.h
Source/WebCore/platform/gtk/GtkWidgetBackingStoreX11.cpp
Source/WebKit/gtk/ChangeLog
Source/WebKit/gtk/WebCoreSupport/ChromeClientGtk.cpp
Source/WebKit/gtk/WebCoreSupport/ChromeClientGtk.h
Source/WebKit/gtk/webkit/webkitwebview.cpp
Source/WebKit/gtk/webkit/webkitwebviewprivate.h

index ed066b113a491d655ef3bce00abd938ddd03d77b..542d2b3e3d45c4acfa14314842f24a1694bba6d6 100644 (file)
@@ -1,3 +1,25 @@
+2011-10-29  Martin Robinson  <mrobinson@igalia.com>
+
+        [GTK] Switch to a backing store approach for painting WebKitWebView
+        https://bugs.webkit.org/show_bug.cgi?id=70213
+
+        Reviewed by Gustavo Noronha Silva.
+
+        No new tests. The changes to WebCore should not change
+        behavior. The changes in WebKit are covered by existing
+        tests.
+
+        * platform/cairo/WidgetBackingStore.h:
+        (WebCore::WidgetBackingStore::size): Added this getter for the size.
+        * platform/cairo/WidgetBackingStoreCairo.cpp:
+        (WebCore::WidgetBackingStore::WidgetBackingStore): Initialize size.
+        * platform/graphics/cairo/CairoUtilities.cpp:
+        (WebCore::copyRectFromCairoSurfaceToContext): Added this new helper.
+        (WebCore::copyRectFromOneSurfaceToAnother): Use the new helper.
+        * platform/graphics/cairo/CairoUtilities.h:
+        * platform/gtk/GtkWidgetBackingStoreX11.cpp:
+        (WebCore::WidgetBackingStore::WidgetBackingStore): Initialize the size.
+
 2011-10-30  Sheriff Bot  <webkit.review.bot@gmail.com>
 
         Unreviewed, rolling out r98803.
index e37e99e6173535f212db08700e929bf7e9abdb36..98938623304e4c5c69bf85f62f23aa40f41d5ee8 100644 (file)
@@ -51,11 +51,13 @@ public:
     ~WidgetBackingStore();
     cairo_surface_t* cairoSurface();
     void scroll(const IntRect& scrollRect, const IntSize& scrollOffset);
+    const IntSize& size() { return m_size; }
 
 private:
     WidgetBackingStore(PlatformWidget, const IntSize&);
 
     OwnPtr<WidgetBackingStorePrivate> m_private;
+    IntSize m_size;
 };
 
 } // namespace WebCore
index b96784a23b5b04f8d7c1780b7988d4d35689eaa9..dfa73ec9c9f210ab6d600f90cf13af12cd52d59f 100644 (file)
@@ -72,6 +72,7 @@ PassOwnPtr<WidgetBackingStore> WidgetBackingStore::create(PlatformWidget widget,
 
 WidgetBackingStore::WidgetBackingStore(PlatformWidget widget, const IntSize& size)
     : m_private(WidgetBackingStorePrivate::create(widget, size))
+    , m_size(size)
 {
 }
 
index 1632259a5662830d801c49ad1611ffe66c0e84a1..a640357f25a1b7aca0a631176c0be97da7042562 100644 (file)
@@ -186,12 +186,17 @@ PassRefPtr<cairo_surface_t> copyCairoImageSurface(cairo_surface_t* originalSurfa
     return newSurface.release();
 }
 
+void copyRectFromCairoSurfaceToContext(cairo_surface_t* from, cairo_t* to, const IntSize& offset, const IntRect& rect)
+{
+    cairo_set_source_surface(to, from, offset.width(), offset.height());
+    cairo_rectangle(to, rect.x(), rect.y(), rect.width(), rect.height());
+    cairo_fill(to);
+}
+
 void copyRectFromOneSurfaceToAnother(cairo_surface_t* from, cairo_surface_t* to, const IntSize& offset, const IntRect& rect)
 {
     RefPtr<cairo_t> context = adoptRef(cairo_create(to));
-    cairo_set_source_surface(context.get(), from, offset.width(), offset.height());
-    cairo_rectangle(context.get(), rect.x(), rect.y(), rect.width(), rect.height());
-    cairo_fill(context.get());
+    copyRectFromCairoSurfaceToContext(from, context.get(), offset, rect);
 }
 
 } // namespace WebCore
index 3d6eb79f6eda91ab626c96bb15100cff4b3beb82..344bc49a170cf107823c3a7b02d3ff7900fa2c64 100644 (file)
@@ -49,7 +49,9 @@ cairo_operator_t toCairoOperator(CompositeOperator op);
 void drawPatternToCairoContext(cairo_t* cr, cairo_surface_t* image, const IntSize& imageSize, const FloatRect& tileRect,
                                const AffineTransform& patternTransform, const FloatPoint& phase, cairo_operator_t op, const FloatRect& destRect);
 PassRefPtr<cairo_surface_t> copyCairoImageSurface(cairo_surface_t*);
-void copyRectFromOneSurfaceToAnother(cairo_surface_t* from, cairo_surface_t* to, const IntSize&, const IntRect&);
+
+void copyRectFromCairoSurfaceToContext(cairo_surface_t* from, cairo_t* to, const IntSize& offset, const IntRect&);
+void copyRectFromOneSurfaceToAnother(cairo_surface_t* from, cairo_surface_t* to, const IntSize& offset, const IntRect&);
 
 } // namespace WebCore
 
index ad5baa5d02bea5f1d5f4d624193b5ce779389c96..1b13b0666cb1dad4f55e50baf666b1bb61674493 100644 (file)
@@ -77,6 +77,7 @@ PassOwnPtr<WidgetBackingStore> WidgetBackingStore::create(GtkWidget* widget, con
 
 WidgetBackingStore::WidgetBackingStore(GtkWidget* widget, const IntSize& size)
     : m_private(WidgetBackingStorePrivate::create(widget, size))
+    , m_size(size)
 {
 }
 
index 4e9a0b03c8d4d6a18b4896d8bbde220e4d640f7f..77c78440ebb74465068b6012d3abc8441f503780 100644 (file)
@@ -1,3 +1,45 @@
+2011-10-29  Martin Robinson  <mrobinson@igalia.com>
+
+        [GTK] Switch to a backing store approach for painting WebKitWebView
+        https://bugs.webkit.org/show_bug.cgi?id=70213
+
+        Reviewed by Gustavo Noronha Silva.
+
+        Paint the WebView into a backing store. This prevents expose events from
+        triggering a layout, making scrolling and resizing much smoother.
+
+        * WebCoreSupport/ChromeClientGtk.cpp:
+        (WebKit::ChromeClient::ChromeClient): Initialize new members.
+        (WebKit::repaintEverythingSoonTimeout): Added this helper which repaints
+        the WebView more quickly during resize events.
+        (WebKit::clipOutOldWidgetArea): Clips out the old widget area, so that we
+        can clear out invalid pixels when growing the widget into a pre-existing
+        backing store.
+        (WebKit::clearEverywhereInBackingStore): Clear out the entire backing store.
+        (WebKit::ChromeClient::widgetSizeChanged): Handle widget size changes by
+        intelligently resizing or reusing the existing backing store.
+        (WebKit::coalesceRectsIfPossible): Moved this method from webkitwebview.cpp.
+        (WebKit::paintWebView): Moved this method from webkitwebview.cpp.
+        (WebKit::ChromeClient::performAllPendingScrolls): If there are any pending
+        scrolling operations, perform them by scrolling the backing store.
+        (WebKit::ChromeClient::paint): Added this timer callback, which paints the
+        WebView after a short timeout.
+        (WebKit::ChromeClient::invalidateWindow): No need to do anything here any longer.
+        (WebKit::ChromeClient::invalidateContentsAndWindow): Now unite the rect with
+        the existing dirty region and queue the paint timeout.
+        (WebKit::ChromeClient::scroll): Now just queue a scroll event in the repaint timeout.
+        Intelligently calculate the area to move and the dirty area using code derived
+        from WebKit2.
+        * WebCoreSupport/ChromeClientGtk.h: Added new method definitions and also use
+        the WebCore namespace to avoid lots of uses of "WebCore::".
+        * webkit/webkitwebview.cpp:
+        (webkit_web_view_draw): Now just blit the backing store into the widget.
+        (webkit_web_view_expose): Ditto.
+        (webkit_web_view_size_allocate): Let the ChromeClient know the widget size changed.
+        (webkit_web_view_init): Turn off GDK double buffering as we have our own double
+        buffer.
+        * webkit/webkitwebviewprivate.h: Add the backing store member.
+
 2011-10-28  Jochen Eisinger  <jochen@chromium.org>
 
         Rename a number of methods mentioning JavaScript to just Script instead
index 10f811804d5fcda6f7b5dcc2ad66d166d3acedd2..23581a0fd2f0a914d948a5c8eec5d2c3bde564ec 100644 (file)
@@ -39,6 +39,7 @@
 #include "HTMLNames.h"
 #include "HitTestResult.h"
 #include "Icon.h"
+#include "InspectorController.h"
 #include "IntRect.h"
 #include "KURL.h"
 #include "NavigationAction.h"
@@ -46,6 +47,7 @@
 #include "PlatformString.h"
 #include "PopupMenuClient.h"
 #include "PopupMenuGtk.h"
+#include "RefPtrCairo.h"
 #include "SearchPopupMenuGtk.h"
 #include "SecurityOrigin.h"
 #include "WindowFeatures.h"
@@ -61,6 +63,7 @@
 #include <glib.h>
 #include <glib/gi18n-lib.h>
 #include <gtk/gtk.h>
+#include <wtf/MathExtras.h>
 #include <wtf/text/CString.h>
 
 #if ENABLE(SQL_DATABASE)
@@ -75,7 +78,9 @@ ChromeClient::ChromeClient(WebKitWebView* webView)
     : m_webView(webView)
     , m_adjustmentWatcher(webView)
     , m_closeSoonTimer(0)
-    , m_pendingScrollInvalidations(false)
+    , m_displayTimer(this, &ChromeClient::paint)
+    , m_lastDisplayTime(0)
+    , m_repaintSoonSourceId(0)
 {
     ASSERT(m_webView);
 }
@@ -377,82 +382,233 @@ void ChromeClient::registerProtocolHandler(const String& scheme, const String& b
 } 
 #endif 
 
-void ChromeClient::invalidateWindow(const IntRect&, bool immediate)
+static gboolean repaintEverythingSoonTimeout(ChromeClient* client)
 {
-    // If we've invalidated regions for scrolling, force GDK to process those invalidations
-    // now. This will also cause child windows to move right away. This prevents redraw
-    // artifacts with child windows (e.g. Flash plugin instances).
-    if (immediate && m_pendingScrollInvalidations) {
-        m_pendingScrollInvalidations = false;
-        if (GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(m_webView)))
-            gdk_window_process_updates(window, TRUE);
-    }
+    client->paint(0);
+    return FALSE;
 }
 
-void ChromeClient::invalidateContentsAndWindow(const IntRect& updateRect, bool immediate)
+static void clipOutOldWidgetArea(cairo_t* cr, const IntSize& oldSize, const IntSize& newSize)
+{
+    cairo_move_to(cr, oldSize.width(), 0);
+    cairo_line_to(cr, newSize.width(), 0);
+    cairo_line_to(cr, newSize.width(), newSize.height());
+    cairo_line_to(cr, 0, newSize.height());
+    cairo_line_to(cr, 0, oldSize.height());
+    cairo_line_to(cr, oldSize.width(), oldSize.height());
+    cairo_close_path(cr);
+    cairo_clip(cr);
+}
+
+static void clearEverywhereInBackingStore(WebKitWebView* webView, cairo_t* cr)
+{
+    // The strategy here is to quickly draw white into this new canvas, so that
+    // when a user quickly resizes the WebView in an environment that has opaque
+    // resizing (like Gnome Shell), there are no drawing artifacts.
+    if (!webView->priv->transparent) {
+        cairo_set_source_rgb(cr, 1, 1, 1);
+        cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+    } else
+        cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
+    cairo_paint(cr);
+}
+
+void ChromeClient::widgetSizeChanged(const IntSize& oldWidgetSize, IntSize newSize)
 {
-    GdkRectangle rect = updateRect;
-    GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(m_webView));
+    WidgetBackingStore* backingStore = m_webView->priv->backingStore.get();
+
+    // Grow the backing store by at least 1.5 times the current size. This prevents
+    // lots of unnecessary allocations during an opaque resize.
+    if (backingStore) {
+        const IntSize& oldSize = backingStore->size();
+        if (newSize.width() > oldSize.width())
+            newSize.setWidth(std::max(newSize.width(), static_cast<int>(oldSize.width() * 1.5)));
+        if (newSize.height() > oldSize.height())
+            newSize.setHeight(std::max(newSize.height(), static_cast<int>(oldSize.height() * 1.5)));
+    }
+
+    // If we did not have a backing store before or if the backing store is growing, we need
+    // to reallocate a new one and set it up so that we don't see artifacts while resizing.
+    if (!backingStore
+        || newSize.width() > backingStore->size().width()
+        || newSize.height() > backingStore->size().height()) {
+
+        OwnPtr<WidgetBackingStore> newBackingStore =
+            WebCore::WidgetBackingStore::create(GTK_WIDGET(m_webView), newSize);
+        RefPtr<cairo_t> cr = adoptRef(cairo_create(newBackingStore->cairoSurface()));
+
+        clearEverywhereInBackingStore(m_webView, cr.get());
+
+        // Now we copy the old backing store image over the new cleared surface to prevent
+        // annoying flashing as the widget grows. We do the "real" paint in a timeout
+        // since we don't want to block resizing too long.
+        if (backingStore) {
+            cairo_set_source_surface(cr.get(), backingStore->cairoSurface(), 0, 0);
+            cairo_rectangle(cr.get(), 0, 0, backingStore->size().width(), backingStore->size().height());
+            cairo_fill(cr.get());
+        }
+
+        m_webView->priv->backingStore = newBackingStore.release();
+        backingStore = m_webView->priv->backingStore.get();
 
-    if (window && !updateRect.isEmpty()) {
-        gdk_window_invalidate_rect(window, &rect, FALSE);
-        // We don't currently do immediate updates since they delay other UI elements.
-        //if (immediate)
-        //    gdk_window_process_updates(window, FALSE);
+    } else if (oldWidgetSize.width() < newSize.width() || oldWidgetSize.height() < newSize.height()) {
+        // The widget is growing, but we did not need to create a new backing store.
+        // We should clear any old data outside of the old widget region.
+        RefPtr<cairo_t> cr = adoptRef(cairo_create(backingStore->cairoSurface()));
+        clipOutOldWidgetArea(cr.get(), oldWidgetSize, newSize);
+        clearEverywhereInBackingStore(m_webView, cr.get());
     }
+
+    // We need to force a redraw and ignore the framerate cap.
+    m_lastDisplayTime = 0;
+    m_dirtyRegion.unite(IntRect(IntPoint(), backingStore->size()));
+
+    // WebCore timers by default have a lower priority which leads to more artifacts when opaque
+    // resize is on, thus we use g_timeout_add here to force a higher timeout priority.
+    if (!m_repaintSoonSourceId)
+        m_repaintSoonSourceId = g_timeout_add(0, reinterpret_cast<GSourceFunc>(repaintEverythingSoonTimeout), this);
+}
+
+static void coalesceRectsIfPossible(const IntRect& clipRect, Vector<IntRect>& rects)
+{
+    const unsigned int cRectThreshold = 10;
+    const float cWastedSpaceThreshold = 0.75f;
+    bool useUnionedRect = (rects.size() <= 1) || (rects.size() > cRectThreshold);
+    if (!useUnionedRect) {
+        // Attempt to guess whether or not we should use the unioned rect or the individual rects.
+        // We do this by computing the percentage of "wasted space" in the union. If that wasted space
+        // is too large, then we will do individual rect painting instead.
+        float unionPixels = (clipRect.width() * clipRect.height());
+        float singlePixels = 0;
+        for (size_t i = 0; i < rects.size(); ++i)
+            singlePixels += rects[i].width() * rects[i].height();
+        float wastedSpace = 1 - (singlePixels / unionPixels);
+        if (wastedSpace <= cWastedSpaceThreshold)
+            useUnionedRect = true;
+    }
+
+    if (!useUnionedRect)
+        return;
+
+    rects.clear();
+    rects.append(clipRect);
 }
 
-void ChromeClient::invalidateContentsForSlowScroll(const IntRect& updateRect, bool immediate)
+static void paintWebView(WebKitWebView* webView, Frame* frame, Region dirtyRegion)
 {
-    invalidateContentsAndWindow(updateRect, immediate);
-    m_adjustmentWatcher.updateAdjustmentsFromScrollbarsLater();
+    if (!webView->priv->backingStore)
+        return;
+
+    Vector<IntRect> rects = dirtyRegion.rects();
+    coalesceRectsIfPossible(dirtyRegion.bounds(), rects);
+    frame->view()->updateLayoutAndStyleIfNeededRecursive();
+
+    RefPtr<cairo_t> backingStoreContext = adoptRef(cairo_create(webView->priv->backingStore->cairoSurface()));
+    GraphicsContext gc(backingStoreContext.get());
+    for (size_t i = 0; i < rects.size(); i++) {
+        const IntRect& rect = rects[i];
+
+        gc.save();
+        gc.clip(rect);
+        if (webView->priv->transparent)
+            gc.clearRect(rect);
+        frame->view()->paint(&gc, rect);
+        gc.restore();
+    }
+
+    gc.save();
+    gc.clip(dirtyRegion.bounds());
+    frame->page()->inspectorController()->drawHighlight(gc);
+    gc.restore();
 }
 
-void ChromeClient::scroll(const IntSize& delta, const IntRect& rectToScroll, const IntRect& clipRect)
+void ChromeClient::performAllPendingScrolls()
 {
-    GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(m_webView));
-    if (!window)
+    if (!m_webView->priv->backingStore)
         return;
 
-    m_pendingScrollInvalidations = true;
+    // Scroll all pending scroll rects and invalidate those parts of the widget.
+    for (size_t i = 0; i < m_rectsToScroll.size(); i++) {
+        IntRect& scrollRect = m_rectsToScroll[i];
+        m_webView->priv->backingStore->scroll(scrollRect, m_scrollOffsets[i]);
+        gtk_widget_queue_draw_area(GTK_WIDGET(m_webView),
+                                   scrollRect.x(), scrollRect.y(),
+                                   scrollRect.width(), scrollRect.height());
+    }
 
-    // We cannot use gdk_window_scroll here because it is only able to
-    // scroll the whole window at once, and we often need to scroll
-    // portions of the window only (think frames).
-    GdkRectangle area = clipRect;
-    GdkRectangle moveRect;
+    m_rectsToScroll.clear();
+    m_scrollOffsets.clear();
+}
 
-    GdkRectangle sourceRect = area;
-    sourceRect.x -= delta.width();
-    sourceRect.y -= delta.height();
 
-#ifdef GTK_API_VERSION_2
-    GdkRegion* invalidRegion = gdk_region_rectangle(&area);
+void ChromeClient::paint(WebCore::Timer<ChromeClient>*)
+{
+    static const double minimumFrameInterval = 1.0 / 60.0; // No more than 60 frames a second.
+    double timeSinceLastDisplay = currentTime() - m_lastDisplayTime;
+    double timeUntilNextDisplay = minimumFrameInterval - timeSinceLastDisplay;
 
-    if (gdk_rectangle_intersect(&area, &sourceRect, &moveRect)) {
-        GdkRegion* moveRegion = gdk_region_rectangle(&moveRect);
-        gdk_window_move_region(window, moveRegion, delta.width(), delta.height());
-        gdk_region_offset(moveRegion, delta.width(), delta.height());
-        gdk_region_subtract(invalidRegion, moveRegion);
-        gdk_region_destroy(moveRegion);
+    if (timeUntilNextDisplay > 0) {
+        m_displayTimer.startOneShot(timeUntilNextDisplay);
+        return;
     }
 
-    gdk_window_invalidate_region(window, invalidRegion, FALSE);
-    gdk_region_destroy(invalidRegion);
-#else
-    cairo_region_t* invalidRegion = cairo_region_create_rectangle(&area);
-
-    if (gdk_rectangle_intersect(&area, &sourceRect, &moveRect)) {
-        cairo_region_t* moveRegion = cairo_region_create_rectangle(&moveRect);
-        gdk_window_move_region(window, moveRegion, delta.width(), delta.height());
-        cairo_region_translate(moveRegion, delta.width(), delta.height());
-        cairo_region_subtract(invalidRegion, moveRegion);
-        cairo_region_destroy(moveRegion);
+    Frame* frame = core(m_webView)->mainFrame();
+    if (!frame || !frame->contentRenderer() || !frame->view())
+        return;
+
+    performAllPendingScrolls();
+    paintWebView(m_webView, frame, m_dirtyRegion);
+
+    const IntRect& rect = m_dirtyRegion.bounds();
+    gtk_widget_queue_draw_area(GTK_WIDGET(m_webView), rect.x(), rect.y(), rect.width(), rect.height());
+
+    m_dirtyRegion = Region();
+    m_lastDisplayTime = currentTime();
+    m_repaintSoonSourceId = 0;
+}
+
+void ChromeClient::invalidateWindow(const IntRect&, bool immediate)
+{
+}
+
+void ChromeClient::invalidateContentsAndWindow(const IntRect& updateRect, bool immediate)
+{
+    if (updateRect.isEmpty())
+        return;
+    m_dirtyRegion.unite(updateRect);
+    m_displayTimer.startOneShot(0);
+}
+
+void ChromeClient::invalidateContentsForSlowScroll(const IntRect& updateRect, bool immediate)
+{
+    invalidateContentsAndWindow(updateRect, immediate);
+    m_adjustmentWatcher.updateAdjustmentsFromScrollbarsLater();
+}
+
+void ChromeClient::scroll(const IntSize& delta, const IntRect& rectToScroll, const IntRect& clipRect)
+{
+    m_rectsToScroll.append(rectToScroll);
+    m_scrollOffsets.append(delta);
+
+    // The code to calculate the scroll repaint region is originally from WebKit2.
+    // Get the part of the dirty region that is in the scroll rect.
+    Region dirtyRegionInScrollRect = intersect(rectToScroll, m_dirtyRegion);
+    if (!dirtyRegionInScrollRect.isEmpty()) {
+        // There are parts of the dirty region that are inside the scroll rect.
+        // We need to subtract them from the region, move them and re-add them.
+        m_dirtyRegion.subtract(rectToScroll);
+
+        // Move the dirty parts.
+        Region movedDirtyRegionInScrollRect = intersect(translate(dirtyRegionInScrollRect, delta), rectToScroll);
+
+        // And add them back.
+        m_dirtyRegion.unite(movedDirtyRegionInScrollRect);
     }
 
-    gdk_window_invalidate_region(window, invalidRegion, FALSE);
-    cairo_region_destroy(invalidRegion);
-#endif
+    // Compute the scroll repaint region.
+    Region scrollRepaintRegion = subtract(rectToScroll, translate(rectToScroll, delta));
+    m_dirtyRegion.unite(scrollRepaintRegion);
+    m_displayTimer.startOneShot(0);
 
     m_adjustmentWatcher.updateAdjustmentsFromScrollbarsLater();
 }
index 7881bb56e6b7c448d4dd726abbee8464c8e6aace..250511e5fba30f1083a6fc48e9e92bcacd30642d 100644 (file)
 
 #include "ChromeClient.h"
 #include "GtkAdjustmentWatcher.h"
+#include "IntRect.h"
+#include "IntSize.h"
 #include "KURL.h"
 #include "PopupMenu.h"
+#include "Region.h"
 #include "SearchPopupMenu.h"
+#include "Timer.h"
 
+using namespace WebCore;
 typedef struct _WebKitWebView WebKitWebView;
 
 namespace WebCore {
@@ -43,21 +48,21 @@ namespace WebKit {
 
         virtual void chromeDestroyed();
 
-        virtual void setWindowRect(const WebCore::FloatRect&);
-        virtual WebCore::FloatRect windowRect();
+        virtual void setWindowRect(const FloatRect&);
+        virtual FloatRect windowRect();
 
-        virtual WebCore::FloatRect pageRect();
+        virtual FloatRect pageRect();
 
         virtual void focus();
         virtual void unfocus();
 
-        virtual bool canTakeFocus(WebCore::FocusDirection);
-        virtual void takeFocus(WebCore::FocusDirection);
+        virtual bool canTakeFocus(FocusDirection);
+        virtual void takeFocus(FocusDirection);
 
-        virtual void focusedNodeChanged(WebCore::Node*);
-        virtual void focusedFrameChanged(WebCore::Frame*);
+        virtual void focusedNodeChanged(Node*);
+        virtual void focusedFrameChanged(Frame*);
 
-        virtual WebCore::Page* createWindow(WebCore::Frame*, const WebCore::FrameLoadRequest&, const WebCore::WindowFeatures&, const WebCore::NavigationAction&);
+        virtual Page* createWindow(Frame*, const FrameLoadRequest&, const WindowFeatures&, const NavigationAction&);
         virtual void show();
 
         virtual bool canRunModal();
@@ -77,96 +82,106 @@ namespace WebKit {
 
         virtual void setResizable(bool);
 
-        virtual void addMessageToConsole(WebCore::MessageSource source, WebCore::MessageType type,
-                                         WebCore::MessageLevel level, const WTF::String& message,
+        virtual void addMessageToConsole(MessageSource source, MessageType type,
+                                         MessageLevel level, const WTF::String& message,
                                          unsigned int lineNumber, const WTF::String& sourceID);
 
         virtual bool canRunBeforeUnloadConfirmPanel();
-        virtual bool runBeforeUnloadConfirmPanel(const WTF::String& message, WebCore::Frame* frame);
+        virtual bool runBeforeUnloadConfirmPanel(const WTF::String& message, Frame* frame);
 
         virtual void closeWindowSoon();
 
-        virtual void runJavaScriptAlert(WebCore::Frame*, const WTF::String&);
-        virtual bool runJavaScriptConfirm(WebCore::Frame*, const WTF::String&);
-        virtual bool runJavaScriptPrompt(WebCore::Frame*, const WTF::String& message, const WTF::String& defaultValue, WTF::String& result);
+        virtual void runJavaScriptAlert(Frame*, const WTF::String&);
+        virtual bool runJavaScriptConfirm(Frame*, const WTF::String&);
+        virtual bool runJavaScriptPrompt(Frame*, const WTF::String& message, const WTF::String& defaultValue, WTF::String& result);
         virtual void setStatusbarText(const WTF::String&);
         virtual bool shouldInterruptJavaScript();
-        virtual WebCore::KeyboardUIMode keyboardUIMode();
+        virtual KeyboardUIMode keyboardUIMode();
 
-        virtual WebCore::IntRect windowResizerRect() const;
+        virtual IntRect windowResizerRect() const;
 #if ENABLE(REGISTER_PROTOCOL_HANDLER) 
         virtual void registerProtocolHandler(const WTF::String&, const WTF::String&, const WTF::String&, const WTF::String&); 
 #endif 
-        virtual void invalidateWindow(const WebCore::IntRect&, bool);
-        virtual void invalidateContentsAndWindow(const WebCore::IntRect&, bool);
-        virtual void invalidateContentsForSlowScroll(const WebCore::IntRect&, bool);
-        virtual void scroll(const WebCore::IntSize& scrollDelta, const WebCore::IntRect& rectToScroll, const WebCore::IntRect& clipRect);
+        virtual void invalidateWindow(const IntRect&, bool);
+        virtual void invalidateContentsAndWindow(const IntRect&, bool);
+        virtual void invalidateContentsForSlowScroll(const IntRect&, bool);
+        virtual void scroll(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect);
 
-        virtual WebCore::IntPoint screenToWindow(const WebCore::IntPoint&) const;
-        virtual WebCore::IntRect windowToScreen(const WebCore::IntRect&) const;
+        virtual IntPoint screenToWindow(const IntPoint&) const;
+        virtual IntRect windowToScreen(const IntRect&) const;
         virtual PlatformPageClient platformPageClient() const;
-        virtual void contentsSizeChanged(WebCore::Frame*, const WebCore::IntSize&) const;
+        virtual void contentsSizeChanged(Frame*, const IntSize&) const;
 
         virtual void scrollbarsModeDidChange() const;
-        virtual void mouseDidMoveOverElement(const WebCore::HitTestResult&, unsigned modifierFlags);
+        virtual void mouseDidMoveOverElement(const HitTestResult&, unsigned modifierFlags);
 
-        virtual void setToolTip(const WTF::String&, WebCore::TextDirection);
+        virtual void setToolTip(const WTF::String&, TextDirection);
 
-        virtual void dispatchViewportPropertiesDidChange(const WebCore::ViewportArguments&) const;
+        virtual void dispatchViewportPropertiesDidChange(const ViewportArguments&) const;
 
-        virtual void print(WebCore::Frame*);
+        virtual void print(Frame*);
 #if ENABLE(SQL_DATABASE)
-        virtual void exceededDatabaseQuota(WebCore::Frame*, const WTF::String&);
+        virtual void exceededDatabaseQuota(Frame*, const WTF::String&);
 #endif
         virtual void reachedMaxAppCacheSize(int64_t spaceNeeded);
-        virtual void reachedApplicationCacheOriginQuota(WebCore::SecurityOrigin*, int64_t totalSpaceNeeded);
+        virtual void reachedApplicationCacheOriginQuota(SecurityOrigin*, int64_t totalSpaceNeeded);
 #if ENABLE(CONTEXT_MENUS)
         virtual void showContextMenu() { }
 #endif
-        virtual void runOpenPanel(WebCore::Frame*, PassRefPtr<WebCore::FileChooser>);
-        virtual void loadIconForFiles(const Vector<WTF::String>&, WebCore::FileIconLoader*);
+        virtual void runOpenPanel(Frame*, PassRefPtr<FileChooser>);
+        virtual void loadIconForFiles(const Vector<WTF::String>&, FileIconLoader*);
 
-        virtual void formStateDidChange(const WebCore::Node*) { }
+        virtual void formStateDidChange(const Node*) { }
 
-        virtual void setCursor(const WebCore::Cursor&);
+        virtual void setCursor(const Cursor&);
         virtual void setCursorHiddenUntilMouseMoves(bool);
 
-        virtual void scrollRectIntoView(const WebCore::IntRect&) const { }
-        virtual void requestGeolocationPermissionForFrame(WebCore::Frame*, WebCore::Geolocation*);
-        virtual void cancelGeolocationPermissionRequestForFrame(WebCore::Frame*, WebCore::Geolocation*);
+        virtual void scrollRectIntoView(const IntRect&) const { }
+        virtual void requestGeolocationPermissionForFrame(Frame*, Geolocation*);
+        virtual void cancelGeolocationPermissionRequestForFrame(Frame*, Geolocation*);
 
         virtual bool selectItemWritingDirectionIsNatural();
         virtual bool selectItemAlignmentFollowsMenuWritingDirection();
-        virtual PassRefPtr<WebCore::PopupMenu> createPopupMenu(WebCore::PopupMenuClient*) const;
-        virtual PassRefPtr<WebCore::SearchPopupMenu> createSearchPopupMenu(WebCore::PopupMenuClient*) const;
+        virtual PassRefPtr<PopupMenu> createPopupMenu(PopupMenuClient*) const;
+        virtual PassRefPtr<SearchPopupMenu> createSearchPopupMenu(PopupMenuClient*) const;
 #if ENABLE(VIDEO)
-        virtual bool supportsFullscreenForNode(const WebCore::Node*);
-        virtual void enterFullscreenForNode(WebCore::Node*);
-        virtual void exitFullscreenForNode(WebCore::Node*);
+        virtual bool supportsFullscreenForNode(const Node*);
+        virtual void enterFullscreenForNode(Node*);
+        virtual void exitFullscreenForNode(Node*);
 #endif
 
 #if ENABLE(FULLSCREEN_API)
-        virtual bool supportsFullScreenForElement(const WebCore::Element*, bool withKeyboard);
-        virtual void enterFullScreenForElement(WebCore::Element*);
-        virtual void exitFullScreenForElement(WebCore::Element*);
+        virtual bool supportsFullScreenForElement(const Element*, bool withKeyboard);
+        virtual void enterFullScreenForElement(Element*);
+        virtual void exitFullScreenForElement(Element*);
 #endif
 
-        virtual bool shouldRubberBandInDirection(WebCore::ScrollDirection) const { return true; }
+        virtual bool shouldRubberBandInDirection(ScrollDirection) const { return true; }
         virtual void numWheelEventHandlersChanged(unsigned) { }
 
 #if USE(ACCELERATED_COMPOSITING) 
-        virtual void attachRootGraphicsLayer(WebCore::Frame*, WebCore::GraphicsLayer*);
+        virtual void attachRootGraphicsLayer(Frame*, GraphicsLayer*);
         virtual void setNeedsOneShotDrawingSynchronization();
         virtual void scheduleCompositingLayerSync();
         virtual CompositingTriggerFlags allowedCompositingTriggers() const;
 #endif 
 
+        void performAllPendingScrolls();
+        void paint(Timer<ChromeClient>*);
+        void widgetSizeChanged(const IntSize& oldWidgetSize, IntSize newSize);
+
     private:
         WebKitWebView* m_webView;
         GtkAdjustmentWatcher m_adjustmentWatcher;
-        WebCore::KURL m_hoveredLinkURL;
+        KURL m_hoveredLinkURL;
         unsigned int m_closeSoonTimer;
-        bool m_pendingScrollInvalidations;
+
+        Timer <ChromeClient> m_displayTimer;
+        Region m_dirtyRegion;
+        Vector<IntRect> m_rectsToScroll;
+        Vector<IntSize> m_scrollOffsets;
+        double m_lastDisplayTime;
+        uint m_repaintSoonSourceId;
     };
 }
 
index 070cdec4b9b746a5852ca9a492baf5755396dfc7..4e7ada6980276af2bd840a616a2eed2cef5916e3 100644 (file)
@@ -33,6 +33,7 @@
 #include "AXObjectCache.h"
 #include "AbstractDatabase.h"
 #include "BackForwardListImpl.h"
+#include "CairoUtilities.h"
 #include "Chrome.h"
 #include "ChromeClientGtk.h"
 #include "ClipboardUtilitiesGtk.h"
@@ -67,7 +68,6 @@
 #include "HitTestResult.h"
 #include "IconDatabase.h"
 #include "InspectorClientGtk.h"
-#include "InspectorController.h"
 #include "MemoryCache.h"
 #include "MouseEventWithHitTestResults.h"
 #include "NotImplemented.h"
@@ -622,108 +622,39 @@ static void webkit_web_view_set_property(GObject* object, guint prop_id, const G
     }
 }
 
-static bool shouldCoalesce(const IntRect& rect, const Vector<IntRect>& rects)
-{
-    const unsigned int cRectThreshold = 10;
-    const float cWastedSpaceThreshold = 0.75f;
-    bool useUnionedRect = (rects.size() <= 1) || (rects.size() > cRectThreshold);
-    if (useUnionedRect)
-        return true;
-    // Attempt to guess whether or not we should use the unioned rect or the individual rects.
-    // We do this by computing the percentage of "wasted space" in the union.  If that wasted space
-    // is too large, then we will do individual rect painting instead.
-    float unionPixels = (rect.width() * rect.height());
-    float singlePixels = 0;
-    for (size_t i = 0; i < rects.size(); ++i)
-        singlePixels += rects[i].width() * rects[i].height();
-    float wastedSpace = 1 - (singlePixels / unionPixels);
-    if (wastedSpace <= cWastedSpaceThreshold)
-        useUnionedRect = true;
-    return useUnionedRect;
-}
-
-static void paintWebView(Frame* frame, gboolean transparent, GraphicsContext& context, const IntRect& clipRect, const Vector<IntRect>& rects)
-{
-    bool coalesce = true;
-
-    if (rects.size() > 0)
-        coalesce = shouldCoalesce(clipRect, rects);
-
-    if (coalesce) {
-        context.clip(clipRect);
-        if (transparent)
-            context.clearRect(clipRect);
-        frame->view()->paint(&context, clipRect);
-    } else {
-        for (size_t i = 0; i < rects.size(); i++) {
-            IntRect rect = rects[i];
-            context.save();
-            context.clip(rect);
-            if (transparent)
-                context.clearRect(rect);
-            frame->view()->paint(&context, rect);
-            context.restore();
-        }
-    }
-
-    context.save();
-    context.clip(clipRect);
-    frame->page()->inspectorController()->drawHighlight(context);
-    context.restore();
-}
 #ifdef GTK_API_VERSION_2
 static gboolean webkit_web_view_expose_event(GtkWidget* widget, GdkEventExpose* event)
 {
-    WebKitWebView* webView = WEBKIT_WEB_VIEW(widget);
-    WebKitWebViewPrivate* priv = webView->priv;
-
-    Frame* frame = core(webView)->mainFrame();
-    if (frame->contentRenderer() && frame->view()) {
-        frame->view()->updateLayoutAndStyleIfNeededRecursive();
-
-        RefPtr<cairo_t> cr = adoptRef(gdk_cairo_create(event->window));
-        GraphicsContext gc(cr.get());
-        gc.setGdkExposeEvent(event);
-
-        int rectCount;
-        GOwnPtr<GdkRectangle> rects;
-        gdk_region_get_rectangles(event->region, &rects.outPtr(), &rectCount);
-        Vector<IntRect> paintRects;
-        for (int i = 0; i < rectCount; i++)
-            paintRects.append(IntRect(rects.get()[i]));
+    int rectCount;
+    GOwnPtr<GdkRectangle> rects;
+    gdk_region_get_rectangles(event->region, &rects.outPtr(), &rectCount);
 
-        paintWebView(frame, priv->transparent, gc, static_cast<IntRect>(event->area), paintRects);
+    RefPtr<cairo_t> cr = adoptRef(gdk_cairo_create(event->window));
+    for (int i = 0; i < rectCount; i++) {
+        copyRectFromCairoSurfaceToContext(WEBKIT_WEB_VIEW(widget)->priv->backingStore->cairoSurface(),
+                                          cr.get(), IntSize(), IntRect(rects.get()[i]));
     }
-
     return FALSE;
 }
 #else
 static gboolean webkit_web_view_draw(GtkWidget* widget, cairo_t* cr)
 {
-    WebKitWebView* webView = WEBKIT_WEB_VIEW(widget);
-    WebKitWebViewPrivate* priv = webView->priv;
     GdkRectangle clipRect;
-
     if (!gdk_cairo_get_clip_rectangle(cr, &clipRect))
         return FALSE;
 
-    Frame* frame = core(webView)->mainFrame();
-    if (frame->contentRenderer() && frame->view()) {
-        GraphicsContext gc(cr);
-        IntRect rect = clipRect;
-        cairo_rectangle_list_t* rectList = cairo_copy_clip_rectangle_list(cr);
-
-        frame->view()->updateLayoutAndStyleIfNeededRecursive();
-
-        Vector<IntRect> rects;
-        if (!rectList->status && rectList->num_rectangles > 0) {
-            for (int i = 0; i < rectList->num_rectangles; i++)
-                rects.append(enclosingIntRect(FloatRect(rectList->rectangles[i])));
-        }
-        paintWebView(frame, priv->transparent, gc, rect, rects);
-
+    cairo_rectangle_list_t* rectList = cairo_copy_clip_rectangle_list(cr);
+    if (rectList->status || !rectList->num_rectangles) {
         cairo_rectangle_list_destroy(rectList);
+        return FALSE;
+    }
+
+    Vector<IntRect> rects;
+    for (int i = 0; i < rectList->num_rectangles; i++) {
+        copyRectFromCairoSurfaceToContext(WEBKIT_WEB_VIEW(widget)->priv->backingStore->cairoSurface(),
+                                          cr, IntSize(), enclosingIntRect(FloatRect(rectList->rectangles[i])));
     }
+    cairo_rectangle_list_destroy(rectList);
 
     return FALSE;
 }
@@ -914,14 +845,16 @@ static void webkit_web_view_size_allocate(GtkWidget* widget, GtkAllocation* allo
     GTK_WIDGET_CLASS(webkit_web_view_parent_class)->size_allocate(widget, allocation);
 
     WebKitWebView* webView = WEBKIT_WEB_VIEW(widget);
-
     Page* page = core(webView);
-    Frame* frame = page->mainFrame();
-    if (!frame->view())
-        return;
+    IntSize oldSize;
+    if (FrameView* frameView = page->mainFrame()->view()) {
+        oldSize = frameView->size();
+        frameView->resize(allocation->width, allocation->height);
+    }
 
-    frame->view()->resize(allocation->width, allocation->height);
-    static_cast<WebKit::ChromeClient*>(page->chrome()->client())->adjustmentWatcher()->updateAdjustmentsFromScrollbars();
+    WebKit::ChromeClient* chromeClient = static_cast<WebKit::ChromeClient*>(page->chrome()->client());
+    chromeClient->widgetSizeChanged(oldSize, IntSize(allocation->width, allocation->height));
+    chromeClient->adjustmentWatcher()->updateAdjustmentsFromScrollbars();
 }
 
 static void webkit_web_view_grab_focus(GtkWidget* widget)
@@ -3347,6 +3280,8 @@ static void webkit_web_view_init(WebKitWebView* webView)
     priv->viewportAttributes->priv->webView = webView;
 
     gtk_widget_set_can_focus(GTK_WIDGET(webView), TRUE);
+    gtk_widget_set_double_buffered(GTK_WIDGET(webView), FALSE);
+
     priv->mainFrame = WEBKIT_WEB_FRAME(webkit_web_frame_new(webView));
     priv->lastPopupXPosition = priv->lastPopupYPosition = -1;
 
index 99444aa3a84f1f9180eeb482fc58de3fe1ae6c4e..51e5e8e623d2c8b58990a8399cb9dd2d0c14f771 100644 (file)
@@ -29,6 +29,7 @@
 #include "GOwnPtr.h"
 #include "Page.h"
 #include "ResourceHandle.h"
+#include "WidgetBackingStore.h"
 #include <webkit/webkitwebview.h>
 
 namespace WebKit {
@@ -42,6 +43,7 @@ extern "C" {
 typedef struct _WebKitWebViewPrivate WebKitWebViewPrivate;
 struct _WebKitWebViewPrivate {
     WebCore::Page* corePage;
+    OwnPtr<WebCore::WidgetBackingStore> backingStore;
     GRefPtr<WebKitWebSettings> webSettings;
     GRefPtr<WebKitWebInspector> webInspector;
     GRefPtr<WebKitViewportAttributes> viewportAttributes;