Commit ca38f6b1 authored by Erik Luo's avatar Erik Luo Committed by Commit Bot

DevTools: traverse DOM and send boxes for spacing tool

One step towards building a distance tool is to send rects to the
overlay page, which will calculate what to draw.

In this CL, rects for elements and individual text lines are sent
and outlined in gray. In the future, the list of boxes will be
fewer (only interesting elements) and gray outlines will not be
shown.
Drive-by: zooming the page w/ Cmd +/- now scales the boxes.

Screenshot: https://imgur.com/a/n6a46YS

Can be enabled in DevTools on DevTools via:
`Main.sendOverProtocol(
    'Overlay.setInspectMode',
    {mode: 'showDistances', highlightConfig: {}}
)`

Bug: 946873
Change-Id: I1d9aa0fb4fdc6aab435fb4d60ae8c24221475b81
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1546504
Commit-Queue: Erik Luo <luoe@chromium.org>
Reviewed-by: default avatarDmitry Gozman <dgozman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#657579}
parent 3f6ad2d2
...@@ -16,6 +16,9 @@ function drawDistances(data) ...@@ -16,6 +16,9 @@ function drawDistances(data)
const rect = quadToRect(getVisualQuad(data)); const rect = quadToRect(getVisualQuad(data));
const context = window.context; const context = window.context;
context.save(); context.save();
context.strokeStyle = '#ccc';
for (const box of data['boxes'])
context.strokeRect(box[0], box[1], box[2], box[3]);
context.strokeStyle = '#f00'; context.strokeStyle = '#f00';
context.lineWidth = 1; context.lineWidth = 1;
context.rect(rect.x - 0.5, rect.y - 0.5, rect.w + 1, rect.h + 1); context.rect(rect.x - 0.5, rect.y - 0.5, rect.w + 1, rect.h + 1);
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "third_party/blink/renderer/core/css/css_computed_style_declaration.h" #include "third_party/blink/renderer/core/css/css_computed_style_declaration.h"
#include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h" #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
#include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/pseudo_element.h"
#include "third_party/blink/renderer/core/dom/shadow_root.h" #include "third_party/blink/renderer/core/dom/shadow_root.h"
#include "third_party/blink/renderer/core/dom/static_node_list.h" #include "third_party/blink/renderer/core/dom/static_node_list.h"
#include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame.h"
...@@ -21,6 +22,7 @@ ...@@ -21,6 +22,7 @@
#include "third_party/blink/renderer/core/html/html_frame_owner_element.h" #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
#include "third_party/blink/renderer/core/inspector/inspector_css_agent.h" #include "third_party/blink/renderer/core/inspector/inspector_css_agent.h"
#include "third_party/blink/renderer/core/inspector/inspector_dom_agent.h" #include "third_party/blink/renderer/core/inspector/inspector_dom_agent.h"
#include "third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.h"
#include "third_party/blink/renderer/core/layout/hit_test_location.h" #include "third_party/blink/renderer/core/layout/hit_test_location.h"
#include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/page/chrome_client.h" #include "third_party/blink/renderer/core/page/chrome_client.h"
...@@ -93,6 +95,40 @@ Node* HoveredNodeForEvent(LocalFrame* frame, ...@@ -93,6 +95,40 @@ Node* HoveredNodeForEvent(LocalFrame* frame,
ignore_pointer_events_none); ignore_pointer_events_none);
} }
std::unique_ptr<protocol::Array<double>> RectForLayoutRect(
const LayoutRect& rect) {
std::unique_ptr<protocol::Array<double>> result =
protocol::Array<double>::create();
result->addItem(rect.X());
result->addItem(rect.Y());
result->addItem(rect.Width());
result->addItem(rect.Height());
return result;
}
// Returns |layout_object|'s bounding box in document coordinates.
LayoutRect RectInRootFrame(const LayoutObject* layout_object) {
LocalFrameView* local_frame_view = layout_object->GetFrameView();
LayoutRect rect_in_absolute(layout_object->AbsoluteBoundingBoxFloatRect());
return local_frame_view
? local_frame_view->ConvertToRootFrame(rect_in_absolute)
: rect_in_absolute;
}
LayoutRect TextFragmentRectInRootFrame(
const LayoutObject* layout_object,
const LayoutText::TextBoxInfo& text_box) {
FloatRect local_coords_text_box_rect(text_box.local_rect);
LayoutRect absolute_coords_text_box_rect(
layout_object->LocalToAbsoluteQuad(local_coords_text_box_rect)
.BoundingBox());
LocalFrameView* local_frame_view = layout_object->GetFrameView();
return local_frame_view ? local_frame_view->ConvertToRootFrame(
absolute_coords_text_box_rect)
: absolute_coords_text_box_rect;
}
} // namespace } // namespace
// SearchingForNodeTool -------------------------------------------------------- // SearchingForNodeTool --------------------------------------------------------
...@@ -414,9 +450,17 @@ void NearbyDistanceTool::Draw(float scale) { ...@@ -414,9 +450,17 @@ void NearbyDistanceTool::Draw(float scale) {
} }
std::unique_ptr<protocol::DOM::BoxModel> model; std::unique_ptr<protocol::DOM::BoxModel> model;
InspectorHighlight::GetBoxModel(node, &model); InspectorHighlight::GetBoxModel(node, &model, false);
boxes_ = protocol::Array<protocol::Array<double>>::create();
VisitNode(&(node->GetDocument()));
LayoutRect document_rect(node->GetDocument().GetLayoutView()->DocumentRect());
LocalFrameView* local_frame_view = node->GetDocument().View();
boxes_->addItem(
RectForLayoutRect(local_frame_view->ConvertToRootFrame(document_rect)));
std::unique_ptr<protocol::DictionaryValue> object = std::unique_ptr<protocol::DictionaryValue> object =
protocol::DictionaryValue::create(); protocol::DictionaryValue::create();
object->setArray("boxes", boxes_->toValue());
object->setArray("content", model->getContent()->toValue()); object->setArray("content", model->getContent()->toValue());
object->setArray("padding", model->getPadding()->toValue()); object->setArray("padding", model->getPadding()->toValue());
object->setArray("border", model->getBorder()->toValue()); object->setArray("border", model->getBorder()->toValue());
...@@ -424,6 +468,61 @@ void NearbyDistanceTool::Draw(float scale) { ...@@ -424,6 +468,61 @@ void NearbyDistanceTool::Draw(float scale) {
overlay_->EvaluateInOverlay("drawDistances", std::move(object)); overlay_->EvaluateInOverlay("drawDistances", std::move(object));
} }
void NearbyDistanceTool::VisitNode(Node* node) {
LayoutObject* layout_object = node->GetLayoutObject();
if (layout_object)
AddLayoutBox(layout_object);
if (node->IsElementNode()) {
Element* element = ToElement(node);
if (element->GetPseudoId()) {
if (layout_object)
VisitPseudoLayoutChildren(element->GetPseudoId(), layout_object);
} else {
for (PseudoId pseudo_id :
{kPseudoIdFirstLetter, kPseudoIdBefore, kPseudoIdAfter}) {
if (Node* pseudo_node = element->GetPseudoElement(pseudo_id))
VisitNode(pseudo_node);
}
}
}
if (!node->IsContainerNode())
return;
Node* first_child = InspectorDOMSnapshotAgent::FirstChild(*node, false);
for (Node* child = first_child; child;
child = InspectorDOMSnapshotAgent::NextSibling(*child, false))
VisitNode(child);
}
void NearbyDistanceTool::VisitPseudoLayoutChildren(
PseudoId pseudo_id,
LayoutObject* layout_object) {
protocol::DOM::PseudoType pseudo_type;
if (!InspectorDOMAgent::GetPseudoElementType(pseudo_id, &pseudo_type))
return;
for (LayoutObject* child = layout_object->SlowFirstChild(); child;
child = child->NextSibling()) {
if (child->IsAnonymous())
AddLayoutBox(child);
}
}
void NearbyDistanceTool::AddLayoutBox(LayoutObject* layout_object) {
if (layout_object->IsText()) {
LayoutText* layout_text = ToLayoutText(layout_object);
for (const auto& text_box : layout_text->GetTextBoxInfo()) {
LayoutRect text_rect(
TextFragmentRectInRootFrame(layout_object, text_box));
boxes_->addItem(RectForLayoutRect(text_rect));
}
} else {
LayoutRect rect(RectInRootFrame(layout_object));
boxes_->addItem(RectForLayoutRect(rect));
}
}
void NearbyDistanceTool::Trace(blink::Visitor* visitor) { void NearbyDistanceTool::Trace(blink::Visitor* visitor) {
InspectTool::Trace(visitor); InspectTool::Trace(visitor);
visitor->Trace(hovered_node_); visitor->Trace(hovered_node_);
......
...@@ -101,9 +101,14 @@ class NearbyDistanceTool : public InspectTool { ...@@ -101,9 +101,14 @@ class NearbyDistanceTool : public InspectTool {
bool HandleMouseMove(const WebMouseEvent& event) override; bool HandleMouseMove(const WebMouseEvent& event) override;
bool HandleMouseUp(const WebMouseEvent& event) override; bool HandleMouseUp(const WebMouseEvent& event) override;
void Draw(float scale) override; void Draw(float scale) override;
void VisitNode(Node* node);
void VisitPseudoLayoutChildren(PseudoId pseudo_id,
LayoutObject* layout_object);
void AddLayoutBox(LayoutObject* layout_object);
void Trace(blink::Visitor* visitor) override; void Trace(blink::Visitor* visitor) override;
Member<Node> hovered_node_; Member<Node> hovered_node_;
std::unique_ptr<protocol::Array<protocol::Array<double>>> boxes_;
DISALLOW_COPY_AND_ASSIGN(NearbyDistanceTool); DISALLOW_COPY_AND_ASSIGN(NearbyDistanceTool);
}; };
......
...@@ -1290,7 +1290,7 @@ Response InspectorDOMAgent::getBoxModel( ...@@ -1290,7 +1290,7 @@ Response InspectorDOMAgent::getBoxModel(
if (!response.isSuccess()) if (!response.isSuccess())
return response; return response;
bool result = InspectorHighlight::GetBoxModel(node, model); bool result = InspectorHighlight::GetBoxModel(node, model, true);
if (!result) if (!result)
return Response::Error("Could not compute box model."); return Response::Error("Could not compute box model.");
return Response::OK(); return Response::OK();
......
...@@ -61,6 +61,12 @@ class CORE_EXPORT InspectorDOMSnapshotAgent final ...@@ -61,6 +61,12 @@ class CORE_EXPORT InspectorDOMSnapshotAgent final
void CharacterDataModified(CharacterData*); void CharacterDataModified(CharacterData*);
void DidInsertDOMNode(Node*); void DidInsertDOMNode(Node*);
// Helpers for traversal.
static Node* FirstChild(const Node& node,
bool include_user_agent_shadow_tree);
static Node* NextSibling(const Node& node,
bool include_user_agent_shadow_tree);
private: private:
// Unconditionally enables the agent, even if |enabled_.Get()==true|. // Unconditionally enables the agent, even if |enabled_.Get()==true|.
// For idempotence, call enable(). // For idempotence, call enable().
...@@ -82,12 +88,8 @@ class CORE_EXPORT InspectorDOMSnapshotAgent final ...@@ -82,12 +88,8 @@ class CORE_EXPORT InspectorDOMSnapshotAgent final
int VisitNode2(Node*, int parent_index); int VisitNode2(Node*, int parent_index);
// Helpers for VisitContainerChildren. // Helpers for VisitContainerChildren.
static Node* FirstChild(const Node& node,
bool include_user_agent_shadow_tree);
static bool HasChildren(const Node& node, static bool HasChildren(const Node& node,
bool include_user_agent_shadow_tree); bool include_user_agent_shadow_tree);
static Node* NextSibling(const Node& node,
bool include_user_agent_shadow_tree);
std::unique_ptr<protocol::Array<int>> VisitContainerChildren( std::unique_ptr<protocol::Array<int>> VisitContainerChildren(
Node* container, Node* container,
......
...@@ -566,7 +566,8 @@ std::unique_ptr<protocol::DictionaryValue> InspectorHighlight::AsProtocolValue() ...@@ -566,7 +566,8 @@ std::unique_ptr<protocol::DictionaryValue> InspectorHighlight::AsProtocolValue()
// static // static
bool InspectorHighlight::GetBoxModel( bool InspectorHighlight::GetBoxModel(
Node* node, Node* node,
std::unique_ptr<protocol::DOM::BoxModel>* model) { std::unique_ptr<protocol::DOM::BoxModel>* model,
bool use_absolute_zoom) {
node->GetDocument().EnsurePaintLocationDataValidForNode(node); node->GetDocument().EnsurePaintLocationDataValidForNode(node);
LayoutObject* layout_object = node->GetLayoutObject(); LayoutObject* layout_object = node->GetLayoutObject();
LocalFrameView* view = node->GetDocument().View(); LocalFrameView* view = node->GetDocument().View();
...@@ -586,10 +587,12 @@ bool InspectorHighlight::GetBoxModel( ...@@ -586,10 +587,12 @@ bool InspectorHighlight::GetBoxModel(
return false; return false;
} }
AdjustForAbsoluteZoom::AdjustFloatQuad(content, *layout_object); if (use_absolute_zoom) {
AdjustForAbsoluteZoom::AdjustFloatQuad(padding, *layout_object); AdjustForAbsoluteZoom::AdjustFloatQuad(content, *layout_object);
AdjustForAbsoluteZoom::AdjustFloatQuad(border, *layout_object); AdjustForAbsoluteZoom::AdjustFloatQuad(padding, *layout_object);
AdjustForAbsoluteZoom::AdjustFloatQuad(margin, *layout_object); AdjustForAbsoluteZoom::AdjustFloatQuad(border, *layout_object);
AdjustForAbsoluteZoom::AdjustFloatQuad(margin, *layout_object);
}
float scale = 1 / view->GetPage()->GetVisualViewport().Scale(); float scale = 1 / view->GetPage()->GetVisualViewport().Scale();
content.Scale(scale, scale); content.Scale(scale, scale);
......
...@@ -58,7 +58,9 @@ class CORE_EXPORT InspectorHighlight { ...@@ -58,7 +58,9 @@ class CORE_EXPORT InspectorHighlight {
explicit InspectorHighlight(float scale); explicit InspectorHighlight(float scale);
~InspectorHighlight(); ~InspectorHighlight();
static bool GetBoxModel(Node*, std::unique_ptr<protocol::DOM::BoxModel>*); static bool GetBoxModel(Node*,
std::unique_ptr<protocol::DOM::BoxModel>*,
bool use_absolute_zoom);
static bool GetContentQuads( static bool GetContentQuads(
Node*, Node*,
std::unique_ptr<protocol::Array<protocol::Array<double>>>*); std::unique_ptr<protocol::Array<protocol::Array<double>>>*);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment