blob: 7cb99b7e9cde4c153cc3e6cd11a5392cffbeb614 [file] [log] [blame]
/*
* Copyright (C) 2012 Apple Inc. All rights reserved.
* Copyright (C) 2013 Google 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 THE COPYRIGHT
* OWNER 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 "core/inspector/InspectorLayerTreeAgent.h"
#include <memory>
#include "core/dom/DOMNodeIds.h"
#include "core/dom/Document.h"
#include "core/frame/FrameView.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/VisualViewport.h"
#include "core/inspector/IdentifiersFactory.h"
#include "core/inspector/InspectedFrames.h"
#include "core/layout/LayoutPart.h"
#include "core/layout/api/LayoutViewItem.h"
#include "core/layout/compositing/CompositedLayerMapping.h"
#include "core/layout/compositing/PaintLayerCompositor.h"
#include "core/loader/DocumentLoader.h"
#include "core/page/ChromeClient.h"
#include "core/page/Page.h"
#include "platform/geometry/IntRect.h"
#include "platform/graphics/CompositingReasons.h"
#include "platform/graphics/GraphicsLayer.h"
#include "platform/graphics/PictureSnapshot.h"
#include "platform/image-encoders/PNGImageEncoder.h"
#include "platform/transforms/TransformationMatrix.h"
#include "platform/wtf/text/Base64.h"
#include "platform/wtf/text/StringBuilder.h"
#include "public/platform/WebFloatPoint.h"
#include "public/platform/WebLayer.h"
namespace blink {
using protocol::Array;
using protocol::Maybe;
using protocol::Response;
unsigned InspectorLayerTreeAgent::last_snapshot_id_;
inline String IdForLayer(const GraphicsLayer* graphics_layer) {
return String::Number(graphics_layer->PlatformLayer()->Id());
}
static std::unique_ptr<protocol::LayerTree::ScrollRect> BuildScrollRect(
const WebRect& rect,
const String& type) {
std::unique_ptr<protocol::DOM::Rect> rect_object =
protocol::DOM::Rect::create()
.setX(rect.x)
.setY(rect.y)
.setHeight(rect.height)
.setWidth(rect.width)
.build();
std::unique_ptr<protocol::LayerTree::ScrollRect> scroll_rect_object =
protocol::LayerTree::ScrollRect::create()
.setRect(std::move(rect_object))
.setType(type)
.build();
return scroll_rect_object;
}
static std::unique_ptr<Array<protocol::LayerTree::ScrollRect>>
BuildScrollRectsForLayer(GraphicsLayer* graphics_layer,
bool report_wheel_scrollers) {
std::unique_ptr<Array<protocol::LayerTree::ScrollRect>> scroll_rects =
Array<protocol::LayerTree::ScrollRect>::create();
WebLayer* web_layer = graphics_layer->PlatformLayer();
WebVector<WebRect> non_fast_scrollable_rects =
web_layer->NonFastScrollableRegion();
for (size_t i = 0; i < non_fast_scrollable_rects.size(); ++i) {
scroll_rects->addItem(BuildScrollRect(
non_fast_scrollable_rects[i],
protocol::LayerTree::ScrollRect::TypeEnum::RepaintsOnScroll));
}
WebVector<WebRect> touch_event_handler_rects =
web_layer->TouchEventHandlerRegion();
for (size_t i = 0; i < touch_event_handler_rects.size(); ++i) {
scroll_rects->addItem(BuildScrollRect(
touch_event_handler_rects[i],
protocol::LayerTree::ScrollRect::TypeEnum::TouchEventHandler));
}
if (report_wheel_scrollers) {
WebRect web_rect(web_layer->GetPosition().x, web_layer->GetPosition().y,
web_layer->Bounds().width, web_layer->Bounds().height);
scroll_rects->addItem(BuildScrollRect(
web_rect,
protocol::LayerTree::ScrollRect::TypeEnum::WheelEventHandler));
}
return scroll_rects->length() ? std::move(scroll_rects) : nullptr;
}
static std::unique_ptr<protocol::LayerTree::Layer> BuildObjectForLayer(
GraphicsLayer* graphics_layer,
int node_id,
bool report_wheel_event_listeners) {
WebLayer* web_layer = graphics_layer->PlatformLayer();
std::unique_ptr<protocol::LayerTree::Layer> layer_object =
protocol::LayerTree::Layer::create()
.setLayerId(IdForLayer(graphics_layer))
.setOffsetX(web_layer->GetPosition().x)
.setOffsetY(web_layer->GetPosition().y)
.setWidth(web_layer->Bounds().width)
.setHeight(web_layer->Bounds().height)
.setPaintCount(graphics_layer->PaintCount())
.setDrawsContent(web_layer->DrawsContent())
.build();
if (node_id)
layer_object->setBackendNodeId(node_id);
GraphicsLayer* parent = graphics_layer->Parent();
if (parent)
layer_object->setParentLayerId(IdForLayer(parent));
if (!graphics_layer->ContentsAreVisible())
layer_object->setInvisible(true);
const TransformationMatrix& transform = graphics_layer->Transform();
if (!transform.IsIdentity()) {
TransformationMatrix::FloatMatrix4 flattened_matrix;
transform.ToColumnMajorFloatArray(flattened_matrix);
std::unique_ptr<Array<double>> transform_array = Array<double>::create();
for (size_t i = 0; i < WTF_ARRAY_LENGTH(flattened_matrix); ++i)
transform_array->addItem(flattened_matrix[i]);
layer_object->setTransform(std::move(transform_array));
const FloatPoint3D& transform_origin = graphics_layer->TransformOrigin();
// FIXME: rename these to setTransformOrigin*
if (web_layer->Bounds().width > 0)
layer_object->setAnchorX(transform_origin.X() /
web_layer->Bounds().width);
else
layer_object->setAnchorX(0.0);
if (web_layer->Bounds().height > 0)
layer_object->setAnchorY(transform_origin.Y() /
web_layer->Bounds().height);
else
layer_object->setAnchorY(0.0);
layer_object->setAnchorZ(transform_origin.Z());
}
std::unique_ptr<Array<protocol::LayerTree::ScrollRect>> scroll_rects =
BuildScrollRectsForLayer(graphics_layer, report_wheel_event_listeners);
if (scroll_rects)
layer_object->setScrollRects(std::move(scroll_rects));
return layer_object;
}
InspectorLayerTreeAgent::InspectorLayerTreeAgent(
InspectedFrames* inspected_frames)
: inspected_frames_(inspected_frames),
suppress_layer_paint_events_(false) {}
InspectorLayerTreeAgent::~InspectorLayerTreeAgent() {}
DEFINE_TRACE(InspectorLayerTreeAgent) {
visitor->Trace(inspected_frames_);
InspectorBaseAgent::Trace(visitor);
}
void InspectorLayerTreeAgent::Restore() {
// We do not re-enable layer agent automatically after navigation. This is
// because it depends on DOMAgent and node ids in particular, so we let
// front-end request document and re-enable the agent manually after this.
}
Response InspectorLayerTreeAgent::enable() {
instrumenting_agents_->addInspectorLayerTreeAgent(this);
Document* document = inspected_frames_->Root()->GetDocument();
if (document &&
document->Lifecycle().GetState() >= DocumentLifecycle::kCompositingClean)
LayerTreeDidChange();
return Response::OK();
}
Response InspectorLayerTreeAgent::disable() {
instrumenting_agents_->removeInspectorLayerTreeAgent(this);
snapshot_by_id_.clear();
return Response::OK();
}
void InspectorLayerTreeAgent::LayerTreeDidChange() {
GetFrontend()->layerTreeDidChange(BuildLayerTree());
}
void InspectorLayerTreeAgent::DidPaint(const GraphicsLayer* graphics_layer,
GraphicsContext&,
const LayoutRect& rect) {
if (suppress_layer_paint_events_)
return;
// Should only happen for FrameView paints when compositing is off. Consider
// different instrumentation method for that.
if (!graphics_layer)
return;
std::unique_ptr<protocol::DOM::Rect> dom_rect = protocol::DOM::Rect::create()
.setX(rect.X())
.setY(rect.Y())
.setWidth(rect.Width())
.setHeight(rect.Height())
.build();
GetFrontend()->layerPainted(IdForLayer(graphics_layer), std::move(dom_rect));
}
std::unique_ptr<Array<protocol::LayerTree::Layer>>
InspectorLayerTreeAgent::BuildLayerTree() {
PaintLayerCompositor* compositor = GetPaintLayerCompositor();
if (!compositor || !compositor->InCompositingMode())
return nullptr;
LayerIdToNodeIdMap layer_id_to_node_id_map;
std::unique_ptr<Array<protocol::LayerTree::Layer>> layers =
Array<protocol::LayerTree::Layer>::create();
BuildLayerIdToNodeIdMap(compositor->RootLayer(), layer_id_to_node_id_map);
int scrolling_layer_id = inspected_frames_->Root()
->View()
->LayerForScrolling()
->PlatformLayer()
->Id();
bool have_blocking_wheel_event_handlers =
inspected_frames_->Root()->GetChromeClient().EventListenerProperties(
inspected_frames_->Root(), WebEventListenerClass::kMouseWheel) ==
WebEventListenerProperties::kBlocking;
GatherGraphicsLayers(RootGraphicsLayer(), layer_id_to_node_id_map, layers,
have_blocking_wheel_event_handlers, scrolling_layer_id);
return layers;
}
void InspectorLayerTreeAgent::BuildLayerIdToNodeIdMap(
PaintLayer* root,
LayerIdToNodeIdMap& layer_id_to_node_id_map) {
if (root->HasCompositedLayerMapping()) {
if (Node* node = root->GetLayoutObject().GeneratingNode()) {
GraphicsLayer* graphics_layer =
root->GetCompositedLayerMapping()->ChildForSuperlayers();
layer_id_to_node_id_map.Set(graphics_layer->PlatformLayer()->Id(),
IdForNode(node));
}
}
for (PaintLayer* child = root->FirstChild(); child;
child = child->NextSibling())
BuildLayerIdToNodeIdMap(child, layer_id_to_node_id_map);
if (!root->GetLayoutObject().IsLayoutIFrame())
return;
FrameView* child_frame_view =
ToLayoutPart(root->GetLayoutObject()).ChildFrameView();
LayoutViewItem child_layout_view_item = child_frame_view->GetLayoutViewItem();
if (!child_layout_view_item.IsNull()) {
if (PaintLayerCompositor* child_compositor =
child_layout_view_item.Compositor())
BuildLayerIdToNodeIdMap(child_compositor->RootLayer(),
layer_id_to_node_id_map);
}
}
void InspectorLayerTreeAgent::GatherGraphicsLayers(
GraphicsLayer* root,
HashMap<int, int>& layer_id_to_node_id_map,
std::unique_ptr<Array<protocol::LayerTree::Layer>>& layers,
bool has_wheel_event_handlers,
int scrolling_layer_id) {
int layer_id = root->PlatformLayer()->Id();
if (page_overlay_layer_ids_.Find(layer_id) != WTF::kNotFound)
return;
layers->addItem(BuildObjectForLayer(
root, layer_id_to_node_id_map.at(layer_id),
has_wheel_event_handlers && layer_id == scrolling_layer_id));
for (size_t i = 0, size = root->Children().size(); i < size; ++i)
GatherGraphicsLayers(root->Children()[i], layer_id_to_node_id_map, layers,
has_wheel_event_handlers, scrolling_layer_id);
}
int InspectorLayerTreeAgent::IdForNode(Node* node) {
return DOMNodeIds::IdForNode(node);
}
PaintLayerCompositor* InspectorLayerTreeAgent::GetPaintLayerCompositor() {
LayoutViewItem layout_view = inspected_frames_->Root()->ContentLayoutItem();
PaintLayerCompositor* compositor =
layout_view.IsNull() ? nullptr : layout_view.Compositor();
return compositor;
}
GraphicsLayer* InspectorLayerTreeAgent::RootGraphicsLayer() {
return inspected_frames_->Root()
->GetPage()
->GetVisualViewport()
.RootGraphicsLayer();
}
static GraphicsLayer* FindLayerById(GraphicsLayer* root, int layer_id) {
if (root->PlatformLayer()->Id() == layer_id)
return root;
for (size_t i = 0, size = root->Children().size(); i < size; ++i) {
if (GraphicsLayer* layer = FindLayerById(root->Children()[i], layer_id))
return layer;
}
return nullptr;
}
Response InspectorLayerTreeAgent::LayerById(const String& layer_id,
GraphicsLayer*& result) {
bool ok;
int id = layer_id.ToInt(&ok);
if (!ok)
return Response::Error("Invalid layer id");
PaintLayerCompositor* compositor = GetPaintLayerCompositor();
if (!compositor)
return Response::Error("Not in compositing mode");
result = FindLayerById(RootGraphicsLayer(), id);
if (!result)
return Response::Error("No layer matching given id found");
return Response::OK();
}
Response InspectorLayerTreeAgent::compositingReasons(
const String& layer_id,
std::unique_ptr<Array<String>>* reason_strings) {
GraphicsLayer* graphics_layer = nullptr;
Response response = LayerById(layer_id, graphics_layer);
if (!response.isSuccess())
return response;
CompositingReasons reasons_bitmask = graphics_layer->GetCompositingReasons();
*reason_strings = Array<String>::create();
for (size_t i = 0; i < kNumberOfCompositingReasons; ++i) {
if (!(reasons_bitmask & kCompositingReasonStringMap[i].reason))
continue;
(*reason_strings)->addItem(kCompositingReasonStringMap[i].short_name);
#ifndef _NDEBUG
reasons_bitmask &= ~kCompositingReasonStringMap[i].reason;
#endif
}
DCHECK(!reasons_bitmask);
return Response::OK();
}
Response InspectorLayerTreeAgent::makeSnapshot(const String& layer_id,
String* snapshot_id) {
GraphicsLayer* layer = nullptr;
Response response = LayerById(layer_id, layer);
if (!response.isSuccess())
return response;
if (!layer->DrawsContent())
return Response::Error("Layer does not draw content");
IntSize size = ExpandedIntSize(layer->Size());
IntRect interest_rect(IntPoint(0, 0), size);
suppress_layer_paint_events_ = true;
layer->Paint(&interest_rect);
suppress_layer_paint_events_ = false;
GraphicsContext context(layer->GetPaintController());
context.BeginRecording(interest_rect);
layer->GetPaintController().GetPaintArtifact().Replay(interest_rect, context);
RefPtr<PictureSnapshot> snapshot = AdoptRef(
new PictureSnapshot(ToSkPicture(context.EndRecording(), interest_rect)));
*snapshot_id = String::Number(++last_snapshot_id_);
bool new_entry = snapshot_by_id_.insert(*snapshot_id, snapshot).is_new_entry;
DCHECK(new_entry);
return Response::OK();
}
Response InspectorLayerTreeAgent::loadSnapshot(
std::unique_ptr<Array<protocol::LayerTree::PictureTile>> tiles,
String* snapshot_id) {
if (!tiles->length())
return Response::Error("Invalid argument, no tiles provided");
Vector<RefPtr<PictureSnapshot::TilePictureStream>> decoded_tiles;
decoded_tiles.Grow(tiles->length());
for (size_t i = 0; i < tiles->length(); ++i) {
protocol::LayerTree::PictureTile* tile = tiles->get(i);
decoded_tiles[i] = AdoptRef(new PictureSnapshot::TilePictureStream());
decoded_tiles[i]->layer_offset.Set(tile->getX(), tile->getY());
if (!Base64Decode(tile->getPicture(), decoded_tiles[i]->data))
return Response::Error("Invalid base64 encoding");
}
RefPtr<PictureSnapshot> snapshot = PictureSnapshot::Load(decoded_tiles);
if (!snapshot)
return Response::Error("Invalid snapshot format");
if (snapshot->IsEmpty())
return Response::Error("Empty snapshot");
*snapshot_id = String::Number(++last_snapshot_id_);
bool new_entry = snapshot_by_id_.insert(*snapshot_id, snapshot).is_new_entry;
DCHECK(new_entry);
return Response::OK();
}
Response InspectorLayerTreeAgent::releaseSnapshot(const String& snapshot_id) {
SnapshotById::iterator it = snapshot_by_id_.find(snapshot_id);
if (it == snapshot_by_id_.end())
return Response::Error("Snapshot not found");
snapshot_by_id_.erase(it);
return Response::OK();
}
Response InspectorLayerTreeAgent::GetSnapshotById(
const String& snapshot_id,
const PictureSnapshot*& result) {
SnapshotById::iterator it = snapshot_by_id_.find(snapshot_id);
if (it == snapshot_by_id_.end())
return Response::Error("Snapshot not found");
result = it->value.Get();
return Response::OK();
}
Response InspectorLayerTreeAgent::replaySnapshot(const String& snapshot_id,
Maybe<int> from_step,
Maybe<int> to_step,
Maybe<double> scale,
String* data_url) {
const PictureSnapshot* snapshot = nullptr;
Response response = GetSnapshotById(snapshot_id, snapshot);
if (!response.isSuccess())
return response;
std::unique_ptr<Vector<char>> base64_data = snapshot->Replay(
from_step.fromMaybe(0), to_step.fromMaybe(0), scale.fromMaybe(1.0));
if (!base64_data)
return Response::Error("Image encoding failed");
StringBuilder url;
url.Append("data:image/png;base64,");
url.ReserveCapacity(url.length() + base64_data->size());
url.Append(base64_data->begin(), base64_data->size());
*data_url = url.ToString();
return Response::OK();
}
static void ParseRect(protocol::DOM::Rect* object, FloatRect* rect) {
*rect = FloatRect(object->getX(), object->getY(), object->getWidth(),
object->getHeight());
}
Response InspectorLayerTreeAgent::profileSnapshot(
const String& snapshot_id,
Maybe<int> min_repeat_count,
Maybe<double> min_duration,
Maybe<protocol::DOM::Rect> clip_rect,
std::unique_ptr<protocol::Array<protocol::Array<double>>>* out_timings) {
const PictureSnapshot* snapshot = nullptr;
Response response = GetSnapshotById(snapshot_id, snapshot);
if (!response.isSuccess())
return response;
FloatRect rect;
if (clip_rect.isJust())
ParseRect(clip_rect.fromJust(), &rect);
std::unique_ptr<PictureSnapshot::Timings> timings = snapshot->Profile(
min_repeat_count.fromMaybe(1), min_duration.fromMaybe(0),
clip_rect.isJust() ? &rect : 0);
*out_timings = Array<Array<double>>::create();
for (size_t i = 0; i < timings->size(); ++i) {
const Vector<double>& row = (*timings)[i];
std::unique_ptr<Array<double>> out_row = Array<double>::create();
for (size_t j = 0; j < row.size(); ++j)
out_row->addItem(row[j]);
(*out_timings)->addItem(std::move(out_row));
}
return Response::OK();
}
Response InspectorLayerTreeAgent::snapshotCommandLog(
const String& snapshot_id,
std::unique_ptr<Array<protocol::DictionaryValue>>* command_log) {
const PictureSnapshot* snapshot = nullptr;
Response response = GetSnapshotById(snapshot_id, snapshot);
if (!response.isSuccess())
return response;
protocol::ErrorSupport errors;
std::unique_ptr<protocol::Value> log_value = protocol::StringUtil::parseJSON(
snapshot->SnapshotCommandLog()->ToJSONString());
*command_log =
Array<protocol::DictionaryValue>::fromValue(log_value.get(), &errors);
if (errors.hasErrors())
return Response::Error(errors.errors());
return Response::OK();
}
void InspectorLayerTreeAgent::WillAddPageOverlay(const GraphicsLayer* layer) {
page_overlay_layer_ids_.push_back(layer->PlatformLayer()->Id());
}
void InspectorLayerTreeAgent::DidRemovePageOverlay(const GraphicsLayer* layer) {
size_t index = page_overlay_layer_ids_.Find(layer->PlatformLayer()->Id());
if (index == WTF::kNotFound)
return;
page_overlay_layer_ids_.erase(index);
}
} // namespace blink