| Index: third_party/WebKit/Source/core/inspector/InspectorDOMSnapshotAgent.cpp
 | 
| diff --git a/third_party/WebKit/Source/core/inspector/InspectorDOMSnapshotAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorDOMSnapshotAgent.cpp
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..1ecd6051f9a41233769659ebec093a37a9f212aa
 | 
| --- /dev/null
 | 
| +++ b/third_party/WebKit/Source/core/inspector/InspectorDOMSnapshotAgent.cpp
 | 
| @@ -0,0 +1,380 @@
 | 
| +// Copyright 2017 The Chromium Authors. All rights reserved.
 | 
| +// Use of this source code is governed by a BSD-style license that can be
 | 
| +// found in the LICENSE file.
 | 
| +
 | 
| +#include "core/inspector/InspectorDOMSnapshotAgent.h"
 | 
| +
 | 
| +#include "core/css/CSSComputedStyleDeclaration.h"
 | 
| +#include "core/dom/Attribute.h"
 | 
| +#include "core/dom/AttributeCollection.h"
 | 
| +#include "core/dom/DOMNodeIds.h"
 | 
| +#include "core/dom/Document.h"
 | 
| +#include "core/dom/DocumentType.h"
 | 
| +#include "core/dom/Element.h"
 | 
| +#include "core/dom/Node.h"
 | 
| +#include "core/dom/PseudoElement.h"
 | 
| +#include "core/dom/QualifiedName.h"
 | 
| +#include "core/frame/LocalFrame.h"
 | 
| +#include "core/html/HTMLFrameOwnerElement.h"
 | 
| +#include "core/html/HTMLLinkElement.h"
 | 
| +#include "core/html/HTMLTemplateElement.h"
 | 
| +#include "core/inspector/IdentifiersFactory.h"
 | 
| +#include "core/inspector/InspectedFrames.h"
 | 
| +#include "core/inspector/InspectorDOMAgent.h"
 | 
| +#include "core/layout/LayoutObject.h"
 | 
| +#include "core/layout/LayoutText.h"
 | 
| +#include "core/layout/line/InlineTextBox.h"
 | 
| +#include "platform/wtf/PtrUtil.h"
 | 
| +
 | 
| +namespace blink {
 | 
| +
 | 
| +using protocol::Maybe;
 | 
| +using protocol::Response;
 | 
| +
 | 
| +namespace {
 | 
| +
 | 
| +std::unique_ptr<protocol::DOM::Rect> BuildRectForFloatRect(
 | 
| +    const FloatRect& rect) {
 | 
| +  return protocol::DOM::Rect::create()
 | 
| +      .setX(rect.X())
 | 
| +      .setY(rect.Y())
 | 
| +      .setWidth(rect.Width())
 | 
| +      .setHeight(rect.Height())
 | 
| +      .build();
 | 
| +}
 | 
| +
 | 
| +}  // namespace
 | 
| +
 | 
| +struct InspectorDOMSnapshotAgent::VectorStringHashTraits
 | 
| +    : public WTF::GenericHashTraits<Vector<String>> {
 | 
| +  static unsigned GetHash(const Vector<String>& vec) {
 | 
| +    unsigned h = DefaultHash<size_t>::Hash::GetHash(vec.size());
 | 
| +    for (size_t i = 0; i < vec.size(); i++) {
 | 
| +      h = WTF::HashInts(h, DefaultHash<String>::Hash::GetHash(vec[i]));
 | 
| +    }
 | 
| +    return h;
 | 
| +  }
 | 
| +
 | 
| +  static bool Equal(const Vector<String>& a, const Vector<String>& b) {
 | 
| +    if (a.size() != b.size())
 | 
| +      return false;
 | 
| +    for (size_t i = 0; i < a.size(); i++) {
 | 
| +      if (a[i] != b[i])
 | 
| +        return false;
 | 
| +    }
 | 
| +    return true;
 | 
| +  }
 | 
| +
 | 
| +  static void ConstructDeletedValue(Vector<String>& vec, bool) {
 | 
| +    vec.clear();
 | 
| +    vec.push_back(String(WTF::kHashTableDeletedValue));
 | 
| +  }
 | 
| +
 | 
| +  static bool IsDeletedValue(const Vector<String>& vec) {
 | 
| +    return !vec.IsEmpty() && vec[0].IsHashTableDeletedValue();
 | 
| +  }
 | 
| +
 | 
| +  static bool IsEmptyValue(const Vector<String>& vec) { return vec.IsEmpty(); }
 | 
| +
 | 
| +  static const bool kEmptyValueIsZero = false;
 | 
| +  static const bool safe_to_compare_to_empty_or_deleted = false;
 | 
| +  static const bool kHasIsEmptyValueFunction = true;
 | 
| +};
 | 
| +
 | 
| +InspectorDOMSnapshotAgent::InspectorDOMSnapshotAgent(
 | 
| +    InspectedFrames* inspected_frames)
 | 
| +    : inspected_frames_(inspected_frames) {}
 | 
| +
 | 
| +InspectorDOMSnapshotAgent::~InspectorDOMSnapshotAgent() {}
 | 
| +
 | 
| +Response InspectorDOMSnapshotAgent::getSnapshot(
 | 
| +    std::unique_ptr<protocol::Array<String>> style_whitelist,
 | 
| +    std::unique_ptr<protocol::Array<protocol::DOMSnapshot::DOMNode>>* dom_nodes,
 | 
| +    std::unique_ptr<protocol::Array<protocol::DOMSnapshot::LayoutTreeNode>>*
 | 
| +        layout_tree_nodes,
 | 
| +    std::unique_ptr<protocol::Array<protocol::DOMSnapshot::ComputedStyle>>*
 | 
| +        computed_styles) {
 | 
| +  DCHECK(!dom_nodes_ && !layout_tree_nodes_ && !computed_styles_);
 | 
| +
 | 
| +  Document* document = inspected_frames_->Root()->GetDocument();
 | 
| +  if (!document)
 | 
| +    return Response::Error("Document is not available");
 | 
| +
 | 
| +  // Setup snapshot.
 | 
| +  dom_nodes_ = protocol::Array<protocol::DOMSnapshot::DOMNode>::create();
 | 
| +  layout_tree_nodes_ =
 | 
| +      protocol::Array<protocol::DOMSnapshot::LayoutTreeNode>::create();
 | 
| +  computed_styles_ =
 | 
| +      protocol::Array<protocol::DOMSnapshot::ComputedStyle>::create();
 | 
| +  computed_styles_map_ = WTF::MakeUnique<ComputedStylesMap>();
 | 
| +  css_property_whitelist_ = WTF::MakeUnique<CSSPropertyWhitelist>();
 | 
| +
 | 
| +  // Look up the CSSPropertyIDs for each entry in |style_whitelist|.
 | 
| +  for (size_t i = 0; i < style_whitelist->length(); i++) {
 | 
| +    CSSPropertyID property_id = cssPropertyID(style_whitelist->get(i));
 | 
| +    if (property_id == CSSPropertyInvalid)
 | 
| +      continue;
 | 
| +    css_property_whitelist_->push_back(
 | 
| +        std::make_pair(style_whitelist->get(i), property_id));
 | 
| +  }
 | 
| +
 | 
| +  // Actual traversal.
 | 
| +  VisitNode(document);
 | 
| +
 | 
| +  // Extract results from state and reset.
 | 
| +  *dom_nodes = std::move(dom_nodes_);
 | 
| +  *layout_tree_nodes = std::move(layout_tree_nodes_);
 | 
| +  *computed_styles = std::move(computed_styles_);
 | 
| +  computed_styles_map_.reset();
 | 
| +  css_property_whitelist_.reset();
 | 
| +  return Response::OK();
 | 
| +}
 | 
| +
 | 
| +int InspectorDOMSnapshotAgent::VisitNode(Node* node) {
 | 
| +  if (node->IsDocumentNode()) {
 | 
| +    // Update layout tree before traversal of document so that we inspect a
 | 
| +    // current and consistent state of all trees.
 | 
| +    node->GetDocument().UpdateStyleAndLayoutTree();
 | 
| +  }
 | 
| +
 | 
| +  String node_value;
 | 
| +  switch (node->getNodeType()) {
 | 
| +    case Node::kTextNode:
 | 
| +    case Node::kAttributeNode:
 | 
| +    case Node::kCommentNode:
 | 
| +    case Node::kCdataSectionNode:
 | 
| +      node_value = node->nodeValue();
 | 
| +      break;
 | 
| +    default:
 | 
| +      break;
 | 
| +  }
 | 
| +
 | 
| +  // Create DOMNode object and add it to the result array before traversing
 | 
| +  // children, so that parents appear before their children in the array.
 | 
| +  std::unique_ptr<protocol::DOMSnapshot::DOMNode> owned_value =
 | 
| +      protocol::DOMSnapshot::DOMNode::create()
 | 
| +          .setNodeType(static_cast<int>(node->getNodeType()))
 | 
| +          .setNodeName(node->nodeName())
 | 
| +          .setNodeValue(node_value)
 | 
| +          .setBackendNodeId(DOMNodeIds::IdForNode(node))
 | 
| +          .build();
 | 
| +  protocol::DOMSnapshot::DOMNode* value = owned_value.get();
 | 
| +  int index = dom_nodes_->length();
 | 
| +  dom_nodes_->addItem(std::move(owned_value));
 | 
| +
 | 
| +  int layoutNodeIndex = VisitLayoutTreeNode(node, index);
 | 
| +  if (layoutNodeIndex != -1)
 | 
| +    value->setLayoutNodeIndex(layoutNodeIndex);
 | 
| +
 | 
| +  if (node->IsElementNode()) {
 | 
| +    Element* element = ToElement(node);
 | 
| +    value->setAttributes(BuildArrayForElementAttributes(element));
 | 
| +
 | 
| +    if (node->IsFrameOwnerElement()) {
 | 
| +      HTMLFrameOwnerElement* frame_owner = ToHTMLFrameOwnerElement(node);
 | 
| +      if (LocalFrame* frame =
 | 
| +              frame_owner->ContentFrame() &&
 | 
| +                      frame_owner->ContentFrame()->IsLocalFrame()
 | 
| +                  ? ToLocalFrame(frame_owner->ContentFrame())
 | 
| +                  : nullptr) {
 | 
| +        value->setFrameId(IdentifiersFactory::FrameId(frame));
 | 
| +      }
 | 
| +      if (Document* doc = frame_owner->contentDocument()) {
 | 
| +        value->setContentDocumentIndex(VisitNode(doc));
 | 
| +      }
 | 
| +    }
 | 
| +
 | 
| +    if (node->parentNode() && node->parentNode()->IsDocumentNode()) {
 | 
| +      LocalFrame* frame = node->GetDocument().GetFrame();
 | 
| +      if (frame)
 | 
| +        value->setFrameId(IdentifiersFactory::FrameId(frame));
 | 
| +    }
 | 
| +
 | 
| +    if (isHTMLLinkElement(*element)) {
 | 
| +      HTMLLinkElement& link_element = toHTMLLinkElement(*element);
 | 
| +      if (link_element.IsImport() && link_element.import() &&
 | 
| +          InspectorDOMAgent::InnerParentNode(link_element.import()) ==
 | 
| +              link_element) {
 | 
| +        value->setImportedDocumentIndex(VisitNode(link_element.import()));
 | 
| +      }
 | 
| +    }
 | 
| +
 | 
| +    if (isHTMLTemplateElement(*element)) {
 | 
| +      value->setTemplateContentIndex(
 | 
| +          VisitNode(toHTMLTemplateElement(*element).content()));
 | 
| +    }
 | 
| +
 | 
| +    if (element->GetPseudoId()) {
 | 
| +      protocol::DOM::PseudoType pseudo_type;
 | 
| +      if (InspectorDOMAgent::GetPseudoElementType(element->GetPseudoId(),
 | 
| +                                                  &pseudo_type)) {
 | 
| +        value->setPseudoType(pseudo_type);
 | 
| +      }
 | 
| +    } else {
 | 
| +      value->setPseudoElementIndexes(VisitPseudoElements(element));
 | 
| +    }
 | 
| +  } else if (node->IsDocumentNode()) {
 | 
| +    Document* document = ToDocument(node);
 | 
| +    value->setDocumentURL(InspectorDOMAgent::DocumentURLString(document));
 | 
| +    value->setBaseURL(InspectorDOMAgent::DocumentBaseURLString(document));
 | 
| +  } else if (node->IsDocumentTypeNode()) {
 | 
| +    DocumentType* doc_type = ToDocumentType(node);
 | 
| +    value->setPublicId(doc_type->publicId());
 | 
| +    value->setSystemId(doc_type->systemId());
 | 
| +  }
 | 
| +
 | 
| +  if (node->IsContainerNode())
 | 
| +    value->setChildNodeIndexes(VisitContainerChildren(node));
 | 
| +
 | 
| +  return index;
 | 
| +}
 | 
| +
 | 
| +std::unique_ptr<protocol::Array<int>>
 | 
| +InspectorDOMSnapshotAgent::VisitContainerChildren(Node* container) {
 | 
| +  auto children = protocol::Array<int>::create();
 | 
| +
 | 
| +  if (!FlatTreeTraversal::HasChildren(*container))
 | 
| +    return nullptr;
 | 
| +
 | 
| +  Node* child = FlatTreeTraversal::FirstChild(*container);
 | 
| +  while (child) {
 | 
| +    children->addItem(VisitNode(child));
 | 
| +    child = FlatTreeTraversal::NextSibling(*child);
 | 
| +  }
 | 
| +
 | 
| +  return children;
 | 
| +}
 | 
| +
 | 
| +std::unique_ptr<protocol::Array<int>>
 | 
| +InspectorDOMSnapshotAgent::VisitPseudoElements(Element* parent) {
 | 
| +  if (!parent->GetPseudoElement(kPseudoIdBefore) &&
 | 
| +      !parent->GetPseudoElement(kPseudoIdAfter)) {
 | 
| +    return nullptr;
 | 
| +  }
 | 
| +
 | 
| +  auto pseudo_elements = protocol::Array<int>::create();
 | 
| +
 | 
| +  if (parent->GetPseudoElement(kPseudoIdBefore)) {
 | 
| +    pseudo_elements->addItem(
 | 
| +        VisitNode(parent->GetPseudoElement(kPseudoIdBefore)));
 | 
| +  }
 | 
| +  if (parent->GetPseudoElement(kPseudoIdAfter)) {
 | 
| +    pseudo_elements->addItem(
 | 
| +        VisitNode(parent->GetPseudoElement(kPseudoIdAfter)));
 | 
| +  }
 | 
| +
 | 
| +  return pseudo_elements;
 | 
| +}
 | 
| +
 | 
| +std::unique_ptr<protocol::Array<protocol::DOMSnapshot::NameValue>>
 | 
| +InspectorDOMSnapshotAgent::BuildArrayForElementAttributes(Element* element) {
 | 
| +  auto attributes_value =
 | 
| +      protocol::Array<protocol::DOMSnapshot::NameValue>::create();
 | 
| +  AttributeCollection attributes = element->Attributes();
 | 
| +  for (const auto& attribute : attributes) {
 | 
| +    attributes_value->addItem(protocol::DOMSnapshot::NameValue::create()
 | 
| +                                  .setName(attribute.GetName().ToString())
 | 
| +                                  .setValue(attribute.Value())
 | 
| +                                  .build());
 | 
| +  }
 | 
| +  if (attributes_value->length() == 0)
 | 
| +    return nullptr;
 | 
| +  return attributes_value;
 | 
| +}
 | 
| +
 | 
| +int InspectorDOMSnapshotAgent::VisitLayoutTreeNode(Node* node, int node_index) {
 | 
| +  LayoutObject* layout_object = node->GetLayoutObject();
 | 
| +  if (!layout_object)
 | 
| +    return -1;
 | 
| +
 | 
| +  auto layout_tree_node =
 | 
| +      protocol::DOMSnapshot::LayoutTreeNode::create()
 | 
| +          .setDomNodeIndex(node_index)
 | 
| +          .setBoundingBox(BuildRectForFloatRect(
 | 
| +              node->IsElementNode()
 | 
| +                  ? FloatRect(ToElement(node)->BoundsInViewport())
 | 
| +                  : layout_object->AbsoluteBoundingBoxRect()))
 | 
| +          .build();
 | 
| +
 | 
| +  int style_index = GetStyleIndexForNode(node);
 | 
| +  if (style_index != -1)
 | 
| +    layout_tree_node->setStyleIndex(style_index);
 | 
| +
 | 
| +  if (layout_object->IsText()) {
 | 
| +    LayoutText* layout_text = ToLayoutText(layout_object);
 | 
| +    layout_tree_node->setLayoutText(layout_text->GetText());
 | 
| +    if (layout_text->HasTextBoxes()) {
 | 
| +      std::unique_ptr<protocol::Array<protocol::CSS::InlineTextBox>>
 | 
| +          inline_text_nodes =
 | 
| +              protocol::Array<protocol::CSS::InlineTextBox>::create();
 | 
| +      for (const InlineTextBox* text_box = layout_text->FirstTextBox();
 | 
| +           text_box; text_box = text_box->NextTextBox()) {
 | 
| +        FloatRect local_coords_text_box_rect(text_box->FrameRect());
 | 
| +        FloatRect absolute_coords_text_box_rect =
 | 
| +            layout_object->LocalToAbsoluteQuad(local_coords_text_box_rect)
 | 
| +                .BoundingBox();
 | 
| +        inline_text_nodes->addItem(
 | 
| +            protocol::CSS::InlineTextBox::create()
 | 
| +                .setStartCharacterIndex(text_box->Start())
 | 
| +                .setNumCharacters(text_box->Len())
 | 
| +                .setBoundingBox(
 | 
| +                    BuildRectForFloatRect(absolute_coords_text_box_rect))
 | 
| +                .build());
 | 
| +      }
 | 
| +      layout_tree_node->setInlineTextNodes(std::move(inline_text_nodes));
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  int index = layout_tree_nodes_->length();
 | 
| +  layout_tree_nodes_->addItem(std::move(layout_tree_node));
 | 
| +  return index;
 | 
| +}
 | 
| +
 | 
| +int InspectorDOMSnapshotAgent::GetStyleIndexForNode(Node* node) {
 | 
| +  CSSComputedStyleDeclaration* computed_style_info =
 | 
| +      CSSComputedStyleDeclaration::Create(node, true);
 | 
| +
 | 
| +  Vector<String> style;
 | 
| +  bool all_properties_empty = true;
 | 
| +  for (const auto& pair : *css_property_whitelist_) {
 | 
| +    String value = computed_style_info->GetPropertyValue(pair.second);
 | 
| +    if (!value.IsEmpty())
 | 
| +      all_properties_empty = false;
 | 
| +    style.push_back(value);
 | 
| +  }
 | 
| +
 | 
| +  // -1 means an empty style.
 | 
| +  if (all_properties_empty)
 | 
| +    return -1;
 | 
| +
 | 
| +  ComputedStylesMap::iterator it = computed_styles_map_->find(style);
 | 
| +  if (it != computed_styles_map_->end())
 | 
| +    return it->value;
 | 
| +
 | 
| +  // It's a distinct style, so append to |computedStyles|.
 | 
| +  auto style_properties =
 | 
| +      protocol::Array<protocol::DOMSnapshot::NameValue>::create();
 | 
| +
 | 
| +  for (size_t i = 0; i < style.size(); i++) {
 | 
| +    if (style[i].IsEmpty())
 | 
| +      continue;
 | 
| +    style_properties->addItem(protocol::DOMSnapshot::NameValue::create()
 | 
| +                                  .setName((*css_property_whitelist_)[i].first)
 | 
| +                                  .setValue(style[i])
 | 
| +                                  .build());
 | 
| +  }
 | 
| +
 | 
| +  size_t index = computed_styles_->length();
 | 
| +  computed_styles_->addItem(protocol::DOMSnapshot::ComputedStyle::create()
 | 
| +                                .setProperties(std::move(style_properties))
 | 
| +                                .build());
 | 
| +  computed_styles_map_->insert(std::move(style), index);
 | 
| +  return index;
 | 
| +}
 | 
| +
 | 
| +DEFINE_TRACE(InspectorDOMSnapshotAgent) {
 | 
| +  visitor->Trace(inspected_frames_);
 | 
| +  InspectorBaseAgent::Trace(visitor);
 | 
| +}
 | 
| +
 | 
| +}  // namespace blink
 | 
| 
 |