DrawingAreaImpl.cpp [plain text]
#include "config.h"
#include "DrawingAreaImpl.h"
#include "DrawingAreaProxyMessages.h"
#include "LayerTreeHost.h"
#include "ShareableBitmap.h"
#include "UpdateInfo.h"
#include "WebPage.h"
#include "WebPageCreationParameters.h"
#include "WebPreferencesKeys.h"
#include <WebCore/GraphicsContext.h>
#include <WebCore/Page.h>
#include <WebCore/Settings.h>
#if USE(GLIB_EVENT_LOOP)
#include <wtf/glib/RunLoopSourcePriority.h>
#endif
using namespace WebCore;
namespace WebKit {
DrawingAreaImpl::~DrawingAreaImpl()
{
}
DrawingAreaImpl::DrawingAreaImpl(WebPage& webPage, const WebPageCreationParameters& parameters)
: AcceleratedDrawingArea(webPage, parameters)
, m_displayTimer(RunLoop::main(), this, &DrawingAreaImpl::displayTimerFired)
{
#if USE(GLIB_EVENT_LOOP)
m_displayTimer.setPriority(RunLoopSourcePriority::NonAcceleratedDrawingTimer);
#endif
}
void DrawingAreaImpl::setNeedsDisplay()
{
if (m_layerTreeHost) {
ASSERT(m_dirtyRegion.isEmpty());
AcceleratedDrawingArea::setNeedsDisplay();
return;
}
setNeedsDisplayInRect(m_webPage.bounds());
}
void DrawingAreaImpl::setNeedsDisplayInRect(const IntRect& rect)
{
if (m_layerTreeHost) {
ASSERT(m_dirtyRegion.isEmpty());
AcceleratedDrawingArea::setNeedsDisplayInRect(rect);
return;
}
if (!m_isPaintingEnabled)
return;
IntRect dirtyRect = rect;
dirtyRect.intersect(m_webPage.bounds());
if (dirtyRect.isEmpty())
return;
m_dirtyRegion.unite(dirtyRect);
scheduleDisplay();
}
void DrawingAreaImpl::scroll(const IntRect& scrollRect, const IntSize& scrollDelta)
{
if (m_layerTreeHost) {
ASSERT(m_scrollRect.isEmpty());
ASSERT(m_scrollOffset.isEmpty());
ASSERT(m_dirtyRegion.isEmpty());
AcceleratedDrawingArea::scroll(scrollRect, scrollDelta);
return;
}
if (!m_isPaintingEnabled)
return;
if (scrollRect.isEmpty())
return;
if (m_previousLayerTreeHost)
m_previousLayerTreeHost->scrollNonCompositedContents(scrollRect);
if (!m_scrollRect.isEmpty() && scrollRect != m_scrollRect) {
unsigned scrollArea = scrollRect.width() * scrollRect.height();
unsigned currentScrollArea = m_scrollRect.width() * m_scrollRect.height();
if (currentScrollArea >= scrollArea) {
setNeedsDisplayInRect(scrollRect);
return;
}
setNeedsDisplayInRect(m_scrollRect);
m_scrollRect = IntRect();
m_scrollOffset = IntSize();
}
Region dirtyRegionInScrollRect = intersect(scrollRect, m_dirtyRegion);
if (!dirtyRegionInScrollRect.isEmpty()) {
m_dirtyRegion.subtract(scrollRect);
Region movedDirtyRegionInScrollRect = intersect(translate(dirtyRegionInScrollRect, scrollDelta), scrollRect);
m_dirtyRegion.unite(movedDirtyRegionInScrollRect);
}
Region scrollRepaintRegion = subtract(scrollRect, translate(scrollRect, scrollDelta));
m_dirtyRegion.unite(scrollRepaintRegion);
scheduleDisplay();
m_scrollRect = scrollRect;
m_scrollOffset += scrollDelta;
}
void DrawingAreaImpl::forceRepaint()
{
if (m_inUpdateBackingStoreState) {
m_forceRepaintAfterBackingStoreStateUpdate = true;
return;
}
m_forceRepaintAfterBackingStoreStateUpdate = false;
if (m_layerTreeHost) {
AcceleratedDrawingArea::forceRepaint();
return;
}
m_isWaitingForDidUpdate = false;
if (m_isPaintingEnabled) {
m_dirtyRegion = m_webPage.bounds();
display();
}
}
void DrawingAreaImpl::updatePreferences(const WebPreferencesStore& store)
{
Settings& settings = m_webPage.corePage()->settings();
settings.setForceCompositingMode(store.getBoolValueForKey(WebPreferencesKey::forceCompositingModeKey()));
#if USE(COORDINATED_GRAPHICS_THREADED)
settings.setAcceleratedCompositingForFixedPositionEnabled(settings.acceleratedCompositingEnabled());
#endif
m_alwaysUseCompositing = settings.acceleratedCompositingEnabled() && settings.forceCompositingMode();
if (m_alwaysUseCompositing && !m_layerTreeHost)
enterAcceleratedCompositingMode(nullptr);
}
void DrawingAreaImpl::setRootCompositingLayer(GraphicsLayer* graphicsLayer)
{
if (m_layerTreeHost) {
AcceleratedDrawingArea::setRootCompositingLayer(graphicsLayer);
if (!graphicsLayer && !m_alwaysUseCompositing) {
if (m_inUpdateBackingStoreState)
exitAcceleratedCompositingMode();
else
exitAcceleratedCompositingModeSoon();
}
return;
}
if (!graphicsLayer)
return;
enterAcceleratedCompositingMode(graphicsLayer);
}
void DrawingAreaImpl::updateBackingStoreState(uint64_t stateID, bool respondImmediately, float deviceScaleFactor, const WebCore::IntSize& size, const WebCore::IntSize& scrollOffset)
{
if (stateID != m_backingStoreStateID && !m_layerTreeHost)
m_dirtyRegion = IntRect(IntPoint(), size);
AcceleratedDrawingArea::updateBackingStoreState(stateID, respondImmediately, deviceScaleFactor, size, scrollOffset);
if (m_forceRepaintAfterBackingStoreStateUpdate)
forceRepaint();
}
void DrawingAreaImpl::didUpdateBackingStoreState()
{
m_isWaitingForDidUpdate = false;
}
void DrawingAreaImpl::sendDidUpdateBackingStoreState()
{
ASSERT(!m_isWaitingForDidUpdate);
ASSERT(m_shouldSendDidUpdateBackingStoreState);
if (!m_isPaintingSuspended && !m_layerTreeHost) {
UpdateInfo updateInfo;
display(updateInfo);
if (!m_layerTreeHost) {
m_shouldSendDidUpdateBackingStoreState = false;
LayerTreeContext layerTreeContext;
m_webPage.send(Messages::DrawingAreaProxy::DidUpdateBackingStoreState(m_backingStoreStateID, updateInfo, layerTreeContext));
m_compositingAccordingToProxyMessages = false;
return;
}
}
AcceleratedDrawingArea::sendDidUpdateBackingStoreState();
}
void DrawingAreaImpl::didUpdate()
{
if (m_layerTreeHost)
return;
m_isWaitingForDidUpdate = false;
displayTimerFired();
}
void DrawingAreaImpl::suspendPainting()
{
AcceleratedDrawingArea::suspendPainting();
m_displayTimer.stop();
}
void DrawingAreaImpl::enterAcceleratedCompositingMode(GraphicsLayer* graphicsLayer)
{
AcceleratedDrawingArea::enterAcceleratedCompositingMode(graphicsLayer);
m_dirtyRegion = Region();
m_scrollRect = IntRect();
m_scrollOffset = IntSize();
m_displayTimer.stop();
m_isWaitingForDidUpdate = false;
}
void DrawingAreaImpl::exitAcceleratedCompositingMode()
{
if (m_alwaysUseCompositing)
return;
AcceleratedDrawingArea::exitAcceleratedCompositingModeNow();
m_dirtyRegion = m_webPage.bounds();
if (m_inUpdateBackingStoreState)
return;
if (m_shouldSendDidUpdateBackingStoreState) {
sendDidUpdateBackingStoreState();
return;
}
UpdateInfo updateInfo;
if (m_isPaintingSuspended) {
updateInfo.viewSize = m_webPage.size();
updateInfo.deviceScaleFactor = m_webPage.corePage()->deviceScaleFactor();
} else
display(updateInfo);
if (m_compositingAccordingToProxyMessages) {
m_webPage.send(Messages::DrawingAreaProxy::ExitAcceleratedCompositingMode(m_backingStoreStateID, updateInfo));
m_compositingAccordingToProxyMessages = false;
} else {
m_webPage.send(Messages::DrawingAreaProxy::Update(m_backingStoreStateID, updateInfo));
}
}
void DrawingAreaImpl::scheduleDisplay()
{
ASSERT(!m_layerTreeHost);
if (m_isWaitingForDidUpdate)
return;
if (m_isPaintingSuspended)
return;
if (m_dirtyRegion.isEmpty())
return;
if (m_displayTimer.isActive())
return;
m_displayTimer.startOneShot(0_s);
}
void DrawingAreaImpl::displayTimerFired()
{
display();
}
void DrawingAreaImpl::display()
{
ASSERT(!m_layerTreeHost);
ASSERT(!m_isWaitingForDidUpdate);
ASSERT(!m_inUpdateBackingStoreState);
if (m_isPaintingSuspended)
return;
if (m_dirtyRegion.isEmpty())
return;
if (m_shouldSendDidUpdateBackingStoreState) {
sendDidUpdateBackingStoreState();
return;
}
UpdateInfo updateInfo;
display(updateInfo);
if (m_layerTreeHost) {
return;
}
m_webPage.send(Messages::DrawingAreaProxy::Update(m_backingStoreStateID, updateInfo));
m_isWaitingForDidUpdate = true;
}
static bool shouldPaintBoundsRect(const IntRect& bounds, const Vector<IntRect>& rects)
{
const size_t rectThreshold = 10;
const double wastedSpaceThreshold = 0.75;
if (rects.size() <= 1 || rects.size() > rectThreshold)
return true;
unsigned boundsArea = bounds.width() * bounds.height();
unsigned rectsArea = 0;
for (size_t i = 0; i < rects.size(); ++i)
rectsArea += rects[i].width() * rects[i].height();
double wastedSpace = 1 - (static_cast<double>(rectsArea) / boundsArea);
return wastedSpace <= wastedSpaceThreshold;
}
void DrawingAreaImpl::display(UpdateInfo& updateInfo)
{
ASSERT(!m_isPaintingSuspended);
ASSERT(!m_layerTreeHost);
ASSERT(!m_webPage.size().isEmpty());
m_webPage.layoutIfNeeded();
if (m_layerTreeHost)
return;
updateInfo.viewSize = m_webPage.size();
updateInfo.deviceScaleFactor = m_webPage.corePage()->deviceScaleFactor();
IntRect bounds = m_webPage.drawsBackground() ? m_dirtyRegion.bounds() : m_webPage.bounds();
ASSERT(m_webPage.bounds().contains(bounds));
IntSize bitmapSize = bounds.size();
float deviceScaleFactor = m_webPage.corePage()->deviceScaleFactor();
bitmapSize.scale(deviceScaleFactor);
RefPtr<ShareableBitmap> bitmap = ShareableBitmap::createShareable(bitmapSize, ShareableBitmap::SupportsAlpha);
if (!bitmap)
return;
if (!bitmap->createHandle(updateInfo.bitmapHandle))
return;
Vector<IntRect> rects;
if (m_webPage.drawsBackground()) {
rects = m_dirtyRegion.rects();
if (shouldPaintBoundsRect(bounds, rects)) {
rects.clear();
rects.append(bounds);
}
} else
rects.append(bounds);
updateInfo.scrollRect = m_scrollRect;
updateInfo.scrollOffset = m_scrollOffset;
m_dirtyRegion = Region();
m_scrollRect = IntRect();
m_scrollOffset = IntSize();
auto graphicsContext = bitmap->createGraphicsContext();
graphicsContext->applyDeviceScaleFactor(deviceScaleFactor);
updateInfo.updateRectBounds = bounds;
graphicsContext->translate(-bounds.x(), -bounds.y());
for (const auto& rect : rects) {
m_webPage.drawRect(*graphicsContext, rect);
updateInfo.updateRects.append(rect);
}
m_displayTimer.stop();
}
}