| // 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 "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/scroll/scrollable_area.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(); |
| } |
| |
| void AddFlattenInheritedTransformJSON(const GraphicsLayer* layer, |
| JSONObject& json) { |
| if (layer->Parent() && !layer->Parent()->ShouldFlattenTransform()) |
| json.SetBoolean("flattenInheritedTransform", false); |
| } |
| |
| void AddTransformJSONProperties(const GraphicsLayer* layer, |
| JSONObject& json, |
| RenderingContextMap& rendering_context_map) { |
| const TransformationMatrix& transform = layer->Transform(); |
| if (!transform.IsIdentity()) |
| json.SetArray("transform", TransformAsJSONArray(transform)); |
| |
| if (!transform.IsIdentityOrTranslation()) |
| json.SetArray("origin", PointAsJSONArray(layer->TransformOrigin())); |
| |
| AddFlattenInheritedTransformJSON(layer, json); |
| |
| if (int rendering_context3d = layer->GetRenderingContext3D()) { |
| auto 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("renderingContext", context_id); |
| } |
| } |
| |
| std::unique_ptr<JSONObject> GraphicsLayerAsJSON( |
| const GraphicsLayer* layer, |
| LayerTreeFlags flags, |
| RenderingContextMap& rendering_context_map, |
| const FloatPoint& position) { |
| std::unique_ptr<JSONObject> json = JSONObject::Create(); |
| |
| if (flags & kLayerTreeIncludesDebugInfo) |
| json->SetString("this", PointerAsString(layer)); |
| |
| 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())); |
| } |
| |
| if (layer->Size() != IntSize()) |
| json->SetArray("bounds", SizeAsJSONArray(layer->Size())); |
| |
| if (layer->Opacity() != 1) |
| json->SetDouble("opacity", layer->Opacity()); |
| |
| if (layer->GetBlendMode() != BlendMode::kNormal) { |
| json->SetString("blendMode", CompositeOperatorName(kCompositeSourceOver, |
| layer->GetBlendMode())); |
| } |
| |
| if (layer->IsRootForIsolatedGroup()) |
| json->SetBoolean("isolate", true); |
| |
| 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 (layer->BackgroundColor().Alpha()) { |
| json->SetString("backgroundColor", |
| layer->BackgroundColor().NameForLayoutTreeAsText()); |
| } |
| |
| if (flags & kOutputAsLayerTree) { |
| AddTransformJSONProperties(layer, *json, rendering_context_map); |
| if (!layer->ShouldFlattenTransform()) |
| json->SetBoolean("shouldFlattenTransform", false); |
| 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) { |
| 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 (layer->HasScrollParent()) |
| json->SetBoolean("hasScrollParent", true); |
| if (layer->HasClipParent()) |
| json->SetBoolean("hasClipParent", true); |
| } |
| |
| if (flags & |
| (kLayerTreeIncludesDebugInfo | kLayerTreeIncludesCompositingReasons)) { |
| bool debug = flags & kLayerTreeIncludesDebugInfo; |
| { |
| std::unique_ptr<JSONArray> compositing_reasons_json = JSONArray::Create(); |
| 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)); |
| } |
| { |
| std::unique_ptr<JSONArray> squashing_disallowed_reasons_json = |
| JSONArray::Create(); |
| 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()) { |
| std::unique_ptr<JSONArray> mask_layer_json = JSONArray::Create(); |
| mask_layer_json->PushObject( |
| GraphicsLayerAsJSON(layer->MaskLayer(), flags, rendering_context_map, |
| layer->MaskLayer()->GetPosition())); |
| json->SetArray("maskLayer", std::move(mask_layer_json)); |
| } |
| |
| if (layer->ContentsClippingMaskLayer()) { |
| std::unique_ptr<JSONArray> contents_clipping_mask_layer_json = |
| JSONArray::Create(); |
| contents_clipping_mask_layer_json->PushObject(GraphicsLayerAsJSON( |
| layer->ContentsClippingMaskLayer(), flags, rendering_context_map, |
| layer->ContentsClippingMaskLayer()->GetPosition())); |
| json->SetArray("contentsClippingMaskLayer", |
| std::move(contents_clipping_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->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_(JSONArray::Create()), |
| transforms_json_(JSONArray::Create()) {} |
| |
| // 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 = JSONObject::Create(); |
| Walk(layer, 0, FloatPoint()); |
| json->SetArray("layers", std::move(layers_json_)); |
| if (transforms_json_->size()) |
| json->SetArray("transforms", std::move(transforms_json_)); |
| return json; |
| } |
| |
| JSONObject* AddTransformJSON(int& transform_id) { |
| auto transform_json = JSONObject::Create(); |
| int parent_transform_id = transform_id; |
| transform_id = next_transform_id_++; |
| transform_json->SetInteger("id", transform_id); |
| if (parent_transform_id) |
| transform_json->SetInteger("parent", parent_transform_id); |
| auto* result = transform_json.get(); |
| transforms_json_->PushObject(std::move(transform_json)); |
| return result; |
| } |
| |
| void AddLayer(const GraphicsLayer& layer, |
| int& transform_id, |
| FloatPoint& position) { |
| FloatPoint scroll_position = ScrollPosition(layer); |
| if (scroll_position != FloatPoint()) { |
| // Output scroll position as a transform. |
| auto* scroll_translate_json = AddTransformJSON(transform_id); |
| scroll_translate_json->SetArray( |
| "transform", TransformAsJSONArray(TransformationMatrix().Translate( |
| -scroll_position.X(), -scroll_position.Y()))); |
| AddFlattenInheritedTransformJSON(&layer, *scroll_translate_json); |
| } |
| |
| if (!layer.Transform().IsIdentity() || layer.GetRenderingContext3D() || |
| layer.GetCompositingReasons() & CompositingReason::k3DTransform) { |
| if (position != FloatPoint()) { |
| // Output position offset as a transform. |
| auto* position_translate_json = AddTransformJSON(transform_id); |
| position_translate_json->SetArray( |
| "transform", TransformAsJSONArray(TransformationMatrix().Translate( |
| position.X(), position.Y()))); |
| AddFlattenInheritedTransformJSON(&layer, *position_translate_json); |
| if (layer.Parent() && !layer.Parent()->ShouldFlattenTransform()) { |
| position_translate_json->SetBoolean("flattenInheritedTransform", |
| false); |
| } |
| position = FloatPoint(); |
| } |
| |
| if (!layer.Transform().IsIdentity() || layer.GetRenderingContext3D()) { |
| auto* transform_json = AddTransformJSON(transform_id); |
| AddTransformJSONProperties(&layer, *transform_json, |
| rendering_context_map_); |
| } |
| } |
| |
| auto json = |
| GraphicsLayerAsJSON(&layer, flags_, rendering_context_map_, position); |
| if (transform_id) |
| json->SetInteger("transform", transform_id); |
| layers_json_->PushObject(std::move(json)); |
| } |
| |
| void Walk(const GraphicsLayer& layer, |
| int parent_transform_id, |
| const FloatPoint& parent_position) { |
| FloatPoint position = parent_position + layer.GetPosition(); |
| int transform_id = parent_transform_id; |
| AddLayer(layer, transform_id, position); |
| for (auto* const child : layer.Children()) |
| Walk(*child, transform_id, position); |
| } |
| |
| private: |
| LayerTreeFlags flags_; |
| int next_transform_id_; |
| RenderingContextMap rendering_context_map_; |
| std::unique_ptr<JSONArray> layers_json_; |
| std::unique_ptr<JSONArray> transforms_json_; |
| }; |
| |
| // This is the SPv1 version of ContentLayerClientImpl::LayerAsJSON(). |
| std::unique_ptr<JSONObject> GraphicsLayerTreeAsJSON( |
| const GraphicsLayer* layer, |
| LayerTreeFlags flags, |
| RenderingContextMap& rendering_context_map) { |
| std::unique_ptr<JSONObject> json = GraphicsLayerAsJSON( |
| layer, flags, rendering_context_map, layer->GetPosition()); |
| |
| if (layer->Children().size()) { |
| std::unique_ptr<JSONArray> children_json = JSONArray::Create(); |
| for (size_t i = 0; i < layer->Children().size(); i++) { |
| children_json->PushObject(GraphicsLayerTreeAsJSON( |
| layer->Children()[i], flags, rendering_context_map)); |
| } |
| json->SetArray("children", std::move(children_json)); |
| } |
| |
| return json; |
| } |
| |
| } // namespace |
| |
| std::unique_ptr<JSONObject> GraphicsLayerTreeAsJSON(const GraphicsLayer* layer, |
| LayerTreeFlags flags) { |
| if (flags & kOutputAsLayerTree) { |
| RenderingContextMap rendering_context_map; |
| return GraphicsLayerTreeAsJSON(layer, flags, rendering_context_map); |
| } |
| |
| return LayersAsJSONArray(flags)(*layer); |
| } |
| |
| String GraphicsLayerTreeAsTextForTesting(const GraphicsLayer* layer, |
| LayerTreeFlags flags) { |
| return GraphicsLayerTreeAsJSON(layer, flags)->ToPrettyJSONString(); |
| } |
| |
| } // 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().data(); |
| } |
| |
| 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().data(); |
| } |
| #endif |