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

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

Powered by Google App Engine
This is Rietveld 408576698