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

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: rebase 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 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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698