debug-server-next
Version:
Dev server for hippy-core.
384 lines (383 loc) • 13.5 kB
JavaScript
/*
* 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.
*/
/* eslint-disable rulesdir/no_underscored_properties */
import * as Common from '../../core/common/common.js';
import * as SDK from '../../core/sdk/sdk.js';
import * as UI from '../../ui/legacy/legacy.js';
export class LayerTreeModel extends SDK.SDKModel.SDKModel {
_layerTreeAgent;
_paintProfilerModel;
_layerTree;
_throttler;
_enabled;
_lastPaintRectByLayerId;
constructor(target) {
super(target);
this._layerTreeAgent = target.layerTreeAgent();
target.registerLayerTreeDispatcher(new LayerTreeDispatcher(this));
this._paintProfilerModel =
target.model(SDK.PaintProfiler.PaintProfilerModel);
const resourceTreeModel = target.model(SDK.ResourceTreeModel.ResourceTreeModel);
if (resourceTreeModel) {
resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.MainFrameNavigated, this._onMainFrameNavigated, this);
}
this._layerTree = null;
this._throttler = new Common.Throttler.Throttler(20);
}
async disable() {
if (!this._enabled) {
return;
}
this._enabled = false;
await this._layerTreeAgent.invoke_disable();
}
enable() {
if (this._enabled) {
return;
}
this._enabled = true;
this._forceEnable();
}
async _forceEnable() {
this._lastPaintRectByLayerId = new Map();
if (!this._layerTree) {
this._layerTree = new AgentLayerTree(this);
}
await this._layerTreeAgent.invoke_enable();
}
layerTree() {
return this._layerTree;
}
async _layerTreeChanged(layers) {
if (!this._enabled) {
return;
}
this._throttler.schedule(this._innerSetLayers.bind(this, layers));
}
async _innerSetLayers(layers) {
const layerTree = this._layerTree;
await layerTree.setLayers(layers);
if (!this._lastPaintRectByLayerId) {
this._lastPaintRectByLayerId = new Map();
}
for (const layerId of this._lastPaintRectByLayerId.keys()) {
const lastPaintRect = this._lastPaintRectByLayerId.get(layerId);
const layer = layerTree.layerById(layerId);
if (layer) {
layer._lastPaintRect = lastPaintRect;
}
}
this._lastPaintRectByLayerId = new Map();
this.dispatchEventToListeners(Events.LayerTreeChanged);
}
_layerPainted(layerId, clipRect) {
if (!this._enabled) {
return;
}
const layerTree = this._layerTree;
const layer = layerTree.layerById(layerId);
if (!layer) {
if (!this._lastPaintRectByLayerId) {
this._lastPaintRectByLayerId = new Map();
}
this._lastPaintRectByLayerId.set(layerId, clipRect);
return;
}
layer._didPaint(clipRect);
this.dispatchEventToListeners(Events.LayerPainted, layer);
}
_onMainFrameNavigated() {
this._layerTree = null;
if (this._enabled) {
this._forceEnable();
}
}
}
SDK.SDKModel.SDKModel.register(LayerTreeModel, { capabilities: SDK.Target.Capability.DOM, autostart: false });
// TODO(crbug.com/1167717): Make this a const enum again
// eslint-disable-next-line rulesdir/const_enum
export var Events;
(function (Events) {
Events["LayerTreeChanged"] = "LayerTreeChanged";
Events["LayerPainted"] = "LayerPainted";
})(Events || (Events = {}));
export class AgentLayerTree extends SDK.LayerTreeBase.LayerTreeBase {
_layerTreeModel;
constructor(layerTreeModel) {
super(layerTreeModel.target());
this._layerTreeModel = layerTreeModel;
}
async setLayers(payload) {
if (!payload) {
this._innerSetLayers(payload);
return;
}
const idsToResolve = new Set();
for (let i = 0; i < payload.length; ++i) {
const backendNodeId = payload[i].backendNodeId;
if (!backendNodeId || this.backendNodeIdToNode().has(backendNodeId)) {
continue;
}
idsToResolve.add(backendNodeId);
}
await this.resolveBackendNodeIds(idsToResolve);
this._innerSetLayers(payload);
}
_innerSetLayers(layers) {
this.setRoot(null);
this.setContentRoot(null);
// Payload will be null when not in the composited mode.
if (!layers) {
return;
}
let root;
const oldLayersById = this.layersById;
this.layersById = new Map();
for (let i = 0; i < layers.length; ++i) {
const layerId = layers[i].layerId;
let layer = oldLayersById.get(layerId);
if (layer) {
layer._reset(layers[i]);
}
else {
layer = new AgentLayer(this._layerTreeModel, layers[i]);
}
this.layersById.set(layerId, layer);
const backendNodeId = layers[i].backendNodeId;
if (backendNodeId) {
layer._setNode(this.backendNodeIdToNode().get(backendNodeId) || null);
}
if (!this.contentRoot() && layer.drawsContent()) {
this.setContentRoot(layer);
}
const parentId = layer.parentId();
if (parentId) {
const parent = this.layersById.get(parentId);
if (!parent) {
throw new Error(`Missing parent ${parentId} for layer ${layerId}`);
}
parent.addChild(layer);
}
else {
if (root) {
console.assert(false, 'Multiple root layers');
}
root = layer;
}
}
if (root) {
this.setRoot(root);
root._calculateQuad(new WebKitCSSMatrix());
}
}
}
export class AgentLayer {
_scrollRects;
_quad;
_children;
_image;
_parent;
_layerPayload;
_layerTreeModel;
_node;
_lastPaintRect;
_paintCount;
_stickyPositionConstraint;
constructor(layerTreeModel, layerPayload) {
this._layerTreeModel = layerTreeModel;
this._reset(layerPayload);
}
id() {
return this._layerPayload.layerId;
}
parentId() {
return this._layerPayload.parentLayerId || null;
}
parent() {
return this._parent;
}
isRoot() {
return !this.parentId();
}
children() {
return this._children;
}
addChild(childParam) {
const child = childParam;
if (child._parent) {
console.assert(false, 'Child already has a parent');
}
this._children.push(child);
child._parent = this;
}
_setNode(node) {
this._node = node;
}
node() {
return this._node || null;
}
nodeForSelfOrAncestor() {
let layer = this;
for (; layer; layer = layer._parent) {
if (layer._node) {
return layer._node;
}
}
return null;
}
offsetX() {
return this._layerPayload.offsetX;
}
offsetY() {
return this._layerPayload.offsetY;
}
width() {
return this._layerPayload.width;
}
height() {
return this._layerPayload.height;
}
transform() {
return this._layerPayload.transform || null;
}
quad() {
return this._quad;
}
anchorPoint() {
return [
this._layerPayload.anchorX || 0,
this._layerPayload.anchorY || 0,
this._layerPayload.anchorZ || 0,
];
}
invisible() {
return this._layerPayload.invisible || false;
}
paintCount() {
return this._paintCount || this._layerPayload.paintCount;
}
lastPaintRect() {
return this._lastPaintRect || null;
}
scrollRects() {
return this._scrollRects;
}
stickyPositionConstraint() {
return this._stickyPositionConstraint || null;
}
async requestCompositingReasonIds() {
const reasons = await this._layerTreeModel._layerTreeAgent.invoke_compositingReasons({ layerId: this.id() });
return reasons.compositingReasonIds || [];
}
drawsContent() {
return this._layerPayload.drawsContent;
}
gpuMemoryUsage() {
/**
* @const
*/
const bytesPerPixel = 4;
return this.drawsContent() ? this.width() * this.height() * bytesPerPixel : 0;
}
snapshots() {
const promise = this._layerTreeModel._paintProfilerModel.makeSnapshot(this.id()).then(snapshot => {
if (!snapshot) {
return null;
}
return { rect: { x: 0, y: 0, width: this.width(), height: this.height() }, snapshot: snapshot };
});
return [promise];
}
_didPaint(rect) {
this._lastPaintRect = rect;
this._paintCount = this.paintCount() + 1;
this._image = null;
}
_reset(layerPayload) {
/** @type {?SDK.DOMModel.DOMNode} */
this._node = null;
this._children = [];
this._parent = null;
this._paintCount = 0;
this._layerPayload = layerPayload;
this._image = null;
this._scrollRects = this._layerPayload.scrollRects || [];
this._stickyPositionConstraint = this._layerPayload.stickyPositionConstraint ?
new SDK.LayerTreeBase.StickyPositionConstraint(this._layerTreeModel.layerTree(), this._layerPayload.stickyPositionConstraint) :
null;
}
_matrixFromArray(a) {
function toFixed9(x) {
return x.toFixed(9);
}
return new WebKitCSSMatrix('matrix3d(' + a.map(toFixed9).join(',') + ')');
}
_calculateTransformToViewport(parentTransform) {
const offsetMatrix = new WebKitCSSMatrix().translate(this._layerPayload.offsetX, this._layerPayload.offsetY);
let matrix = offsetMatrix;
if (this._layerPayload.transform) {
const transformMatrix = this._matrixFromArray(this._layerPayload.transform);
const anchorVector = new UI.Geometry.Vector(this._layerPayload.width * this.anchorPoint()[0], this._layerPayload.height * this.anchorPoint()[1], this.anchorPoint()[2]);
const anchorPoint = UI.Geometry.multiplyVectorByMatrixAndNormalize(anchorVector, matrix);
const anchorMatrix = new WebKitCSSMatrix().translate(-anchorPoint.x, -anchorPoint.y, -anchorPoint.z);
matrix = anchorMatrix.inverse().multiply(transformMatrix.multiply(anchorMatrix.multiply(matrix)));
}
matrix = parentTransform.multiply(matrix);
return matrix;
}
_createVertexArrayForRect(width, height) {
return [0, 0, 0, width, 0, 0, width, height, 0, 0, height, 0];
}
_calculateQuad(parentTransform) {
const matrix = this._calculateTransformToViewport(parentTransform);
this._quad = [];
const vertices = this._createVertexArrayForRect(this._layerPayload.width, this._layerPayload.height);
for (let i = 0; i < 4; ++i) {
const point = UI.Geometry.multiplyVectorByMatrixAndNormalize(new UI.Geometry.Vector(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]), matrix);
this._quad.push(point.x, point.y);
}
function calculateQuadForLayer(layer) {
layer._calculateQuad(matrix);
}
this._children.forEach(calculateQuadForLayer);
}
}
class LayerTreeDispatcher {
_layerTreeModel;
constructor(layerTreeModel) {
this._layerTreeModel = layerTreeModel;
}
layerTreeDidChange({ layers }) {
this._layerTreeModel._layerTreeChanged(layers || null);
}
layerPainted({ layerId, clip }) {
this._layerTreeModel._layerPainted(layerId, clip);
}
}