1/*
2 * Copyright (C) 2012 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32
33#if ENABLE(INSPECTOR)
34
35#include "InspectorLayerTreeAgent.h"
36
37#include "InspectorDOMAgent.h"
38#include "InspectorWebFrontendDispatchers.h"
39#include "InstrumentingAgents.h"
40#include "IntRect.h"
41#include "PseudoElement.h"
42#include "RenderLayer.h"
43#include "RenderLayerBacking.h"
44#include "RenderLayerCompositor.h"
45#include "RenderView.h"
46#include <inspector/IdentifiersFactory.h>
47
48using namespace Inspector;
49
50namespace WebCore {
51
52InspectorLayerTreeAgent::InspectorLayerTreeAgent(InstrumentingAgents* instrumentingAgents)
53    : InspectorAgentBase(ASCIILiteral("LayerTree"), instrumentingAgents)
54{
55}
56
57InspectorLayerTreeAgent::~InspectorLayerTreeAgent()
58{
59    reset();
60}
61
62void InspectorLayerTreeAgent::didCreateFrontendAndBackend(Inspector::InspectorFrontendChannel* frontendChannel, InspectorBackendDispatcher* backendDispatcher)
63{
64    m_frontendDispatcher = std::make_unique<InspectorLayerTreeFrontendDispatcher>(frontendChannel);
65    m_backendDispatcher = InspectorLayerTreeBackendDispatcher::create(backendDispatcher, this);
66}
67
68void InspectorLayerTreeAgent::willDestroyFrontendAndBackend(InspectorDisconnectReason)
69{
70    m_frontendDispatcher = nullptr;
71    m_backendDispatcher.clear();
72
73    disable(nullptr);
74}
75
76void InspectorLayerTreeAgent::reset()
77{
78    m_documentLayerToIdMap.clear();
79    m_idToLayer.clear();
80    m_pseudoElementToIdMap.clear();
81    m_idToPseudoElement.clear();
82}
83
84void InspectorLayerTreeAgent::enable(ErrorString*)
85{
86    m_instrumentingAgents->setInspectorLayerTreeAgent(this);
87}
88
89void InspectorLayerTreeAgent::disable(ErrorString*)
90{
91    m_instrumentingAgents->setInspectorLayerTreeAgent(nullptr);
92}
93
94void InspectorLayerTreeAgent::layerTreeDidChange()
95{
96    m_frontendDispatcher->layerTreeDidChange();
97}
98
99void InspectorLayerTreeAgent::renderLayerDestroyed(const RenderLayer* renderLayer)
100{
101    unbind(renderLayer);
102}
103
104void InspectorLayerTreeAgent::pseudoElementDestroyed(PseudoElement* pseudoElement)
105{
106    unbindPseudoElement(pseudoElement);
107}
108
109void InspectorLayerTreeAgent::layersForNode(ErrorString* errorString, int nodeId, RefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::LayerTree::Layer>>& layers)
110{
111    layers = Inspector::TypeBuilder::Array<Inspector::TypeBuilder::LayerTree::Layer>::create();
112
113    Node* node = m_instrumentingAgents->inspectorDOMAgent()->nodeForId(nodeId);
114    if (!node) {
115        *errorString = "Provided node id doesn't match any known node";
116        return;
117    }
118
119    RenderObject* renderer = node->renderer();
120    if (!renderer) {
121        *errorString = "Node for provided node id doesn't have a renderer";
122        return;
123    }
124
125    gatherLayersUsingRenderObjectHierarchy(errorString, renderer, layers);
126}
127
128void InspectorLayerTreeAgent::gatherLayersUsingRenderObjectHierarchy(ErrorString* errorString, RenderObject* renderer, RefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::LayerTree::Layer>>& layers)
129{
130    if (renderer->hasLayer()) {
131        gatherLayersUsingRenderLayerHierarchy(errorString, toRenderLayerModelObject(renderer)->layer(), layers);
132        return;
133    }
134
135    for (renderer = renderer->firstChildSlow(); renderer; renderer = renderer->nextSibling())
136        gatherLayersUsingRenderObjectHierarchy(errorString, renderer, layers);
137}
138
139void InspectorLayerTreeAgent::gatherLayersUsingRenderLayerHierarchy(ErrorString* errorString, RenderLayer* renderLayer, RefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::LayerTree::Layer>>& layers)
140{
141    if (renderLayer->isComposited())
142        layers->addItem(buildObjectForLayer(errorString, renderLayer));
143
144    for (renderLayer = renderLayer->firstChild(); renderLayer; renderLayer = renderLayer->nextSibling())
145        gatherLayersUsingRenderLayerHierarchy(errorString, renderLayer, layers);
146}
147
148PassRefPtr<Inspector::TypeBuilder::LayerTree::Layer> InspectorLayerTreeAgent::buildObjectForLayer(ErrorString* errorString, RenderLayer* renderLayer)
149{
150    RenderObject* renderer = &renderLayer->renderer();
151    RenderLayerBacking* backing = renderLayer->backing();
152    Node* node = renderer->node();
153
154    bool isReflection = renderLayer->isReflection();
155    bool isGenerated = (isReflection ? renderer->parent() : renderer)->isBeforeOrAfterContent();
156    bool isAnonymous = renderer->isAnonymous();
157
158    if (renderer->isRenderView())
159        node = &renderer->document();
160    else if (isReflection && isGenerated)
161        node = renderer->parent()->generatingElement();
162    else if (isGenerated)
163        node = renderer->generatingNode();
164    else if (isReflection || isAnonymous)
165        node = renderer->parent()->element();
166
167    // Basic set of properties.
168    RefPtr<Inspector::TypeBuilder::LayerTree::Layer> layerObject = Inspector::TypeBuilder::LayerTree::Layer::create()
169        .setLayerId(bind(renderLayer))
170        .setNodeId(idForNode(errorString, node))
171        .setBounds(buildObjectForIntRect(renderer->absoluteBoundingBoxRect()))
172        .setMemory(backing->backingStoreMemoryEstimate())
173        .setCompositedBounds(buildObjectForIntRect(enclosingIntRect(backing->compositedBounds())))
174        .setPaintCount(backing->graphicsLayer()->repaintCount());
175
176    if (node && node->shadowHost())
177        layerObject->setIsInShadowTree(true);
178
179    if (isReflection)
180        layerObject->setIsReflection(true);
181
182    if (isGenerated) {
183        if (isReflection)
184            renderer = renderer->parent();
185        layerObject->setIsGeneratedContent(true);
186        layerObject->setPseudoElementId(bindPseudoElement(toPseudoElement(renderer->node())));
187        if (renderer->isBeforeContent())
188            layerObject->setPseudoElement("before");
189        else if (renderer->isAfterContent())
190            layerObject->setPseudoElement("after");
191    }
192
193    // FIXME: RenderView is now really anonymous but don't tell about it to the frontend before making sure it can handle it.
194    if (isAnonymous && !renderer->isRenderView()) {
195        layerObject->setIsAnonymous(true);
196        const RenderStyle& style = renderer->style();
197        if (style.styleType() == FIRST_LETTER)
198            layerObject->setPseudoElement("first-letter");
199        else if (style.styleType() == FIRST_LINE)
200            layerObject->setPseudoElement("first-line");
201    }
202
203    return layerObject;
204}
205
206int InspectorLayerTreeAgent::idForNode(ErrorString* errorString, Node* node)
207{
208    if (!node)
209        return 0;
210
211    InspectorDOMAgent* domAgent = m_instrumentingAgents->inspectorDOMAgent();
212
213    int nodeId = domAgent->boundNodeId(node);
214    if (!nodeId)
215        nodeId = domAgent->pushNodeToFrontend(errorString, domAgent->boundNodeId(&node->document()), node);
216
217    return nodeId;
218}
219
220PassRefPtr<Inspector::TypeBuilder::LayerTree::IntRect> InspectorLayerTreeAgent::buildObjectForIntRect(const IntRect& rect)
221{
222    return Inspector::TypeBuilder::LayerTree::IntRect::create()
223        .setX(rect.x())
224        .setY(rect.y())
225        .setWidth(rect.width())
226        .setHeight(rect.height()).release();
227}
228
229void InspectorLayerTreeAgent::reasonsForCompositingLayer(ErrorString* errorString, const String& layerId, RefPtr<Inspector::TypeBuilder::LayerTree::CompositingReasons>& compositingReasons)
230{
231    const RenderLayer* renderLayer = m_idToLayer.get(layerId);
232
233    if (!renderLayer) {
234        *errorString = "Could not find a bound layer for the provided id";
235        return;
236    }
237
238    CompositingReasons reasonsBitmask = renderLayer->compositor().reasonsForCompositing(*renderLayer);
239    compositingReasons = Inspector::TypeBuilder::LayerTree::CompositingReasons::create();
240
241    if (reasonsBitmask & CompositingReason3DTransform)
242        compositingReasons->setTransform3D(true);
243
244    if (reasonsBitmask & CompositingReasonVideo)
245        compositingReasons->setVideo(true);
246    else if (reasonsBitmask & CompositingReasonCanvas)
247        compositingReasons->setCanvas(true);
248    else if (reasonsBitmask & CompositingReasonPlugin)
249        compositingReasons->setPlugin(true);
250    else if (reasonsBitmask & CompositingReasonIFrame)
251        compositingReasons->setIFrame(true);
252
253    if (reasonsBitmask & CompositingReasonBackfaceVisibilityHidden)
254        compositingReasons->setBackfaceVisibilityHidden(true);
255
256    if (reasonsBitmask & CompositingReasonClipsCompositingDescendants)
257        compositingReasons->setClipsCompositingDescendants(true);
258
259    if (reasonsBitmask & CompositingReasonAnimation)
260        compositingReasons->setAnimation(true);
261
262    if (reasonsBitmask & CompositingReasonFilters)
263        compositingReasons->setFilters(true);
264
265    if (reasonsBitmask & CompositingReasonPositionFixed)
266        compositingReasons->setPositionFixed(true);
267
268    if (reasonsBitmask & CompositingReasonPositionSticky)
269        compositingReasons->setPositionSticky(true);
270
271    if (reasonsBitmask & CompositingReasonOverflowScrollingTouch)
272        compositingReasons->setOverflowScrollingTouch(true);
273
274    if (reasonsBitmask & CompositingReasonStacking)
275        compositingReasons->setStacking(true);
276
277    if (reasonsBitmask & CompositingReasonOverlap)
278        compositingReasons->setOverlap(true);
279
280    if (reasonsBitmask & CompositingReasonNegativeZIndexChildren)
281        compositingReasons->setNegativeZIndexChildren(true);
282
283    if (reasonsBitmask & CompositingReasonTransformWithCompositedDescendants)
284        compositingReasons->setTransformWithCompositedDescendants(true);
285
286    if (reasonsBitmask & CompositingReasonOpacityWithCompositedDescendants)
287        compositingReasons->setOpacityWithCompositedDescendants(true);
288
289    if (reasonsBitmask & CompositingReasonMaskWithCompositedDescendants)
290        compositingReasons->setMaskWithCompositedDescendants(true);
291
292    if (reasonsBitmask & CompositingReasonReflectionWithCompositedDescendants)
293        compositingReasons->setReflectionWithCompositedDescendants(true);
294
295    if (reasonsBitmask & CompositingReasonFilterWithCompositedDescendants)
296        compositingReasons->setFilterWithCompositedDescendants(true);
297
298    if (reasonsBitmask & CompositingReasonBlendingWithCompositedDescendants)
299        compositingReasons->setBlendingWithCompositedDescendants(true);
300
301    if (reasonsBitmask & CompositingReasonIsolatesCompositedBlendingDescendants)
302        compositingReasons->setIsolatesCompositedBlendingDescendants(true);
303
304    if (reasonsBitmask & CompositingReasonPerspective)
305        compositingReasons->setPerspective(true);
306
307    if (reasonsBitmask & CompositingReasonPreserve3D)
308        compositingReasons->setPreserve3D(true);
309
310    if (reasonsBitmask & CompositingReasonRoot)
311        compositingReasons->setRoot(true);
312}
313
314String InspectorLayerTreeAgent::bind(const RenderLayer* layer)
315{
316    if (!layer)
317        return emptyString();
318    String identifier = m_documentLayerToIdMap.get(layer);
319    if (identifier.isNull()) {
320        identifier = IdentifiersFactory::createIdentifier();
321        m_documentLayerToIdMap.set(layer, identifier);
322        m_idToLayer.set(identifier, layer);
323    }
324    return identifier;
325}
326
327void InspectorLayerTreeAgent::unbind(const RenderLayer* layer)
328{
329    HashMap<const RenderLayer*, String>::iterator iterator = m_documentLayerToIdMap.find(layer);
330    if (iterator == m_documentLayerToIdMap.end())
331        return;
332    m_idToLayer.remove(iterator->value);
333    m_documentLayerToIdMap.remove(iterator);
334}
335
336String InspectorLayerTreeAgent::bindPseudoElement(PseudoElement* pseudoElement)
337{
338    if (!pseudoElement)
339        return emptyString();
340    String identifier = m_pseudoElementToIdMap.get(pseudoElement);
341    if (identifier.isNull()) {
342        identifier = IdentifiersFactory::createIdentifier();
343        m_pseudoElementToIdMap.set(pseudoElement, identifier);
344        m_idToPseudoElement.set(identifier, pseudoElement);
345    }
346    return identifier;
347}
348
349void InspectorLayerTreeAgent::unbindPseudoElement(PseudoElement* pseudoElement)
350{
351    HashMap<PseudoElement*, String>::iterator iterator = m_pseudoElementToIdMap.find(pseudoElement);
352    if (iterator == m_pseudoElementToIdMap.end())
353        return;
354    m_idToPseudoElement.remove(iterator->value);
355    m_pseudoElementToIdMap.remove(iterator);
356}
357
358} // namespace WebCore
359
360#endif // ENABLE(INSPECTOR)
361