1/*
2 * Copyright (C) 2011 Collabora Ltd.
3 * Copyright (C) 2012 Igalia, S.L.
4 *
5 *  This library is free software; you can redistribute it and/or
6 *  modify it under the terms of the GNU Lesser General Public
7 *  License as published by the Free Software Foundation; either
8 *  version 2 of the License, or (at your option) any later version.
9 *
10 *  This library is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 *  Lesser General Public License for more details.
14 *
15 *  You should have received a copy of the GNU Lesser General Public
16 *  License along with this library; if not, write to the Free Software
17 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19
20#include "config.h"
21#include "AcceleratedCompositingContext.h"
22
23#if USE(ACCELERATED_COMPOSITING) && USE(CLUTTER)
24
25#include "Frame.h"
26#include "FrameView.h"
27#include "GraphicsLayer.h"
28#include "GraphicsLayerActor.h"
29#include "NotImplemented.h"
30#include "Settings.h"
31#include "webkitwebviewprivate.h"
32#include <clutter-gtk/clutter-gtk.h>
33#include <clutter/clutter.h>
34
35using namespace WebCore;
36
37namespace WebKit {
38
39AcceleratedCompositingContext::AcceleratedCompositingContext(WebKitWebView* webView)
40    : m_webView(webView)
41    , m_layerFlushTimerCallbackId(0)
42    , m_rootLayerEmbedder(0)
43{
44}
45
46void AcceleratedCompositingContext::initialize()
47{
48    if (m_rootLayer)
49        return;
50
51    GtkAllocation allocation;
52    gtk_widget_get_allocation(GTK_WIDGET(m_webView), &allocation);
53    IntSize pageSize(allocation.width, allocation.height);
54
55    m_rootLayer = GraphicsLayer::create(0, this);
56    m_rootLayer->setDrawsContent(false);
57    m_rootLayer->setSize(pageSize);
58
59    // The non-composited contents are a child of the root layer.
60    m_nonCompositedContentLayer = GraphicsLayer::create(0, this);
61    m_nonCompositedContentLayer->setDrawsContent(true);
62    m_nonCompositedContentLayer->setContentsOpaque(!m_webView->priv->transparent);
63    m_nonCompositedContentLayer->setSize(pageSize);
64    if (core(m_webView)->settings()->acceleratedDrawingEnabled())
65        m_nonCompositedContentLayer->setAcceleratesDrawing(true);
66
67#ifndef NDEBUG
68    m_rootLayer->setName("Root layer");
69    m_nonCompositedContentLayer->setName("Non-composited content");
70#endif
71
72    m_rootLayer->addChild(m_nonCompositedContentLayer.get());
73    m_nonCompositedContentLayer->setNeedsDisplay();
74
75    scheduleLayerFlush();
76}
77
78AcceleratedCompositingContext::~AcceleratedCompositingContext()
79{
80    if (m_layerFlushTimerCallbackId)
81        g_source_remove(m_layerFlushTimerCallbackId);
82}
83
84bool AcceleratedCompositingContext::enabled()
85{
86    return m_rootLayer;
87}
88
89bool AcceleratedCompositingContext::renderLayersToWindow(cairo_t*, const IntRect& clipRect)
90{
91    notImplemented();
92    return true;
93}
94
95void AcceleratedCompositingContext::setRootCompositingLayer(GraphicsLayer* graphicsLayer)
96{
97    if (!graphicsLayer) {
98        gtk_container_remove(GTK_CONTAINER(m_webView), m_rootLayerEmbedder);
99        m_rootLayerEmbedder = 0;
100        m_rootLayer = nullptr;
101        m_nonCompositedContentLayer = nullptr;
102        return;
103    }
104
105    // Create an instance of GtkClutterEmbed to embed actors as GraphicsLayers.
106    if (!m_rootLayerEmbedder) {
107        m_rootLayerEmbedder = gtk_clutter_embed_new();
108        gtk_container_add(GTK_CONTAINER(m_webView), m_rootLayerEmbedder);
109
110        GtkAllocation allocation;
111        gtk_widget_get_allocation(GTK_WIDGET(m_webView), &allocation);
112        gtk_widget_size_allocate(GTK_WIDGET(m_rootLayerEmbedder), &allocation);
113        gtk_widget_show(m_rootLayerEmbedder);
114    }
115
116    // Add the accelerated layer tree hierarchy.
117    initialize();
118
119    m_nonCompositedContentLayer->removeAllChildren();
120    m_nonCompositedContentLayer->addChild(graphicsLayer);
121
122    // Add a root GraphicsLayer to the stage.
123    if (graphicsLayer) {
124        ClutterColor stageColor = { 0xFF, 0xFF, 0xFF, 0xFF };
125        ClutterActor* stage = gtk_clutter_embed_get_stage(GTK_CLUTTER_EMBED(m_rootLayerEmbedder));
126        clutter_actor_set_background_color(stage, &stageColor);
127        clutter_actor_add_child(stage, m_rootLayer->platformLayer());
128    }
129
130    scheduleLayerFlush();
131}
132
133void AcceleratedCompositingContext::setNonCompositedContentsNeedDisplay(const IntRect& rect)
134{
135    if (!m_rootLayer)
136        return;
137
138    if (rect.isEmpty()) {
139        m_rootLayer->setNeedsDisplay();
140        return;
141    }
142
143    m_nonCompositedContentLayer->setNeedsDisplayInRect(rect);
144    scheduleLayerFlush();
145}
146
147void AcceleratedCompositingContext::resizeRootLayer(const IntSize& newSize)
148{
149    if (!m_rootLayerEmbedder)
150        return;
151
152    if (m_rootLayer->size() == newSize)
153        return;
154
155    GtkAllocation allocation;
156    allocation.x = 0;
157    allocation.y = 0;
158    allocation.width = newSize.width();
159    allocation.height = newSize.height();
160    gtk_widget_size_allocate(GTK_WIDGET(m_rootLayerEmbedder), &allocation);
161
162    m_rootLayer->setSize(newSize);
163
164    // If the newSize exposes new areas of the non-composited content a setNeedsDisplay is needed
165    // for those newly exposed areas.
166    FloatSize oldSize = m_nonCompositedContentLayer->size();
167    m_nonCompositedContentLayer->setSize(newSize);
168
169    if (newSize.width() > oldSize.width()) {
170        float height = std::min(static_cast<float>(newSize.height()), oldSize.height());
171        m_nonCompositedContentLayer->setNeedsDisplayInRect(FloatRect(oldSize.width(), 0, newSize.width() - oldSize.width(), height));
172    }
173
174    if (newSize.height() > oldSize.height())
175        m_nonCompositedContentLayer->setNeedsDisplayInRect(FloatRect(0, oldSize.height(), newSize.width(), newSize.height() - oldSize.height()));
176
177    m_nonCompositedContentLayer->setNeedsDisplayInRect(IntRect(IntPoint(), newSize));
178    scheduleLayerFlush();
179}
180
181void AcceleratedCompositingContext::scrollNonCompositedContents(const IntRect& scrollRect, const IntSize& scrollOffset)
182{
183    m_nonCompositedContentLayer->setNeedsDisplayInRect(scrollRect);
184    scheduleLayerFlush();
185}
186
187gboolean AcceleratedCompositingContext::layerFlushTimerFiredCallback(AcceleratedCompositingContext* context)
188{
189    context->flushAndRenderLayers();
190    return FALSE;
191}
192
193void AcceleratedCompositingContext::scheduleLayerFlush()
194{
195    if (m_layerFlushTimerCallbackId)
196        return;
197
198    // We use a GLib timer because otherwise GTK+ event handling during
199    // dragging can starve WebCore timers, which have a lower priority.
200    m_layerFlushTimerCallbackId = g_timeout_add_full(GDK_PRIORITY_EVENTS, 0, reinterpret_cast<GSourceFunc>(layerFlushTimerFiredCallback), this, 0);
201}
202
203bool AcceleratedCompositingContext::flushPendingLayerChanges()
204{
205    if (m_rootLayer) {
206        m_rootLayer->flushCompositingStateForThisLayerOnly();
207        m_nonCompositedContentLayer->flushCompositingStateForThisLayerOnly();
208    }
209
210    return core(m_webView)->mainFrame()->view()->flushCompositingStateIncludingSubframes();
211}
212
213void AcceleratedCompositingContext::flushAndRenderLayers()
214{
215    m_layerFlushTimerCallbackId = 0;
216
217    if (!enabled())
218        return;
219
220    Frame* frame = core(m_webView)->mainFrame();
221    if (!frame || !frame->contentRenderer() || !frame->view())
222        return;
223    frame->view()->updateLayoutAndStyleIfNeededRecursive();
224
225    if (!flushPendingLayerChanges())
226        return;
227}
228
229void AcceleratedCompositingContext::notifyAnimationStarted(const WebCore::GraphicsLayer*, double time)
230{
231    notImplemented();
232}
233void AcceleratedCompositingContext::notifyFlushRequired(const WebCore::GraphicsLayer*)
234{
235    notImplemented();
236}
237
238void AcceleratedCompositingContext::paintContents(const WebCore::GraphicsLayer*, WebCore::GraphicsContext& context, WebCore::GraphicsLayerPaintingPhase, const WebCore::IntRect& rectToPaint)
239{
240    context.save();
241    context.clip(rectToPaint);
242    core(m_webView)->mainFrame()->view()->paint(&context, rectToPaint);
243    context.restore();
244}
245
246} // namespace WebKit
247
248#endif // USE(ACCELERATED_COMPOSITING) && USE(CLUTTER)
249