Draw intermediate snapshots if possible
https://bugs.webkit.org/show_bug.cgi?id=110811

Reviewed by Simon Fraser.

After http://webkit.org/b/110495 we delayed snapshotting until we've
received a nice image, but this made the page look like it is broken.
We should draw any intermediate snapshots that we find, which might
include content such as progress bars/spinners.

Source/WebCore:

* html/HTMLPlugInElement.h:
(WebCore::HTMLPlugInElement::isPlugInImageElement): Expose virtual method
to indicate if this is a HTMLPlugInImageElement or not.
* html/HTMLPlugInImageElement.cpp:
(WebCore::HTMLPlugInImageElement::updateSnapshot): If we have
a RenderEmbeddedObject renderer, then tell it to repaint.
* html/HTMLPlugInImageElement.h:
(WebCore::HTMLPlugInImageElement::snapshotImage): Expose an
accessor for snapshot images.
* rendering/RenderEmbeddedObject.cpp:
(WebCore::RenderEmbeddedObject::paintSnapshotImage): New helper
method to render an image directly. This code is similar to
that in RenderSnapshottedPlugIn.
(WebCore::RenderEmbeddedObject::paintContents): The virtual implementation
of this method for use when we have a snapshot to paint. If we are a plugin that is
in the process of being snapshotted, ask our HTMLPlugInImageElement for a
snapshot and paint that instead. In the case where we are not snapshotting,
or we do not yet have a snapshot, this will call back into the RenderWidget code.
* rendering/RenderEmbeddedObject.h:
(RenderEmbeddedObject): New methods paintSnapshotImage and paintContents
* rendering/RenderWidget.cpp:
(WebCore::RenderWidget::paintContents): New method called in the middle
of paint() that can be overridden by RenderEmbeddedObject. The code here was
simply moved out of the previous paint().
(WebCore::RenderWidget::paint): Call paintContents at the appropriate time.
* rendering/RenderWidget.h:
(RenderWidget): New virtual method paintContents.

Source/WebKit2:

* WebProcess/Plugins/PluginView.cpp:
(WebKit): Reinstate 60 attempts at snapshots before giving up.
(WebKit::PluginView::isAcceleratedCompositingEnabled): We do not
want accelerated compositing enabled when we are trying to capture
snapshots.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@144067 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index dd83bcb..020706d 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,43 @@
+2013-02-26  Dean Jackson  <dino@apple.com>
+
+        Draw intermediate snapshots if possible
+        https://bugs.webkit.org/show_bug.cgi?id=110811
+
+        Reviewed by Simon Fraser.
+
+        After http://webkit.org/b/110495 we delayed snapshotting until we've
+        received a nice image, but this made the page look like it is broken.
+        We should draw any intermediate snapshots that we find, which might
+        include content such as progress bars/spinners.
+
+        * html/HTMLPlugInElement.h:
+        (WebCore::HTMLPlugInElement::isPlugInImageElement): Expose virtual method
+        to indicate if this is a HTMLPlugInImageElement or not.
+        * html/HTMLPlugInImageElement.cpp:
+        (WebCore::HTMLPlugInImageElement::updateSnapshot): If we have
+        a RenderEmbeddedObject renderer, then tell it to repaint.
+        * html/HTMLPlugInImageElement.h:
+        (WebCore::HTMLPlugInImageElement::snapshotImage): Expose an
+        accessor for snapshot images.
+        * rendering/RenderEmbeddedObject.cpp:
+        (WebCore::RenderEmbeddedObject::paintSnapshotImage): New helper
+        method to render an image directly. This code is similar to
+        that in RenderSnapshottedPlugIn.
+        (WebCore::RenderEmbeddedObject::paintContents): The virtual implementation
+        of this method for use when we have a snapshot to paint. If we are a plugin that is
+        in the process of being snapshotted, ask our HTMLPlugInImageElement for a
+        snapshot and paint that instead. In the case where we are not snapshotting,
+        or we do not yet have a snapshot, this will call back into the RenderWidget code.
+        * rendering/RenderEmbeddedObject.h:
+        (RenderEmbeddedObject): New methods paintSnapshotImage and paintContents
+        * rendering/RenderWidget.cpp:
+        (WebCore::RenderWidget::paintContents): New method called in the middle
+        of paint() that can be overridden by RenderEmbeddedObject. The code here was
+        simply moved out of the previous paint().
+        (WebCore::RenderWidget::paint): Call paintContents at the appropriate time.
+        * rendering/RenderWidget.h:
+        (RenderWidget): New virtual method paintContents.
+
 2013-02-26  Levi Weintraub  <leviw@chromium.org>
 
         Add support for 8 bit TextRuns for Chromium/HarfBuzz
diff --git a/Source/WebCore/html/HTMLPlugInElement.h b/Source/WebCore/html/HTMLPlugInElement.h
index ce6fd02..a7559a8 100644
--- a/Source/WebCore/html/HTMLPlugInElement.h
+++ b/Source/WebCore/html/HTMLPlugInElement.h
@@ -74,6 +74,8 @@
 
     virtual bool willRespondToMouseClickEvents() OVERRIDE;
 
+    virtual bool isPlugInImageElement() const { return false; }
+
 protected:
     HTMLPlugInElement(const QualifiedName& tagName, Document*);
 
diff --git a/Source/WebCore/html/HTMLPlugInImageElement.cpp b/Source/WebCore/html/HTMLPlugInImageElement.cpp
index b165b2d..84e0edd 100644
--- a/Source/WebCore/html/HTMLPlugInImageElement.cpp
+++ b/Source/WebCore/html/HTMLPlugInImageElement.cpp
@@ -284,10 +284,14 @@
         return;
 
     m_snapshotImage = image;
+
     if (renderer()->isSnapshottedPlugIn()) {
         toRenderSnapshottedPlugIn(renderer())->updateSnapshot(image);
         return;
     }
+
+    if (renderer()->isEmbeddedObject())
+        renderer()->repaint();
 }
 
 static AtomicString classNameForShadowRoot(const Node* node)
diff --git a/Source/WebCore/html/HTMLPlugInImageElement.h b/Source/WebCore/html/HTMLPlugInImageElement.h
index e6a3fe9..f85b3f2 100644
--- a/Source/WebCore/html/HTMLPlugInImageElement.h
+++ b/Source/WebCore/html/HTMLPlugInImageElement.h
@@ -66,6 +66,7 @@
 
     void userDidClickSnapshot(PassRefPtr<MouseEvent>);
     void updateSnapshotInfo();
+    Image* snapshotImage() const { return m_snapshotImage.get(); }
 
     // Plug-in URL might not be the same as url() with overriding parameters.
     void subframeLoaderWillCreatePlugIn(const KURL& plugInURL);
@@ -111,6 +112,8 @@
 
     void swapRendererTimerFired(Timer<HTMLPlugInImageElement>*);
 
+    virtual bool isPlugInImageElement() const OVERRIDE { return true; }
+
     bool m_needsWidgetUpdate;
     bool m_shouldPreferPlugInsForImages;
     bool m_needsDocumentActivationCallbacks;
diff --git a/Source/WebCore/rendering/RenderEmbeddedObject.cpp b/Source/WebCore/rendering/RenderEmbeddedObject.cpp
index 5d16d28..aabe76c 100644
--- a/Source/WebCore/rendering/RenderEmbeddedObject.cpp
+++ b/Source/WebCore/rendering/RenderEmbeddedObject.cpp
@@ -148,6 +148,47 @@
     repaint();
 }
 
+void RenderEmbeddedObject::paintSnapshotImage(PaintInfo& paintInfo, const LayoutPoint& paintOffset, Image* image)
+{
+    LayoutUnit cWidth = contentWidth();
+    LayoutUnit cHeight = contentHeight();
+    if (!cWidth || !cHeight)
+        return;
+
+    GraphicsContext* context = paintInfo.context;
+    LayoutSize contentSize(cWidth, cHeight);
+    LayoutPoint contentLocation = location() + paintOffset;
+    contentLocation.move(borderLeft() + paddingLeft(), borderTop() + paddingTop());
+
+    LayoutRect rect(contentLocation, contentSize);
+    IntRect alignedRect = pixelSnappedIntRect(rect);
+    if (alignedRect.width() <= 0 || alignedRect.height() <= 0)
+        return;
+
+    bool useLowQualityScaling = shouldPaintAtLowQuality(context, image, image, alignedRect.size());
+    context->drawImage(image, style()->colorSpace(), alignedRect, CompositeSourceOver, shouldRespectImageOrientation(), useLowQualityScaling);
+}
+
+void RenderEmbeddedObject::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
+{
+    Element* element = static_cast<Element*>(node());
+    if (!element || !element->isPluginElement())
+        return;
+
+    HTMLPlugInElement* plugInElement = static_cast<HTMLPlugInElement*>(element);
+    if (plugInElement->displayState() > HTMLPlugInElement::DisplayingSnapshot) {
+        RenderPart::paintContents(paintInfo, paintOffset);
+        return;
+    }
+
+    if (!plugInElement->isPlugInImageElement())
+        return;
+
+    Image* snapshot = static_cast<HTMLPlugInImageElement*>(plugInElement)->snapshotImage();
+    if (snapshot)
+        paintSnapshotImage(paintInfo, paintOffset, snapshot);
+}
+
 void RenderEmbeddedObject::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
 {
     Page* page = 0;
diff --git a/Source/WebCore/rendering/RenderEmbeddedObject.h b/Source/WebCore/rendering/RenderEmbeddedObject.h
index 277d2ec..6964707 100644
--- a/Source/WebCore/rendering/RenderEmbeddedObject.h
+++ b/Source/WebCore/rendering/RenderEmbeddedObject.h
@@ -75,6 +75,9 @@
     virtual const char* renderName() const { return "RenderEmbeddedObject"; }
     virtual bool isEmbeddedObject() const { return true; }
 
+    void paintSnapshotImage(PaintInfo&, const LayoutPoint&, Image*);
+    virtual void paintContents(PaintInfo&, const LayoutPoint&) OVERRIDE;
+
 #if USE(ACCELERATED_COMPOSITING)
     virtual bool requiresLayer() const;
 #endif
diff --git a/Source/WebCore/rendering/RenderWidget.cpp b/Source/WebCore/rendering/RenderWidget.cpp
index fb396f0..c07681f 100644
--- a/Source/WebCore/rendering/RenderWidget.cpp
+++ b/Source/WebCore/rendering/RenderWidget.cpp
@@ -235,6 +235,39 @@
         m_widget->notifyWidget(notification);
 }
 
+void RenderWidget::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
+{
+    LayoutPoint adjustedPaintOffset = paintOffset + location();
+
+    // Tell the widget to paint now. This is the only time the widget is allowed
+    // to paint itself. That way it will composite properly with z-indexed layers.
+    IntPoint widgetLocation = m_widget->frameRect().location();
+    IntPoint paintLocation(roundToInt(adjustedPaintOffset.x() + borderLeft() + paddingLeft()),
+        roundToInt(adjustedPaintOffset.y() + borderTop() + paddingTop()));
+    IntRect paintRect = paintInfo.rect;
+
+    IntSize widgetPaintOffset = paintLocation - widgetLocation;
+    // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer,
+    // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing.
+    if (!widgetPaintOffset.isZero()) {
+        paintInfo.context->translate(widgetPaintOffset);
+        paintRect.move(-widgetPaintOffset);
+    }
+    m_widget->paint(paintInfo.context, paintRect);
+
+    if (!widgetPaintOffset.isZero())
+        paintInfo.context->translate(-widgetPaintOffset);
+
+    if (m_widget->isFrameView()) {
+        FrameView* frameView = static_cast<FrameView*>(m_widget.get());
+        bool runOverlapTests = !frameView->useSlowRepaintsIfNotOverlapped() || frameView->hasCompositedContentIncludingDescendants();
+        if (paintInfo.overlapTestRequests && runOverlapTests) {
+            ASSERT(!paintInfo.overlapTestRequests->contains(this));
+            paintInfo.overlapTestRequests->set(this, m_widget->frameRect());
+        }
+    }
+}
+
 void RenderWidget::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
 {
     if (!shouldPaint(paintInfo, paintOffset))
@@ -274,35 +307,8 @@
         clipRoundedInnerRect(paintInfo.context, borderRect, roundedInnerRect);
     }
 
-    if (m_widget) {
-        // Tell the widget to paint now.  This is the only time the widget is allowed
-        // to paint itself.  That way it will composite properly with z-indexed layers.
-        IntPoint widgetLocation = m_widget->frameRect().location();
-        IntPoint paintLocation(roundToInt(adjustedPaintOffset.x() + borderLeft() + paddingLeft()),
-            roundToInt(adjustedPaintOffset.y() + borderTop() + paddingTop()));
-        IntRect paintRect = paintInfo.rect;
-
-        IntSize widgetPaintOffset = paintLocation - widgetLocation;
-        // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer,
-        // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing.
-        if (!widgetPaintOffset.isZero()) {
-            paintInfo.context->translate(widgetPaintOffset);
-            paintRect.move(-widgetPaintOffset);
-        }
-        m_widget->paint(paintInfo.context, paintRect);
-
-        if (!widgetPaintOffset.isZero())
-            paintInfo.context->translate(-widgetPaintOffset);
-
-        if (m_widget->isFrameView()) {
-            FrameView* frameView = static_cast<FrameView*>(m_widget.get());
-            bool runOverlapTests = !frameView->useSlowRepaintsIfNotOverlapped() || frameView->hasCompositedContentIncludingDescendants();
-            if (paintInfo.overlapTestRequests && runOverlapTests) {
-                ASSERT(!paintInfo.overlapTestRequests->contains(this));
-                paintInfo.overlapTestRequests->set(this, m_widget->frameRect());
-            }
-         }
-    }
+    if (m_widget)
+        paintContents(paintInfo, paintOffset);
 
     if (style()->hasBorderRadius())
         paintInfo.context->restore();
diff --git a/Source/WebCore/rendering/RenderWidget.h b/Source/WebCore/rendering/RenderWidget.h
index 9d2ad7f..ef8f3b7 100644
--- a/Source/WebCore/rendering/RenderWidget.h
+++ b/Source/WebCore/rendering/RenderWidget.h
@@ -85,6 +85,8 @@
     virtual CursorDirective getCursor(const LayoutPoint&, Cursor&) const;
     virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction) OVERRIDE;
 
+    virtual void paintContents(PaintInfo&, const LayoutPoint&);
+
 private:
     virtual bool isWidget() const { return true; }
 
diff --git a/Source/WebKit2/ChangeLog b/Source/WebKit2/ChangeLog
index 713e86f..e96ee36 100644
--- a/Source/WebKit2/ChangeLog
+++ b/Source/WebKit2/ChangeLog
@@ -1,3 +1,21 @@
+2013-02-26  Dean Jackson  <dino@apple.com>
+
+        Draw intermediate snapshots if possible
+        https://bugs.webkit.org/show_bug.cgi?id=110811
+
+        Reviewed by Simon Fraser.
+
+        After http://webkit.org/b/110495 we delayed snapshotting until we've
+        received a nice image, but this made the page look like it is broken.
+        We should draw any intermediate snapshots that we find, which might
+        include content such as progress bars/spinners.
+
+        * WebProcess/Plugins/PluginView.cpp:
+        (WebKit): Reinstate 60 attempts at snapshots before giving up.
+        (WebKit::PluginView::isAcceleratedCompositingEnabled): We do not
+        want accelerated compositing enabled when we are trying to capture
+        snapshots.
+
 2013-02-26  Andras Becsi  <andras.becsi@digia.com>
 
         Remove nonexistent StringPairVector.h from Target.pri after r142839
diff --git a/Source/WebKit2/WebProcess/Plugins/PluginView.cpp b/Source/WebKit2/WebProcess/Plugins/PluginView.cpp
index 96611ee..8df14cb 100644
--- a/Source/WebKit2/WebProcess/Plugins/PluginView.cpp
+++ b/Source/WebKit2/WebProcess/Plugins/PluginView.cpp
@@ -71,7 +71,7 @@
 
 // This simulated mouse click delay in HTMLPlugInImageElement.cpp should generally be the same or shorter than this delay.
 static const double pluginSnapshotTimerDelay = 1.1;
-static const unsigned maximumSnapshotRetries = 4;
+static const unsigned maximumSnapshotRetries = 60;
 
 class PluginView::URLRequest : public RefCounted<URLRequest> {
 public:
@@ -1349,6 +1349,8 @@
     if (!settings)
         return false;
 
+    if (m_pluginElement->displayState() < HTMLPlugInElement::PlayingWithPendingMouseClick)
+        return false;
     return settings->acceleratedCompositingEnabled();
 }