1/*
2 * Copyright (C) 2012-2017 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#include "InspectorLayerTreeAgent.h"
33
34#include "InspectorDOMAgent.h"
35#include "InstrumentingAgents.h"
36#include "IntRect.h"
37#include "PseudoElement.h"
38#include "RenderChildIterator.h"
39#include "RenderLayer.h"
40#include "RenderLayerBacking.h"
41#include "RenderLayerCompositor.h"
42#include "RenderView.h"
43#include <JavaScriptCore/IdentifiersFactory.h>
44
45namespace WebCore {
46
47using namespace Inspector;
48
49InspectorLayerTreeAgent::InspectorLayerTreeAgent(WebAgentContext& context)
50 : InspectorAgentBase("LayerTree"_s, context)
51 , m_frontendDispatcher(std::make_unique<Inspector::LayerTreeFrontendDispatcher>(context.frontendRouter))
52 , m_backendDispatcher(Inspector::LayerTreeBackendDispatcher::create(context.backendDispatcher, this))
53{
54}
55
56InspectorLayerTreeAgent::~InspectorLayerTreeAgent()
57{
58 reset();
59}
60
61void InspectorLayerTreeAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
62{
63}
64
65void InspectorLayerTreeAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
66{
67 ErrorString unused;
68 disable(unused);
69}
70
71void InspectorLayerTreeAgent::reset()
72{
73 m_documentLayerToIdMap.clear();
74 m_idToLayer.clear();
75 m_pseudoElementToIdMap.clear();
76 m_idToPseudoElement.clear();
77 m_suppressLayerChangeEvents = false;
78}
79
80void InspectorLayerTreeAgent::enable(ErrorString&)
81{
82 m_instrumentingAgents.setInspectorLayerTreeAgent(this);
83}
84
85void InspectorLayerTreeAgent::disable(ErrorString&)
86{
87 m_instrumentingAgents.setInspectorLayerTreeAgent(nullptr);
88}
89
90void InspectorLayerTreeAgent::layerTreeDidChange()
91{
92 if (m_suppressLayerChangeEvents)
93 return;
94
95 m_suppressLayerChangeEvents = true;
96
97 m_frontendDispatcher->layerTreeDidChange();
98}
99
100void InspectorLayerTreeAgent::renderLayerDestroyed(const RenderLayer& renderLayer)
101{
102 unbind(&renderLayer);
103}
104
105void InspectorLayerTreeAgent::pseudoElementDestroyed(PseudoElement& pseudoElement)
106{
107 unbindPseudoElement(&pseudoElement);
108}
109
110void InspectorLayerTreeAgent::layersForNode(ErrorString& errorString, int nodeId, RefPtr<JSON::ArrayOf<Inspector::Protocol::LayerTree::Layer>>& layers)
111{
112 layers = JSON::ArrayOf<Inspector::Protocol::LayerTree::Layer>::create();
113
114 auto* node = m_instrumentingAgents.inspectorDOMAgent()->nodeForId(nodeId);
115 if (!node) {
116 errorString = "Provided node id doesn't match any known node"_s;
117 return;
118 }
119
120 auto* renderer = node->renderer();
121 if (!renderer) {
122 errorString = "Node for provided node id doesn't have a renderer"_s;
123 return;
124 }
125
126 if (!is<RenderElement>(*renderer))
127 return;
128
129 gatherLayersUsingRenderObjectHierarchy(errorString, downcast<RenderElement>(*renderer), layers);
130
131 m_suppressLayerChangeEvents = false;
132}
133
134void InspectorLayerTreeAgent::gatherLayersUsingRenderObjectHierarchy(ErrorString& errorString, RenderElement& renderer, RefPtr<JSON::ArrayOf<Inspector::Protocol::LayerTree::Layer>>& layers)
135{
136 if (renderer.hasLayer()) {
137 gatherLayersUsingRenderLayerHierarchy(errorString, downcast<RenderLayerModelObject>(renderer).layer(), layers);
138 return;
139 }
140
141 for (auto& child : childrenOfType<RenderElement>(renderer))
142 gatherLayersUsingRenderObjectHierarchy(errorString, child, layers);
143}
144
145void InspectorLayerTreeAgent::gatherLayersUsingRenderLayerHierarchy(ErrorString& errorString, RenderLayer* renderLayer, RefPtr<JSON::ArrayOf<Inspector::Protocol::LayerTree::Layer>>& layers)
146{
147 if (renderLayer->isComposited())
148 layers->addItem(buildObjectForLayer(errorString, renderLayer));
149
150 for (renderLayer = renderLayer->firstChild(); renderLayer; renderLayer = renderLayer->nextSibling())
151 gatherLayersUsingRenderLayerHierarchy(errorString, renderLayer, layers);
152}
153
154Ref<Inspector::Protocol::LayerTree::Layer> InspectorLayerTreeAgent::buildObjectForLayer(ErrorString& errorString, RenderLayer* renderLayer)
155{
156 RenderObject* renderer = &renderLayer->renderer();
157 RenderLayerBacking* backing = renderLayer->backing();
158 Node* node = renderer->node();
159
160 bool isReflection = renderLayer->isReflection();
161 bool isGenerated = (isReflection ? renderer->parent() : renderer)->isBeforeOrAfterContent();
162 bool isAnonymous = renderer->isAnonymous();
163
164 if (renderer->isRenderView())
165 node = &renderer->document();
166 else if (isReflection && isGenerated)
167 node = renderer->parent()->generatingElement();
168 else if (isGenerated)
169 node = renderer->generatingNode();
170 else if (isReflection || isAnonymous)
171 node = renderer->parent()->element();
172
173 // Basic set of properties.
174 auto layerObject = Inspector::Protocol::LayerTree::Layer::create()
175 .setLayerId(bind(renderLayer))
176 .setNodeId(idForNode(errorString, node))
177 .setBounds(buildObjectForIntRect(renderer->absoluteBoundingBoxRect()))
178 .setMemory(backing->backingStoreMemoryEstimate())
179 .setCompositedBounds(buildObjectForIntRect(enclosingIntRect(backing->compositedBounds())))
180 .setPaintCount(backing->graphicsLayer()->repaintCount())
181 .release();
182
183 if (node && node->shadowHost())
184 layerObject->setIsInShadowTree(true);
185
186 if (isReflection)
187 layerObject->setIsReflection(true);
188
189 if (isGenerated) {
190 if (isReflection)
191 renderer = renderer->parent();
192 layerObject->setIsGeneratedContent(true);
193 layerObject->setPseudoElementId(bindPseudoElement(downcast<PseudoElement>(renderer->node())));
194 if (renderer->isBeforeContent())
195 layerObject->setPseudoElement("before");
196 else if (renderer->isAfterContent())
197 layerObject->setPseudoElement("after");
198 }
199
200 // FIXME: RenderView is now really anonymous but don't tell about it to the frontend before making sure it can handle it.
201 if (isAnonymous && !renderer->isRenderView()) {
202 layerObject->setIsAnonymous(true);
203 const RenderStyle& style = renderer->style();
204 if (style.styleType() == PseudoId::FirstLetter)
205 layerObject->setPseudoElement("first-letter");
206 else if (style.styleType() == PseudoId::FirstLine)
207 layerObject->setPseudoElement("first-line");
208 }
209
210 return layerObject;
211}
212
213int InspectorLayerTreeAgent::idForNode(ErrorString& errorString, Node* node)
214{
215 if (!node)
216 return 0;
217
218 InspectorDOMAgent* domAgent = m_instrumentingAgents.inspectorDOMAgent();
219
220 int nodeId = domAgent->boundNodeId(node);
221 if (!nodeId)
222 nodeId = domAgent->pushNodeToFrontend(errorString, domAgent->boundNodeId(&node->document()), node);
223
224 return nodeId;
225}
226
227Ref<Inspector::Protocol::LayerTree::IntRect> InspectorLayerTreeAgent::buildObjectForIntRect(const IntRect& rect)
228{
229 return Inspector::Protocol::LayerTree::IntRect::create()
230 .setX(rect.x())
231 .setY(rect.y())
232 .setWidth(rect.width())
233 .setHeight(rect.height())
234 .release();
235}
236
237void InspectorLayerTreeAgent::reasonsForCompositingLayer(ErrorString& errorString, const String& layerId, RefPtr<Inspector::Protocol::LayerTree::CompositingReasons>& compositingReasonsResult)
238{
239 const RenderLayer* renderLayer = m_idToLayer.get(layerId);
240
241 if (!renderLayer) {
242 errorString = "Could not find a bound layer for the provided id"_s;
243 return;
244 }
245
246 OptionSet<CompositingReason> reasons = renderLayer->compositor().reasonsForCompositing(*renderLayer);
247 auto compositingReasons = Inspector::Protocol::LayerTree::CompositingReasons::create().release();
248
249 if (reasons.contains(CompositingReason::Transform3D))
250 compositingReasons->setTransform3D(true);
251
252 if (reasons.contains(CompositingReason::Video))
253 compositingReasons->setVideo(true);
254 else if (reasons.contains(CompositingReason::Canvas))
255 compositingReasons->setCanvas(true);
256 else if (reasons.contains(CompositingReason::Plugin))
257 compositingReasons->setPlugin(true);
258 else if (reasons.contains(CompositingReason::IFrame))
259 compositingReasons->setIFrame(true);
260
261 if (reasons.contains(CompositingReason::BackfaceVisibilityHidden))
262 compositingReasons->setBackfaceVisibilityHidden(true);
263
264 if (reasons.contains(CompositingReason::ClipsCompositingDescendants))
265 compositingReasons->setClipsCompositingDescendants(true);
266
267 if (reasons.contains(CompositingReason::Animation))
268 compositingReasons->setAnimation(true);
269
270 if (reasons.contains(CompositingReason::Filters))
271 compositingReasons->setFilters(true);
272
273 if (reasons.contains(CompositingReason::PositionFixed))
274 compositingReasons->setPositionFixed(true);
275
276 if (reasons.contains(CompositingReason::PositionSticky))
277 compositingReasons->setPositionSticky(true);
278
279 if (reasons.contains(CompositingReason::OverflowScrolling))
280 compositingReasons->setOverflowScrollingTouch(true);
281
282 // FIXME: handle OverflowScrollPositioning (webkit.org/b/195985).
283
284 if (reasons.contains(CompositingReason::Stacking))
285 compositingReasons->setStacking(true);
286
287 if (reasons.contains(CompositingReason::Overlap))
288 compositingReasons->setOverlap(true);
289
290 if (reasons.contains(CompositingReason::NegativeZIndexChildren))
291 compositingReasons->setNegativeZIndexChildren(true);
292
293 if (reasons.contains(CompositingReason::TransformWithCompositedDescendants))
294 compositingReasons->setTransformWithCompositedDescendants(true);
295
296 if (reasons.contains(CompositingReason::OpacityWithCompositedDescendants))
297 compositingReasons->setOpacityWithCompositedDescendants(true);
298
299 if (reasons.contains(CompositingReason::MaskWithCompositedDescendants))
300 compositingReasons->setMaskWithCompositedDescendants(true);
301
302 if (reasons.contains(CompositingReason::ReflectionWithCompositedDescendants))
303 compositingReasons->setReflectionWithCompositedDescendants(true);
304
305 if (reasons.contains(CompositingReason::FilterWithCompositedDescendants))
306 compositingReasons->setFilterWithCompositedDescendants(true);
307
308 if (reasons.contains(CompositingReason::BlendingWithCompositedDescendants))
309 compositingReasons->setBlendingWithCompositedDescendants(true);
310
311 if (reasons.contains(CompositingReason::IsolatesCompositedBlendingDescendants))
312 compositingReasons->setIsolatesCompositedBlendingDescendants(true);
313
314 if (reasons.contains(CompositingReason::Perspective))
315 compositingReasons->setPerspective(true);
316
317 if (reasons.contains(CompositingReason::Preserve3D))
318 compositingReasons->setPreserve3D(true);
319
320 if (reasons.contains(CompositingReason::WillChange))
321 compositingReasons->setWillChange(true);
322
323 if (reasons.contains(CompositingReason::Root))
324 compositingReasons->setRoot(true);
325
326 compositingReasonsResult = WTFMove(compositingReasons);
327}
328
329String InspectorLayerTreeAgent::bind(const RenderLayer* layer)
330{
331 if (!layer)
332 return emptyString();
333 return m_documentLayerToIdMap.ensure(layer, [this, layer] {
334 auto identifier = IdentifiersFactory::createIdentifier();
335 m_idToLayer.set(identifier, layer);
336 return identifier;
337 }).iterator->value;
338}
339
340void InspectorLayerTreeAgent::unbind(const RenderLayer* layer)
341{
342 auto identifier = m_documentLayerToIdMap.take(layer);
343 if (identifier.isNull())
344 return;
345 m_idToLayer.remove(identifier);
346}
347
348String InspectorLayerTreeAgent::bindPseudoElement(PseudoElement* pseudoElement)
349{
350 if (!pseudoElement)
351 return emptyString();
352 return m_pseudoElementToIdMap.ensure(pseudoElement, [this, pseudoElement] {
353 auto identifier = IdentifiersFactory::createIdentifier();
354 m_idToPseudoElement.set(identifier, pseudoElement);
355 return identifier;
356 }).iterator->value;
357}
358
359void InspectorLayerTreeAgent::unbindPseudoElement(PseudoElement* pseudoElement)
360{
361 auto identifier = m_pseudoElementToIdMap.take(pseudoElement);
362 if (identifier.isNull())
363 return;
364 m_idToPseudoElement.remove(identifier);
365}
366
367} // namespace WebCore
368