OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "core/inspector/InspectorDOMSnapshotAgent.h" | |
6 | |
7 #include "core/css/CSSComputedStyleDeclaration.h" | |
8 #include "core/dom/Attribute.h" | |
9 #include "core/dom/AttributeCollection.h" | |
10 #include "core/dom/DOMNodeIds.h" | |
11 #include "core/dom/Document.h" | |
12 #include "core/dom/DocumentType.h" | |
13 #include "core/dom/Element.h" | |
14 #include "core/dom/Node.h" | |
15 #include "core/dom/PseudoElement.h" | |
16 #include "core/dom/QualifiedName.h" | |
17 #include "core/frame/LocalFrame.h" | |
18 #include "core/html/HTMLFrameOwnerElement.h" | |
19 #include "core/html/HTMLLinkElement.h" | |
20 #include "core/html/HTMLTemplateElement.h" | |
21 #include "core/inspector/IdentifiersFactory.h" | |
22 #include "core/inspector/InspectedFrames.h" | |
23 #include "core/inspector/InspectorDOMAgent.h" | |
24 #include "core/layout/LayoutObject.h" | |
25 #include "core/layout/LayoutText.h" | |
26 #include "core/layout/line/InlineTextBox.h" | |
27 #include "platform/wtf/PtrUtil.h" | |
28 | |
29 namespace blink { | |
30 | |
31 using protocol::Maybe; | |
32 using protocol::Response; | |
33 | |
34 namespace { | |
35 | |
36 std::unique_ptr<protocol::DOM::Rect> BuildRectForFloatRect( | |
37 const FloatRect& rect) { | |
38 return protocol::DOM::Rect::create() | |
39 .setX(rect.X()) | |
40 .setY(rect.Y()) | |
41 .setWidth(rect.Width()) | |
42 .setHeight(rect.Height()) | |
43 .build(); | |
44 } | |
45 | |
46 protocol::DOMSnapshot::AuxiliaryProperties* GetAux( | |
47 protocol::DOMSnapshot::DOMNode* node) { | |
48 if (!node->hasAuxProperties()) { | |
49 node->setAuxProperties( | |
50 protocol::DOMSnapshot::AuxiliaryProperties::create().build()); | |
51 } | |
52 return node->getAuxProperties(nullptr); | |
alex clarke (OOO till 29th)
2017/06/07 12:56:44
I wonder if the compiler is smart enough to compil
Eric Seckler
2017/06/07 13:05:17
Probably not, but even if it would, that doesn't a
| |
53 } | |
54 | |
55 } // namespace | |
56 | |
57 struct InspectorDOMSnapshotAgent::VectorStringHashTraits | |
58 : public WTF::GenericHashTraits<Vector<String>> { | |
59 static unsigned GetHash(const Vector<String>& vec) { | |
60 unsigned h = DefaultHash<size_t>::Hash::GetHash(vec.size()); | |
61 for (size_t i = 0; i < vec.size(); i++) { | |
62 h = WTF::HashInts(h, DefaultHash<String>::Hash::GetHash(vec[i])); | |
63 } | |
64 return h; | |
65 } | |
66 | |
67 static bool Equal(const Vector<String>& a, const Vector<String>& b) { | |
68 if (a.size() != b.size()) | |
69 return false; | |
70 for (size_t i = 0; i < a.size(); i++) { | |
71 if (a[i] != b[i]) | |
72 return false; | |
73 } | |
74 return true; | |
75 } | |
76 | |
77 static void ConstructDeletedValue(Vector<String>& vec, bool) { | |
78 vec.clear(); | |
79 vec.push_back(String(WTF::kHashTableDeletedValue)); | |
80 } | |
81 | |
82 static bool IsDeletedValue(const Vector<String>& vec) { | |
83 return !vec.IsEmpty() && vec[0].IsHashTableDeletedValue(); | |
84 } | |
85 | |
86 static bool IsEmptyValue(const Vector<String>& vec) { return vec.IsEmpty(); } | |
87 | |
88 static const bool kEmptyValueIsZero = false; | |
89 static const bool safe_to_compare_to_empty_or_deleted = false; | |
90 static const bool kHasIsEmptyValueFunction = true; | |
91 }; | |
92 | |
93 InspectorDOMSnapshotAgent::InspectorDOMSnapshotAgent( | |
94 InspectedFrames* inspected_frames) | |
95 : inspected_frames_(inspected_frames) {} | |
96 | |
97 InspectorDOMSnapshotAgent::~InspectorDOMSnapshotAgent() {} | |
98 | |
99 Response InspectorDOMSnapshotAgent::getSnapshot( | |
100 std::unique_ptr<protocol::Array<String>> style_whitelist, | |
101 std::unique_ptr<protocol::Array<protocol::DOMSnapshot::DOMNode>>* dom_nodes, | |
102 std::unique_ptr<protocol::Array<protocol::DOMSnapshot::LayoutTreeNode>>* | |
103 layout_tree_nodes, | |
104 std::unique_ptr<protocol::Array<protocol::DOMSnapshot::ComputedStyle>>* | |
105 computed_styles) { | |
106 DCHECK(!dom_nodes_ && !layout_tree_nodes_ && !computed_styles_); | |
107 | |
108 Document* document = inspected_frames_->Root()->GetDocument(); | |
109 if (!document) | |
110 return Response::Error("Document is not available"); | |
111 | |
112 // Setup snapshot. | |
113 dom_nodes_ = protocol::Array<protocol::DOMSnapshot::DOMNode>::create(); | |
114 layout_tree_nodes_ = | |
115 protocol::Array<protocol::DOMSnapshot::LayoutTreeNode>::create(); | |
116 computed_styles_ = | |
117 protocol::Array<protocol::DOMSnapshot::ComputedStyle>::create(); | |
118 computed_styles_map_ = WTF::MakeUnique<ComputedStylesMap>(); | |
119 css_property_whitelist_ = WTF::MakeUnique<CSSPropertyWhitelist>(); | |
120 | |
121 // Look up the CSSPropertyIDs for each entry in |style_whitelist|. | |
122 for (size_t i = 0; i < style_whitelist->length(); i++) { | |
123 CSSPropertyID property_id = cssPropertyID(style_whitelist->get(i)); | |
124 if (property_id == CSSPropertyInvalid) | |
125 continue; | |
126 css_property_whitelist_->push_back( | |
127 std::make_pair(style_whitelist->get(i), property_id)); | |
128 } | |
129 | |
130 // Actual traversal. | |
131 VisitNode(document); | |
132 | |
133 // Extract results from state and reset. | |
134 *dom_nodes = std::move(dom_nodes_); | |
135 *layout_tree_nodes = std::move(layout_tree_nodes_); | |
136 *computed_styles = std::move(computed_styles_); | |
137 computed_styles_map_.reset(); | |
138 css_property_whitelist_.reset(); | |
139 return Response::OK(); | |
140 } | |
141 | |
142 int InspectorDOMSnapshotAgent::VisitNode(Node* node) { | |
143 if (node->IsDocumentNode()) { | |
144 // Update layout tree before traversal of document so that we inspect a | |
145 // current and consistent state of all trees. | |
146 node->GetDocument().UpdateStyleAndLayoutTree(); | |
147 } | |
148 | |
149 String node_value; | |
150 switch (node->getNodeType()) { | |
151 case Node::kTextNode: | |
152 case Node::kAttributeNode: | |
153 case Node::kCommentNode: | |
154 case Node::kCdataSectionNode: | |
155 node_value = node->nodeValue(); | |
156 break; | |
157 default: | |
158 break; | |
159 } | |
160 | |
161 // Create DOMNode object and add it to the result array before traversing | |
162 // children, so that parents appear before their children in the array. | |
163 std::unique_ptr<protocol::DOMSnapshot::DOMNode> owned_value = | |
164 protocol::DOMSnapshot::DOMNode::create() | |
165 .setNodeType(static_cast<int>(node->getNodeType())) | |
166 .setNodeName(node->nodeName()) | |
167 .setNodeValue(node_value) | |
168 .setBackendNodeId(DOMNodeIds::IdForNode(node)) | |
169 .setAuxProperties( | |
170 protocol::DOMSnapshot::AuxiliaryProperties::create().build()) | |
171 .build(); | |
172 protocol::DOMSnapshot::DOMNode* value = owned_value.get(); | |
173 int index = dom_nodes_->length(); | |
174 dom_nodes_->addItem(std::move(owned_value)); | |
175 | |
176 int layoutNodeIndex = VisitLayoutTreeNode(node, index); | |
177 if (layoutNodeIndex != -1) | |
178 value->setLayoutNodeIndex(layoutNodeIndex); | |
179 | |
180 if (node->IsElementNode()) { | |
181 Element* element = ToElement(node); | |
182 value->setAttributes(BuildArrayForElementAttributes(element)); | |
183 | |
184 if (node->IsFrameOwnerElement()) { | |
185 HTMLFrameOwnerElement* frame_owner = ToHTMLFrameOwnerElement(node); | |
186 if (LocalFrame* frame = | |
187 frame_owner->ContentFrame() && | |
188 frame_owner->ContentFrame()->IsLocalFrame() | |
189 ? ToLocalFrame(frame_owner->ContentFrame()) | |
190 : nullptr) { | |
191 GetAux(value)->setFrameId(IdentifiersFactory::FrameId(frame)); | |
192 } | |
193 if (Document* doc = frame_owner->contentDocument()) { | |
194 GetAux(value)->setContentDocumentIndex(VisitNode(doc)); | |
195 } | |
196 } | |
197 | |
198 if (node->parentNode() && node->parentNode()->IsDocumentNode()) { | |
199 LocalFrame* frame = node->GetDocument().GetFrame(); | |
200 if (frame) | |
201 GetAux(value)->setFrameId(IdentifiersFactory::FrameId(frame)); | |
202 } | |
203 | |
204 if (isHTMLLinkElement(*element)) { | |
205 HTMLLinkElement& link_element = toHTMLLinkElement(*element); | |
206 if (link_element.IsImport() && link_element.import() && | |
207 InspectorDOMAgent::InnerParentNode(link_element.import()) == | |
208 link_element) { | |
209 GetAux(value)->setImportedDocumentIndex( | |
210 VisitNode(link_element.import())); | |
211 } | |
212 } | |
213 | |
214 if (isHTMLTemplateElement(*element)) { | |
215 GetAux(value)->setTemplateContentIndex( | |
216 VisitNode(toHTMLTemplateElement(*element).content())); | |
217 } | |
218 | |
219 if (element->GetPseudoId()) { | |
220 protocol::DOM::PseudoType pseudo_type; | |
221 if (InspectorDOMAgent::GetPseudoElementType(element->GetPseudoId(), | |
222 &pseudo_type)) { | |
223 GetAux(value)->setPseudoType(pseudo_type); | |
224 } | |
225 } else { | |
226 value->setPseudoElementIndexes(VisitPseudoElements(element)); | |
227 } | |
228 } else if (node->IsDocumentNode()) { | |
229 Document* document = ToDocument(node); | |
230 GetAux(value)->setDocumentURL( | |
231 InspectorDOMAgent::DocumentURLString(document)); | |
232 GetAux(value)->setBaseURL( | |
233 InspectorDOMAgent::DocumentBaseURLString(document)); | |
234 } else if (node->IsDocumentTypeNode()) { | |
235 DocumentType* doc_type = ToDocumentType(node); | |
236 GetAux(value)->setPublicId(doc_type->publicId()); | |
237 GetAux(value)->setSystemId(doc_type->systemId()); | |
238 } | |
239 | |
240 if (node->IsContainerNode()) | |
241 value->setChildNodeIndexes(VisitContainerChildren(node)); | |
242 | |
243 return index; | |
244 } | |
245 | |
246 std::unique_ptr<protocol::Array<int>> | |
247 InspectorDOMSnapshotAgent::VisitContainerChildren(Node* container) { | |
248 auto children = protocol::Array<int>::create(); | |
249 | |
250 if (!FlatTreeTraversal::HasChildren(*container)) | |
251 return nullptr; | |
252 | |
253 Node* child = FlatTreeTraversal::FirstChild(*container); | |
254 while (child) { | |
255 children->addItem(VisitNode(child)); | |
256 child = FlatTreeTraversal::NextSibling(*child); | |
257 } | |
258 | |
259 return children; | |
260 } | |
261 | |
262 std::unique_ptr<protocol::Array<int>> | |
263 InspectorDOMSnapshotAgent::VisitPseudoElements(Element* parent) { | |
264 if (!parent->GetPseudoElement(kPseudoIdBefore) && | |
265 !parent->GetPseudoElement(kPseudoIdAfter)) { | |
266 return nullptr; | |
267 } | |
268 | |
269 auto pseudo_elements = protocol::Array<int>::create(); | |
270 | |
271 if (parent->GetPseudoElement(kPseudoIdBefore)) { | |
272 pseudo_elements->addItem( | |
273 VisitNode(parent->GetPseudoElement(kPseudoIdBefore))); | |
274 } | |
275 if (parent->GetPseudoElement(kPseudoIdAfter)) { | |
276 pseudo_elements->addItem( | |
277 VisitNode(parent->GetPseudoElement(kPseudoIdAfter))); | |
278 } | |
279 | |
280 return pseudo_elements; | |
281 } | |
282 | |
283 std::unique_ptr<protocol::Array<protocol::DOMSnapshot::NameValue>> | |
284 InspectorDOMSnapshotAgent::BuildArrayForElementAttributes(Element* element) { | |
285 auto attributes_value = | |
286 protocol::Array<protocol::DOMSnapshot::NameValue>::create(); | |
287 AttributeCollection attributes = element->Attributes(); | |
288 for (const auto& attribute : attributes) { | |
289 attributes_value->addItem(protocol::DOMSnapshot::NameValue::create() | |
290 .setName(attribute.GetName().ToString()) | |
291 .setValue(attribute.Value()) | |
292 .build()); | |
293 } | |
294 return attributes_value; | |
295 } | |
296 | |
297 int InspectorDOMSnapshotAgent::VisitLayoutTreeNode(Node* node, int node_index) { | |
298 LayoutObject* layout_object = node->GetLayoutObject(); | |
299 if (!layout_object) | |
300 return -1; | |
301 | |
302 auto layout_tree_node = | |
303 protocol::DOMSnapshot::LayoutTreeNode::create() | |
304 .setDomNodeIndex(node_index) | |
305 .setBoundingBox(BuildRectForFloatRect( | |
306 node->IsElementNode() | |
307 ? FloatRect(ToElement(node)->BoundsInViewport()) | |
308 : layout_object->AbsoluteBoundingBoxRect())) | |
309 .build(); | |
310 | |
311 int style_index = GetStyleIndexForNode(node); | |
312 if (style_index != -1) | |
313 layout_tree_node->setStyleIndex(style_index); | |
314 | |
315 if (layout_object->IsText()) { | |
316 LayoutText* layout_text = ToLayoutText(layout_object); | |
317 layout_tree_node->setLayoutText(layout_text->GetText()); | |
318 if (layout_text->HasTextBoxes()) { | |
319 std::unique_ptr<protocol::Array<protocol::CSS::InlineTextBox>> | |
320 inline_text_nodes = | |
321 protocol::Array<protocol::CSS::InlineTextBox>::create(); | |
322 for (const InlineTextBox* text_box = layout_text->FirstTextBox(); | |
323 text_box; text_box = text_box->NextTextBox()) { | |
324 FloatRect local_coords_text_box_rect(text_box->FrameRect()); | |
325 FloatRect absolute_coords_text_box_rect = | |
326 layout_object->LocalToAbsoluteQuad(local_coords_text_box_rect) | |
327 .BoundingBox(); | |
328 inline_text_nodes->addItem( | |
329 protocol::CSS::InlineTextBox::create() | |
330 .setStartCharacterIndex(text_box->Start()) | |
331 .setNumCharacters(text_box->Len()) | |
332 .setBoundingBox( | |
333 BuildRectForFloatRect(absolute_coords_text_box_rect)) | |
334 .build()); | |
335 } | |
336 layout_tree_node->setInlineTextNodes(std::move(inline_text_nodes)); | |
337 } | |
338 } | |
339 | |
340 int index = layout_tree_nodes_->length(); | |
341 layout_tree_nodes_->addItem(std::move(layout_tree_node)); | |
342 return index; | |
343 } | |
344 | |
345 int InspectorDOMSnapshotAgent::GetStyleIndexForNode(Node* node) { | |
346 CSSComputedStyleDeclaration* computed_style_info = | |
347 CSSComputedStyleDeclaration::Create(node, true); | |
348 | |
349 Vector<String> style; | |
350 bool all_properties_empty = true; | |
351 for (const auto& pair : *css_property_whitelist_) { | |
352 String value = computed_style_info->GetPropertyValue(pair.second); | |
353 if (!value.IsEmpty()) | |
354 all_properties_empty = false; | |
355 style.push_back(value); | |
356 } | |
357 | |
358 // -1 means an empty style. | |
359 if (all_properties_empty) | |
360 return -1; | |
361 | |
362 ComputedStylesMap::iterator it = computed_styles_map_->find(style); | |
363 if (it != computed_styles_map_->end()) | |
364 return it->value; | |
365 | |
366 // It's a distinct style, so append to |computedStyles|. | |
367 auto style_properties = | |
368 protocol::Array<protocol::DOMSnapshot::NameValue>::create(); | |
369 | |
370 for (size_t i = 0; i < style.size(); i++) { | |
371 if (style[i].IsEmpty()) | |
372 continue; | |
373 style_properties->addItem(protocol::DOMSnapshot::NameValue::create() | |
374 .setName((*css_property_whitelist_)[i].first) | |
375 .setValue(style[i]) | |
376 .build()); | |
377 } | |
378 | |
379 size_t index = computed_styles_->length(); | |
380 computed_styles_->addItem(protocol::DOMSnapshot::ComputedStyle::create() | |
381 .setProperties(std::move(style_properties)) | |
382 .build()); | |
383 computed_styles_map_->insert(std::move(style), index); | |
384 return index; | |
385 } | |
386 | |
387 DEFINE_TRACE(InspectorDOMSnapshotAgent) { | |
388 visitor->Trace(inspected_frames_); | |
389 InspectorBaseAgent::Trace(visitor); | |
390 } | |
391 | |
392 } // namespace blink | |
OLD | NEW |