blob: 690e5ac6734038642fbd035132b96015463050a8 [file] [log] [blame]
/*
* Copyright (C) 2009 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 "platform/graphics/GraphicsLayer.h"
#include <algorithm>
#include <cmath>
#include <memory>
#include <utility>
#include "SkImageFilter.h"
#include "SkMatrix44.h"
#include "base/trace_event/trace_event_argument.h"
#include "cc/layers/layer.h"
#include "platform/DragImage.h"
#include "platform/geometry/FloatRect.h"
#include "platform/geometry/LayoutRect.h"
#include "platform/geometry/Region.h"
#include "platform/graphics/BitmapImage.h"
#include "platform/graphics/CompositorFilterOperations.h"
#include "platform/graphics/FirstPaintInvalidationTracking.h"
#include "platform/graphics/GraphicsContext.h"
#include "platform/graphics/Image.h"
#include "platform/graphics/LinkHighlight.h"
#include "platform/graphics/paint/DrawingRecorder.h"
#include "platform/graphics/paint/PaintCanvas.h"
#include "platform/graphics/paint/PaintController.h"
#include "platform/graphics/paint/RasterInvalidationTracking.h"
#include "platform/instrumentation/tracing/TraceEvent.h"
#include "platform/json/JSONValues.h"
#include "platform/scroll/ScrollableArea.h"
#include "platform/text/TextStream.h"
#include "platform/wtf/CurrentTime.h"
#include "platform/wtf/HashMap.h"
#include "platform/wtf/HashSet.h"
#include "platform/wtf/MathExtras.h"
#include "platform/wtf/PtrUtil.h"
#include "platform/wtf/text/StringUTF8Adaptor.h"
#include "platform/wtf/text/WTFString.h"
#include "public/platform/Platform.h"
#include "public/platform/WebCompositorSupport.h"
#include "public/platform/WebFloatPoint.h"
#include "public/platform/WebFloatRect.h"
#include "public/platform/WebLayer.h"
#include "public/platform/WebPoint.h"
#include "public/platform/WebSize.h"
#ifndef NDEBUG
#include <stdio.h>
#endif
namespace blink {
template class RasterInvalidationTrackingMap<const GraphicsLayer>;
static RasterInvalidationTrackingMap<const GraphicsLayer>&
GetRasterInvalidationTrackingMap() {
DEFINE_STATIC_LOCAL(RasterInvalidationTrackingMap<const GraphicsLayer>, map,
());
return map;
}
std::unique_ptr<GraphicsLayer> GraphicsLayer::Create(
GraphicsLayerClient* client) {
return WTF::WrapUnique(new GraphicsLayer(client));
}
GraphicsLayer::GraphicsLayer(GraphicsLayerClient* client)
: client_(client),
background_color_(Color::kTransparent),
opacity_(1),
blend_mode_(kWebBlendModeNormal),
has_transform_origin_(false),
contents_opaque_(false),
should_flatten_transform_(true),
backface_visibility_(true),
draws_content_(false),
contents_visible_(true),
is_root_for_isolated_group_(false),
has_scroll_parent_(false),
has_clip_parent_(false),
painted_(false),
is_tracking_raster_invalidations_(
client && client->IsTrackingRasterInvalidations()),
painting_phase_(kGraphicsLayerPaintAllWithOverflowClip),
parent_(0),
mask_layer_(0),
contents_clipping_mask_layer_(0),
paint_count_(0),
contents_layer_(0),
contents_layer_id_(0),
scrollable_area_(nullptr),
rendering_context3d_(0) {
#if DCHECK_IS_ON()
if (client_)
client_->VerifyNotPainting();
#endif
content_layer_delegate_ = WTF::MakeUnique<ContentLayerDelegate>(this);
layer_ = Platform::Current()->CompositorSupport()->CreateContentLayer(
content_layer_delegate_.get());
layer_->Layer()->SetDrawsContent(draws_content_ && contents_visible_);
layer_->Layer()->SetLayerClient(this);
}
GraphicsLayer::~GraphicsLayer() {
for (size_t i = 0; i < link_highlights_.size(); ++i)
link_highlights_[i]->ClearCurrentGraphicsLayer();
link_highlights_.clear();
#if DCHECK_IS_ON()
if (client_)
client_->VerifyNotPainting();
#endif
RemoveAllChildren();
RemoveFromParent();
GetRasterInvalidationTrackingMap().Remove(this);
DCHECK(!parent_);
}
LayoutRect GraphicsLayer::VisualRect() const {
LayoutRect bounds = LayoutRect(FloatPoint(), Size());
bounds.Move(OffsetFromLayoutObjectWithSubpixelAccumulation());
return bounds;
}
void GraphicsLayer::SetHasWillChangeTransformHint(
bool has_will_change_transform) {
layer_->Layer()->SetHasWillChangeTransformHint(has_will_change_transform);
}
void GraphicsLayer::SetParent(GraphicsLayer* layer) {
#if DCHECK_IS_ON()
DCHECK(!layer || !layer->HasAncestor(this));
#endif
parent_ = layer;
}
#if DCHECK_IS_ON()
bool GraphicsLayer::HasAncestor(GraphicsLayer* ancestor) const {
for (GraphicsLayer* curr = Parent(); curr; curr = curr->Parent()) {
if (curr == ancestor)
return true;
}
return false;
}
#endif
bool GraphicsLayer::SetChildren(const GraphicsLayerVector& new_children) {
// If the contents of the arrays are the same, nothing to do.
if (new_children == children_)
return false;
RemoveAllChildren();
size_t list_size = new_children.size();
for (size_t i = 0; i < list_size; ++i)
AddChildInternal(new_children[i]);
UpdateChildList();
return true;
}
void GraphicsLayer::AddChildInternal(GraphicsLayer* child_layer) {
DCHECK_NE(child_layer, this);
if (child_layer->Parent())
child_layer->RemoveFromParent();
child_layer->SetParent(this);
children_.push_back(child_layer);
// Don't call updateChildList here, this function is used in cases where it
// should not be called until all children are processed.
}
void GraphicsLayer::AddChild(GraphicsLayer* child_layer) {
AddChildInternal(child_layer);
UpdateChildList();
}
void GraphicsLayer::AddChildBelow(GraphicsLayer* child_layer,
GraphicsLayer* sibling) {
DCHECK_NE(child_layer, this);
child_layer->RemoveFromParent();
bool found = false;
for (unsigned i = 0; i < children_.size(); i++) {
if (sibling == children_[i]) {
children_.insert(i, child_layer);
found = true;
break;
}
}
child_layer->SetParent(this);
if (!found)
children_.push_back(child_layer);
UpdateChildList();
}
void GraphicsLayer::RemoveAllChildren() {
while (!children_.IsEmpty()) {
GraphicsLayer* cur_layer = children_.back();
DCHECK(cur_layer->Parent());
cur_layer->RemoveFromParent();
}
}
void GraphicsLayer::RemoveFromParent() {
if (parent_) {
// We use reverseFind so that removeAllChildren() isn't n^2.
parent_->children_.erase(parent_->children_.ReverseFind(this));
SetParent(0);
}
PlatformLayer()->RemoveFromParent();
}
void GraphicsLayer::SetOffsetFromLayoutObject(
const IntSize& offset,
ShouldSetNeedsDisplay should_set_needs_display) {
SetOffsetDoubleFromLayoutObject(offset);
}
void GraphicsLayer::SetOffsetDoubleFromLayoutObject(
const DoubleSize& offset,
ShouldSetNeedsDisplay should_set_needs_display) {
if (offset == offset_from_layout_object_)
return;
offset_from_layout_object_ = offset;
PlatformLayer()->SetFiltersOrigin(FloatPoint() - ToFloatSize(offset));
// If the compositing layer offset changes, we need to repaint.
if (should_set_needs_display == kSetNeedsDisplay)
SetNeedsDisplay();
}
LayoutSize GraphicsLayer::OffsetFromLayoutObjectWithSubpixelAccumulation()
const {
return LayoutSize(OffsetFromLayoutObject()) +
Client()->SubpixelAccumulation();
}
IntRect GraphicsLayer::InterestRect() {
return previous_interest_rect_;
}
void GraphicsLayer::Paint(const IntRect* interest_rect,
GraphicsContext::DisabledMode disabled_mode) {
if (PaintWithoutCommit(interest_rect, disabled_mode)) {
GetPaintController().CommitNewDisplayItems(
OffsetFromLayoutObjectWithSubpixelAccumulation());
if (RuntimeEnabledFeatures::paintUnderInvalidationCheckingEnabled()) {
sk_sp<PaintRecord> record = CaptureRecord();
CheckPaintUnderInvalidations(record);
RasterInvalidationTracking& tracking =
GetRasterInvalidationTrackingMap().Add(this);
tracking.last_painted_record = std::move(record);
tracking.last_interest_rect = previous_interest_rect_;
tracking.invalidation_region_since_last_paint = Region();
}
}
}
bool GraphicsLayer::PaintWithoutCommit(
const IntRect* interest_rect,
GraphicsContext::DisabledMode disabled_mode) {
DCHECK(DrawsContent());
if (!client_)
return false;
if (FirstPaintInvalidationTracking::IsEnabled())
debug_info_.ClearAnnotatedInvalidateRects();
IncrementPaintCount();
IntRect new_interest_rect;
if (!interest_rect) {
new_interest_rect =
client_->ComputeInterestRect(this, previous_interest_rect_);
interest_rect = &new_interest_rect;
}
if (!GetPaintController().SubsequenceCachingIsDisabled() &&
!client_->NeedsRepaint(*this) && !GetPaintController().CacheIsEmpty() &&
previous_interest_rect_ == *interest_rect) {
return false;
}
GraphicsContext context(GetPaintController(), disabled_mode, nullptr);
previous_interest_rect_ = *interest_rect;
client_->PaintContents(this, context, painting_phase_, *interest_rect);
return true;
}
void GraphicsLayer::UpdateChildList() {
WebLayer* child_host = layer_->Layer();
child_host->RemoveAllChildren();
ClearContentsLayerIfUnregistered();
if (contents_layer_) {
// FIXME: Add the contents layer in the correct order with negative z-order
// children. This does not currently cause visible rendering issues because
// contents layers are only used for replaced elements that don't have
// children.
child_host->AddChild(contents_layer_);
}
for (size_t i = 0; i < children_.size(); ++i)
child_host->AddChild(children_[i]->PlatformLayer());
for (size_t i = 0; i < link_highlights_.size(); ++i)
child_host->AddChild(link_highlights_[i]->Layer());
}
void GraphicsLayer::UpdateLayerIsDrawable() {
// For the rest of the accelerated compositor code, there is no reason to make
// a distinction between drawsContent and contentsVisible. So, for
// m_layer->layer(), these two flags are combined here. |m_contentsLayer|
// shouldn't receive the drawsContent flag, so it is only given
// contentsVisible.
layer_->Layer()->SetDrawsContent(draws_content_ && contents_visible_);
if (WebLayer* contents_layer = ContentsLayerIfRegistered())
contents_layer->SetDrawsContent(contents_visible_);
if (draws_content_) {
layer_->Layer()->Invalidate();
for (size_t i = 0; i < link_highlights_.size(); ++i)
link_highlights_[i]->Invalidate();
}
}
void GraphicsLayer::UpdateContentsRect() {
WebLayer* contents_layer = ContentsLayerIfRegistered();
if (!contents_layer)
return;
contents_layer->SetPosition(
FloatPoint(contents_rect_.X(), contents_rect_.Y()));
contents_layer->SetBounds(
IntSize(contents_rect_.Width(), contents_rect_.Height()));
if (contents_clipping_mask_layer_) {
if (contents_clipping_mask_layer_->Size() != contents_rect_.Size()) {
contents_clipping_mask_layer_->SetSize(FloatSize(contents_rect_.Size()));
contents_clipping_mask_layer_->SetNeedsDisplay();
}
contents_clipping_mask_layer_->SetPosition(FloatPoint());
contents_clipping_mask_layer_->SetOffsetFromLayoutObject(
OffsetFromLayoutObject() +
IntSize(contents_rect_.Location().X(), contents_rect_.Location().Y()));
}
}
static HashSet<int>* g_registered_layer_set;
void GraphicsLayer::RegisterContentsLayer(WebLayer* layer) {
if (!g_registered_layer_set)
g_registered_layer_set = new HashSet<int>;
if (g_registered_layer_set->Contains(layer->Id()))
CRASH();
g_registered_layer_set->insert(layer->Id());
}
void GraphicsLayer::UnregisterContentsLayer(WebLayer* layer) {
DCHECK(g_registered_layer_set);
if (!g_registered_layer_set->Contains(layer->Id()))
CRASH();
g_registered_layer_set->erase(layer->Id());
}
void GraphicsLayer::SetContentsTo(WebLayer* layer) {
bool children_changed = false;
if (layer) {
DCHECK(g_registered_layer_set);
if (!g_registered_layer_set->Contains(layer->Id()))
CRASH();
if (contents_layer_id_ != layer->Id()) {
SetupContentsLayer(layer);
children_changed = true;
}
UpdateContentsRect();
} else {
if (contents_layer_) {
children_changed = true;
// The old contents layer will be removed via updateChildList.
contents_layer_ = 0;
contents_layer_id_ = 0;
}
}
if (children_changed)
UpdateChildList();
}
void GraphicsLayer::SetupContentsLayer(WebLayer* contents_layer) {
DCHECK(contents_layer);
contents_layer_ = contents_layer;
contents_layer_id_ = contents_layer_->Id();
contents_layer_->SetLayerClient(this);
contents_layer_->SetTransformOrigin(FloatPoint3D());
contents_layer_->SetUseParentBackfaceVisibility(true);
// It is necessary to call setDrawsContent as soon as we receive the new
// contentsLayer, for the correctness of early exit conditions in
// setDrawsContent() and setContentsVisible().
contents_layer_->SetDrawsContent(contents_visible_);
// Insert the content layer first. Video elements require this, because they
// have shadow content that must display in front of the video.
layer_->Layer()->InsertChild(contents_layer_, 0);
WebLayer* border_web_layer =
contents_clipping_mask_layer_
? contents_clipping_mask_layer_->PlatformLayer()
: 0;
contents_layer_->SetMaskLayer(border_web_layer);
contents_layer_->SetRenderingContext(rendering_context3d_);
}
void GraphicsLayer::ClearContentsLayerIfUnregistered() {
if (!contents_layer_id_ ||
g_registered_layer_set->Contains(contents_layer_id_))
return;
contents_layer_ = 0;
contents_layer_id_ = 0;
}
GraphicsLayerDebugInfo& GraphicsLayer::DebugInfo() {
return debug_info_;
}
WebLayer* GraphicsLayer::ContentsLayerIfRegistered() {
ClearContentsLayerIfUnregistered();
return contents_layer_;
}
void GraphicsLayer::SetTracksRasterInvalidations(
bool tracks_raster_invalidations) {
ResetTrackedRasterInvalidations();
is_tracking_raster_invalidations_ = tracks_raster_invalidations;
}
void GraphicsLayer::ResetTrackedRasterInvalidations() {
RasterInvalidationTracking* tracking =
GetRasterInvalidationTrackingMap().Find(this);
if (!tracking)
return;
if (RuntimeEnabledFeatures::paintUnderInvalidationCheckingEnabled())
tracking->invalidations.clear();
else
GetRasterInvalidationTrackingMap().Remove(this);
}
bool GraphicsLayer::HasTrackedRasterInvalidations() const {
if (auto* tracking = GetRasterInvalidationTracking())
return !tracking->invalidations.IsEmpty();
return false;
}
const RasterInvalidationTracking* GraphicsLayer::GetRasterInvalidationTracking()
const {
return GetRasterInvalidationTrackingMap().Find(this);
}
void GraphicsLayer::TrackRasterInvalidation(const DisplayItemClient& client,
const IntRect& rect,
PaintInvalidationReason reason) {
if (!IsTrackingOrCheckingRasterInvalidations() || rect.IsEmpty())
return;
RasterInvalidationTracking& tracking =
GetRasterInvalidationTrackingMap().Add(this);
if (is_tracking_raster_invalidations_) {
RasterInvalidationInfo info;
info.client = &client;
info.client_debug_name = client.DebugName();
info.rect = rect;
info.reason = reason;
tracking.invalidations.push_back(info);
}
if (RuntimeEnabledFeatures::paintUnderInvalidationCheckingEnabled()) {
// TODO(crbug.com/496260): Some antialiasing effects overflow the paint
// invalidation rect.
IntRect r = rect;
r.Inflate(1);
tracking.invalidation_region_since_last_paint.Unite(r);
}
}
template <typename T>
static std::unique_ptr<JSONArray> PointAsJSONArray(const T& point) {
std::unique_ptr<JSONArray> array = JSONArray::Create();
array->PushDouble(point.X());
array->PushDouble(point.Y());
return array;
}
template <typename T>
static std::unique_ptr<JSONArray> SizeAsJSONArray(const T& size) {
std::unique_ptr<JSONArray> array = JSONArray::Create();
array->PushDouble(size.Width());
array->PushDouble(size.Height());
return array;
}
static double RoundCloseToZero(double number) {
return std::abs(number) < 1e-7 ? 0 : number;
}
static std::unique_ptr<JSONArray> TransformAsJSONArray(
const TransformationMatrix& t) {
std::unique_ptr<JSONArray> array = JSONArray::Create();
{
std::unique_ptr<JSONArray> row = JSONArray::Create();
row->PushDouble(RoundCloseToZero(t.M11()));
row->PushDouble(RoundCloseToZero(t.M12()));
row->PushDouble(RoundCloseToZero(t.M13()));
row->PushDouble(RoundCloseToZero(t.M14()));
array->PushArray(std::move(row));
}
{
std::unique_ptr<JSONArray> row = JSONArray::Create();
row->PushDouble(RoundCloseToZero(t.M21()));
row->PushDouble(RoundCloseToZero(t.M22()));
row->PushDouble(RoundCloseToZero(t.M23()));
row->PushDouble(RoundCloseToZero(t.M24()));
array->PushArray(std::move(row));
}
{
std::unique_ptr<JSONArray> row = JSONArray::Create();
row->PushDouble(RoundCloseToZero(t.M31()));
row->PushDouble(RoundCloseToZero(t.M32()));
row->PushDouble(RoundCloseToZero(t.M33()));
row->PushDouble(RoundCloseToZero(t.M34()));
array->PushArray(std::move(row));
}
{
std::unique_ptr<JSONArray> row = JSONArray::Create();
row->PushDouble(RoundCloseToZero(t.M41()));
row->PushDouble(RoundCloseToZero(t.M42()));
row->PushDouble(RoundCloseToZero(t.M43()));
row->PushDouble(RoundCloseToZero(t.M44()));
array->PushArray(std::move(row));
}
return array;
}
static String PointerAsString(const void* ptr) {
TextStream ts;
ts << ptr;
return ts.Release();
}
std::unique_ptr<JSONObject> GraphicsLayer::LayerTreeAsJSON(
LayerTreeFlags flags) const {
RenderingContextMap rendering_context_map;
if (flags & kOutputAsLayerTree)
return LayerTreeAsJSONInternal(flags, rendering_context_map);
std::unique_ptr<JSONObject> json = JSONObject::Create();
std::unique_ptr<JSONArray> layers_array = JSONArray::Create();
for (auto& child : children_)
child->LayersAsJSONArray(flags, rendering_context_map, layers_array.get());
json->SetArray("layers", std::move(layers_array));
return json;
}
std::unique_ptr<JSONObject> GraphicsLayer::LayerAsJSONInternal(
LayerTreeFlags flags,
RenderingContextMap& rendering_context_map) const {
std::unique_ptr<JSONObject> json = JSONObject::Create();
if (flags & kLayerTreeIncludesDebugInfo)
json->SetString("this", PointerAsString(this));
json->SetString("name", DebugName());
if (position_ != FloatPoint())
json->SetArray("position", PointAsJSONArray(position_));
if (flags & kLayerTreeIncludesDebugInfo &&
offset_from_layout_object_ != DoubleSize()) {
json->SetArray("offsetFromLayoutObject",
SizeAsJSONArray(offset_from_layout_object_));
}
if (has_transform_origin_ &&
transform_origin_ !=
FloatPoint3D(size_.Width() * 0.5f, size_.Height() * 0.5f, 0))
json->SetArray("transformOrigin", PointAsJSONArray(transform_origin_));
if (size_ != IntSize())
json->SetArray("bounds", SizeAsJSONArray(size_));
if (opacity_ != 1)
json->SetDouble("opacity", opacity_);
if (blend_mode_ != kWebBlendModeNormal) {
json->SetString("blendMode",
CompositeOperatorName(kCompositeSourceOver, blend_mode_));
}
if (is_root_for_isolated_group_)
json->SetBoolean("isolate", is_root_for_isolated_group_);
if (contents_opaque_)
json->SetBoolean("contentsOpaque", contents_opaque_);
if (!should_flatten_transform_)
json->SetBoolean("shouldFlattenTransform", should_flatten_transform_);
if (rendering_context3d_) {
RenderingContextMap::const_iterator it =
rendering_context_map.find(rendering_context3d_);
int context_id = rendering_context_map.size() + 1;
if (it == rendering_context_map.end())
rendering_context_map.Set(rendering_context3d_, context_id);
else
context_id = it->value;
json->SetInteger("3dRenderingContext", context_id);
}
if (draws_content_)
json->SetBoolean("drawsContent", draws_content_);
if (!contents_visible_)
json->SetBoolean("contentsVisible", contents_visible_);
if (!backface_visibility_) {
json->SetString("backfaceVisibility",
backface_visibility_ ? "visible" : "hidden");
}
if (flags & kLayerTreeIncludesDebugInfo)
json->SetString("client", PointerAsString(client_));
if (background_color_.Alpha()) {
json->SetString("backgroundColor",
background_color_.NameForLayoutTreeAsText());
}
if (!transform_.IsIdentity())
json->SetArray("transform", TransformAsJSONArray(transform_));
if (flags & kLayerTreeIncludesPaintInvalidations)
GetRasterInvalidationTrackingMap().AsJSON(this, json.get());
if ((flags & kLayerTreeIncludesPaintingPhases) && painting_phase_) {
std::unique_ptr<JSONArray> painting_phases_json = JSONArray::Create();
if (painting_phase_ & kGraphicsLayerPaintBackground)
painting_phases_json->PushString("GraphicsLayerPaintBackground");
if (painting_phase_ & kGraphicsLayerPaintForeground)
painting_phases_json->PushString("GraphicsLayerPaintForeground");
if (painting_phase_ & kGraphicsLayerPaintMask)
painting_phases_json->PushString("GraphicsLayerPaintMask");
if (painting_phase_ & kGraphicsLayerPaintChildClippingMask)
painting_phases_json->PushString("GraphicsLayerPaintChildClippingMask");
if (painting_phase_ & kGraphicsLayerPaintAncestorClippingMask)
painting_phases_json->PushString(
"GraphicsLayerPaintAncestorClippingMask");
if (painting_phase_ & kGraphicsLayerPaintOverflowContents)
painting_phases_json->PushString("GraphicsLayerPaintOverflowContents");
if (painting_phase_ & kGraphicsLayerPaintCompositedScroll)
painting_phases_json->PushString("GraphicsLayerPaintCompositedScroll");
if (painting_phase_ & kGraphicsLayerPaintDecoration)
painting_phases_json->PushString("GraphicsLayerPaintDecoration");
json->SetArray("paintingPhases", std::move(painting_phases_json));
}
if (flags & kLayerTreeIncludesClipAndScrollParents) {
if (has_scroll_parent_)
json->SetBoolean("hasScrollParent", true);
if (has_clip_parent_)
json->SetBoolean("hasClipParent", true);
}
if (flags &
(kLayerTreeIncludesDebugInfo | kLayerTreeIncludesCompositingReasons)) {
bool debug = flags & kLayerTreeIncludesDebugInfo;
std::unique_ptr<JSONArray> compositing_reasons_json = JSONArray::Create();
for (size_t i = 0; i < kNumberOfCompositingReasons; ++i) {
if (debug_info_.GetCompositingReasons() &
kCompositingReasonStringMap[i].reason) {
compositing_reasons_json->PushString(
debug ? kCompositingReasonStringMap[i].description
: kCompositingReasonStringMap[i].short_name);
}
}
json->SetArray("compositingReasons", std::move(compositing_reasons_json));
std::unique_ptr<JSONArray> squashing_disallowed_reasons_json =
JSONArray::Create();
for (size_t i = 0; i < kNumberOfSquashingDisallowedReasons; ++i) {
if (debug_info_.GetSquashingDisallowedReasons() &
kSquashingDisallowedReasonStringMap[i].reason) {
squashing_disallowed_reasons_json->PushString(
debug ? kSquashingDisallowedReasonStringMap[i].description
: kSquashingDisallowedReasonStringMap[i].short_name);
}
}
json->SetArray("squashingDisallowedReasons",
std::move(squashing_disallowed_reasons_json));
}
if (mask_layer_) {
std::unique_ptr<JSONArray> mask_layer_json = JSONArray::Create();
mask_layer_json->PushObject(
mask_layer_->LayerAsJSONInternal(flags, rendering_context_map));
json->SetArray("maskLayer", std::move(mask_layer_json));
}
if (contents_clipping_mask_layer_) {
std::unique_ptr<JSONArray> contents_clipping_mask_layer_json =
JSONArray::Create();
contents_clipping_mask_layer_json->PushObject(
contents_clipping_mask_layer_->LayerAsJSONInternal(
flags, rendering_context_map));
json->SetArray("contentsClippingMaskLayer",
std::move(contents_clipping_mask_layer_json));
}
return json;
}
std::unique_ptr<JSONObject> GraphicsLayer::LayerTreeAsJSONInternal(
LayerTreeFlags flags,
RenderingContextMap& rendering_context_map) const {
std::unique_ptr<JSONObject> json =
LayerAsJSONInternal(flags, rendering_context_map);
if (children_.size()) {
std::unique_ptr<JSONArray> children_json = JSONArray::Create();
for (size_t i = 0; i < children_.size(); i++) {
children_json->PushObject(
children_[i]->LayerTreeAsJSONInternal(flags, rendering_context_map));
}
json->SetArray("children", std::move(children_json));
}
return json;
}
void GraphicsLayer::LayersAsJSONArray(
LayerTreeFlags flags,
RenderingContextMap& rendering_context_map,
JSONArray* json_array) const {
json_array->PushObject(LayerAsJSONInternal(flags, rendering_context_map));
if (children_.size()) {
for (auto& child : children_)
child->LayersAsJSONArray(flags, rendering_context_map, json_array);
}
}
String GraphicsLayer::LayerTreeAsText(LayerTreeFlags flags) const {
return LayerTreeAsJSON(flags)->ToPrettyJSONString();
}
static const cc::Layer* CcLayerForWebLayer(const WebLayer* web_layer) {
return web_layer ? web_layer->CcLayer() : nullptr;
}
String GraphicsLayer::DebugName(cc::Layer* layer) const {
String name;
if (!client_)
return name;
String highlight_debug_name;
for (size_t i = 0; i < link_highlights_.size(); ++i) {
if (layer == CcLayerForWebLayer(link_highlights_[i]->Layer())) {
highlight_debug_name = "LinkHighlight[" + String::Number(i) + "] for " +
client_->DebugName(this);
break;
}
}
if (layer->id() == contents_layer_id_) {
name = "ContentsLayer for " + client_->DebugName(this);
} else if (!highlight_debug_name.IsEmpty()) {
name = highlight_debug_name;
} else if (layer == CcLayerForWebLayer(layer_->Layer())) {
name = client_->DebugName(this);
} else {
NOTREACHED();
}
return name;
}
void GraphicsLayer::SetCompositingReasons(CompositingReasons reasons) {
debug_info_.SetCompositingReasons(reasons);
}
void GraphicsLayer::SetSquashingDisallowedReasons(
SquashingDisallowedReasons reasons) {
debug_info_.SetSquashingDisallowedReasons(reasons);
}
void GraphicsLayer::SetOwnerNodeId(int node_id) {
debug_info_.SetOwnerNodeId(node_id);
}
void GraphicsLayer::SetPosition(const FloatPoint& point) {
position_ = point;
PlatformLayer()->SetPosition(position_);
}
void GraphicsLayer::SetSize(const FloatSize& size) {
// We are receiving negative sizes here that cause assertions to fail in the
// compositor. Clamp them to 0 to avoid those assertions.
// FIXME: This should be an DCHECK instead, as negative sizes should not exist
// in WebCore.
FloatSize clamped_size = size;
if (clamped_size.Width() < 0 || clamped_size.Height() < 0)
clamped_size = FloatSize();
if (clamped_size == size_)
return;
size_ = clamped_size;
layer_->Layer()->SetBounds(FlooredIntSize(size_));
// Note that we don't resize m_contentsLayer. It's up the caller to do that.
}
void GraphicsLayer::SetTransform(const TransformationMatrix& transform) {
transform_ = transform;
PlatformLayer()->SetTransform(TransformationMatrix::ToSkMatrix44(transform_));
}
void GraphicsLayer::SetTransformOrigin(const FloatPoint3D& transform_origin) {
has_transform_origin_ = true;
transform_origin_ = transform_origin;
PlatformLayer()->SetTransformOrigin(transform_origin);
}
void GraphicsLayer::SetShouldFlattenTransform(bool should_flatten) {
if (should_flatten == should_flatten_transform_)
return;
should_flatten_transform_ = should_flatten;
layer_->Layer()->SetShouldFlattenTransform(should_flatten);
}
void GraphicsLayer::SetRenderingContext(int context) {
if (rendering_context3d_ == context)
return;
rendering_context3d_ = context;
layer_->Layer()->SetRenderingContext(context);
if (contents_layer_)
contents_layer_->SetRenderingContext(rendering_context3d_);
}
bool GraphicsLayer::MasksToBounds() const {
return layer_->Layer()->MasksToBounds();
}
void GraphicsLayer::SetMasksToBounds(bool masks_to_bounds) {
layer_->Layer()->SetMasksToBounds(masks_to_bounds);
}
void GraphicsLayer::SetDrawsContent(bool draws_content) {
// NOTE: This early-exit is only correct because we also properly call
// WebLayer::setDrawsContent() whenever |m_contentsLayer| is set to a new
// layer in setupContentsLayer().
if (draws_content == draws_content_)
return;
draws_content_ = draws_content;
UpdateLayerIsDrawable();
if (!draws_content && paint_controller_)
paint_controller_.reset();
}
void GraphicsLayer::SetContentsVisible(bool contents_visible) {
// NOTE: This early-exit is only correct because we also properly call
// WebLayer::setDrawsContent() whenever |m_contentsLayer| is set to a new
// layer in setupContentsLayer().
if (contents_visible == contents_visible_)
return;
contents_visible_ = contents_visible;
UpdateLayerIsDrawable();
}
void GraphicsLayer::SetClipParent(WebLayer* parent) {
has_clip_parent_ = !!parent;
layer_->Layer()->SetClipParent(parent);
}
void GraphicsLayer::SetScrollParent(WebLayer* parent) {
has_scroll_parent_ = !!parent;
layer_->Layer()->SetScrollParent(parent);
}
void GraphicsLayer::SetBackgroundColor(const Color& color) {
if (color == background_color_)
return;
background_color_ = color;
layer_->Layer()->SetBackgroundColor(background_color_.Rgb());
}
void GraphicsLayer::SetContentsOpaque(bool opaque) {
contents_opaque_ = opaque;
layer_->Layer()->SetOpaque(contents_opaque_);
ClearContentsLayerIfUnregistered();
if (contents_layer_)
contents_layer_->SetOpaque(opaque);
}
void GraphicsLayer::SetMaskLayer(GraphicsLayer* mask_layer) {
if (mask_layer == mask_layer_)
return;
mask_layer_ = mask_layer;
WebLayer* mask_web_layer = mask_layer_ ? mask_layer_->PlatformLayer() : 0;
layer_->Layer()->SetMaskLayer(mask_web_layer);
}
void GraphicsLayer::SetContentsClippingMaskLayer(
GraphicsLayer* contents_clipping_mask_layer) {
if (contents_clipping_mask_layer == contents_clipping_mask_layer_)
return;
contents_clipping_mask_layer_ = contents_clipping_mask_layer;
WebLayer* contents_layer = ContentsLayerIfRegistered();
if (!contents_layer)
return;
WebLayer* contents_clipping_mask_web_layer =
contents_clipping_mask_layer_
? contents_clipping_mask_layer_->PlatformLayer()
: 0;
contents_layer->SetMaskLayer(contents_clipping_mask_web_layer);
UpdateContentsRect();
}
void GraphicsLayer::SetBackfaceVisibility(bool visible) {
backface_visibility_ = visible;
PlatformLayer()->SetDoubleSided(backface_visibility_);
}
void GraphicsLayer::SetOpacity(float opacity) {
float clamped_opacity = clampTo(opacity, 0.0f, 1.0f);
opacity_ = clamped_opacity;
PlatformLayer()->SetOpacity(opacity);
}
void GraphicsLayer::SetBlendMode(WebBlendMode blend_mode) {
if (blend_mode_ == blend_mode)
return;
blend_mode_ = blend_mode;
PlatformLayer()->SetBlendMode(blend_mode);
}
void GraphicsLayer::SetIsRootForIsolatedGroup(bool isolated) {
if (is_root_for_isolated_group_ == isolated)
return;
is_root_for_isolated_group_ = isolated;
PlatformLayer()->SetIsRootForIsolatedGroup(isolated);
}
void GraphicsLayer::SetContentsNeedsDisplay() {
if (WebLayer* contents_layer = ContentsLayerIfRegistered()) {
contents_layer->Invalidate();
TrackRasterInvalidation(*this, contents_rect_,
PaintInvalidationReason::kFull);
}
}
void GraphicsLayer::SetNeedsDisplay() {
if (!DrawsContent())
return;
// TODO(chrishtr): Stop invalidating the rects once
// FrameView::paintRecursively() does so.
layer_->Layer()->Invalidate();
for (size_t i = 0; i < link_highlights_.size(); ++i)
link_highlights_[i]->Invalidate();
GetPaintController().InvalidateAll();
TrackRasterInvalidation(*this, IntRect(IntPoint(), ExpandedIntSize(size_)),
PaintInvalidationReason::kFull);
}
DISABLE_CFI_PERF
void GraphicsLayer::SetNeedsDisplayInRect(
const IntRect& rect,
PaintInvalidationReason invalidation_reason,
const DisplayItemClient& client) {
if (!DrawsContent())
return;
layer_->Layer()->InvalidateRect(rect);
if (FirstPaintInvalidationTracking::IsEnabled())
debug_info_.AppendAnnotatedInvalidateRect(rect, invalidation_reason);
for (size_t i = 0; i < link_highlights_.size(); ++i)
link_highlights_[i]->Invalidate();
TrackRasterInvalidation(client, rect, invalidation_reason);
}
void GraphicsLayer::SetContentsRect(const IntRect& rect) {
if (rect == contents_rect_)
return;
contents_rect_ = rect;
UpdateContentsRect();
}
void GraphicsLayer::SetContentsToImage(
Image* image,
RespectImageOrientationEnum respect_image_orientation) {
PaintImage paint_image;
if (image)
paint_image = image->PaintImageForCurrentFrame();
if (paint_image && image->IsBitmapImage() &&
respect_image_orientation == kRespectImageOrientation) {
ImageOrientation image_orientation =
ToBitmapImage(image)->CurrentFrameOrientation();
paint_image =
DragImage::ResizeAndOrientImage(paint_image, image_orientation);
}
if (paint_image) {
if (!image_layer_) {
image_layer_ =
Platform::Current()->CompositorSupport()->CreateImageLayer();
RegisterContentsLayer(image_layer_->Layer());
}
image_layer_->SetImage(std::move(paint_image));
image_layer_->Layer()->SetOpaque(image->CurrentFrameKnownToBeOpaque());
UpdateContentsRect();
} else if (image_layer_) {
UnregisterContentsLayer(image_layer_->Layer());
image_layer_.reset();
}
SetContentsTo(image_layer_ ? image_layer_->Layer() : 0);
}
WebLayer* GraphicsLayer::PlatformLayer() const {
return layer_->Layer();
}
void GraphicsLayer::SetFilters(CompositorFilterOperations filters) {
PlatformLayer()->SetFilters(filters.ReleaseCcFilterOperations());
}
void GraphicsLayer::SetBackdropFilters(CompositorFilterOperations filters) {
PlatformLayer()->SetBackgroundFilters(filters.ReleaseCcFilterOperations());
}
void GraphicsLayer::SetStickyPositionConstraint(
const WebLayerStickyPositionConstraint& sticky_constraint) {
layer_->Layer()->SetStickyPositionConstraint(sticky_constraint);
}
void GraphicsLayer::SetFilterQuality(SkFilterQuality filter_quality) {
if (image_layer_)
image_layer_->SetNearestNeighbor(filter_quality == kNone_SkFilterQuality);
}
void GraphicsLayer::SetPaintingPhase(GraphicsLayerPaintingPhase phase) {
if (painting_phase_ == phase)
return;
painting_phase_ = phase;
SetNeedsDisplay();
}
void GraphicsLayer::AddLinkHighlight(LinkHighlight* link_highlight) {
DCHECK(link_highlight && !link_highlights_.Contains(link_highlight));
link_highlights_.push_back(link_highlight);
link_highlight->Layer()->SetLayerClient(this);
UpdateChildList();
}
void GraphicsLayer::RemoveLinkHighlight(LinkHighlight* link_highlight) {
link_highlights_.erase(link_highlights_.Find(link_highlight));
UpdateChildList();
}
void GraphicsLayer::SetScrollableArea(ScrollableArea* scrollable_area,
bool is_visual_viewport) {
if (scrollable_area_ == scrollable_area)
return;
scrollable_area_ = scrollable_area;
// VisualViewport scrolling may involve pinch zoom and gets routed through
// WebViewImpl explicitly rather than via ScrollableArea::didScroll since it
// needs to be set in tandem with the page scale delta.
if (is_visual_viewport)
layer_->Layer()->SetScrollClient(nullptr);
else
layer_->Layer()->SetScrollClient(scrollable_area);
}
std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
GraphicsLayer::TakeDebugInfo(cc::Layer* layer) {
std::unique_ptr<base::trace_event::TracedValue> traced_value(
debug_info_.AsTracedValue());
traced_value->SetString(
"layer_name", WTF::StringUTF8Adaptor(DebugName(layer)).AsStringPiece());
return std::move(traced_value);
}
void GraphicsLayer::didUpdateMainThreadScrollingReasons() {
debug_info_.SetMainThreadScrollingReasons(
PlatformLayer()->MainThreadScrollingReasons());
}
void GraphicsLayer::didChangeScrollbarsHidden(bool hidden) {
if (scrollable_area_)
scrollable_area_->SetScrollbarsHidden(hidden);
}
PaintController& GraphicsLayer::GetPaintController() {
CHECK(DrawsContent());
if (!paint_controller_)
paint_controller_ = PaintController::Create();
return *paint_controller_;
}
void GraphicsLayer::SetElementId(const CompositorElementId& id) {
if (WebLayer* layer = PlatformLayer())
layer->SetElementId(id);
}
CompositorElementId GraphicsLayer::GetElementId() const {
if (WebLayer* layer = PlatformLayer())
return layer->GetElementId();
return CompositorElementId();
}
void GraphicsLayer::SetCompositorMutableProperties(uint32_t properties) {
if (WebLayer* layer = PlatformLayer())
layer->SetCompositorMutableProperties(properties);
}
sk_sp<PaintRecord> GraphicsLayer::CaptureRecord() {
if (!DrawsContent())
return nullptr;
FloatRect bounds(IntRect(IntPoint(0, 0), ExpandedIntSize(Size())));
GraphicsContext graphics_context(GetPaintController(),
GraphicsContext::kNothingDisabled, nullptr);
graphics_context.BeginRecording(bounds);
GetPaintController().GetPaintArtifact().Replay(bounds, graphics_context);
return graphics_context.EndRecording();
}
static bool PixelComponentsDiffer(int c1, int c2) {
// Compare strictly for saturated values.
if (c1 == 0 || c1 == 255 || c2 == 0 || c2 == 255)
return c1 != c2;
// Tolerate invisible differences that may occur in gradients etc.
return abs(c1 - c2) > 2;
}
static bool PixelsDiffer(SkColor p1, SkColor p2) {
return PixelComponentsDiffer(SkColorGetA(p1), SkColorGetA(p2)) ||
PixelComponentsDiffer(SkColorGetR(p1), SkColorGetR(p2)) ||
PixelComponentsDiffer(SkColorGetG(p1), SkColorGetG(p2)) ||
PixelComponentsDiffer(SkColorGetB(p1), SkColorGetB(p2));
}
// This method is used to graphically verify any under invalidation when
// RuntimeEnabledFeatures::paintUnderInvalidationCheckingEnabled is being
// used. It compares the last recording made by GraphicsLayer::Paint against
// |new_record|, by rastering both into bitmaps. Any differences are colored
// as dark red.
void GraphicsLayer::CheckPaintUnderInvalidations(
sk_sp<PaintRecord> new_record) {
if (!DrawsContent())
return;
RasterInvalidationTracking* tracking =
GetRasterInvalidationTrackingMap().Find(this);
if (!tracking)
return;
if (!tracking->last_painted_record)
return;
IntRect rect = Intersection(tracking->last_interest_rect, InterestRect());
if (rect.IsEmpty())
return;
SkBitmap old_bitmap;
old_bitmap.allocPixels(
SkImageInfo::MakeN32Premul(rect.Width(), rect.Height()));
{
SkiaPaintCanvas canvas(old_bitmap);
canvas.clear(SK_ColorTRANSPARENT);
canvas.translate(-rect.X(), -rect.Y());
canvas.drawPicture(tracking->last_painted_record);
}
SkBitmap new_bitmap;
new_bitmap.allocPixels(
SkImageInfo::MakeN32Premul(rect.Width(), rect.Height()));
{
SkiaPaintCanvas canvas(new_bitmap);
canvas.clear(SK_ColorTRANSPARENT);
canvas.translate(-rect.X(), -rect.Y());
canvas.drawPicture(std::move(new_record));
}
int mismatching_pixels = 0;
static const int kMaxMismatchesToReport = 50;
for (int bitmap_y = 0; bitmap_y < rect.Height(); ++bitmap_y) {
int layer_y = bitmap_y + rect.Y();
for (int bitmap_x = 0; bitmap_x < rect.Width(); ++bitmap_x) {
int layer_x = bitmap_x + rect.X();
SkColor old_pixel = old_bitmap.getColor(bitmap_x, bitmap_y);
SkColor new_pixel = new_bitmap.getColor(bitmap_x, bitmap_y);
if (PixelsDiffer(old_pixel, new_pixel) &&
!tracking->invalidation_region_since_last_paint.Contains(
IntPoint(layer_x, layer_y))) {
if (mismatching_pixels < kMaxMismatchesToReport) {
UnderRasterInvalidation under_invalidation = {layer_x, layer_y,
old_pixel, new_pixel};
tracking->under_invalidations.push_back(under_invalidation);
LOG(ERROR) << DebugName()
<< " Uninvalidated old/new pixels mismatch at " << layer_x
<< "," << layer_y << " old:" << std::hex << old_pixel
<< " new:" << new_pixel;
} else if (mismatching_pixels == kMaxMismatchesToReport) {
LOG(ERROR) << "and more...";
}
++mismatching_pixels;
*new_bitmap.getAddr32(bitmap_x, bitmap_y) =
SkColorSetARGB(0xFF, 0xA0, 0, 0); // Dark red.
} else {
*new_bitmap.getAddr32(bitmap_x, bitmap_y) = SK_ColorTRANSPARENT;
}
}
}
// Visualize under-invalidations by overlaying the new bitmap (containing red
// pixels indicating under-invalidations, and transparent pixels otherwise)
// onto the painting.
PaintRecorder recorder;
recorder.beginRecording(rect);
recorder.getRecordingCanvas()->drawBitmap(new_bitmap, rect.X(), rect.Y());
sk_sp<PaintRecord> record = recorder.finishRecordingAsPicture();
GetPaintController().AppendDebugDrawingAfterCommit(
*this, record, rect, OffsetFromLayoutObjectWithSubpixelAccumulation());
}
} // namespace blink
#ifndef NDEBUG
void showGraphicsLayerTree(const blink::GraphicsLayer* layer) {
if (!layer) {
LOG(INFO) << "Cannot showGraphicsLayerTree for (nil).";
return;
}
String output = layer->LayerTreeAsText(blink::kLayerTreeIncludesDebugInfo);
LOG(INFO) << output.Utf8().data();
}
#endif