1/*
2 * Copyright (C) 2009, 2013 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "CACFLayerTreeHost.h"
28
29#include "CACFLayerTreeHostClient.h"
30#include "DefWndProcWindowClass.h"
31#include "LayerChangesFlusher.h"
32#include "LegacyCACFLayerTreeHost.h"
33#include "PlatformCALayerWin.h"
34#include "WKCACFViewLayerTreeHost.h"
35#include "WebCoreInstanceHandle.h"
36#include <limits.h>
37#include <QuartzCore/CABase.h>
38#include <wtf/CurrentTime.h>
39#include <wtf/StdLibExtras.h>
40#include <wtf/win/GDIObject.h>
41
42#ifdef DEBUG_ALL
43#pragma comment(lib, "QuartzCore_debug")
44#else
45#pragma comment(lib, "QuartzCore")
46#endif
47
48inline static CGRect winRectToCGRect(RECT rc)
49{
50    return CGRectMake(rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top));
51}
52
53inline static CGRect winRectToCGRect(RECT rc, RECT relativeToRect)
54{
55    return CGRectMake(rc.left, (relativeToRect.bottom-rc.bottom), (rc.right - rc.left), (rc.bottom - rc.top));
56}
57
58namespace WebCore {
59
60bool CACFLayerTreeHost::acceleratedCompositingAvailable()
61{
62    static bool available;
63    static bool tested;
64
65    if (tested)
66        return available;
67
68    tested = true;
69
70    // Initialize available to true since this function will be called from a
71    // propagation within createRenderer(). We want to be able to return true
72    // when that happens so that the test can continue.
73    available = true;
74
75    HMODULE library = LoadLibrary(TEXT("d3d9.dll"));
76    if (!library) {
77        available = false;
78        return available;
79    }
80
81    FreeLibrary(library);
82#ifdef DEBUG_ALL
83    library = LoadLibrary(TEXT("QuartzCore_debug.dll"));
84#else
85    library = LoadLibrary(TEXT("QuartzCore.dll"));
86#endif
87    if (!library) {
88        available = false;
89        return available;
90    }
91
92    FreeLibrary(library);
93
94    // Make a dummy HWND.
95    HWND testWindow = ::CreateWindow(defWndProcWindowClassName(), L"CoreAnimationTesterWindow", WS_POPUP, -500, -500, 20, 20, 0, 0, 0, 0);
96
97    if (!testWindow) {
98        available = false;
99        return available;
100    }
101
102    RefPtr<CACFLayerTreeHost> host = CACFLayerTreeHost::create();
103    host->setWindow(testWindow);
104    available = host->createRenderer();
105    host->setWindow(0);
106    ::DestroyWindow(testWindow);
107
108    return available;
109}
110
111PassRefPtr<CACFLayerTreeHost> CACFLayerTreeHost::create()
112{
113    if (!acceleratedCompositingAvailable())
114        return 0;
115    RefPtr<CACFLayerTreeHost> host = WKCACFViewLayerTreeHost::create();
116    if (!host)
117        host = LegacyCACFLayerTreeHost::create();
118    host->initialize();
119    return host.release();
120}
121
122CACFLayerTreeHost::CACFLayerTreeHost()
123    : m_client(0)
124    , m_rootLayer(PlatformCALayerWin::create(PlatformCALayer::LayerTypeRootLayer, 0))
125    , m_window(0)
126    , m_shouldFlushPendingGraphicsLayerChanges(false)
127    , m_isFlushingLayerChanges(false)
128#if !ASSERT_DISABLED
129    , m_state(WindowNotSet)
130#endif
131{
132}
133
134void CACFLayerTreeHost::initialize()
135{
136    // Point the CACFContext to this
137    initializeContext(this, m_rootLayer.get());
138
139    // Under the root layer, we have a clipping layer to clip the content,
140    // that contains a scroll layer that we use for scrolling the content.
141    // The root layer is the size of the client area of the window.
142    // The clipping layer is the size of the WebView client area (window less the scrollbars).
143    // The scroll layer is the size of the root child layer.
144    // Resizing the window will change the bounds of the rootLayer and the clip layer and will not
145    // cause any repositioning.
146    // Scrolling will affect only the position of the scroll layer without affecting the bounds.
147
148    m_rootLayer->setName("CACFLayerTreeHost rootLayer");
149    m_rootLayer->setAnchorPoint(FloatPoint3D(0, 0, 0));
150    m_rootLayer->setGeometryFlipped(true);
151
152#ifndef NDEBUG
153    CGColorRef debugColor = CGColorCreateGenericRGB(1, 0, 0, 0.8);
154    m_rootLayer->setBackgroundColor(debugColor);
155    CGColorRelease(debugColor);
156#endif
157}
158
159CACFLayerTreeHost::~CACFLayerTreeHost()
160{
161    ASSERT_WITH_MESSAGE(m_state != WindowSet, "Must call setWindow(0) before destroying CACFLayerTreeHost");
162}
163
164void CACFLayerTreeHost::setWindow(HWND window)
165{
166    if (window == m_window)
167        return;
168
169#if !ASSERT_DISABLED
170    switch (m_state) {
171    case WindowNotSet:
172        ASSERT_ARG(window, window);
173        ASSERT(!m_window);
174        m_state = WindowSet;
175        break;
176    case WindowSet:
177        ASSERT_ARG(window, !window);
178        ASSERT(m_window);
179        m_state = WindowCleared;
180        break;
181    case WindowCleared:
182        ASSERT_NOT_REACHED();
183        break;
184    }
185#endif
186
187    if (m_window)
188        destroyRenderer();
189
190    m_window = window;
191}
192
193PlatformCALayer* CACFLayerTreeHost::rootLayer() const
194{
195    return m_rootLayer.get();
196}
197
198void CACFLayerTreeHost::addPendingAnimatedLayer(PassRefPtr<PlatformCALayer> layer)
199{
200    m_pendingAnimatedLayers.add(layer);
201}
202
203void CACFLayerTreeHost::setRootChildLayer(PlatformCALayer* layer)
204{
205    m_rootLayer->removeAllSublayers();
206    m_rootChildLayer = layer;
207    if (m_rootChildLayer)
208        m_rootLayer->appendSublayer(m_rootChildLayer.get());
209}
210
211void CACFLayerTreeHost::layerTreeDidChange()
212{
213    if (m_isFlushingLayerChanges) {
214        // The layer tree is changing as a result of flushing GraphicsLayer changes to their
215        // underlying PlatformCALayers. We'll flush those changes to the context as part of that
216        // process, so there's no need to schedule another flush here.
217        return;
218    }
219
220    // The layer tree is changing as a result of someone modifying a PlatformCALayer that doesn't
221    // have a corresponding GraphicsLayer. Schedule a flush since we won't schedule one through the
222    // normal GraphicsLayer mechanisms.
223    LayerChangesFlusher::shared().flushPendingLayerChangesSoon(this);
224}
225
226void CACFLayerTreeHost::destroyRenderer()
227{
228    m_rootLayer = 0;
229    m_rootChildLayer = 0;
230    LayerChangesFlusher::shared().cancelPendingFlush(this);
231}
232
233static void getDirtyRects(HWND window, Vector<CGRect>& outRects)
234{
235    ASSERT_ARG(outRects, outRects.isEmpty());
236
237    RECT clientRect;
238    if (!GetClientRect(window, &clientRect))
239        return;
240
241    auto region = adoptGDIObject(::CreateRectRgn(0, 0, 0, 0));
242    int regionType = GetUpdateRgn(window, region.get(), false);
243    if (regionType != COMPLEXREGION) {
244        RECT dirtyRect;
245        if (GetUpdateRect(window, &dirtyRect, false))
246            outRects.append(winRectToCGRect(dirtyRect, clientRect));
247        return;
248    }
249
250    DWORD dataSize = ::GetRegionData(region.get(), 0, 0);
251    auto regionDataBuffer = std::make_unique<unsigned char[]>(dataSize);
252    RGNDATA* regionData = reinterpret_cast<RGNDATA*>(regionDataBuffer.get());
253    if (!::GetRegionData(region.get(), dataSize, regionData))
254        return;
255
256    outRects.resize(regionData->rdh.nCount);
257
258    RECT* rect = reinterpret_cast<RECT*>(regionData->Buffer);
259    for (size_t i = 0; i < outRects.size(); ++i, ++rect)
260        outRects[i] = winRectToCGRect(*rect, clientRect);
261}
262
263void CACFLayerTreeHost::paint()
264{
265    Vector<CGRect> dirtyRects;
266    getDirtyRects(m_window, dirtyRects);
267    render(dirtyRects);
268}
269
270void CACFLayerTreeHost::flushPendingGraphicsLayerChangesSoon()
271{
272    m_shouldFlushPendingGraphicsLayerChanges = true;
273    LayerChangesFlusher::shared().flushPendingLayerChangesSoon(this);
274}
275
276void CACFLayerTreeHost::setShouldInvertColors(bool)
277{
278}
279
280void CACFLayerTreeHost::flushPendingLayerChangesNow()
281{
282    // Calling out to the client could cause our last reference to go away.
283    RefPtr<CACFLayerTreeHost> protector(this);
284
285    m_isFlushingLayerChanges = true;
286
287    // Flush changes stored up in GraphicsLayers to their underlying PlatformCALayers, if
288    // requested.
289    if (m_client && m_shouldFlushPendingGraphicsLayerChanges) {
290        m_shouldFlushPendingGraphicsLayerChanges = false;
291        m_client->flushPendingGraphicsLayerChanges();
292    }
293
294    // Flush changes stored up in PlatformCALayers to the context so they will be rendered.
295    flushContext();
296
297    m_isFlushingLayerChanges = false;
298}
299
300void CACFLayerTreeHost::contextDidChange()
301{
302    // All pending animations will have been started with the flush. Fire the animationStarted calls.
303    notifyAnimationsStarted();
304}
305
306void CACFLayerTreeHost::notifyAnimationsStarted()
307{
308    // Send currentTime to the pending animations. This function is called by CACF in a callback
309    // which occurs after the drawInContext calls. So currentTime is very close to the time
310    // the animations actually start
311    double currentTime = monotonicallyIncreasingTime();
312
313    HashSet<RefPtr<PlatformCALayer> >::iterator end = m_pendingAnimatedLayers.end();
314    for (HashSet<RefPtr<PlatformCALayer> >::iterator it = m_pendingAnimatedLayers.begin(); it != end; ++it)
315        (*it)->animationStarted(String(), currentTime);
316
317    m_pendingAnimatedLayers.clear();
318}
319
320CGRect CACFLayerTreeHost::bounds() const
321{
322    RECT clientRect;
323    GetClientRect(m_window, &clientRect);
324
325    return winRectToCGRect(clientRect);
326}
327
328}
329