blob: 53f6e37c540ae0fd70cf91dd20d261f81f8293fa [file] [log] [blame]
/*
* Copyright (C) 2014-2017 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. AND ITS CONTRIBUTORS ``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 ITS 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 "PageOverlayController.h"
#include "Chrome.h"
#include "ChromeClient.h"
#include "Frame.h"
#include "FrameView.h"
#include "GraphicsContext.h"
#include "GraphicsLayer.h"
#include "Page.h"
#include "PageOverlay.h"
#include "ScrollingCoordinator.h"
#include "Settings.h"
#include "TiledBacking.h"
// FIXME: Someone needs to call didChangeSettings() if we want dynamic updates of layer border/repaint counter settings.
namespace WebCore {
PageOverlayController::PageOverlayController(Page& page)
: m_page(page)
{
}
PageOverlayController::~PageOverlayController() = default;
void PageOverlayController::createRootLayersIfNeeded()
{
if (m_initialized)
return;
m_initialized = true;
ASSERT(!m_documentOverlayRootLayer);
ASSERT(!m_viewOverlayRootLayer);
m_documentOverlayRootLayer = GraphicsLayer::create(m_page.chrome().client().graphicsLayerFactory(), *this);
m_viewOverlayRootLayer = GraphicsLayer::create(m_page.chrome().client().graphicsLayerFactory(), *this);
m_documentOverlayRootLayer->setName("Document overlay Container");
m_viewOverlayRootLayer->setName("View overlay container");
}
void PageOverlayController::installedPageOverlaysChanged()
{
if (hasViewOverlays())
attachViewOverlayLayers();
else
detachViewOverlayLayers();
if (auto* frameView = m_page.mainFrame().view())
frameView->setNeedsCompositingConfigurationUpdate();
updateForceSynchronousScrollLayerPositionUpdates();
}
bool PageOverlayController::hasDocumentOverlays() const
{
for (const auto& overlay : m_pageOverlays) {
if (overlay->overlayType() == PageOverlay::OverlayType::Document)
return true;
}
return false;
}
bool PageOverlayController::hasViewOverlays() const
{
for (const auto& overlay : m_pageOverlays) {
if (overlay->overlayType() == PageOverlay::OverlayType::View)
return true;
}
return false;
}
void PageOverlayController::attachViewOverlayLayers()
{
if (hasViewOverlays())
m_page.chrome().client().attachViewOverlayGraphicsLayer(&layerWithViewOverlays());
}
void PageOverlayController::detachViewOverlayLayers()
{
m_page.chrome().client().attachViewOverlayGraphicsLayer(nullptr);
}
GraphicsLayer* PageOverlayController::documentOverlayRootLayer() const
{
return m_documentOverlayRootLayer.get();
}
GraphicsLayer* PageOverlayController::viewOverlayRootLayer() const
{
return m_viewOverlayRootLayer.get();
}
static void updateOverlayGeometry(PageOverlay& overlay, GraphicsLayer& graphicsLayer)
{
IntRect overlayFrame = overlay.frame();
if (overlayFrame.location() == graphicsLayer.position() && overlayFrame.size() == graphicsLayer.size())
return;
graphicsLayer.setPosition(overlayFrame.location());
graphicsLayer.setSize(overlayFrame.size());
}
GraphicsLayer& PageOverlayController::layerWithDocumentOverlays()
{
createRootLayersIfNeeded();
bool inWindow = m_page.isInWindow();
for (auto& overlayAndLayer : m_overlayGraphicsLayers) {
PageOverlay& overlay = *overlayAndLayer.key;
if (overlay.overlayType() != PageOverlay::OverlayType::Document)
continue;
auto& layer = overlayAndLayer.value;
GraphicsLayer::traverse(layer.get(), [inWindow](GraphicsLayer& layer) {
layer.setIsInWindow(inWindow);
});
updateOverlayGeometry(overlay, layer.get());
if (!layer->parent())
m_documentOverlayRootLayer->addChild(layer.copyRef());
}
return *m_documentOverlayRootLayer;
}
GraphicsLayer& PageOverlayController::layerWithViewOverlays()
{
createRootLayersIfNeeded();
bool inWindow = m_page.isInWindow();
for (auto& overlayAndLayer : m_overlayGraphicsLayers) {
PageOverlay& overlay = *overlayAndLayer.key;
if (overlay.overlayType() != PageOverlay::OverlayType::View)
continue;
auto& layer = overlayAndLayer.value;
GraphicsLayer::traverse(layer.get(), [inWindow](GraphicsLayer& layer) {
layer.setIsInWindow(inWindow);
});
updateOverlayGeometry(overlay, layer.get());
if (!layer->parent())
m_viewOverlayRootLayer->addChild(layer.copyRef());
}
return *m_viewOverlayRootLayer;
}
void PageOverlayController::installPageOverlay(PageOverlay& overlay, PageOverlay::FadeMode fadeMode)
{
createRootLayersIfNeeded();
if (m_pageOverlays.contains(&overlay))
return;
m_pageOverlays.append(&overlay);
auto layer = GraphicsLayer::create(m_page.chrome().client().graphicsLayerFactory(), *this);
layer->setAnchorPoint({ });
layer->setBackgroundColor(overlay.backgroundColor());
layer->setName("Overlay content");
updateSettingsForLayer(layer.get());
switch (overlay.overlayType()) {
case PageOverlay::OverlayType::View:
m_viewOverlayRootLayer->addChild(layer.get());
break;
case PageOverlay::OverlayType::Document:
m_documentOverlayRootLayer->addChild(layer.get());
break;
}
auto& rawLayer = layer.get();
m_overlayGraphicsLayers.set(&overlay, WTFMove(layer));
overlay.setPage(&m_page);
if (FrameView* frameView = m_page.mainFrame().view())
frameView->enterCompositingMode();
updateOverlayGeometry(overlay, rawLayer);
if (fadeMode == PageOverlay::FadeMode::Fade)
overlay.startFadeInAnimation();
installedPageOverlaysChanged();
}
void PageOverlayController::uninstallPageOverlay(PageOverlay& overlay, PageOverlay::FadeMode fadeMode)
{
if (fadeMode == PageOverlay::FadeMode::Fade) {
overlay.startFadeOutAnimation();
return;
}
overlay.setPage(nullptr);
if (auto optionalLayer = m_overlayGraphicsLayers.take(&overlay))
optionalLayer.value()->removeFromParent();
bool removed = m_pageOverlays.removeFirst(&overlay);
ASSERT_UNUSED(removed, removed);
installedPageOverlaysChanged();
}
void PageOverlayController::updateForceSynchronousScrollLayerPositionUpdates()
{
#if ENABLE(ASYNC_SCROLLING)
bool forceSynchronousScrollLayerPositionUpdates = false;
for (auto& overlay : m_pageOverlays) {
if (overlay->needsSynchronousScrolling())
forceSynchronousScrollLayerPositionUpdates = true;
}
if (ScrollingCoordinator* scrollingCoordinator = m_page.scrollingCoordinator())
scrollingCoordinator->setForceSynchronousScrollLayerPositionUpdates(forceSynchronousScrollLayerPositionUpdates);
#endif
}
void PageOverlayController::setPageOverlayNeedsDisplay(PageOverlay& overlay, const IntRect& dirtyRect)
{
ASSERT(m_pageOverlays.contains(&overlay));
auto* graphicsLayer = m_overlayGraphicsLayers.get(&overlay);
if (!graphicsLayer->drawsContent()) {
graphicsLayer->setDrawsContent(true);
updateOverlayGeometry(overlay, *graphicsLayer);
}
graphicsLayer->setNeedsDisplayInRect(dirtyRect);
}
void PageOverlayController::setPageOverlayOpacity(PageOverlay& overlay, float opacity)
{
ASSERT(m_pageOverlays.contains(&overlay));
m_overlayGraphicsLayers.get(&overlay)->setOpacity(opacity);
}
void PageOverlayController::clearPageOverlay(PageOverlay& overlay)
{
ASSERT(m_pageOverlays.contains(&overlay));
m_overlayGraphicsLayers.get(&overlay)->setDrawsContent(false);
}
GraphicsLayer& PageOverlayController::layerForOverlay(PageOverlay& overlay) const
{
ASSERT(m_pageOverlays.contains(&overlay));
return *m_overlayGraphicsLayers.get(&overlay);
}
void PageOverlayController::didChangeViewSize()
{
for (auto& overlayAndLayer : m_overlayGraphicsLayers) {
if (overlayAndLayer.key->overlayType() == PageOverlay::OverlayType::View)
updateOverlayGeometry(*overlayAndLayer.key, overlayAndLayer.value.get());
}
}
void PageOverlayController::didChangeDocumentSize()
{
for (auto& overlayAndLayer : m_overlayGraphicsLayers) {
if (overlayAndLayer.key->overlayType() == PageOverlay::OverlayType::Document)
updateOverlayGeometry(*overlayAndLayer.key, overlayAndLayer.value.get());
}
}
void PageOverlayController::didChangeSettings()
{
// FIXME: We should apply these settings to all overlay sublayers recursively.
for (auto& graphicsLayer : m_overlayGraphicsLayers.values())
updateSettingsForLayer(graphicsLayer.get());
}
void PageOverlayController::didChangeDeviceScaleFactor()
{
if (!m_initialized)
return;
m_documentOverlayRootLayer->noteDeviceOrPageScaleFactorChangedIncludingDescendants();
m_viewOverlayRootLayer->noteDeviceOrPageScaleFactorChangedIncludingDescendants();
for (auto& graphicsLayer : m_overlayGraphicsLayers.values())
graphicsLayer->setNeedsDisplay();
}
void PageOverlayController::didChangeViewExposedRect()
{
m_page.renderingUpdateScheduler().scheduleTimedRenderingUpdate();
}
void PageOverlayController::didScrollFrame(Frame& frame)
{
for (auto& overlayAndLayer : m_overlayGraphicsLayers) {
if (overlayAndLayer.key->overlayType() == PageOverlay::OverlayType::View || !frame.isMainFrame())
overlayAndLayer.value->setNeedsDisplay();
overlayAndLayer.key->didScrollFrame(frame);
}
}
void PageOverlayController::updateSettingsForLayer(GraphicsLayer& layer)
{
Settings& settings = m_page.settings();
layer.setAcceleratesDrawing(settings.acceleratedDrawingEnabled());
layer.setShowDebugBorder(settings.showDebugBorders());
layer.setShowRepaintCounter(settings.showRepaintCounter());
}
bool PageOverlayController::handleMouseEvent(const PlatformMouseEvent& mouseEvent)
{
if (m_pageOverlays.isEmpty())
return false;
for (auto it = m_pageOverlays.rbegin(), end = m_pageOverlays.rend(); it != end; ++it) {
if ((*it)->mouseEvent(mouseEvent))
return true;
}
return false;
}
bool PageOverlayController::copyAccessibilityAttributeStringValueForPoint(String attribute, FloatPoint parameter, String& value)
{
if (m_pageOverlays.isEmpty())
return false;
for (auto it = m_pageOverlays.rbegin(), end = m_pageOverlays.rend(); it != end; ++it) {
if ((*it)->copyAccessibilityAttributeStringValueForPoint(attribute, parameter, value))
return true;
}
return false;
}
bool PageOverlayController::copyAccessibilityAttributeBoolValueForPoint(String attribute, FloatPoint parameter, bool& value)
{
if (m_pageOverlays.isEmpty())
return false;
for (auto it = m_pageOverlays.rbegin(), end = m_pageOverlays.rend(); it != end; ++it) {
if ((*it)->copyAccessibilityAttributeBoolValueForPoint(attribute, parameter, value))
return true;
}
return false;
}
Vector<String> PageOverlayController::copyAccessibilityAttributesNames(bool parameterizedNames)
{
if (m_pageOverlays.isEmpty())
return { };
for (auto it = m_pageOverlays.rbegin(), end = m_pageOverlays.rend(); it != end; ++it) {
Vector<String> names = (*it)->copyAccessibilityAttributeNames(parameterizedNames);
if (!names.isEmpty())
return names;
}
return { };
}
void PageOverlayController::paintContents(const GraphicsLayer* graphicsLayer, GraphicsContext& graphicsContext, OptionSet<GraphicsLayerPaintingPhase>, const FloatRect& clipRect, GraphicsLayerPaintBehavior)
{
for (auto& overlayAndGraphicsLayer : m_overlayGraphicsLayers) {
if (overlayAndGraphicsLayer.value.ptr() != graphicsLayer)
continue;
GraphicsContextStateSaver stateSaver(graphicsContext);
graphicsContext.clip(clipRect);
overlayAndGraphicsLayer.key->drawRect(graphicsContext, enclosingIntRect(clipRect));
return;
}
}
float PageOverlayController::deviceScaleFactor() const
{
return m_page.deviceScaleFactor();
}
void PageOverlayController::notifyFlushRequired(const GraphicsLayer*)
{
m_page.renderingUpdateScheduler().scheduleTimedRenderingUpdate();
}
void PageOverlayController::didChangeOverlayFrame(PageOverlay& overlay)
{
ASSERT(m_pageOverlays.contains(&overlay));
if (auto* layer = m_overlayGraphicsLayers.get(&overlay))
updateOverlayGeometry(overlay, *layer);
}
void PageOverlayController::didChangeOverlayBackgroundColor(PageOverlay& overlay)
{
ASSERT(m_pageOverlays.contains(&overlay));
if (auto* layer = m_overlayGraphicsLayers.get(&overlay))
layer->setBackgroundColor(overlay.backgroundColor());
}
bool PageOverlayController::shouldSkipLayerInDump(const GraphicsLayer*, LayerTreeAsTextBehavior behavior) const
{
return !(behavior & LayerTreeAsTextIncludePageOverlayLayers);
}
void PageOverlayController::tiledBackingUsageChanged(const GraphicsLayer* graphicsLayer, bool usingTiledBacking)
{
if (usingTiledBacking)
graphicsLayer->tiledBacking()->setIsInWindow(m_page.isInWindow());
}
} // namespace WebKit