| // Copyright 2019 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 "third_party/blink/renderer/core/inspector/inspect_tools.h" |
| |
| #include "third_party/blink/public/common/input/web_gesture_event.h" |
| #include "third_party/blink/public/common/input/web_input_event.h" |
| #include "third_party/blink/public/common/input/web_keyboard_event.h" |
| #include "third_party/blink/public/common/input/web_pointer_event.h" |
| #include "third_party/blink/public/platform/web_input_event_result.h" |
| #include "third_party/blink/public/resources/grit/inspector_overlay_resources_map.h" |
| #include "third_party/blink/renderer/core/css/css_color_value.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/dom/element.h" |
| #include "third_party/blink/renderer/core/dom/node_computed_style.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/frame/local_frame.h" |
| #include "third_party/blink/renderer/core/frame/root_frame_viewport.h" |
| #include "third_party/blink/renderer/core/frame/visual_viewport.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_dom_agent.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/page/chrome_client.h" |
| #include "third_party/blink/renderer/core/page/page.h" |
| #include "third_party/blink/renderer/platform/cursors.h" |
| #include "third_party/blink/renderer/platform/keyboard_codes.h" |
| #include "third_party/inspector_protocol/crdtp/json.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| InspectorHighlightContrastInfo FetchContrast(Node* node) { |
| InspectorHighlightContrastInfo result; |
| auto* element = DynamicTo<Element>(node); |
| if (!element) |
| return result; |
| |
| Vector<Color> bgcolors; |
| String font_size; |
| String font_weight; |
| InspectorCSSAgent::GetBackgroundColors(element, &bgcolors, &font_size, |
| &font_weight); |
| if (bgcolors.size() == 1) { |
| result.font_size = font_size; |
| result.font_weight = font_weight; |
| result.background_color = bgcolors[0]; |
| } |
| return result; |
| } |
| |
| Node* HoveredNodeForPoint(LocalFrame* frame, |
| const IntPoint& point_in_root_frame, |
| bool ignore_pointer_events_none) { |
| HitTestRequest::HitTestRequestType hit_type = |
| HitTestRequest::kMove | HitTestRequest::kReadOnly | |
| HitTestRequest::kAllowChildFrameContent; |
| if (ignore_pointer_events_none) |
| hit_type |= HitTestRequest::kIgnorePointerEventsNone; |
| HitTestRequest request(hit_type); |
| HitTestLocation location( |
| frame->View()->ConvertFromRootFrame(point_in_root_frame)); |
| HitTestResult result(request, location); |
| frame->ContentLayoutObject()->HitTest(location, result); |
| Node* node = result.InnerPossiblyPseudoNode(); |
| while (node && node->getNodeType() == Node::kTextNode) |
| node = node->parentNode(); |
| return node; |
| } |
| |
| Node* HoveredNodeForEvent(LocalFrame* frame, |
| const WebGestureEvent& event, |
| bool ignore_pointer_events_none) { |
| return HoveredNodeForPoint( |
| frame, RoundedIntPoint(FloatPoint(event.PositionInRootFrame())), |
| ignore_pointer_events_none); |
| } |
| |
| Node* HoveredNodeForEvent(LocalFrame* frame, |
| const WebMouseEvent& event, |
| bool ignore_pointer_events_none) { |
| return HoveredNodeForPoint( |
| frame, RoundedIntPoint(FloatPoint(event.PositionInRootFrame())), |
| ignore_pointer_events_none); |
| } |
| |
| Node* HoveredNodeForEvent(LocalFrame* frame, |
| const WebPointerEvent& event, |
| bool ignore_pointer_events_none) { |
| WebPointerEvent transformed_point = event.WebPointerEventInRootFrame(); |
| return HoveredNodeForPoint( |
| frame, RoundedIntPoint(FloatPoint(transformed_point.PositionInWidget())), |
| ignore_pointer_events_none); |
| } |
| |
| } // namespace |
| |
| // SearchingForNodeTool -------------------------------------------------------- |
| |
| SearchingForNodeTool::SearchingForNodeTool(InspectorDOMAgent* dom_agent, |
| bool ua_shadow, |
| const std::vector<uint8_t>& config) |
| : dom_agent_(dom_agent), ua_shadow_(ua_shadow) { |
| auto parsed_config = protocol::Overlay::HighlightConfig::FromBinary( |
| config.data(), config.size()); |
| if (parsed_config) { |
| highlight_config_ = |
| InspectorOverlayAgent::ToHighlightConfig(parsed_config.get()); |
| } |
| } |
| |
| void SearchingForNodeTool::Trace(Visitor* visitor) const { |
| InspectTool::Trace(visitor); |
| visitor->Trace(dom_agent_); |
| visitor->Trace(hovered_node_); |
| visitor->Trace(event_target_node_); |
| } |
| |
| void SearchingForNodeTool::Draw(float scale) { |
| Node* node = hovered_node_.Get(); |
| if (!hovered_node_) |
| return; |
| bool append_element_info = (node->IsElementNode() || node->IsTextNode()) && |
| !omit_tooltip_ && highlight_config_->show_info && |
| node->GetLayoutObject() && |
| node->GetDocument().GetFrame(); |
| InspectorHighlight highlight(node, *highlight_config_, contrast_info_, |
| append_element_info, false, is_locked_ancestor_); |
| if (event_target_node_) { |
| highlight.AppendEventTargetQuads(event_target_node_.Get(), |
| *highlight_config_); |
| } |
| overlay_->EvaluateInOverlay("drawHighlight", highlight.AsProtocolValue()); |
| } |
| |
| bool SearchingForNodeTool::HandleInputEvent(LocalFrameView* frame_view, |
| const WebInputEvent& input_event, |
| bool* swallow_next_mouse_up) { |
| if (input_event.GetType() == WebInputEvent::Type::kGestureScrollBegin || |
| input_event.GetType() == WebInputEvent::Type::kGestureScrollUpdate) { |
| hovered_node_.Clear(); |
| event_target_node_.Clear(); |
| overlay_->ScheduleUpdate(); |
| return false; |
| } |
| return InspectTool::HandleInputEvent(frame_view, input_event, |
| swallow_next_mouse_up); |
| } |
| |
| bool SearchingForNodeTool::HandleMouseMove(const WebMouseEvent& event) { |
| LocalFrame* frame = overlay_->GetFrame(); |
| if (!frame || !frame->View() || !frame->ContentLayoutObject()) |
| return false; |
| Node* node = HoveredNodeForEvent( |
| frame, event, event.GetModifiers() & WebInputEvent::kShiftKey); |
| |
| // Do not highlight within user agent shadow root unless requested. |
| if (!ua_shadow_) { |
| ShadowRoot* shadow_root = InspectorDOMAgent::UserAgentShadowRoot(node); |
| if (shadow_root) |
| node = &shadow_root->host(); |
| } |
| |
| // Shadow roots don't have boxes - use host element instead. |
| if (node && node->IsShadowRoot()) |
| node = node->ParentOrShadowHostNode(); |
| |
| if (!node) |
| return true; |
| |
| // If |node| is in a display locked subtree, highlight the highest locked |
| // element instead. |
| if (Node* locked_ancestor = |
| DisplayLockUtilities::HighestLockedExclusiveAncestor(*node)) { |
| node = locked_ancestor; |
| is_locked_ancestor_ = true; |
| } else { |
| is_locked_ancestor_ = false; |
| } |
| |
| if (auto* frame_owner = DynamicTo<HTMLFrameOwnerElement>(node)) { |
| if (!IsA<LocalFrame>(frame_owner->ContentFrame())) { |
| // Do not consume event so that remote frame can handle it. |
| overlay_->hideHighlight(); |
| hovered_node_.Clear(); |
| return false; |
| } |
| } |
| |
| // Store values for the highlight. |
| hovered_node_ = node; |
| event_target_node_ = (event.GetModifiers() & WebInputEvent::kShiftKey) |
| ? HoveredNodeForEvent(frame, event, false) |
| : nullptr; |
| if (event_target_node_ == hovered_node_) |
| event_target_node_ = nullptr; |
| omit_tooltip_ = event.GetModifiers() & |
| (WebInputEvent::kControlKey | WebInputEvent::kMetaKey); |
| |
| contrast_info_ = FetchContrast(node); |
| NodeHighlightRequested(node); |
| return true; |
| } |
| |
| bool SearchingForNodeTool::HandleMouseDown(const WebMouseEvent& event, |
| bool* swallow_next_mouse_up) { |
| if (hovered_node_) { |
| *swallow_next_mouse_up = true; |
| overlay_->Inspect(hovered_node_.Get()); |
| hovered_node_.Clear(); |
| return true; |
| } |
| return false; |
| } |
| |
| bool SearchingForNodeTool::HandleGestureTapEvent(const WebGestureEvent& event) { |
| Node* node = HoveredNodeForEvent(overlay_->GetFrame(), event, false); |
| if (node) { |
| overlay_->Inspect(node); |
| return true; |
| } |
| return false; |
| } |
| |
| bool SearchingForNodeTool::HandlePointerEvent(const WebPointerEvent& event) { |
| // Trigger Inspect only when a pointer device is pressed down. |
| if (event.GetType() != WebInputEvent::Type::kPointerDown) |
| return false; |
| Node* node = HoveredNodeForEvent(overlay_->GetFrame(), event, false); |
| if (node) { |
| overlay_->Inspect(node); |
| return true; |
| } |
| return false; |
| } |
| |
| void SearchingForNodeTool::NodeHighlightRequested(Node* node) { |
| while (node && !node->IsElementNode() && !node->IsDocumentNode() && |
| !node->IsDocumentFragment()) |
| node = node->ParentOrShadowHostNode(); |
| |
| if (!node) |
| return; |
| |
| int node_id = dom_agent_->PushNodePathToFrontend(node); |
| if (node_id) |
| frontend_->nodeHighlightRequested(node_id); |
| } |
| |
| // QuadHighlightTool ----------------------------------------------------------- |
| |
| QuadHighlightTool::QuadHighlightTool(std::unique_ptr<FloatQuad> quad, |
| Color color, |
| Color outline_color) |
| : quad_(std::move(quad)), color_(color), outline_color_(outline_color) {} |
| |
| bool QuadHighlightTool::ForwardEventsToOverlay() { |
| return false; |
| } |
| |
| bool QuadHighlightTool::HideOnHideHighlight() { |
| return true; |
| } |
| |
| void QuadHighlightTool::Draw(float scale) { |
| InspectorHighlight highlight(scale); |
| highlight.AppendQuad(*quad_, color_, outline_color_); |
| overlay_->EvaluateInOverlay("drawHighlight", highlight.AsProtocolValue()); |
| } |
| |
| // NodeHighlightTool ----------------------------------------------------------- |
| |
| NodeHighlightTool::NodeHighlightTool( |
| Member<Node> node, |
| String selector_list, |
| std::unique_ptr<InspectorHighlightConfig> highlight_config) |
| : selector_list_(selector_list), |
| highlight_config_(std::move(highlight_config)) { |
| if (Node* locked_ancestor = |
| DisplayLockUtilities::HighestLockedExclusiveAncestor(*node)) { |
| is_locked_ancestor_ = true; |
| node_ = locked_ancestor; |
| } else { |
| node_ = node; |
| } |
| contrast_info_ = FetchContrast(node_); |
| } |
| |
| bool NodeHighlightTool::ForwardEventsToOverlay() { |
| return false; |
| } |
| |
| bool NodeHighlightTool::HideOnHideHighlight() { |
| return true; |
| } |
| |
| bool NodeHighlightTool::HideOnMouseMove() { |
| return true; |
| } |
| |
| void NodeHighlightTool::Draw(float scale) { |
| DrawNode(); |
| DrawMatchingSelector(); |
| } |
| |
| void NodeHighlightTool::DrawNode() { |
| bool append_element_info = (node_->IsElementNode() || node_->IsTextNode()) && |
| highlight_config_->show_info && |
| node_->GetLayoutObject() && |
| node_->GetDocument().GetFrame(); |
| overlay_->EvaluateInOverlay( |
| "drawHighlight", |
| GetNodeInspectorHighlightAsJson(append_element_info, |
| false /* append_distance_info */)); |
| } |
| |
| void NodeHighlightTool::DrawMatchingSelector() { |
| if (selector_list_.IsEmpty() || !node_) |
| return; |
| DummyExceptionStateForTesting exception_state; |
| ContainerNode* query_base = node_->ContainingShadowRoot(); |
| if (!query_base) |
| query_base = node_->ownerDocument(); |
| StaticElementList* elements = query_base->QuerySelectorAll( |
| AtomicString(selector_list_), exception_state); |
| if (exception_state.HadException()) |
| return; |
| |
| for (unsigned i = 0; i < elements->length(); ++i) { |
| Element* element = elements->item(i); |
| // Skip elements in locked subtrees. |
| if (DisplayLockUtilities::NearestLockedExclusiveAncestor(*element)) |
| continue; |
| InspectorHighlight highlight(element, *highlight_config_, contrast_info_, |
| false /* append_element_info */, |
| false /* append_distance_info */, |
| false /* is_locked_ancestor */); |
| overlay_->EvaluateInOverlay("drawHighlight", highlight.AsProtocolValue()); |
| } |
| } |
| |
| void NodeHighlightTool::Trace(Visitor* visitor) const { |
| InspectTool::Trace(visitor); |
| visitor->Trace(node_); |
| } |
| |
| std::unique_ptr<protocol::DictionaryValue> |
| NodeHighlightTool::GetNodeInspectorHighlightAsJson( |
| bool append_element_info, |
| bool append_distance_info) const { |
| InspectorHighlight highlight(node_.Get(), *highlight_config_, contrast_info_, |
| append_element_info, append_distance_info, |
| is_locked_ancestor_); |
| return highlight.AsProtocolValue(); |
| } |
| |
| // GridHighlightTool ----------------------------------------------------------- |
| |
| int GridHighlightTool::GetDataResourceId() { |
| return IDR_INSPECT_TOOL_HIGHLIGHT_GRID_JS; |
| } |
| |
| void GridHighlightTool::AddGridConfig( |
| Node* node, |
| std::unique_ptr<InspectorGridHighlightConfig> grid_highlight_config) { |
| grid_node_highlights_.emplace_back( |
| std::make_pair(node, std::move(grid_highlight_config))); |
| } |
| |
| bool GridHighlightTool::ForwardEventsToOverlay() { |
| return false; |
| } |
| |
| bool GridHighlightTool::HideOnHideHighlight() { |
| return false; |
| } |
| |
| bool GridHighlightTool::HideOnMouseMove() { |
| return false; |
| } |
| |
| void GridHighlightTool::Draw(float scale) { |
| for (auto& entry : grid_node_highlights_) { |
| std::unique_ptr<protocol::Value> highlight = |
| InspectorGridHighlight(entry.first.Get(), *(entry.second)); |
| if (!highlight) |
| continue; |
| overlay_->EvaluateInOverlay("drawGridHighlight", std::move(highlight)); |
| } |
| } |
| |
| std::unique_ptr<protocol::DictionaryValue> |
| GridHighlightTool::GetGridInspectorHighlightsAsJson() const { |
| std::unique_ptr<protocol::ListValue> highlights = |
| protocol::ListValue::create(); |
| for (auto& entry : grid_node_highlights_) { |
| std::unique_ptr<protocol::Value> highlight = |
| InspectorGridHighlight(entry.first.Get(), *(entry.second)); |
| if (!highlight) |
| continue; |
| highlights->pushValue(std::move(highlight)); |
| } |
| std::unique_ptr<protocol::DictionaryValue> result = |
| protocol::DictionaryValue::create(); |
| if (highlights->size() > 0) { |
| result->setValue("gridHighlights", std::move(highlights)); |
| } |
| return result; |
| } |
| |
| // SourceOrderTool ----------------------------------------------------------- |
| |
| SourceOrderTool::SourceOrderTool( |
| Node* node, |
| std::unique_ptr<InspectorSourceOrderConfig> source_order_config) |
| : source_order_config_(std::move(source_order_config)) { |
| if (Node* locked_ancestor = |
| DisplayLockUtilities::HighestLockedExclusiveAncestor(*node)) { |
| node_ = locked_ancestor; |
| } else { |
| node_ = node; |
| } |
| } |
| |
| void SourceOrderTool::Draw(float scale) { |
| DrawParentNode(); |
| |
| // Draw child outlines and labels. |
| int position_number = 1; |
| for (Node& child_node : NodeTraversal::ChildrenOf(*node_)) { |
| // Don't draw if it's not an element or is not the direct child of the |
| // parent node. |
| if (!child_node.IsElementNode()) |
| continue; |
| // Don't draw if it's not rendered/would be ignored by a screen reader. |
| if (child_node.GetComputedStyle()) { |
| bool display_none = |
| child_node.GetComputedStyle()->Display() == EDisplay::kNone; |
| bool visibility_hidden = |
| child_node.GetComputedStyle()->Visibility() == EVisibility::kHidden; |
| if (display_none || visibility_hidden) |
| continue; |
| } |
| DrawNode(&child_node, position_number); |
| position_number++; |
| } |
| } |
| |
| void SourceOrderTool::DrawNode(Node* node, int source_order_position) { |
| InspectorSourceOrderHighlight highlight( |
| node, source_order_config_->child_outline_color, source_order_position); |
| overlay_->EvaluateInOverlay("drawSourceOrder", highlight.AsProtocolValue()); |
| } |
| |
| void SourceOrderTool::DrawParentNode() { |
| InspectorSourceOrderHighlight highlight( |
| node_.Get(), source_order_config_->parent_outline_color, 0); |
| overlay_->EvaluateInOverlay("drawSourceOrder", highlight.AsProtocolValue()); |
| } |
| |
| bool SourceOrderTool::HideOnHideHighlight() { |
| return true; |
| } |
| |
| bool SourceOrderTool::HideOnMouseMove() { |
| return false; |
| } |
| |
| int SourceOrderTool::GetDataResourceId() { |
| return IDR_INSPECT_TOOL_SOURCE_ORDER_JS; |
| } |
| |
| std::unique_ptr<protocol::DictionaryValue> |
| SourceOrderTool::GetNodeInspectorSourceOrderHighlightAsJson() const { |
| InspectorSourceOrderHighlight highlight( |
| node_.Get(), source_order_config_->parent_outline_color, 0); |
| return highlight.AsProtocolValue(); |
| } |
| |
| void SourceOrderTool::Trace(Visitor* visitor) const { |
| InspectTool::Trace(visitor); |
| visitor->Trace(node_); |
| } |
| |
| // NearbyDistanceTool ---------------------------------------------------------- |
| |
| int NearbyDistanceTool::GetDataResourceId() { |
| return IDR_INSPECT_TOOL_DISTANCES_JS; |
| } |
| |
| bool NearbyDistanceTool::HandleMouseDown(const WebMouseEvent& event, |
| bool* swallow_next_mouse_up) { |
| return true; |
| } |
| |
| bool NearbyDistanceTool::HandleMouseMove(const WebMouseEvent& event) { |
| Node* node = HoveredNodeForEvent(overlay_->GetFrame(), event, true); |
| |
| // Do not highlight within user agent shadow root |
| ShadowRoot* shadow_root = InspectorDOMAgent::UserAgentShadowRoot(node); |
| if (shadow_root) |
| node = &shadow_root->host(); |
| |
| // Shadow roots don't have boxes - use host element instead. |
| if (node && node->IsShadowRoot()) |
| node = node->ParentOrShadowHostNode(); |
| |
| if (!node) |
| return true; |
| |
| if (auto* frame_owner = DynamicTo<HTMLFrameOwnerElement>(node)) { |
| if (!IsA<LocalFrame>(frame_owner->ContentFrame())) { |
| // Do not consume event so that remote frame can handle it. |
| overlay_->hideHighlight(); |
| hovered_node_.Clear(); |
| return false; |
| } |
| } |
| |
| // If |node| is in a display locked subtree, highlight the highest locked |
| // element instead. |
| if (Node* locked_ancestor = |
| DisplayLockUtilities::HighestLockedExclusiveAncestor(*node)) |
| node = locked_ancestor; |
| |
| // Store values for the highlight. |
| hovered_node_ = node; |
| return true; |
| } |
| |
| bool NearbyDistanceTool::HandleMouseUp(const WebMouseEvent& event) { |
| return true; |
| } |
| |
| void NearbyDistanceTool::Draw(float scale) { |
| Node* node = hovered_node_.Get(); |
| if (!node) |
| return; |
| InspectorHighlight highlight( |
| node, InspectorHighlight::DefaultConfig(), |
| InspectorHighlightContrastInfo(), false /* append_element_info */, |
| true /* append_distance_info */, false /* is_locked_ancestor */); |
| overlay_->EvaluateInOverlay("drawDistances", highlight.AsProtocolValue()); |
| } |
| |
| void NearbyDistanceTool::Trace(Visitor* visitor) const { |
| InspectTool::Trace(visitor); |
| visitor->Trace(hovered_node_); |
| } |
| |
| // ShowViewSizeTool ------------------------------------------------------------ |
| |
| void ShowViewSizeTool::Draw(float scale) { |
| overlay_->EvaluateInOverlay("drawViewSize", ""); |
| } |
| |
| int ShowViewSizeTool::GetDataResourceId() { |
| return IDR_INSPECT_TOOL_VIEWPORT_SIZE_JS; |
| } |
| |
| bool ShowViewSizeTool::ForwardEventsToOverlay() { |
| return false; |
| } |
| |
| // ScreenshotTool -------------------------------------------------------------- |
| |
| void ScreenshotTool::DoInit() { |
| auto& client = overlay_->GetFrame()->GetPage()->GetChromeClient(); |
| client.SetCursorOverridden(false); |
| client.SetCursor(CrossCursor(), overlay_->GetFrame()); |
| client.SetCursorOverridden(true); |
| } |
| |
| int ScreenshotTool::GetDataResourceId() { |
| return IDR_INSPECT_TOOL_SCREENSHOT_JS; |
| } |
| |
| void ScreenshotTool::Dispatch(const String& message) { |
| if (message.IsEmpty()) |
| return; |
| std::vector<uint8_t> cbor; |
| if (message.Is8Bit()) { |
| crdtp::json::ConvertJSONToCBOR( |
| crdtp::span<uint8_t>(message.Characters8(), message.length()), &cbor); |
| } else { |
| crdtp::json::ConvertJSONToCBOR( |
| crdtp::span<uint16_t>( |
| reinterpret_cast<const uint16_t*>(message.Characters16()), |
| message.length()), |
| &cbor); |
| } |
| std::unique_ptr<protocol::DOM::Rect> box = |
| protocol::DOM::Rect::FromBinary(cbor.data(), cbor.size()); |
| if (!box) |
| return; |
| float scale = 1.0f; |
| // Capture values in the CSS pixels. |
| IntPoint p1(box->getX(), box->getY()); |
| IntPoint p2(box->getX() + box->getWidth(), box->getY() + box->getHeight()); |
| |
| if (LocalFrame* frame = overlay_->GetFrame()) { |
| float emulation_scale = overlay_->GetFrame() |
| ->GetPage() |
| ->GetChromeClient() |
| .InputEventsScaleForEmulation(); |
| // Convert from overlay terms into the absolute. |
| p1.Scale(1 / emulation_scale, 1 / emulation_scale); |
| p2.Scale(1 / emulation_scale, 1 / emulation_scale); |
| |
| // Scroll offset in the viewport is in the device pixels, convert before |
| // calling ViewportToRootFrame. |
| float dip_to_dp = overlay_->WindowToViewportScale(); |
| p1.Scale(dip_to_dp, dip_to_dp); |
| p2.Scale(dip_to_dp, dip_to_dp); |
| |
| const VisualViewport& visual_viewport = |
| frame->GetPage()->GetVisualViewport(); |
| p1 = visual_viewport.ViewportToRootFrame(p1); |
| p2 = visual_viewport.ViewportToRootFrame(p2); |
| |
| scale = frame->GetPage()->PageScaleFactor(); |
| if (const RootFrameViewport* root_frame_viewport = |
| frame->View()->GetRootFrameViewport()) { |
| IntSize scroll_offset = FlooredIntSize( |
| root_frame_viewport->LayoutViewport().GetScrollOffset()); |
| // Accunt for the layout scroll (different from viewport scroll offset). |
| p1 += scroll_offset; |
| p2 += scroll_offset; |
| } |
| } |
| |
| // Go back to dip for the protocol. |
| float dp_to_dip = 1.f / overlay_->WindowToViewportScale(); |
| p1.Scale(dp_to_dip, dp_to_dip); |
| p2.Scale(dp_to_dip, dp_to_dip); |
| |
| // Points are in device independent pixels (dip) now. |
| IntRect rect = |
| UnionRectEvenIfEmpty(IntRect(p1, IntSize()), IntRect(p2, IntSize())); |
| frontend_->screenshotRequested(protocol::Page::Viewport::create() |
| .setX(rect.X()) |
| .setY(rect.Y()) |
| .setWidth(rect.Width()) |
| .setHeight(rect.Height()) |
| .setScale(scale) |
| .build()); |
| } |
| |
| // PausedInDebuggerTool -------------------------------------------------------- |
| |
| int PausedInDebuggerTool::GetDataResourceId() { |
| return IDR_INSPECT_TOOL_PAUSED_JS; |
| } |
| |
| void PausedInDebuggerTool::Draw(float scale) { |
| overlay_->EvaluateInOverlay("drawPausedInDebuggerMessage", message_); |
| } |
| |
| void PausedInDebuggerTool::Dispatch(const String& message) { |
| if (message == "resume") |
| v8_session_->resume(); |
| else if (message == "stepOver") |
| v8_session_->stepOver(); |
| } |
| |
| } // namespace blink |