blob: 490fc47ef8b8f25a0c7525e4b856b193d6e4b239 [file] [log] [blame]
/*
* Copyright (C) 2009, 2010 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 COMPUTER, 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 "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
#include "base/optional.h"
#include "third_party/blink/renderer/core/animation/document_animations.h"
#include "third_party/blink/renderer/core/animation/document_timeline.h"
#include "third_party/blink/renderer/core/animation/element_animations.h"
#include "third_party/blink/renderer/core/dom/dom_node_ids.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_client.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/frame/visual_viewport.h"
#include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
#include "third_party/blink/renderer/core/html/html_iframe_element.h"
#include "third_party/blink/renderer/core/html/media/html_video_element.h"
#include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
#include "third_party/blink/renderer/core/layout/layout_video.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
#include "third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h"
#include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
#include "third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.h"
#include "third_party/blink/renderer/core/paint/compositing/compositing_layer_assigner.h"
#include "third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater.h"
#include "third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_builder.h"
#include "third_party/blink/renderer/core/paint/compositing/graphics_layer_updater.h"
#include "third_party/blink/renderer/core/paint/object_paint_invalidator.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/core/probe/core_probes.h"
#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
#include "third_party/blink/renderer/platform/geometry/float_rect.h"
#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
#include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
#include "third_party/blink/renderer/platform/histogram.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
namespace blink {
PaintLayerCompositor::PaintLayerCompositor(LayoutView& layout_view)
: layout_view_(layout_view),
compositing_reason_finder_(layout_view),
pending_update_type_(kCompositingUpdateNone),
has_accelerated_compositing_(true),
compositing_(false),
root_should_always_composite_dirty_(true),
in_overlay_fullscreen_video_(false),
root_layer_attachment_(kRootLayerUnattached) {
UpdateAcceleratedCompositingSettings();
}
PaintLayerCompositor::~PaintLayerCompositor() {
DCHECK_EQ(root_layer_attachment_, kRootLayerUnattached);
}
bool PaintLayerCompositor::InCompositingMode() const {
// FIXME: This should assert that lifecycle is >= CompositingClean since
// the last step of updateIfNeeded can set this bit to false.
DCHECK(layout_view_.Layer()->IsAllowedToQueryCompositingState());
return compositing_;
}
bool PaintLayerCompositor::StaleInCompositingMode() const {
return compositing_;
}
void PaintLayerCompositor::SetCompositingModeEnabled(bool enable) {
if (enable == compositing_)
return;
compositing_ = enable;
if (compositing_)
EnsureRootLayer();
else
DestroyRootLayer();
// Schedule an update in the parent frame so the <iframe>'s layer in the owner
// document matches the compositing state here.
if (HTMLFrameOwnerElement* owner_element =
layout_view_.GetDocument().LocalOwner())
owner_element->SetNeedsCompositingUpdate();
}
void PaintLayerCompositor::EnableCompositingModeIfNeeded() {
if (!root_should_always_composite_dirty_)
return;
root_should_always_composite_dirty_ = false;
if (compositing_)
return;
if (RootShouldAlwaysComposite()) {
// FIXME: Is this needed? It was added in
// https://bugs.webkit.org/show_bug.cgi?id=26651.
// No tests fail if it's deleted.
SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree);
SetCompositingModeEnabled(true);
}
}
bool PaintLayerCompositor::RootShouldAlwaysComposite() const {
if (!has_accelerated_compositing_)
return false;
return layout_view_.GetFrame()->IsLocalRoot() ||
compositing_reason_finder_.RequiresCompositingForScrollableFrame();
}
void PaintLayerCompositor::UpdateAcceleratedCompositingSettings() {
compositing_reason_finder_.UpdateTriggers();
has_accelerated_compositing_ = layout_view_.GetDocument()
.GetSettings()
->GetAcceleratedCompositingEnabled();
root_should_always_composite_dirty_ = true;
if (root_layer_attachment_ != kRootLayerUnattached)
RootLayer()->SetNeedsCompositingInputsUpdate();
}
bool PaintLayerCompositor::PreferCompositingToLCDTextEnabled() const {
return layout_view_.GetDocument()
.GetSettings()
->GetPreferCompositingToLCDTextEnabled();
}
static LayoutVideo* FindFullscreenVideoLayoutObject(Document& document) {
// Recursively find the document that is in fullscreen.
Element* fullscreen_element = Fullscreen::FullscreenElementFrom(document);
Document* content_document = &document;
while (fullscreen_element && fullscreen_element->IsFrameOwnerElement()) {
content_document =
ToHTMLFrameOwnerElement(fullscreen_element)->contentDocument();
if (!content_document)
return nullptr;
fullscreen_element = Fullscreen::FullscreenElementFrom(*content_document);
}
if (!IsHTMLVideoElement(fullscreen_element))
return nullptr;
LayoutObject* layout_object = fullscreen_element->GetLayoutObject();
if (!layout_object)
return nullptr;
return ToLayoutVideo(layout_object);
}
void PaintLayerCompositor::UpdateIfNeededRecursive(
DocumentLifecycle::LifecycleState target_state) {
CompositingReasonsStats compositing_reasons_stats;
UpdateIfNeededRecursiveInternal(target_state, compositing_reasons_stats);
UMA_HISTOGRAM_CUSTOM_COUNTS("Blink.Compositing.LayerPromotionCount.Overlap",
compositing_reasons_stats.overlap_layers, 1, 100,
5);
UMA_HISTOGRAM_CUSTOM_COUNTS(
"Blink.Compositing.LayerPromotionCount.ActiveAnimation",
compositing_reasons_stats.active_animation_layers, 1, 100, 5);
UMA_HISTOGRAM_CUSTOM_COUNTS(
"Blink.Compositing.LayerPromotionCount.AssumedOverlap",
compositing_reasons_stats.assumed_overlap_layers, 1, 100, 5);
UMA_HISTOGRAM_CUSTOM_COUNTS(
"Blink.Compositing.LayerPromotionCount.IndirectComposited",
compositing_reasons_stats.indirect_composited_layers, 1, 100, 5);
UMA_HISTOGRAM_CUSTOM_COUNTS(
"Blink.Compositing.LayerPromotionCount.TotalComposited",
compositing_reasons_stats.total_composited_layers, 1, 1000, 10);
}
void PaintLayerCompositor::UpdateIfNeededRecursiveInternal(
DocumentLifecycle::LifecycleState target_state,
CompositingReasonsStats& compositing_reasons_stats) {
DCHECK(target_state >= DocumentLifecycle::kCompositingInputsClean);
if (layout_view_.GetFrameView()->ShouldThrottleRendering())
return;
LocalFrameView* view = layout_view_.GetFrameView();
view->ResetNeedsForcedCompositingUpdate();
for (Frame* child =
layout_view_.GetFrameView()->GetFrame().Tree().FirstChild();
child; child = child->Tree().NextSibling()) {
if (!child->IsLocalFrame())
continue;
LocalFrame* local_frame = ToLocalFrame(child);
// It's possible for trusted Pepper plugins to force hit testing in
// situations where the frame tree is in an inconsistent state, such as in
// the middle of frame detach.
// TODO(bbudge) Remove this check when trusted Pepper plugins are gone.
if (local_frame->GetDocument()->IsActive() &&
local_frame->ContentLayoutObject()) {
local_frame->ContentLayoutObject()
->Compositor()
->UpdateIfNeededRecursiveInternal(target_state,
compositing_reasons_stats);
}
}
TRACE_EVENT0("blink,benchmark",
"PaintLayerCompositor::updateIfNeededRecursive");
DCHECK(!layout_view_.NeedsLayout());
ScriptForbiddenScope forbid_script;
// FIXME: enableCompositingModeIfNeeded can trigger a
// CompositingUpdateRebuildTree, which asserts that it's not
// InCompositingUpdate.
EnableCompositingModeIfNeeded();
RootLayer()->UpdateDescendantDependentFlags();
layout_view_.CommitPendingSelection();
UpdateIfNeeded(target_state, compositing_reasons_stats);
DCHECK(Lifecycle().GetState() == DocumentLifecycle::kCompositingInputsClean ||
Lifecycle().GetState() == DocumentLifecycle::kCompositingClean);
if (target_state == DocumentLifecycle::kCompositingInputsClean)
return;
// When printing a document, there is no use in updating composited animations
// since we won't use the results.
//
// RuntimeEnabledFeatures::PrintBrowserEnabled is a mode which runs the
// browser normally, but renders every page as if it were being printed. See
// crbug.com/667547
if (!layout_view_.GetDocument().Printing() ||
RuntimeEnabledFeatures::PrintBrowserEnabled()) {
// Although BlinkGenPropertyTreesEnabled still uses PaintLayerCompositor to
// generate the composited layer tree/list, it also has the CAP behavior of
// removing layers that do not draw content. As such, we use the same path
// as CAP for updating composited animations once we know the final set of
// composited elements (see LocalFrameView::UpdateLifecyclePhasesInternal,
// during kPaintClean).
if (!RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) {
base::Optional<CompositorElementIdSet> composited_element_ids;
DocumentAnimations::UpdateAnimations(layout_view_.GetDocument(),
DocumentLifecycle::kCompositingClean,
composited_element_ids);
}
layout_view_.GetFrameView()
->GetScrollableArea()
->UpdateCompositorScrollAnimations();
if (const LocalFrameView::ScrollableAreaSet* animating_scrollable_areas =
layout_view_.GetFrameView()->AnimatingScrollableAreas()) {
for (PaintLayerScrollableArea* scrollable_area :
*animating_scrollable_areas) {
scrollable_area->UpdateCompositorScrollAnimations();
}
}
}
#if DCHECK_IS_ON()
DCHECK_EQ(Lifecycle().GetState(), DocumentLifecycle::kCompositingClean);
AssertNoUnresolvedDirtyBits();
for (Frame* child =
layout_view_.GetFrameView()->GetFrame().Tree().FirstChild();
child; child = child->Tree().NextSibling()) {
if (!child->IsLocalFrame())
continue;
LocalFrame* local_frame = ToLocalFrame(child);
if (local_frame->ShouldThrottleRendering() ||
!local_frame->ContentLayoutObject())
continue;
local_frame->ContentLayoutObject()
->Compositor()
->AssertNoUnresolvedDirtyBits();
}
#endif
}
void PaintLayerCompositor::SetNeedsCompositingUpdate(
CompositingUpdateType update_type) {
DCHECK_NE(update_type, kCompositingUpdateNone);
pending_update_type_ = std::max(pending_update_type_, update_type);
if (Page* page = GetPage())
page->Animator().ScheduleVisualUpdate(layout_view_.GetFrame());
Lifecycle().EnsureStateAtMost(DocumentLifecycle::kLayoutClean);
}
void PaintLayerCompositor::DidLayout() {
// FIXME: Technically we only need to do this when the LocalFrameView's
// isScrollable method would return a different value.
root_should_always_composite_dirty_ = true;
EnableCompositingModeIfNeeded();
}
#if DCHECK_IS_ON()
void PaintLayerCompositor::AssertNoUnresolvedDirtyBits() {
DCHECK_EQ(pending_update_type_, kCompositingUpdateNone);
DCHECK(!root_should_always_composite_dirty_);
}
#endif
GraphicsLayer* PaintLayerCompositor::OverlayFullscreenVideoGraphicsLayer() {
LayoutVideo* video =
FindFullscreenVideoLayoutObject(layout_view_.GetDocument());
if (!video || !video->Layer()->HasCompositedLayerMapping() ||
!video->VideoElement()->UsesOverlayFullscreenVideo()) {
return nullptr;
}
return video->Layer()->GetCompositedLayerMapping()->MainGraphicsLayer();
}
void PaintLayerCompositor::ApplyOverlayFullscreenVideoAdjustmentIfNeeded() {
in_overlay_fullscreen_video_ = false;
GraphicsLayer* content_parent = ParentForContentLayers();
if (!content_parent)
return;
bool is_local_root = layout_view_.GetFrame()->IsLocalRoot();
GraphicsLayer* video_layer = OverlayFullscreenVideoGraphicsLayer();
AdjustOverlayFullscreenVideoPosition(video_layer);
// Only steal fullscreen video layer and clear all other layers if we are the
// main frame.
if (!is_local_root || !video_layer)
return;
content_parent->RemoveAllChildren();
content_parent->AddChild(video_layer);
in_overlay_fullscreen_video_ = true;
}
void PaintLayerCompositor::AdjustOverlayFullscreenVideoPosition(
GraphicsLayer* video_layer) {
if (!video_layer)
return;
// The fullscreen video has layer position equal to its enclosing frame's
// scroll position because fullscreen container is fixed-positioned.
// We should reset layer position here since it is attached at the
// very top level.
video_layer->SetPosition(FloatPoint());
}
void PaintLayerCompositor::UpdateWithoutAcceleratedCompositing(
CompositingUpdateType update_type) {
DCHECK(!HasAcceleratedCompositing());
if (update_type >= kCompositingUpdateAfterCompositingInputChange)
CompositingInputsUpdater(RootLayer(), compositing_reason_finder_).Update();
#if DCHECK_IS_ON()
CompositingInputsUpdater::AssertNeedsCompositingInputsUpdateBitsCleared(
RootLayer());
#endif
}
static void ForceRecomputeVisualRectsIncludingNonCompositingDescendants(
LayoutObject& layout_object) {
// We clear the previous visual rect as it's wrong (paint invalidation
// container changed, ...). Forcing a full invalidation will make us recompute
// it. Also we are not changing the previous position from our paint
// invalidation container, which is fine as we want a full paint invalidation
// anyway.
layout_object.ClearPreviousVisualRects();
for (LayoutObject* child = layout_object.SlowFirstChild(); child;
child = child->NextSibling()) {
if (!child->IsPaintInvalidationContainer())
ForceRecomputeVisualRectsIncludingNonCompositingDescendants(*child);
}
}
GraphicsLayer* PaintLayerCompositor::ParentForContentLayers(
GraphicsLayer* child_frame_parent_candidate) const {
// Iframe content layers were connected by the parent frame using
// AttachFrameContentLayersToIframeLayer. Return whatever candidate was given
// to us as the child frame parent.
if (!IsMainFrame())
return child_frame_parent_candidate;
// If this is a popup, don't hook into the VisualViewport layers.
if (layout_view_.GetDocument().GetPage()->GetChromeClient().IsPopup())
return nullptr;
return GetVisualViewport().ScrollLayer();
}
#if DCHECK_IS_ON()
static void AssertWholeTreeNotComposited(const PaintLayer& paint_layer) {
DCHECK(paint_layer.GetCompositingState() == kNotComposited);
for (PaintLayer* child = paint_layer.FirstChild(); child;
child = child->NextSibling()) {
AssertWholeTreeNotComposited(*child);
}
}
#endif
void PaintLayerCompositor::UpdateIfNeeded(
DocumentLifecycle::LifecycleState target_state,
CompositingReasonsStats& compositing_reasons_stats) {
DCHECK(target_state >= DocumentLifecycle::kCompositingInputsClean);
Lifecycle().AdvanceTo(DocumentLifecycle::kInCompositingUpdate);
if (pending_update_type_ < kCompositingUpdateAfterCompositingInputChange &&
target_state == DocumentLifecycle::kCompositingInputsClean) {
// The compositing inputs are already clean and that is our target state.
// Early-exit here without clearing the pending update type since we haven't
// handled e.g. geometry updates.
Lifecycle().AdvanceTo(DocumentLifecycle::kCompositingInputsClean);
return;
}
CompositingUpdateType update_type = pending_update_type_;
pending_update_type_ = kCompositingUpdateNone;
if (!HasAcceleratedCompositing()) {
UpdateWithoutAcceleratedCompositing(update_type);
Lifecycle().AdvanceTo(
std::min(DocumentLifecycle::kCompositingClean, target_state));
return;
}
if (update_type == kCompositingUpdateNone) {
Lifecycle().AdvanceTo(
std::min(DocumentLifecycle::kCompositingClean, target_state));
return;
}
PaintLayer* update_root = RootLayer();
Vector<PaintLayer*> layers_needing_paint_invalidation;
if (update_type >= kCompositingUpdateAfterCompositingInputChange) {
CompositingInputsUpdater(update_root, compositing_reason_finder_).Update();
#if DCHECK_IS_ON()
// FIXME: Move this check to the end of the compositing update.
CompositingInputsUpdater::AssertNeedsCompositingInputsUpdateBitsCleared(
update_root);
#endif
// In the case where we only want to make compositing inputs clean, we
// early-exit here. Because we have not handled the other implications of
// |pending_update_type_| > kCompositingUpdateNone, we must restore the
// pending update type for a future call.
if (target_state == DocumentLifecycle::kCompositingInputsClean) {
pending_update_type_ = update_type;
Lifecycle().AdvanceTo(DocumentLifecycle::kCompositingInputsClean);
return;
}
CompositingRequirementsUpdater(layout_view_, compositing_reason_finder_)
.Update(update_root, compositing_reasons_stats);
CompositingLayerAssigner layer_assigner(this);
layer_assigner.Assign(update_root, layers_needing_paint_invalidation);
if (layer_assigner.LayersChanged()) {
update_type = std::max(update_type, kCompositingUpdateRebuildTree);
if (ScrollingCoordinator* scrolling_coordinator =
GetScrollingCoordinator()) {
LocalFrameView* frame_view = layout_view_.GetFrameView();
scrolling_coordinator->NotifyGeometryChanged(frame_view);
}
}
}
GraphicsLayer* current_parent = nullptr;
// Save off our current parent. We need this in subframes, because our
// parent attached us to itself via AttachFrameContentLayersToIframeLayer().
// However, if we're about to update our layer structure in
// GraphicsLayerUpdater, we will sometimes remove our root graphics layer
// from its parent. If there are no further tree updates, this means that
// our root graphics layer will not be attached to anything. Below, we would
// normally get the ParentForContentLayer to fix up this situation. However,
// in RLS non-main frames don't have this parent. So, instead use this
// saved-off parent.
if (!IsMainFrame() && update_root->GetCompositedLayerMapping()) {
current_parent = update_root->GetCompositedLayerMapping()
->ChildForSuperlayers()
->Parent();
}
#if DCHECK_IS_ON()
if (update_root->GetCompositingState() != kPaintsIntoOwnBacking) {
AssertWholeTreeNotComposited(*update_root);
}
#endif
GraphicsLayerUpdater updater;
updater.Update(*update_root, layers_needing_paint_invalidation);
if (updater.NeedsRebuildTree())
update_type = std::max(update_type, kCompositingUpdateRebuildTree);
#if DCHECK_IS_ON()
// FIXME: Move this check to the end of the compositing update.
GraphicsLayerUpdater::AssertNeedsToUpdateGraphicsLayerBitsCleared(
*update_root);
#endif
if (update_type >= kCompositingUpdateRebuildTree) {
GraphicsLayerVector child_list;
{
TRACE_EVENT0("blink", "GraphicsLayerTreeBuilder::rebuild");
GraphicsLayerTreeBuilder().Rebuild(*update_root, child_list);
}
if (!child_list.IsEmpty()) {
CHECK(compositing_);
if (GraphicsLayer* content_parent =
ParentForContentLayers(current_parent)) {
content_parent->SetChildren(child_list);
}
}
ApplyOverlayFullscreenVideoAdjustmentIfNeeded();
} else {
AdjustOverlayFullscreenVideoPosition(OverlayFullscreenVideoGraphicsLayer());
}
for (unsigned i = 0; i < layers_needing_paint_invalidation.size(); i++) {
ForceRecomputeVisualRectsIncludingNonCompositingDescendants(
layers_needing_paint_invalidation[i]->GetLayoutObject());
}
if (root_layer_attachment_ == kRootLayerPendingAttachViaChromeClient) {
if (Page* page = layout_view_.GetFrame()->GetPage()) {
page->GetChromeClient().AttachRootGraphicsLayer(RootGraphicsLayer(),
layout_view_.GetFrame());
root_layer_attachment_ = kRootLayerAttachedViaChromeClient;
}
}
// Inform the inspector that the layer tree has changed.
if (IsMainFrame())
probe::layerTreeDidChange(layout_view_.GetFrame());
Lifecycle().AdvanceTo(DocumentLifecycle::kCompositingClean);
}
static void RestartAnimationOnCompositor(const LayoutObject& layout_object) {
Node* node = layout_object.GetNode();
ElementAnimations* element_animations =
(node && node->IsElementNode()) ? ToElement(node)->GetElementAnimations()
: nullptr;
if (element_animations)
element_animations->RestartAnimationOnCompositor();
}
bool PaintLayerCompositor::AllocateOrClearCompositedLayerMapping(
PaintLayer* layer,
const CompositingStateTransitionType composited_layer_update) {
bool composited_layer_mapping_changed = false;
// FIXME: It would be nice to directly use the layer's compositing reason,
// but allocateOrClearCompositedLayerMapping also gets called without having
// updated compositing requirements fully.
switch (composited_layer_update) {
case kAllocateOwnCompositedLayerMapping:
DCHECK(!layer->HasCompositedLayerMapping());
SetCompositingModeEnabled(true);
// If we need to issue paint invalidations, do so before allocating the
// compositedLayerMapping and clearing out the groupedMapping.
PaintInvalidationOnCompositingChange(layer);
// If this layer was previously squashed, we need to remove its reference
// to a groupedMapping right away, so that computing paint invalidation
// rects will know the layer's correct compositingState.
// FIXME: do we need to also remove the layer from it's location in the
// squashing list of its groupedMapping? Need to create a test where a
// squashed layer pops into compositing. And also to cover all other sorts
// of compositingState transitions.
layer->SetLostGroupedMapping(false);
layer->SetGroupedMapping(
nullptr, PaintLayer::kInvalidateLayerAndRemoveFromMapping);
layer->EnsureCompositedLayerMapping();
composited_layer_mapping_changed = true;
RestartAnimationOnCompositor(layer->GetLayoutObject());
// At this time, the ScrollingCoordinator only supports the top-level
// frame.
if (layer->IsRootLayer() && layout_view_.GetFrame()->IsLocalRoot()) {
if (ScrollingCoordinator* scrolling_coordinator =
GetScrollingCoordinator()) {
scrolling_coordinator->FrameViewRootLayerDidChange(
layout_view_.GetFrameView());
}
}
break;
case kRemoveOwnCompositedLayerMapping:
// PutInSquashingLayer means you might have to remove the composited layer
// mapping first.
case kPutInSquashingLayer:
if (layer->HasCompositedLayerMapping()) {
layer->ClearCompositedLayerMapping();
composited_layer_mapping_changed = true;
}
break;
case kRemoveFromSquashingLayer:
case kNoCompositingStateChange:
// Do nothing.
break;
}
if (!composited_layer_mapping_changed)
return false;
if (layer->GetLayoutObject().IsLayoutEmbeddedContent()) {
PaintLayerCompositor* inner_compositor = FrameContentsCompositor(
ToLayoutEmbeddedContent(layer->GetLayoutObject()));
if (inner_compositor && inner_compositor->StaleInCompositingMode())
inner_compositor->EnsureRootLayer();
}
layer->ClearClipRects(kPaintingClipRects);
// If a fixed position layer gained/lost a compositedLayerMapping or the
// reason not compositing it changed, the scrolling coordinator needs to
// recalculate whether it can do fast scrolling.
if (ScrollingCoordinator* scrolling_coordinator = GetScrollingCoordinator()) {
scrolling_coordinator->FrameViewFixedObjectsDidChange(
layout_view_.GetFrameView());
}
// Compositing state affects whether to create paint offset translation of
// this layer, and amount of paint offset translation of descendants.
layer->GetLayoutObject().SetNeedsPaintPropertyUpdate();
return true;
}
void PaintLayerCompositor::PaintInvalidationOnCompositingChange(
PaintLayer* layer) {
// If the layoutObject is not attached yet, no need to issue paint
// invalidations.
if (&layer->GetLayoutObject() != &layout_view_ &&
!layer->GetLayoutObject().Parent())
return;
// For querying Layer::compositingState()
// Eager invalidation here is correct, since we are invalidating with respect
// to the previous frame's compositing state when changing the compositing
// backing of the layer.
DisableCompositingQueryAsserts disabler;
ObjectPaintInvalidator(layer->GetLayoutObject())
.InvalidatePaintIncludingNonCompositingDescendants();
}
PaintLayerCompositor* PaintLayerCompositor::FrameContentsCompositor(
LayoutEmbeddedContent& layout_object) {
if (!layout_object.GetNode()->IsFrameOwnerElement())
return nullptr;
HTMLFrameOwnerElement* element =
ToHTMLFrameOwnerElement(layout_object.GetNode());
if (Document* content_document = element->contentDocument()) {
if (auto* view = content_document->GetLayoutView())
return view->Compositor();
}
return nullptr;
}
bool PaintLayerCompositor::AttachFrameContentLayersToIframeLayer(
LayoutEmbeddedContent& layout_object) {
PaintLayerCompositor* inner_compositor =
FrameContentsCompositor(layout_object);
if (!inner_compositor || !inner_compositor->StaleInCompositingMode() ||
inner_compositor->root_layer_attachment_ !=
kRootLayerAttachedViaEnclosingFrame)
return false;
PaintLayer* layer = layout_object.Layer();
if (!layer->HasCompositedLayerMapping())
return false;
DisableCompositingQueryAsserts disabler;
inner_compositor->RootLayer()->EnsureCompositedLayerMapping();
layer->GetCompositedLayerMapping()->SetSublayers(
GraphicsLayerVector(1, inner_compositor->RootGraphicsLayer()));
return true;
}
static void FullyInvalidatePaintRecursive(PaintLayer* layer) {
if (layer->GetCompositingState() == kPaintsIntoOwnBacking) {
layer->GetCompositedLayerMapping()->SetContentsNeedDisplay();
layer->GetCompositedLayerMapping()->SetSquashingContentsNeedDisplay();
}
for (PaintLayer* child = layer->FirstChild(); child;
child = child->NextSibling())
FullyInvalidatePaintRecursive(child);
}
void PaintLayerCompositor::FullyInvalidatePaint() {
// We're walking all compositing layers and invalidating them, so there's
// no need to have up-to-date compositing state.
DisableCompositingQueryAsserts disabler;
FullyInvalidatePaintRecursive(RootLayer());
}
PaintLayer* PaintLayerCompositor::RootLayer() const {
return layout_view_.Layer();
}
GraphicsLayer* PaintLayerCompositor::RootGraphicsLayer() const {
if (CompositedLayerMapping* clm = RootLayer()->GetCompositedLayerMapping())
return clm->ChildForSuperlayers();
return nullptr;
}
GraphicsLayer* PaintLayerCompositor::PaintRootGraphicsLayer() const {
if (RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) {
if (layout_view_.GetDocument().GetPage()->GetChromeClient().IsPopup())
return RootGraphicsLayer();
// Start painting at the inner viewport container layer which is an ancestor
// of both the main contents layers and the scrollbar layers.
if (IsMainFrame() && GetVisualViewport().ContainerLayer())
return GetVisualViewport().ContainerLayer();
return RootGraphicsLayer();
}
if (ParentForContentLayers() && ParentForContentLayers()->Children().size()) {
DCHECK_EQ(ParentForContentLayers()->Children().size(), 1U);
return ParentForContentLayers()->Children()[0];
}
return RootGraphicsLayer();
}
GraphicsLayer* PaintLayerCompositor::ScrollLayer() const {
if (ScrollableArea* scrollable_area =
layout_view_.GetFrameView()->GetScrollableArea())
return scrollable_area->LayerForScrolling();
return nullptr;
}
void PaintLayerCompositor::SetIsInWindow(bool is_in_window) {
if (!StaleInCompositingMode())
return;
if (is_in_window) {
if (root_layer_attachment_ != kRootLayerUnattached)
return;
AttachCompositorTimeline();
AttachRootLayer();
} else {
if (root_layer_attachment_ == kRootLayerUnattached)
return;
DetachRootLayer();
DetachCompositorTimeline();
}
}
void PaintLayerCompositor::UpdatePotentialCompositingReasonsFromStyle(
PaintLayer& layer) {
layer.SetPotentialCompositingReasonsFromStyle(
compositing_reason_finder_.PotentialCompositingReasonsFromStyle(
layer.GetLayoutObject()));
}
bool PaintLayerCompositor::CanBeComposited(const PaintLayer* layer) const {
LocalFrameView* frame_view = layer->GetLayoutObject().GetFrameView();
// Elements within an invisible frame must not be composited because they are
// not drawn.
if (frame_view && !frame_view->IsVisible())
return false;
const bool has_compositor_animation =
compositing_reason_finder_.CompositingReasonsForAnimation(
*layer->GetLayoutObject().Style()) != CompositingReason::kNone;
return has_accelerated_compositing_ &&
(has_compositor_animation || !layer->SubtreeIsInvisible()) &&
layer->IsSelfPaintingLayer() &&
!layer->GetLayoutObject().IsLayoutFlowThread() &&
// Don't composite <foreignObject> for the moment, to reduce
// instances of the "fundamental compositing bug" breaking content.
!layer->GetLayoutObject().IsSVGForeignObject();
}
// Return true if the given layer is a stacking context and has compositing
// child layers that it needs to clip, or is an embedded object with a border
// radius. In these cases we insert a clipping GraphicsLayer into the hierarchy
// between this layer and its children in the z-order hierarchy.
bool PaintLayerCompositor::ClipsCompositingDescendants(
const PaintLayer* layer) const {
if (!layer->HasCompositingDescendant())
return false;
if (!layer->GetLayoutObject().IsBox())
return false;
const LayoutBox& box = ToLayoutBox(layer->GetLayoutObject());
return box.ShouldClipOverflow() || box.HasClip() ||
(box.IsLayoutEmbeddedContent() && box.StyleRef().HasBorderRadius());
}
// If an element has composited negative z-index children, those children paint
// in front of the layer background, so we need an extra 'contents' layer for
// the foreground of the layer object.
bool PaintLayerCompositor::NeedsContentsCompositingLayer(
const PaintLayer* layer) const {
if (!layer->HasCompositingDescendant())
return false;
return layer->StackingNode() &&
layer->StackingNode()->HasNegativeZOrderList();
}
static void UpdateTrackingRasterInvalidationsRecursive(
GraphicsLayer* graphics_layer) {
if (!graphics_layer)
return;
graphics_layer->UpdateTrackingRasterInvalidations();
for (wtf_size_t i = 0; i < graphics_layer->Children().size(); ++i)
UpdateTrackingRasterInvalidationsRecursive(graphics_layer->Children()[i]);
if (GraphicsLayer* mask_layer = graphics_layer->MaskLayer())
UpdateTrackingRasterInvalidationsRecursive(mask_layer);
if (GraphicsLayer* clipping_mask_layer =
graphics_layer->ContentsClippingMaskLayer())
UpdateTrackingRasterInvalidationsRecursive(clipping_mask_layer);
}
void PaintLayerCompositor::UpdateTrackingRasterInvalidations() {
#if DCHECK_IS_ON()
DCHECK(Lifecycle().GetState() == DocumentLifecycle::kPaintClean ||
layout_view_.GetFrameView()->ShouldThrottleRendering());
#endif
if (GraphicsLayer* root_layer = RootGraphicsLayer())
UpdateTrackingRasterInvalidationsRecursive(root_layer);
}
void PaintLayerCompositor::EnsureRootLayer() {
if (root_layer_attachment_ != kRootLayerUnattached)
return;
if (IsMainFrame())
GetVisualViewport().CreateLayerTree();
AttachCompositorTimeline();
AttachRootLayer();
}
void PaintLayerCompositor::DestroyRootLayer() {
DetachRootLayer();
}
void PaintLayerCompositor::AttachRootLayer() {
// With CompositeAfterPaint, PaintArtifactCompositor is responsible for the
// root layer.
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
return;
if (layout_view_.GetFrame()->IsLocalRoot()) {
root_layer_attachment_ = kRootLayerPendingAttachViaChromeClient;
} else {
HTMLFrameOwnerElement* owner_element =
layout_view_.GetDocument().LocalOwner();
DCHECK(owner_element);
// The layer will get hooked up via
// CompositedLayerMapping::updateGraphicsLayerConfiguration() for the
// frame's layoutObject in the parent document.
owner_element->SetNeedsCompositingUpdate();
if (owner_element->GetLayoutObject()) {
ToLayoutBoxModelObject(owner_element->GetLayoutObject())
->Layer()
->SetNeedsCompositingInputsUpdate();
}
root_layer_attachment_ = kRootLayerAttachedViaEnclosingFrame;
}
}
void PaintLayerCompositor::DetachRootLayer() {
if (root_layer_attachment_ == kRootLayerUnattached)
return;
switch (root_layer_attachment_) {
case kRootLayerAttachedViaEnclosingFrame: {
// The layer will get unhooked up via
// CompositedLayerMapping::updateGraphicsLayerConfiguration() for the
// frame's layoutObject in the parent document.
if (HTMLFrameOwnerElement* owner_element =
layout_view_.GetDocument().LocalOwner())
owner_element->SetNeedsCompositingUpdate();
break;
}
case kRootLayerAttachedViaChromeClient: {
LocalFrame& frame = layout_view_.GetFrameView()->GetFrame();
Page* page = frame.GetPage();
if (!page)
return;
page->GetChromeClient().AttachRootGraphicsLayer(nullptr, &frame);
break;
}
case kRootLayerPendingAttachViaChromeClient:
case kRootLayerUnattached:
break;
}
root_layer_attachment_ = kRootLayerUnattached;
}
void PaintLayerCompositor::AttachCompositorTimeline() {
LocalFrame& frame = layout_view_.GetFrameView()->GetFrame();
Page* page = frame.GetPage();
if (!page || !frame.GetDocument())
return;
CompositorAnimationTimeline* compositor_timeline =
frame.GetDocument()->Timeline().CompositorTimeline();
if (compositor_timeline) {
page->GetChromeClient().AttachCompositorAnimationTimeline(
compositor_timeline, &frame);
}
}
void PaintLayerCompositor::DetachCompositorTimeline() {
LocalFrame& frame = layout_view_.GetFrameView()->GetFrame();
Page* page = frame.GetPage();
if (!page || !frame.GetDocument())
return;
CompositorAnimationTimeline* compositor_timeline =
frame.GetDocument()->Timeline().CompositorTimeline();
if (compositor_timeline) {
page->GetChromeClient().DetachCompositorAnimationTimeline(
compositor_timeline, &frame);
}
}
ScrollingCoordinator* PaintLayerCompositor::GetScrollingCoordinator() const {
if (Page* page = GetPage())
return page->GetScrollingCoordinator();
return nullptr;
}
Page* PaintLayerCompositor::GetPage() const {
return layout_view_.GetFrameView()->GetFrame().GetPage();
}
DocumentLifecycle& PaintLayerCompositor::Lifecycle() const {
return layout_view_.GetDocument().Lifecycle();
}
bool PaintLayerCompositor::IsMainFrame() const {
return layout_view_.GetFrame()->IsMainFrame();
}
VisualViewport& PaintLayerCompositor::GetVisualViewport() const {
return layout_view_.GetFrameView()->GetPage()->GetVisualViewport();
}
bool PaintLayerCompositor::IsRootScrollerAncestor() const {
const TopDocumentRootScrollerController& global_rsc =
layout_view_.GetDocument().GetPage()->GlobalRootScrollerController();
PaintLayer* root_scroller_layer = global_rsc.RootScrollerPaintLayer();
if (root_scroller_layer) {
Frame* frame = root_scroller_layer->GetLayoutObject().GetFrame();
while (frame) {
if (frame->IsLocalFrame()) {
PaintLayerCompositor* plc =
ToLocalFrame(frame)->View()->GetLayoutView()->Compositor();
if (plc == this)
return true;
}
frame = frame->Tree().Parent();
}
}
return false;
}
} // namespace blink