blob: 3808d6163bd6367632fa308d0511da4bc5ba69e1 [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 "third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_as_text.h"
#include "cc/layers/picture_layer.h"
#include "third_party/blink/renderer/core/scroll/scrollable_area.h"
#include "third_party/blink/renderer/platform/geometry/geometry_as_json.h"
#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
#include "third_party/blink/renderer/platform/graphics/logging_canvas.h"
#include "third_party/blink/renderer/platform/json/json_values.h"
#include "third_party/blink/renderer/platform/wtf/text/text_stream.h"
namespace blink {
namespace {
typedef HashMap<int, int> RenderingContextMap;
String PointerAsString(const void* ptr) {
WTF::TextStream ts;
ts << ptr;
return ts.Release();
}
FloatPoint ScrollPosition(const GraphicsLayer& layer) {
if (const auto* scrollable_area =
layer.Client().GetScrollableAreaForTesting(&layer)) {
return scrollable_area->ScrollPosition();
}
return FloatPoint();
}
std::unique_ptr<JSONObject> GraphicsLayerAsJSON(
const GraphicsLayer* layer,
LayerTreeFlags flags,
const FloatPoint& position) {
auto json = std::make_unique<JSONObject>();
if (flags & kLayerTreeIncludesDebugInfo) {
json->SetString("this", PointerAsString(layer));
json->SetInteger("ccLayerId", layer->CcLayer()->id());
if (layer->HasContentsLayer())
json->SetInteger("ccContentsLayerId", layer->ContentsLayer()->id());
}
json->SetString("name", layer->DebugName());
if (position != FloatPoint())
json->SetArray("position", PointAsJSONArray(position));
if (flags & kLayerTreeIncludesDebugInfo &&
layer->OffsetFromLayoutObject() != IntSize()) {
json->SetArray("offsetFromLayoutObject",
SizeAsJSONArray(layer->OffsetFromLayoutObject()));
}
// This is testing against gfx::Size(), *not* whether the size is empty.
if (layer->Size() != gfx::Size())
json->SetArray("bounds", SizeAsJSONArray(IntSize(layer->Size())));
if (layer->ContentsOpaque())
json->SetBoolean("contentsOpaque", true);
if (!layer->DrawsContent())
json->SetBoolean("drawsContent", false);
if (!layer->ContentsAreVisible())
json->SetBoolean("contentsVisible", false);
if (!layer->BackfaceVisibility())
json->SetString("backfaceVisibility", "hidden");
if (flags & kLayerTreeIncludesDebugInfo)
json->SetString("client", PointerAsString(&layer->Client()));
if (Color(layer->BackgroundColor()).Alpha()) {
json->SetString("backgroundColor",
Color(layer->BackgroundColor()).NameForLayoutTreeAsText());
}
FloatPoint scroll_position(ScrollPosition(*layer));
if (scroll_position != FloatPoint())
json->SetArray("scrollPosition", PointAsJSONArray(scroll_position));
if ((flags & kLayerTreeIncludesPaintInvalidations) &&
layer->Client().IsTrackingRasterInvalidations() &&
layer->GetRasterInvalidationTracking()) {
layer->GetRasterInvalidationTracking()->AsJSON(json.get());
}
GraphicsLayerPaintingPhase painting_phase = layer->PaintingPhase();
if ((flags & kLayerTreeIncludesPaintingPhases) && painting_phase) {
auto painting_phases_json = std::make_unique<JSONArray>();
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 & 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 &
(kLayerTreeIncludesDebugInfo | kLayerTreeIncludesCompositingReasons)) {
bool debug = flags & kLayerTreeIncludesDebugInfo;
{
auto compositing_reasons_json = std::make_unique<JSONArray>();
CompositingReasons compositing_reasons = layer->GetCompositingReasons();
auto names = debug ? CompositingReason::Descriptions(compositing_reasons)
: CompositingReason::ShortNames(compositing_reasons);
for (const char* name : names)
compositing_reasons_json->PushString(name);
json->SetArray("compositingReasons", std::move(compositing_reasons_json));
}
{
auto squashing_disallowed_reasons_json = std::make_unique<JSONArray>();
SquashingDisallowedReasons squashing_disallowed_reasons =
layer->GetSquashingDisallowedReasons();
auto names = debug ? SquashingDisallowedReason::Descriptions(
squashing_disallowed_reasons)
: SquashingDisallowedReason::ShortNames(
squashing_disallowed_reasons);
for (const char* name : names)
squashing_disallowed_reasons_json->PushString(name);
json->SetArray("squashingDisallowedReasons",
std::move(squashing_disallowed_reasons_json));
}
}
if (layer->MaskLayer()) {
auto mask_layer_json = std::make_unique<JSONArray>();
mask_layer_json->PushObject(
GraphicsLayerAsJSON(layer->MaskLayer(), flags,
FloatPoint(layer->MaskLayer()->GetPosition())));
json->SetArray("maskLayer", std::move(mask_layer_json));
}
if (layer->HasLayerState() && (flags & (kLayerTreeIncludesDebugInfo |
kLayerTreeIncludesPaintRecords))) {
json->SetString("layerState", layer->GetPropertyTreeState().ToString());
json->SetValue("layerOffset",
PointAsJSONArray(layer->GetOffsetFromTransformNode()));
}
#if DCHECK_IS_ON()
if (layer->HasLayerState() && layer->DrawsContent() &&
(flags & kLayerTreeIncludesPaintRecords))
json->SetValue("paintRecord", RecordAsJSON(*layer->CapturePaintRecord()));
#endif
return json;
}
class LayersAsJSONArray {
public:
LayersAsJSONArray(LayerTreeFlags flags)
: flags_(flags),
next_transform_id_(1),
layers_json_(std::make_unique<JSONArray>()),
transforms_json_(std::make_unique<JSONArray>()) {}
// Outputs the layer tree rooted at |layer| as a JSON array, in paint order,
// and the transform tree also as a JSON array.
std::unique_ptr<JSONObject> operator()(const GraphicsLayer& layer) {
auto json = std::make_unique<JSONObject>();
Walk(layer);
json->SetArray("layers", std::move(layers_json_));
if (transforms_json_->size())
json->SetArray("transforms", std::move(transforms_json_));
return json;
}
int AddTransformJSON(const TransformPaintPropertyNode& transform) {
auto it = transform_id_map_.find(&transform);
if (it != transform_id_map_.end())
return it->value;
int parent_id = 0;
if (transform.Parent())
parent_id = AddTransformJSON(*transform.Parent());
if (transform.IsIdentity() && !transform.RenderingContextId()) {
transform_id_map_.Set(&transform, parent_id);
return parent_id;
}
auto transform_json = std::make_unique<JSONObject>();
int id = next_transform_id_++;
transform_json->SetInteger("id", id);
if (parent_id)
transform_json->SetInteger("parent", parent_id);
if (!transform.IsIdentity()) {
transform_json->SetArray("transform",
TransformAsJSONArray(transform.SlowMatrix()));
}
if (!transform.IsIdentityOr2DTranslation() &&
!transform.Matrix().IsIdentityOrTranslation())
transform_json->SetArray("origin", PointAsJSONArray(transform.Origin()));
if (!transform.FlattensInheritedTransform())
transform_json->SetBoolean("flattenInheritedTransform", false);
if (auto rendering_context = transform.RenderingContextId()) {
auto context_lookup_result =
rendering_context_map_.find(rendering_context);
int rendering_id = rendering_context_map_.size() + 1;
if (context_lookup_result == rendering_context_map_.end())
rendering_context_map_.Set(rendering_context, rendering_id);
else
rendering_id = context_lookup_result->value;
transform_json->SetInteger("renderingContext", rendering_id);
}
transforms_json_->PushObject(std::move(transform_json));
return id;
}
void AddLayer(const GraphicsLayer& layer) {
if (!layer.DrawsContent() && !(flags_ & kLayerTreeIncludesRootLayer))
return;
FloatPoint offset;
if (layer.HasLayerState())
offset = FloatPoint(layer.GetOffsetFromTransformNode());
auto json = GraphicsLayerAsJSON(&layer, flags_, offset);
if (layer.HasLayerState()) {
int transform_id =
AddTransformJSON(layer.GetPropertyTreeState().Transform());
if (transform_id)
json->SetInteger("transform", transform_id);
}
layers_json_->PushObject(std::move(json));
}
void Walk(const GraphicsLayer& layer) {
AddLayer(layer);
for (auto* const child : layer.Children())
Walk(*child);
}
private:
LayerTreeFlags flags_;
int next_transform_id_;
RenderingContextMap rendering_context_map_;
std::unique_ptr<JSONArray> layers_json_;
HashMap<const TransformPaintPropertyNode*, int> transform_id_map_;
std::unique_ptr<JSONArray> transforms_json_;
};
} // namespace
std::unique_ptr<JSONObject> GraphicsLayerTreeAsJSON(const GraphicsLayer* layer,
LayerTreeFlags flags) {
if (flags & kOutputAsLayerTree) {
std::unique_ptr<JSONObject> json =
GraphicsLayerAsJSON(layer, flags, FloatPoint(layer->GetPosition()));
if (layer->Children().size()) {
auto children_json = std::make_unique<JSONArray>();
for (wtf_size_t i = 0; i < layer->Children().size(); i++) {
children_json->PushObject(
GraphicsLayerTreeAsJSON(layer->Children()[i], flags));
}
json->SetArray("children", std::move(children_json));
}
return json;
}
return LayersAsJSONArray(flags)(*layer);
}
String GraphicsLayerTreeAsTextForTesting(const GraphicsLayer* layer,
LayerTreeFlags flags) {
return GraphicsLayerTreeAsJSON(layer, flags)->ToPrettyJSONString();
}
#if DCHECK_IS_ON()
void VerboseLogGraphicsLayerTree(const GraphicsLayer* root) {
if (!VLOG_IS_ON(2))
return;
using GraphicsLayerTreeMap = HashMap<const GraphicsLayer*, String>;
DEFINE_STATIC_LOCAL(GraphicsLayerTreeMap, s_previous_trees, ());
LayerTreeFlags flags = VLOG_IS_ON(3) ? 0xffffffff : kOutputAsLayerTree;
String new_tree = GraphicsLayerTreeAsTextForTesting(root, flags);
auto it = s_previous_trees.find(root);
if (it == s_previous_trees.end() || it->value != new_tree) {
VLOG(2) << "GraphicsLayer tree:\n" << new_tree.Utf8();
s_previous_trees.Set(root, new_tree);
// For simplification, we don't remove deleted GraphicsLayers from the map.
}
}
#endif
} // namespace blink
#if DCHECK_IS_ON()
void showGraphicsLayerTree(const blink::GraphicsLayer* layer) {
if (!layer) {
LOG(ERROR) << "Cannot showGraphicsLayerTree for (nil).";
return;
}
String output = blink::GraphicsLayerTreeAsTextForTesting(layer, 0xffffffff);
LOG(ERROR) << output.Utf8();
}
void showGraphicsLayers(const blink::GraphicsLayer* layer) {
if (!layer) {
LOG(ERROR) << "Cannot showGraphicsLayers for (nil).";
return;
}
String output = blink::GraphicsLayerTreeAsTextForTesting(
layer, 0xffffffff & ~blink::kOutputAsLayerTree);
LOG(ERROR) << output.Utf8();
}
#endif