blob: 1aba46f4c3bca5d1b2ac3c0c455a1d9b6ee08156 [file] [log] [blame]
/*
* Copyright (C) 2009, 2013, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "CACFLayerTreeHost.h"
#if USE(CA)
#include "CACFLayerTreeHostClient.h"
#include "DebugPageOverlays.h"
#include "DefWndProcWindowClass.h"
#include "Frame.h"
#include "FrameView.h"
#include "LayerChangesFlusher.h"
#include "Logging.h"
#include "PlatformCALayerWin.h"
#include "PlatformLayer.h"
#include "TiledBacking.h"
#include "WKCACFViewLayerTreeHost.h"
#include "WebCoreInstanceHandle.h"
#include <limits.h>
#include <QuartzCore/CABase.h>
#include <wtf/StdLibExtras.h>
#include <wtf/UniqueArray.h>
#include <wtf/win/GDIObject.h>
#ifdef DEBUG_ALL
#pragma comment(lib, "QuartzCore_debug")
#else
#pragma comment(lib, "QuartzCore")
#endif
inline static CGRect winRectToCGRect(RECT rc)
{
return CGRectMake(rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top));
}
inline static CGRect winRectToCGRect(RECT rc, RECT relativeToRect)
{
return CGRectMake(rc.left, (relativeToRect.bottom-rc.bottom), (rc.right - rc.left), (rc.bottom - rc.top));
}
namespace WebCore {
bool CACFLayerTreeHost::acceleratedCompositingAvailable()
{
static bool available;
static bool tested;
if (tested)
return available;
tested = true;
// Initialize available to true since this function will be called from a
// propagation within createRenderer(). We want to be able to return true
// when that happens so that the test can continue.
available = true;
HMODULE library = LoadLibrary(TEXT("d3d9.dll"));
if (!library) {
available = false;
return available;
}
FreeLibrary(library);
#ifdef DEBUG_ALL
library = LoadLibrary(TEXT("QuartzCore_debug.dll"));
#else
library = LoadLibrary(TEXT("QuartzCore.dll"));
#endif
if (!library) {
available = false;
return available;
}
FreeLibrary(library);
// Make a dummy HWND.
HWND testWindow = ::CreateWindow(defWndProcWindowClassName(), L"CoreAnimationTesterWindow", WS_POPUP, -500, -500, 20, 20, 0, 0, 0, 0);
if (!testWindow) {
available = false;
return available;
}
auto host = CACFLayerTreeHost::create();
if (!host) {
available = false;
return available;
}
host->setWindow(testWindow);
available = host->createRenderer();
host->setWindow(0);
::DestroyWindow(testWindow);
return available;
}
RefPtr<CACFLayerTreeHost> CACFLayerTreeHost::create()
{
if (!acceleratedCompositingAvailable())
return nullptr;
auto host = WKCACFViewLayerTreeHost::create();
if (!host) {
LOG_ERROR("Failed to create layer tree host for accelerated compositing.");
return nullptr;
}
host->initialize();
return host;
}
CACFLayerTreeHost::CACFLayerTreeHost()
: m_rootLayer(PlatformCALayerWin::create(PlatformCALayer::LayerTypeRootLayer, nullptr))
{
}
void CACFLayerTreeHost::initialize()
{
// Point the CACFContext to this
initializeContext(this, m_rootLayer.get());
// Under the root layer, we have a clipping layer to clip the content,
// that contains a scroll layer that we use for scrolling the content.
// The root layer is the size of the client area of the window.
// The clipping layer is the size of the WebView client area (window less the scrollbars).
// The scroll layer is the size of the root child layer.
// Resizing the window will change the bounds of the rootLayer and the clip layer and will not
// cause any repositioning.
// Scrolling will affect only the position of the scroll layer without affecting the bounds.
m_rootLayer->setName("CACFLayerTreeHost rootLayer");
m_rootLayer->setAnchorPoint(FloatPoint3D(0, 0, 0));
m_rootLayer->setGeometryFlipped(true);
#ifndef NDEBUG
CGColorRef debugColor = CGColorCreateGenericRGB(1, 0, 0, 0.8);
m_rootLayer->setBackgroundColor(debugColor);
CGColorRelease(debugColor);
#endif
}
CACFLayerTreeHost::~CACFLayerTreeHost()
{
ASSERT_WITH_MESSAGE(m_state != WindowSet, "Must call setWindow(0) before destroying CACFLayerTreeHost");
}
void CACFLayerTreeHost::setWindow(HWND window)
{
if (window == m_window)
return;
#if !ASSERT_DISABLED
switch (m_state) {
case WindowNotSet:
ASSERT_ARG(window, window);
ASSERT(!m_window);
m_state = WindowSet;
break;
case WindowSet:
ASSERT_ARG(window, !window);
ASSERT(m_window);
m_state = WindowCleared;
break;
case WindowCleared:
ASSERT_NOT_REACHED();
break;
}
#endif
if (m_window)
destroyRenderer();
m_window = window;
}
void CACFLayerTreeHost::setPage(Page* page)
{
m_page = page;
}
PlatformCALayer* CACFLayerTreeHost::rootLayer() const
{
return m_rootLayer.get();
}
void CACFLayerTreeHost::addPendingAnimatedLayer(PlatformCALayer& layer)
{
m_pendingAnimatedLayers.add(&layer);
}
void CACFLayerTreeHost::setRootChildLayer(PlatformCALayer* layer)
{
m_rootLayer->removeAllSublayers();
m_rootChildLayer = layer;
if (m_rootChildLayer)
m_rootLayer->appendSublayer(*m_rootChildLayer);
updateDebugInfoLayer(m_page->settings().showTiledScrollingIndicator());
}
void CACFLayerTreeHost::layerTreeDidChange()
{
if (m_isFlushingLayerChanges) {
// The layer tree is changing as a result of flushing GraphicsLayer changes to their
// underlying PlatformCALayers. We'll flush those changes to the context as part of that
// process, so there's no need to schedule another flush here.
return;
}
// The layer tree is changing as a result of someone modifying a PlatformCALayer that doesn't
// have a corresponding GraphicsLayer. Schedule a flush since we won't schedule one through the
// normal GraphicsLayer mechanisms.
LayerChangesFlusher::singleton().flushPendingLayerChangesSoon(this);
}
void CACFLayerTreeHost::destroyRenderer()
{
m_rootLayer = nullptr;
m_rootChildLayer = nullptr;
LayerChangesFlusher::singleton().cancelPendingFlush(this);
}
static void getDirtyRects(HWND window, Vector<CGRect>& outRects)
{
ASSERT_ARG(outRects, outRects.isEmpty());
RECT clientRect;
if (!GetClientRect(window, &clientRect))
return;
auto region = adoptGDIObject(::CreateRectRgn(0, 0, 0, 0));
int regionType = GetUpdateRgn(window, region.get(), false);
if (regionType != COMPLEXREGION) {
RECT dirtyRect;
if (GetUpdateRect(window, &dirtyRect, false))
outRects.append(winRectToCGRect(dirtyRect, clientRect));
return;
}
DWORD dataSize = ::GetRegionData(region.get(), 0, 0);
auto regionDataBuffer = makeUniqueArray<unsigned char>(dataSize);
RGNDATA* regionData = reinterpret_cast<RGNDATA*>(regionDataBuffer.get());
if (!::GetRegionData(region.get(), dataSize, regionData))
return;
outRects.resize(regionData->rdh.nCount);
RECT* rect = reinterpret_cast<RECT*>(regionData->Buffer);
for (size_t i = 0; i < outRects.size(); ++i, ++rect)
outRects[i] = winRectToCGRect(*rect, clientRect);
}
void CACFLayerTreeHost::paint(HDC dc)
{
Vector<CGRect> dirtyRects;
getDirtyRects(m_window, dirtyRects);
render(dirtyRects, dc);
}
void CACFLayerTreeHost::flushPendingGraphicsLayerChangesSoon()
{
m_shouldFlushPendingGraphicsLayerChanges = true;
LayerChangesFlusher::singleton().flushPendingLayerChangesSoon(this);
}
void CACFLayerTreeHost::setShouldInvertColors(bool)
{
}
void CACFLayerTreeHost::flushPendingLayerChangesNow()
{
// Calling out to the client could cause our last reference to go away.
RefPtr<CACFLayerTreeHost> protectedThis(this);
updateDebugInfoLayer(m_page->settings().showTiledScrollingIndicator());
m_isFlushingLayerChanges = true;
// Flush changes stored up in GraphicsLayers to their underlying PlatformCALayers, if
// requested.
if (m_client && m_shouldFlushPendingGraphicsLayerChanges) {
m_shouldFlushPendingGraphicsLayerChanges = false;
m_client->flushPendingGraphicsLayerChanges();
}
// Flush changes stored up in PlatformCALayers to the context so they will be rendered.
flushContext();
m_isFlushingLayerChanges = false;
}
void CACFLayerTreeHost::contextDidChange()
{
// All pending animations will have been started with the flush. Fire the animationStarted calls.
notifyAnimationsStarted();
}
void CACFLayerTreeHost::notifyAnimationsStarted()
{
// Send currentTime to the pending animations. This function is called by CACF in a callback
// which occurs after the drawInContext calls. So currentTime is very close to the time
// the animations actually start
MonotonicTime currentTime = MonotonicTime::now();
HashSet<RefPtr<PlatformCALayer> >::iterator end = m_pendingAnimatedLayers.end();
for (HashSet<RefPtr<PlatformCALayer> >::iterator it = m_pendingAnimatedLayers.begin(); it != end; ++it)
(*it)->animationStarted(String(), currentTime);
m_pendingAnimatedLayers.clear();
}
CGRect CACFLayerTreeHost::bounds() const
{
RECT clientRect;
GetClientRect(m_window, &clientRect);
return winRectToCGRect(clientRect);
}
String CACFLayerTreeHost::layerTreeAsString() const
{
if (!m_rootLayer)
return emptyString();
return m_rootLayer->layerTreeAsString();
}
TiledBacking* CACFLayerTreeHost::mainFrameTiledBacking() const
{
if (!m_page)
return nullptr;
FrameView* frameView = m_page->mainFrame().view();
if (!frameView)
return nullptr;
return frameView->tiledBacking();
}
void CACFLayerTreeHost::updateDebugInfoLayer(bool showLayer)
{
if (showLayer) {
if (!m_debugInfoLayer) {
if (TiledBacking* tiledBacking = mainFrameTiledBacking())
m_debugInfoLayer = tiledBacking->tiledScrollingIndicatorLayer();
}
if (m_debugInfoLayer) {
#ifndef NDEBUG
m_debugInfoLayer->setName("Debug Info");
#endif
m_rootLayer->appendSublayer(*m_debugInfoLayer);
}
} else if (m_debugInfoLayer) {
m_debugInfoLayer->removeFromSuperlayer();
m_debugInfoLayer = nullptr;
}
}
}
#endif