Chromium Code Reviews| 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 |