| /* |
| * 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_ENABLED |
| 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 |