Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1927)

Side by Side Diff: third_party/WebKit/Source/core/inspector/InspectorDOMSnapshotAgent.cpp

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

Powered by Google App Engine
This is Rietveld 408576698