blob: d05be3f3feffb4771b9085d8c12d1b4bdf159621 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/paint/FramePainter.h"
#include "core/editing/markers/DocumentMarkerController.h"
#include "core/fetch/MemoryCache.h"
#include "core/frame/FrameView.h"
#include "core/inspector/InspectorInstrumentation.h"
#include "core/inspector/InspectorTraceEvents.h"
#include "core/layout/LayoutView.h"
#include "core/page/Page.h"
#include "core/paint/LayoutObjectDrawingRecorder.h"
#include "core/paint/PaintInfo.h"
#include "core/paint/PaintLayer.h"
#include "core/paint/PaintLayerPainter.h"
#include "core/paint/ScrollbarPainter.h"
#include "core/paint/TransformRecorder.h"
#include "platform/fonts/FontCache.h"
#include "platform/graphics/GraphicsContext.h"
#include "platform/graphics/paint/ClipRecorder.h"
#include "platform/graphics/paint/ScopedPaintChunkProperties.h"
#include "platform/scroll/ScrollbarTheme.h"
namespace blink {
bool FramePainter::s_inPaintContents = false;
void FramePainter::paint(GraphicsContext& context,
const GlobalPaintFlags globalPaintFlags,
const CullRect& rect) {
frameView().notifyPageThatContentAreaWillPaint();
IntRect documentDirtyRect = rect.m_rect;
IntRect visibleAreaWithoutScrollbars(frameView().location(),
frameView().visibleContentRect().size());
documentDirtyRect.intersect(visibleAreaWithoutScrollbars);
documentDirtyRect.moveBy(-frameView().location() +
frameView().scrollOffsetInt());
bool shouldPaintContents = !documentDirtyRect.isEmpty();
bool shouldPaintScrollbars =
!frameView().scrollbarsSuppressed() &&
(frameView().horizontalScrollbar() || frameView().verticalScrollbar());
if (!shouldPaintContents && !shouldPaintScrollbars)
return;
if (shouldPaintContents) {
// TODO(pdr): Creating frame paint properties here will not be needed once
// settings()->rootLayerScrolls() is enabled.
// TODO(pdr): Make this conditional on the rootLayerScrolls setting.
Optional<ScopedPaintChunkProperties> scopedPaintChunkProperties;
if (RuntimeEnabledFeatures::slimmingPaintV2Enabled() &&
!RuntimeEnabledFeatures::rootLayerScrollingEnabled()) {
if (const PropertyTreeState* contentsState =
m_frameView->totalPropertyTreeStateForContents()) {
PaintChunkProperties properties(
context.getPaintController().currentPaintChunkProperties());
properties.propertyTreeState = *contentsState;
scopedPaintChunkProperties.emplace(context.getPaintController(),
*frameView().layoutView(),
properties);
}
}
TransformRecorder transformRecorder(
context, *frameView().layoutView(),
AffineTransform::translation(frameView().x() - frameView().scrollX(),
frameView().y() - frameView().scrollY()));
if (RuntimeEnabledFeatures::rootLayerScrollingEnabled()) {
paintContents(context, globalPaintFlags, documentDirtyRect);
} else {
ClipRecorder clipRecorder(context, *frameView().layoutView(),
DisplayItem::kClipFrameToVisibleContentRect,
frameView().visibleContentRect());
paintContents(context, globalPaintFlags, documentDirtyRect);
}
}
if (shouldPaintScrollbars) {
DCHECK(!RuntimeEnabledFeatures::rootLayerScrollingEnabled());
IntRect scrollViewDirtyRect = rect.m_rect;
IntRect visibleAreaWithScrollbars(
frameView().location(),
frameView().visibleContentRect(IncludeScrollbars).size());
scrollViewDirtyRect.intersect(visibleAreaWithScrollbars);
scrollViewDirtyRect.moveBy(-frameView().location());
Optional<ScopedPaintChunkProperties> scopedPaintChunkProperties;
if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
if (const PropertyTreeState* contentsState =
m_frameView->totalPropertyTreeStateForContents()) {
// The scrollbar's property nodes are similar to the frame view's
// contents state but we want to exclude the content-specific
// properties. This prevents the scrollbars from scrolling, for example.
PaintChunkProperties properties(
context.getPaintController().currentPaintChunkProperties());
properties.propertyTreeState.setTransform(
m_frameView->preTranslation());
properties.propertyTreeState.setClip(
m_frameView->contentClip()->parent());
properties.propertyTreeState.setEffect(contentsState->effect());
auto* scrollBarScroll = contentsState->scroll();
if (m_frameView->scroll())
scrollBarScroll = m_frameView->scroll()->parent();
properties.propertyTreeState.setScroll(scrollBarScroll);
scopedPaintChunkProperties.emplace(context.getPaintController(),
*frameView().layoutView(),
properties);
}
}
TransformRecorder transformRecorder(
context, *frameView().layoutView(),
AffineTransform::translation(frameView().x(), frameView().y()));
ClipRecorder recorder(
context, *frameView().layoutView(), DisplayItem::kClipFrameScrollbars,
IntRect(IntPoint(), visibleAreaWithScrollbars.size()));
paintScrollbars(context, scrollViewDirtyRect);
}
}
void FramePainter::paintContents(GraphicsContext& context,
const GlobalPaintFlags globalPaintFlags,
const IntRect& rect) {
Document* document = frameView().frame().document();
if (frameView().shouldThrottleRendering() || !document->isActive())
return;
LayoutView* layoutView = frameView().layoutView();
if (!layoutView) {
DLOG(ERROR) << "called FramePainter::paint with nil layoutObject";
return;
}
// TODO(crbug.com/590856): It's still broken when we choose not to crash when
// the check fails.
if (!frameView().checkDoesNotNeedLayout())
return;
// TODO(wangxianzhu): The following check should be stricter, but currently
// this is blocked by the svg root issue (crbug.com/442939).
DCHECK(document->lifecycle().state() >= DocumentLifecycle::CompositingClean);
TRACE_EVENT1("devtools.timeline,rail", "Paint", "data",
InspectorPaintEvent::data(layoutView, LayoutRect(rect), 0));
bool isTopLevelPainter = !s_inPaintContents;
s_inPaintContents = true;
FontCachePurgePreventer fontCachePurgePreventer;
// TODO(jchaffraix): GlobalPaintFlags should be const during a paint
// phase. Thus we should set this flag upfront (crbug.com/510280).
GlobalPaintFlags localPaintFlags = globalPaintFlags;
if (document->printing())
localPaintFlags |=
GlobalPaintFlattenCompositingLayers | GlobalPaintPrinting;
PaintLayer* rootLayer = layoutView->layer();
#if DCHECK_IS_ON()
layoutView->assertSubtreeIsLaidOut();
LayoutObject::SetLayoutNeededForbiddenScope forbidSetNeedsLayout(
*rootLayer->layoutObject());
#endif
PaintLayerPainter layerPainter(*rootLayer);
float deviceScaleFactor =
blink::deviceScaleFactor(rootLayer->layoutObject()->frame());
context.setDeviceScaleFactor(deviceScaleFactor);
layerPainter.paint(context, LayoutRect(rect), localPaintFlags);
if (rootLayer->containsDirtyOverlayScrollbars())
layerPainter.paintOverlayScrollbars(context, LayoutRect(rect),
localPaintFlags);
// Regions may have changed as a result of the visibility/z-index of element
// changing.
if (document->annotatedRegionsDirty())
frameView().updateDocumentAnnotatedRegions();
if (isTopLevelPainter) {
// Everything that happens after paintContents completions is considered
// to be part of the next frame.
memoryCache()->updateFramePaintTimestamp();
s_inPaintContents = false;
}
InspectorInstrumentation::didPaint(layoutView->frame(), 0, context,
LayoutRect(rect));
}
void FramePainter::paintScrollbars(GraphicsContext& context,
const IntRect& rect) {
if (frameView().horizontalScrollbar() &&
!frameView().layerForHorizontalScrollbar())
paintScrollbar(context, *frameView().horizontalScrollbar(), rect);
if (frameView().verticalScrollbar() &&
!frameView().layerForVerticalScrollbar())
paintScrollbar(context, *frameView().verticalScrollbar(), rect);
if (frameView().layerForScrollCorner())
return;
paintScrollCorner(context, frameView().scrollCornerRect());
}
void FramePainter::paintScrollCorner(GraphicsContext& context,
const IntRect& cornerRect) {
if (frameView().scrollCorner()) {
bool needsBackground = frameView().frame().isMainFrame();
if (needsBackground &&
!LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(
context, *frameView().layoutView(),
DisplayItem::kScrollbarCorner)) {
LayoutObjectDrawingRecorder drawingRecorder(
context, *frameView().layoutView(), DisplayItem::kScrollbarCorner,
FloatRect(cornerRect));
context.fillRect(cornerRect, frameView().baseBackgroundColor());
}
ScrollbarPainter::paintIntoRect(*frameView().scrollCorner(), context,
cornerRect.location(),
LayoutRect(cornerRect));
return;
}
ScrollbarTheme::theme().paintScrollCorner(context, *frameView().layoutView(),
cornerRect);
}
void FramePainter::paintScrollbar(GraphicsContext& context,
Scrollbar& bar,
const IntRect& rect) {
bool needsBackground =
bar.isCustomScrollbar() && frameView().frame().isMainFrame();
if (needsBackground) {
IntRect toFill = bar.frameRect();
toFill.intersect(rect);
context.fillRect(toFill, frameView().baseBackgroundColor());
}
bar.paint(context, CullRect(rect));
}
const FrameView& FramePainter::frameView() {
DCHECK(m_frameView);
return *m_frameView;
}
} // namespace blink