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 | |
45 | namespace WebCore { |
46 | |
47 | using namespace Inspector; |
48 | |
49 | InspectorLayerTreeAgent::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 | |
56 | InspectorLayerTreeAgent::~InspectorLayerTreeAgent() |
57 | { |
58 | reset(); |
59 | } |
60 | |
61 | void InspectorLayerTreeAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*) |
62 | { |
63 | } |
64 | |
65 | void InspectorLayerTreeAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason) |
66 | { |
67 | ErrorString unused; |
68 | disable(unused); |
69 | } |
70 | |
71 | void 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 | |
80 | void InspectorLayerTreeAgent::enable(ErrorString&) |
81 | { |
82 | m_instrumentingAgents.setInspectorLayerTreeAgent(this); |
83 | } |
84 | |
85 | void InspectorLayerTreeAgent::disable(ErrorString&) |
86 | { |
87 | m_instrumentingAgents.setInspectorLayerTreeAgent(nullptr); |
88 | } |
89 | |
90 | void InspectorLayerTreeAgent::layerTreeDidChange() |
91 | { |
92 | if (m_suppressLayerChangeEvents) |
93 | return; |
94 | |
95 | m_suppressLayerChangeEvents = true; |
96 | |
97 | m_frontendDispatcher->layerTreeDidChange(); |
98 | } |
99 | |
100 | void InspectorLayerTreeAgent::renderLayerDestroyed(const RenderLayer& renderLayer) |
101 | { |
102 | unbind(&renderLayer); |
103 | } |
104 | |
105 | void InspectorLayerTreeAgent::pseudoElementDestroyed(PseudoElement& pseudoElement) |
106 | { |
107 | unbindPseudoElement(&pseudoElement); |
108 | } |
109 | |
110 | void 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 | |
134 | void 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 | |
145 | void 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 | |
154 | Ref<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 | |
213 | int 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 | |
227 | Ref<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 | |
237 | void 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 | |
329 | String 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 | |
340 | void 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 | |
348 | String 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 | |
359 | void 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 | |