blob: 32b29a15a941dc85f4b71861cf50c1a2a36ddf3f [file] [log] [blame]
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "InspectorOverlay.h"
#include "CacheStorageProvider.h"
#include "DocumentLoader.h"
#include "EditorClient.h"
#include "Element.h"
#include "EmptyClients.h"
#include "FrameView.h"
#include "GraphicsContext.h"
#include "InspectorClient.h"
#include "InspectorOverlayPage.h"
#include "LibWebRTCProvider.h"
#include "MainFrame.h"
#include "Node.h"
#include "Page.h"
#include "PageConfiguration.h"
#include "PolygonShape.h"
#include "PseudoElement.h"
#include "RTCController.h"
#include "RectangleShape.h"
#include "RenderBoxModelObject.h"
#include "RenderElement.h"
#include "RenderInline.h"
#include "RenderView.h"
#include "ScriptController.h"
#include "ScriptSourceCode.h"
#include "Settings.h"
#include "SocketProvider.h"
#include "StyledElement.h"
#include <JavaScriptCore/InspectorProtocolObjects.h>
#include <wtf/JSONValues.h>
namespace WebCore {
using namespace Inspector;
static void contentsQuadToCoordinateSystem(const FrameView* mainView, const FrameView* view, FloatQuad& quad, InspectorOverlay::CoordinateSystem coordinateSystem)
{
quad.setP1(view->contentsToRootView(roundedIntPoint(quad.p1())));
quad.setP2(view->contentsToRootView(roundedIntPoint(quad.p2())));
quad.setP3(view->contentsToRootView(roundedIntPoint(quad.p3())));
quad.setP4(view->contentsToRootView(roundedIntPoint(quad.p4())));
if (coordinateSystem == InspectorOverlay::CoordinateSystem::View)
quad += toIntSize(mainView->scrollPosition());
}
static void contentsQuadToPage(const FrameView* mainView, const FrameView* view, FloatQuad& quad)
{
contentsQuadToCoordinateSystem(mainView, view, quad, InspectorOverlay::CoordinateSystem::View);
}
static void buildRendererHighlight(RenderObject* renderer, const HighlightConfig& highlightConfig, Highlight& highlight, InspectorOverlay::CoordinateSystem coordinateSystem)
{
Frame* containingFrame = renderer->document().frame();
if (!containingFrame)
return;
highlight.setDataFromConfig(highlightConfig);
FrameView* containingView = containingFrame->view();
FrameView* mainView = containingFrame->page()->mainFrame().view();
// RenderSVGRoot should be highlighted through the isBox() code path, all other SVG elements should just dump their absoluteQuads().
bool isSVGRenderer = renderer->node() && renderer->node()->isSVGElement() && !renderer->isSVGRoot();
if (isSVGRenderer) {
highlight.type = HighlightType::Rects;
renderer->absoluteQuads(highlight.quads);
for (auto& quad : highlight.quads)
contentsQuadToCoordinateSystem(mainView, containingView, quad, coordinateSystem);
} else if (is<RenderBox>(*renderer) || is<RenderInline>(*renderer)) {
LayoutRect contentBox;
LayoutRect paddingBox;
LayoutRect borderBox;
LayoutRect marginBox;
if (is<RenderBox>(*renderer)) {
auto& renderBox = downcast<RenderBox>(*renderer);
LayoutBoxExtent margins(renderBox.marginTop(), renderBox.marginRight(), renderBox.marginBottom(), renderBox.marginLeft());
paddingBox = renderBox.clientBoxRect();
contentBox = LayoutRect(paddingBox.x() + renderBox.paddingLeft(), paddingBox.y() + renderBox.paddingTop(),
paddingBox.width() - renderBox.paddingLeft() - renderBox.paddingRight(), paddingBox.height() - renderBox.paddingTop() - renderBox.paddingBottom());
borderBox = LayoutRect(paddingBox.x() - renderBox.borderLeft(), paddingBox.y() - renderBox.borderTop(),
paddingBox.width() + renderBox.borderLeft() + renderBox.borderRight(), paddingBox.height() + renderBox.borderTop() + renderBox.borderBottom());
marginBox = LayoutRect(borderBox.x() - margins.left(), borderBox.y() - margins.top(),
borderBox.width() + margins.left() + margins.right(), borderBox.height() + margins.top() + margins.bottom());
} else {
auto& renderInline = downcast<RenderInline>(*renderer);
// RenderInline's bounding box includes paddings and borders, excludes margins.
borderBox = renderInline.linesBoundingBox();
paddingBox = LayoutRect(borderBox.x() + renderInline.borderLeft(), borderBox.y() + renderInline.borderTop(),
borderBox.width() - renderInline.borderLeft() - renderInline.borderRight(), borderBox.height() - renderInline.borderTop() - renderInline.borderBottom());
contentBox = LayoutRect(paddingBox.x() + renderInline.paddingLeft(), paddingBox.y() + renderInline.paddingTop(),
paddingBox.width() - renderInline.paddingLeft() - renderInline.paddingRight(), paddingBox.height() - renderInline.paddingTop() - renderInline.paddingBottom());
// Ignore marginTop and marginBottom for inlines.
marginBox = LayoutRect(borderBox.x() - renderInline.marginLeft(), borderBox.y(),
borderBox.width() + renderInline.horizontalMarginExtent(), borderBox.height());
}
FloatQuad absContentQuad = renderer->localToAbsoluteQuad(FloatRect(contentBox));
FloatQuad absPaddingQuad = renderer->localToAbsoluteQuad(FloatRect(paddingBox));
FloatQuad absBorderQuad = renderer->localToAbsoluteQuad(FloatRect(borderBox));
FloatQuad absMarginQuad = renderer->localToAbsoluteQuad(FloatRect(marginBox));
contentsQuadToCoordinateSystem(mainView, containingView, absContentQuad, coordinateSystem);
contentsQuadToCoordinateSystem(mainView, containingView, absPaddingQuad, coordinateSystem);
contentsQuadToCoordinateSystem(mainView, containingView, absBorderQuad, coordinateSystem);
contentsQuadToCoordinateSystem(mainView, containingView, absMarginQuad, coordinateSystem);
highlight.type = HighlightType::Node;
highlight.quads.append(absMarginQuad);
highlight.quads.append(absBorderQuad);
highlight.quads.append(absPaddingQuad);
highlight.quads.append(absContentQuad);
}
}
static void buildNodeHighlight(Node& node, const HighlightConfig& highlightConfig, Highlight& highlight, InspectorOverlay::CoordinateSystem coordinateSystem)
{
RenderObject* renderer = node.renderer();
if (!renderer)
return;
buildRendererHighlight(renderer, highlightConfig, highlight, coordinateSystem);
}
static void buildQuadHighlight(const FloatQuad& quad, const HighlightConfig& highlightConfig, Highlight& highlight)
{
highlight.setDataFromConfig(highlightConfig);
highlight.type = HighlightType::Rects;
highlight.quads.append(quad);
}
InspectorOverlay::InspectorOverlay(Page& page, InspectorClient* client)
: m_page(page)
, m_client(client)
, m_paintRectUpdateTimer(*this, &InspectorOverlay::updatePaintRectsTimerFired)
{
}
InspectorOverlay::~InspectorOverlay() = default;
void InspectorOverlay::paint(GraphicsContext& context)
{
if (!shouldShowOverlay())
return;
GraphicsContextStateSaver stateSaver(context);
FrameView* view = overlayPage()->mainFrame().view();
view->updateLayoutAndStyleIfNeededRecursive();
view->paint(context, IntRect(0, 0, view->width(), view->height()));
}
void InspectorOverlay::getHighlight(Highlight& highlight, InspectorOverlay::CoordinateSystem coordinateSystem) const
{
if (!m_highlightNode && !m_highlightQuad && !m_highlightNodeList)
return;
highlight.type = HighlightType::Rects;
if (m_highlightNode)
buildNodeHighlight(*m_highlightNode, m_nodeHighlightConfig, highlight, coordinateSystem);
else if (m_highlightNodeList) {
highlight.setDataFromConfig(m_nodeHighlightConfig);
for (unsigned i = 0; i < m_highlightNodeList->length(); ++i) {
Highlight nodeHighlight;
buildNodeHighlight(*(m_highlightNodeList->item(i)), m_nodeHighlightConfig, nodeHighlight, coordinateSystem);
if (nodeHighlight.type == HighlightType::Node)
highlight.quads.appendVector(nodeHighlight.quads);
}
highlight.type = HighlightType::NodeList;
} else
buildQuadHighlight(*m_highlightQuad, m_quadHighlightConfig, highlight);
}
void InspectorOverlay::setPausedInDebuggerMessage(const String* message)
{
m_pausedInDebuggerMessage = message ? *message : String();
update();
}
void InspectorOverlay::hideHighlight()
{
m_highlightNode = nullptr;
m_highlightNodeList = nullptr;
m_highlightQuad = nullptr;
update();
}
void InspectorOverlay::highlightNodeList(RefPtr<NodeList>&& nodes, const HighlightConfig& highlightConfig)
{
m_nodeHighlightConfig = highlightConfig;
m_highlightNodeList = WTFMove(nodes);
m_highlightNode = nullptr;
update();
}
void InspectorOverlay::highlightNode(Node* node, const HighlightConfig& highlightConfig)
{
m_nodeHighlightConfig = highlightConfig;
m_highlightNode = node;
m_highlightNodeList = nullptr;
update();
}
void InspectorOverlay::highlightQuad(std::unique_ptr<FloatQuad> quad, const HighlightConfig& highlightConfig)
{
if (highlightConfig.usePageCoordinates)
*quad -= toIntSize(m_page.mainFrame().view()->scrollPosition());
m_quadHighlightConfig = highlightConfig;
m_highlightQuad = WTFMove(quad);
update();
}
Node* InspectorOverlay::highlightedNode() const
{
return m_highlightNode.get();
}
void InspectorOverlay::didSetSearchingForNode(bool enabled)
{
m_client->didSetSearchingForNode(enabled);
}
void InspectorOverlay::setIndicating(bool indicating)
{
m_indicating = indicating;
if (m_indicating)
evaluateInOverlay(ASCIILiteral("showPageIndication"));
else
evaluateInOverlay(ASCIILiteral("hidePageIndication"));
update();
}
bool InspectorOverlay::shouldShowOverlay() const
{
return m_highlightNode || m_highlightNodeList || m_highlightQuad || m_indicating || m_showingPaintRects || !m_pausedInDebuggerMessage.isNull();
}
void InspectorOverlay::update()
{
if (!shouldShowOverlay()) {
m_client->hideHighlight();
return;
}
FrameView* view = m_page.mainFrame().view();
if (!view)
return;
FrameView* overlayView = overlayPage()->mainFrame().view();
IntSize viewportSize = view->sizeForVisibleContent();
IntSize frameViewFullSize = view->sizeForVisibleContent(ScrollableArea::IncludeScrollbars);
overlayView->resize(frameViewFullSize);
// Clear canvas and paint things.
// FIXME: Remove extra parameter?
reset(viewportSize, IntSize());
// Include scrollbars to avoid masking them by the gutter.
drawGutter();
drawNodeHighlight();
drawQuadHighlight();
drawPausedInDebuggerMessage();
drawPaintRects();
// Position DOM elements.
overlayPage()->mainFrame().document()->resolveStyle(Document::ResolveStyleType::Rebuild);
if (overlayView->needsLayout())
overlayView->layoutContext().layout();
forcePaint();
}
static Ref<Inspector::Protocol::OverlayTypes::Point> buildObjectForPoint(const FloatPoint& point)
{
return Inspector::Protocol::OverlayTypes::Point::create()
.setX(point.x())
.setY(point.y())
.release();
}
static Ref<Inspector::Protocol::OverlayTypes::Rect> buildObjectForRect(const FloatRect& rect)
{
return Inspector::Protocol::OverlayTypes::Rect::create()
.setX(rect.x())
.setY(rect.y())
.setWidth(rect.width())
.setHeight(rect.height())
.release();
}
static Ref<Inspector::Protocol::OverlayTypes::Quad> buildArrayForQuad(const FloatQuad& quad)
{
auto array = Inspector::Protocol::OverlayTypes::Quad::create();
array->addItem(buildObjectForPoint(quad.p1()));
array->addItem(buildObjectForPoint(quad.p2()));
array->addItem(buildObjectForPoint(quad.p3()));
array->addItem(buildObjectForPoint(quad.p4()));
return array;
}
static Ref<Inspector::Protocol::OverlayTypes::FragmentHighlightData> buildObjectForHighlight(const Highlight& highlight)
{
auto arrayOfQuads = JSON::ArrayOf<Inspector::Protocol::OverlayTypes::Quad>::create();
for (auto& quad : highlight.quads)
arrayOfQuads->addItem(buildArrayForQuad(quad));
return Inspector::Protocol::OverlayTypes::FragmentHighlightData::create()
.setQuads(WTFMove(arrayOfQuads))
.setContentColor(highlight.contentColor.serialized())
.setContentOutlineColor(highlight.contentOutlineColor.serialized())
.setPaddingColor(highlight.paddingColor.serialized())
.setBorderColor(highlight.borderColor.serialized())
.setMarginColor(highlight.marginColor.serialized())
.release();
}
static Ref<Inspector::Protocol::OverlayTypes::Size> buildObjectForSize(const IntSize& size)
{
return Inspector::Protocol::OverlayTypes::Size::create()
.setWidth(size.width())
.setHeight(size.height())
.release();
}
void InspectorOverlay::setShowingPaintRects(bool showingPaintRects)
{
if (m_showingPaintRects == showingPaintRects)
return;
m_showingPaintRects = showingPaintRects;
if (!m_showingPaintRects) {
m_paintRects.clear();
m_paintRectUpdateTimer.stop();
drawPaintRects();
forcePaint();
}
}
void InspectorOverlay::showPaintRect(const FloatRect& rect)
{
if (!m_showingPaintRects)
return;
IntRect rootRect = m_page.mainFrame().view()->contentsToRootView(enclosingIntRect(rect));
const auto removeDelay = 250_ms;
MonotonicTime removeTime = MonotonicTime::now() + removeDelay;
m_paintRects.append(TimeRectPair(removeTime, rootRect));
if (!m_paintRectUpdateTimer.isActive()) {
const Seconds paintRectsUpdateInterval { 32_ms };
m_paintRectUpdateTimer.startRepeating(paintRectsUpdateInterval);
}
drawPaintRects();
forcePaint();
}
void InspectorOverlay::updatePaintRectsTimerFired()
{
MonotonicTime now = MonotonicTime::now();
bool rectsChanged = false;
while (!m_paintRects.isEmpty() && m_paintRects.first().first < now) {
m_paintRects.removeFirst();
rectsChanged = true;
}
if (m_paintRects.isEmpty())
m_paintRectUpdateTimer.stop();
if (rectsChanged) {
drawPaintRects();
forcePaint();
}
}
void InspectorOverlay::drawPaintRects()
{
auto arrayOfRects = JSON::ArrayOf<Inspector::Protocol::OverlayTypes::Rect>::create();
for (const auto& pair : m_paintRects)
arrayOfRects->addItem(buildObjectForRect(pair.second));
evaluateInOverlay(ASCIILiteral("updatePaintRects"), WTFMove(arrayOfRects));
}
void InspectorOverlay::drawGutter()
{
evaluateInOverlay(ASCIILiteral("drawGutter"));
}
static RefPtr<JSON::ArrayOf<Inspector::Protocol::OverlayTypes::FragmentHighlightData>> buildArrayForRendererFragments(RenderObject* renderer, const HighlightConfig& config)
{
auto arrayOfFragments = JSON::ArrayOf<Inspector::Protocol::OverlayTypes::FragmentHighlightData>::create();
Highlight highlight;
buildRendererHighlight(renderer, config, highlight, InspectorOverlay::CoordinateSystem::View);
arrayOfFragments->addItem(buildObjectForHighlight(highlight));
return WTFMove(arrayOfFragments);
}
static FloatPoint localPointToRoot(RenderObject* renderer, const FrameView* mainView, const FrameView* view, const FloatPoint& point)
{
FloatPoint result = renderer->localToAbsolute(point);
result = view->contentsToRootView(roundedIntPoint(result));
result += toIntSize(mainView->scrollPosition());
return result;
}
struct PathApplyInfo {
FrameView* rootView;
FrameView* view;
Inspector::Protocol::OverlayTypes::DisplayPath* pathArray;
RenderObject* renderer;
const ShapeOutsideInfo* shapeOutsideInfo;
};
static void appendPathCommandAndPoints(PathApplyInfo& info, const String& command, const FloatPoint points[], unsigned length)
{
FloatPoint point;
info.pathArray->addItem(command);
for (unsigned i = 0; i < length; i++) {
point = info.shapeOutsideInfo->shapeToRendererPoint(points[i]);
point = localPointToRoot(info.renderer, info.rootView, info.view, point);
info.pathArray->addItem(point.x());
info.pathArray->addItem(point.y());
}
}
static void appendPathSegment(PathApplyInfo& pathApplyInfo, const PathElement& pathElement)
{
FloatPoint point;
switch (pathElement.type) {
// The points member will contain 1 value.
case PathElementMoveToPoint:
appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("M"), pathElement.points, 1);
break;
// The points member will contain 1 value.
case PathElementAddLineToPoint:
appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("L"), pathElement.points, 1);
break;
// The points member will contain 3 values.
case PathElementAddCurveToPoint:
appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("C"), pathElement.points, 3);
break;
// The points member will contain 2 values.
case PathElementAddQuadCurveToPoint:
appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("Q"), pathElement.points, 2);
break;
// The points member will contain no values.
case PathElementCloseSubpath:
appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("Z"), nullptr, 0);
break;
}
}
static RefPtr<Inspector::Protocol::OverlayTypes::ShapeOutsideData> buildObjectForShapeOutside(Frame* containingFrame, RenderBox* renderer)
{
const ShapeOutsideInfo* shapeOutsideInfo = renderer->shapeOutsideInfo();
if (!shapeOutsideInfo)
return nullptr;
LayoutRect shapeBounds = shapeOutsideInfo->computedShapePhysicalBoundingBox();
FloatQuad shapeQuad = renderer->localToAbsoluteQuad(FloatRect(shapeBounds));
contentsQuadToPage(containingFrame->page()->mainFrame().view(), containingFrame->view(), shapeQuad);
auto shapeObject = Inspector::Protocol::OverlayTypes::ShapeOutsideData::create()
.setBounds(buildArrayForQuad(shapeQuad))
.release();
Shape::DisplayPaths paths;
shapeOutsideInfo->computedShape().buildDisplayPaths(paths);
if (paths.shape.length()) {
auto shapePath = Inspector::Protocol::OverlayTypes::DisplayPath::create();
PathApplyInfo info;
info.rootView = containingFrame->page()->mainFrame().view();
info.view = containingFrame->view();
info.pathArray = &shapePath.get();
info.renderer = renderer;
info.shapeOutsideInfo = shapeOutsideInfo;
paths.shape.apply([&info](const PathElement& pathElement) {
appendPathSegment(info, pathElement);
});
shapeObject->setShape(shapePath.copyRef());
if (paths.marginShape.length()) {
auto marginShapePath = Inspector::Protocol::OverlayTypes::DisplayPath::create();
info.pathArray = &marginShapePath.get();
paths.marginShape.apply([&info](const PathElement& pathElement) {
appendPathSegment(info, pathElement);
});
shapeObject->setMarginShape(marginShapePath.copyRef());
}
}
return WTFMove(shapeObject);
}
static RefPtr<Inspector::Protocol::OverlayTypes::ElementData> buildObjectForElementData(Node* node, HighlightType)
{
if (!is<Element>(node) || !node->document().frame())
return nullptr;
Element* effectiveElement = downcast<Element>(node);
if (node->isPseudoElement()) {
Element* hostElement = downcast<PseudoElement>(*node).hostElement();
if (!hostElement)
return nullptr;
effectiveElement = hostElement;
}
Element& element = *effectiveElement;
bool isXHTML = element.document().isXHTMLDocument();
auto elementData = Inspector::Protocol::OverlayTypes::ElementData::create()
.setTagName(isXHTML ? element.nodeName() : element.nodeName().convertToASCIILowercase())
.setIdValue(element.getIdAttribute())
.release();
if (element.hasClass() && is<StyledElement>(element)) {
auto classes = JSON::ArrayOf<String>::create();
HashSet<AtomicString> usedClassNames;
const SpaceSplitString& classNamesString = downcast<StyledElement>(element).classNames();
for (size_t i = 0; i < classNamesString.size(); ++i) {
const AtomicString& className = classNamesString[i];
if (usedClassNames.contains(className))
continue;
usedClassNames.add(className);
classes->addItem(className);
}
elementData->setClasses(WTFMove(classes));
}
if (node->isPseudoElement()) {
if (node->pseudoId() == BEFORE)
elementData->setPseudoElement("before");
else if (node->pseudoId() == AFTER)
elementData->setPseudoElement("after");
}
RenderElement* renderer = element.renderer();
if (!renderer)
return nullptr;
Frame* containingFrame = node->document().frame();
FrameView* containingView = containingFrame->view();
IntRect boundingBox = snappedIntRect(containingView->contentsToRootView(renderer->absoluteBoundingBoxRect()));
RenderBoxModelObject* modelObject = is<RenderBoxModelObject>(*renderer) ? downcast<RenderBoxModelObject>(renderer) : nullptr;
auto sizeObject = Inspector::Protocol::OverlayTypes::Size::create()
.setWidth(modelObject ? adjustForAbsoluteZoom(roundToInt(modelObject->offsetWidth()), *modelObject) : boundingBox.width())
.setHeight(modelObject ? adjustForAbsoluteZoom(roundToInt(modelObject->offsetHeight()), *modelObject) : boundingBox.height())
.release();
elementData->setSize(WTFMove(sizeObject));
if (is<RenderBox>(*renderer)) {
auto& renderBox = downcast<RenderBox>(*renderer);
if (RefPtr<Inspector::Protocol::OverlayTypes::ShapeOutsideData> shapeObject = buildObjectForShapeOutside(containingFrame, &renderBox))
elementData->setShapeOutsideData(WTFMove(shapeObject));
}
// Need to enable AX to get the computed role.
if (!WebCore::AXObjectCache::accessibilityEnabled())
WebCore::AXObjectCache::enableAccessibility();
if (AXObjectCache* axObjectCache = node->document().axObjectCache()) {
if (AccessibilityObject* axObject = axObjectCache->getOrCreate(node))
elementData->setRole(axObject->computedRoleString());
}
return WTFMove(elementData);
}
RefPtr<Inspector::Protocol::OverlayTypes::NodeHighlightData> InspectorOverlay::buildHighlightObjectForNode(Node* node, HighlightType type) const
{
if (!node)
return nullptr;
RenderObject* renderer = node->renderer();
if (!renderer)
return nullptr;
RefPtr<JSON::ArrayOf<Inspector::Protocol::OverlayTypes::FragmentHighlightData>> arrayOfFragmentHighlights = buildArrayForRendererFragments(renderer, m_nodeHighlightConfig);
if (!arrayOfFragmentHighlights)
return nullptr;
// The main view's scroll offset is shared across all quads.
FrameView* mainView = m_page.mainFrame().view();
auto nodeHighlightObject = Inspector::Protocol::OverlayTypes::NodeHighlightData::create()
.setScrollOffset(buildObjectForPoint(!mainView->delegatesScrolling() ? mainView->visibleContentRect().location() : FloatPoint()))
.setFragments(WTFMove(arrayOfFragmentHighlights))
.release();
if (m_nodeHighlightConfig.showInfo) {
if (RefPtr<Inspector::Protocol::OverlayTypes::ElementData> elementData = buildObjectForElementData(node, type))
nodeHighlightObject->setElementData(WTFMove(elementData));
}
return WTFMove(nodeHighlightObject);
}
Ref<JSON::ArrayOf<Inspector::Protocol::OverlayTypes::NodeHighlightData>> InspectorOverlay::buildObjectForHighlightedNodes() const
{
auto highlights = JSON::ArrayOf<Inspector::Protocol::OverlayTypes::NodeHighlightData>::create();
if (m_highlightNode) {
if (RefPtr<Inspector::Protocol::OverlayTypes::NodeHighlightData> nodeHighlightData = buildHighlightObjectForNode(m_highlightNode.get(), HighlightType::Node))
highlights->addItem(WTFMove(nodeHighlightData));
} else if (m_highlightNodeList) {
for (unsigned i = 0; i < m_highlightNodeList->length(); ++i) {
if (RefPtr<Inspector::Protocol::OverlayTypes::NodeHighlightData> nodeHighlightData = buildHighlightObjectForNode(m_highlightNodeList->item(i), HighlightType::NodeList))
highlights->addItem(WTFMove(nodeHighlightData));
}
}
return highlights;
}
void InspectorOverlay::drawNodeHighlight()
{
if (m_highlightNode || m_highlightNodeList)
evaluateInOverlay("drawNodeHighlight", buildObjectForHighlightedNodes());
}
void InspectorOverlay::drawQuadHighlight()
{
if (!m_highlightQuad)
return;
Highlight highlight;
buildQuadHighlight(*m_highlightQuad, m_quadHighlightConfig, highlight);
evaluateInOverlay("drawQuadHighlight", buildObjectForHighlight(highlight));
}
void InspectorOverlay::drawPausedInDebuggerMessage()
{
if (!m_pausedInDebuggerMessage.isNull())
evaluateInOverlay("drawPausedInDebuggerMessage", m_pausedInDebuggerMessage);
}
Page* InspectorOverlay::overlayPage()
{
if (m_overlayPage)
return m_overlayPage.get();
PageConfiguration pageConfiguration(
createEmptyEditorClient(),
SocketProvider::create(),
LibWebRTCProvider::create(),
CacheStorageProvider::create()
);
fillWithEmptyClients(pageConfiguration);
m_overlayPage = std::make_unique<Page>(WTFMove(pageConfiguration));
m_overlayPage->setDeviceScaleFactor(m_page.deviceScaleFactor());
Settings& settings = m_page.settings();
Settings& overlaySettings = m_overlayPage->settings();
overlaySettings.setStandardFontFamily(settings.standardFontFamily());
overlaySettings.setSerifFontFamily(settings.serifFontFamily());
overlaySettings.setSansSerifFontFamily(settings.sansSerifFontFamily());
overlaySettings.setCursiveFontFamily(settings.cursiveFontFamily());
overlaySettings.setFantasyFontFamily(settings.fantasyFontFamily());
overlaySettings.setPictographFontFamily(settings.pictographFontFamily());
overlaySettings.setMinimumFontSize(settings.minimumFontSize());
overlaySettings.setMinimumLogicalFontSize(settings.minimumLogicalFontSize());
overlaySettings.setMediaEnabled(false);
overlaySettings.setScriptEnabled(true);
overlaySettings.setPluginsEnabled(false);
Frame& frame = m_overlayPage->mainFrame();
frame.setView(FrameView::create(frame));
frame.init();
FrameLoader& loader = frame.loader();
frame.view()->setCanHaveScrollbars(false);
frame.view()->setTransparent(true);
ASSERT(loader.activeDocumentLoader());
loader.activeDocumentLoader()->writer().setMIMEType("text/html");
loader.activeDocumentLoader()->writer().begin();
loader.activeDocumentLoader()->writer().addData(reinterpret_cast<const char*>(InspectorOverlayPage_html), sizeof(InspectorOverlayPage_html));
loader.activeDocumentLoader()->writer().end();
#if OS(WINDOWS)
evaluateInOverlay("setPlatform", "windows");
#elif OS(MAC_OS_X)
evaluateInOverlay("setPlatform", "mac");
#elif OS(UNIX)
evaluateInOverlay("setPlatform", "linux");
#endif
return m_overlayPage.get();
}
void InspectorOverlay::forcePaint()
{
// This overlay page is very weird and doesn't automatically paint. We have to force paints manually.
m_client->highlight();
}
void InspectorOverlay::reset(const IntSize& viewportSize, const IntSize& frameViewFullSize)
{
auto configObject = Inspector::Protocol::OverlayTypes::OverlayConfiguration::create()
.setDeviceScaleFactor(m_page.deviceScaleFactor())
.setViewportSize(buildObjectForSize(viewportSize))
.setFrameViewFullSize(buildObjectForSize(frameViewFullSize))
.release();
evaluateInOverlay("reset", WTFMove(configObject));
}
static void evaluateCommandInOverlay(Page* page, Ref<JSON::Array>&& command)
{
page->mainFrame().script().evaluate(ScriptSourceCode(makeString("dispatch(", command->toJSONString(), ')')));
}
void InspectorOverlay::evaluateInOverlay(const String& method)
{
Ref<JSON::Array> command = JSON::Array::create();
command->pushString(method);
evaluateCommandInOverlay(overlayPage(), WTFMove(command));
}
void InspectorOverlay::evaluateInOverlay(const String& method, const String& argument)
{
Ref<JSON::Array> command = JSON::Array::create();
command->pushString(method);
command->pushString(argument);
evaluateCommandInOverlay(overlayPage(), WTFMove(command));
}
void InspectorOverlay::evaluateInOverlay(const String& method, RefPtr<JSON::Value>&& argument)
{
Ref<JSON::Array> command = JSON::Array::create();
command->pushString(method);
command->pushValue(WTFMove(argument));
evaluateCommandInOverlay(overlayPage(), WTFMove(command));
}
void InspectorOverlay::freePage()
{
m_overlayPage = nullptr;
}
} // namespace WebCore