1/* 2 * Copyright (C) 2011 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include "config.h" 30 31#if ENABLE(INSPECTOR) 32#include "InspectorOverlay.h" 33 34#include "DocumentLoader.h" 35#include "Element.h" 36#include "EmptyClients.h" 37#include "FrameView.h" 38#include "GraphicsContext.h" 39#include "InspectorClient.h" 40#include "InspectorOverlayPage.h" 41#include "MainFrame.h" 42#include "Node.h" 43#include "Page.h" 44#include "PolygonShape.h" 45#include "RectangleShape.h" 46#include "RenderBoxModelObject.h" 47#include "RenderElement.h" 48#include "RenderFlowThread.h" 49#include "RenderInline.h" 50#include "RenderNamedFlowFragment.h" 51#include "RenderNamedFlowThread.h" 52#include "RenderRegion.h" 53#include "RenderView.h" 54#include "ScriptController.h" 55#include "ScriptSourceCode.h" 56#include "Settings.h" 57#include "StyledElement.h" 58#include <bindings/ScriptValue.h> 59#include <inspector/InspectorValues.h> 60#include <wtf/text/StringBuilder.h> 61 62using namespace Inspector; 63 64namespace WebCore { 65 66static Path quadToPath(const FloatQuad& quad) 67{ 68 Path quadPath; 69 quadPath.moveTo(quad.p1()); 70 quadPath.addLineTo(quad.p2()); 71 quadPath.addLineTo(quad.p3()); 72 quadPath.addLineTo(quad.p4()); 73 quadPath.closeSubpath(); 74 return quadPath; 75} 76 77static void drawOutlinedQuad(GraphicsContext* context, const FloatQuad& quad, const Color& fillColor, const Color& outlineColor) 78{ 79 static const int outlineThickness = 2; 80 81 Path quadPath = quadToPath(quad); 82 83 // Clip out the quad, then draw with a 2px stroke to get a pixel 84 // of outline (because inflating a quad is hard) 85 { 86 context->save(); 87 context->clipOut(quadPath); 88 89 context->setStrokeThickness(outlineThickness); 90 context->setStrokeColor(outlineColor, ColorSpaceDeviceRGB); 91 context->strokePath(quadPath); 92 93 context->restore(); 94 } 95 96 // Now do the fill 97 context->setFillColor(fillColor, ColorSpaceDeviceRGB); 98 context->fillPath(quadPath); 99} 100 101static void contentsQuadToCoordinateSystem(const FrameView* mainView, const FrameView* view, FloatQuad& quad, InspectorOverlay::CoordinateSystem coordinateSystem) 102{ 103 quad.setP1(view->contentsToRootView(roundedIntPoint(quad.p1()))); 104 quad.setP2(view->contentsToRootView(roundedIntPoint(quad.p2()))); 105 quad.setP3(view->contentsToRootView(roundedIntPoint(quad.p3()))); 106 quad.setP4(view->contentsToRootView(roundedIntPoint(quad.p4()))); 107 108 if (coordinateSystem == InspectorOverlay::CoordinateSystem::View) 109 quad += mainView->scrollOffset(); 110} 111 112static void contentsQuadToPage(const FrameView* mainView, const FrameView* view, FloatQuad& quad) 113{ 114 contentsQuadToCoordinateSystem(mainView, view, quad, InspectorOverlay::CoordinateSystem::View); 115} 116 117static void buildRendererHighlight(RenderObject* renderer, RenderRegion* region, const HighlightConfig& highlightConfig, Highlight* highlight, InspectorOverlay::CoordinateSystem coordinateSystem) 118{ 119 Frame* containingFrame = renderer->document().frame(); 120 if (!containingFrame) 121 return; 122 123 highlight->setDataFromConfig(highlightConfig); 124 FrameView* containingView = containingFrame->view(); 125 FrameView* mainView = containingFrame->page()->mainFrame().view(); 126 127 // RenderSVGRoot should be highlighted through the isBox() code path, all other SVG elements should just dump their absoluteQuads(). 128 bool isSVGRenderer = renderer->node() && renderer->node()->isSVGElement() && !renderer->isSVGRoot(); 129 130 if (isSVGRenderer) { 131 highlight->type = HighlightTypeRects; 132 renderer->absoluteQuads(highlight->quads); 133 for (size_t i = 0; i < highlight->quads.size(); ++i) 134 contentsQuadToCoordinateSystem(mainView, containingView, highlight->quads[i], coordinateSystem); 135 } else if (renderer->isBox() || renderer->isRenderInline()) { 136 LayoutRect contentBox; 137 LayoutRect paddingBox; 138 LayoutRect borderBox; 139 LayoutRect marginBox; 140 141 if (renderer->isBox()) { 142 RenderBox* renderBox = toRenderBox(renderer); 143 144 LayoutBoxExtent margins(renderBox->marginTop(), renderBox->marginRight(), renderBox->marginBottom(), renderBox->marginLeft()); 145 146 if (!renderBox->isOutOfFlowPositioned() && region) { 147 RenderBox::LogicalExtentComputedValues computedValues; 148 renderBox->computeLogicalWidthInRegion(computedValues, region); 149 margins.mutableLogicalLeft(renderBox->style().writingMode()) = computedValues.m_margins.m_start; 150 margins.mutableLogicalRight(renderBox->style().writingMode()) = computedValues.m_margins.m_end; 151 } 152 153 paddingBox = renderBox->clientBoxRectInRegion(region); 154 contentBox = LayoutRect(paddingBox.x() + renderBox->paddingLeft(), paddingBox.y() + renderBox->paddingTop(), 155 paddingBox.width() - renderBox->paddingLeft() - renderBox->paddingRight(), paddingBox.height() - renderBox->paddingTop() - renderBox->paddingBottom()); 156 borderBox = LayoutRect(paddingBox.x() - renderBox->borderLeft(), paddingBox.y() - renderBox->borderTop(), 157 paddingBox.width() + renderBox->borderLeft() + renderBox->borderRight(), paddingBox.height() + renderBox->borderTop() + renderBox->borderBottom()); 158 marginBox = LayoutRect(borderBox.x() - margins.left(), borderBox.y() - margins.top(), 159 borderBox.width() + margins.left() + margins.right(), borderBox.height() + margins.top() + margins.bottom()); 160 } else { 161 RenderInline* renderInline = toRenderInline(renderer); 162 163 // RenderInline's bounding box includes paddings and borders, excludes margins. 164 borderBox = renderInline->linesBoundingBox(); 165 paddingBox = LayoutRect(borderBox.x() + renderInline->borderLeft(), borderBox.y() + renderInline->borderTop(), 166 borderBox.width() - renderInline->borderLeft() - renderInline->borderRight(), borderBox.height() - renderInline->borderTop() - renderInline->borderBottom()); 167 contentBox = LayoutRect(paddingBox.x() + renderInline->paddingLeft(), paddingBox.y() + renderInline->paddingTop(), 168 paddingBox.width() - renderInline->paddingLeft() - renderInline->paddingRight(), paddingBox.height() - renderInline->paddingTop() - renderInline->paddingBottom()); 169 // Ignore marginTop and marginBottom for inlines. 170 marginBox = LayoutRect(borderBox.x() - renderInline->marginLeft(), borderBox.y(), 171 borderBox.width() + renderInline->horizontalMarginExtent(), borderBox.height()); 172 } 173 174 FloatQuad absContentQuad; 175 FloatQuad absPaddingQuad; 176 FloatQuad absBorderQuad; 177 FloatQuad absMarginQuad; 178 179 if (region) { 180 RenderFlowThread* flowThread = region->flowThread(); 181 182 // Figure out the quads in the space of the RenderFlowThread. 183 absContentQuad = renderer->localToContainerQuad(FloatRect(contentBox), flowThread); 184 absPaddingQuad = renderer->localToContainerQuad(FloatRect(paddingBox), flowThread); 185 absBorderQuad = renderer->localToContainerQuad(FloatRect(borderBox), flowThread); 186 absMarginQuad = renderer->localToContainerQuad(FloatRect(marginBox), flowThread); 187 188 // Move the quad relative to the space of the current region. 189 LayoutRect flippedRegionRect(region->flowThreadPortionRect()); 190 flowThread->flipForWritingMode(flippedRegionRect); 191 192 FloatSize delta = region->contentBoxRect().location() - flippedRegionRect.location(); 193 absContentQuad.move(delta); 194 absPaddingQuad.move(delta); 195 absBorderQuad.move(delta); 196 absMarginQuad.move(delta); 197 198 // Resolve the absolute quads starting from the current region. 199 absContentQuad = region->localToAbsoluteQuad(absContentQuad); 200 absPaddingQuad = region->localToAbsoluteQuad(absPaddingQuad); 201 absBorderQuad = region->localToAbsoluteQuad(absBorderQuad); 202 absMarginQuad = region->localToAbsoluteQuad(absMarginQuad); 203 } else { 204 absContentQuad = renderer->localToAbsoluteQuad(FloatRect(contentBox)); 205 absPaddingQuad = renderer->localToAbsoluteQuad(FloatRect(paddingBox)); 206 absBorderQuad = renderer->localToAbsoluteQuad(FloatRect(borderBox)); 207 absMarginQuad = renderer->localToAbsoluteQuad(FloatRect(marginBox)); 208 } 209 210 contentsQuadToCoordinateSystem(mainView, containingView, absContentQuad, coordinateSystem); 211 contentsQuadToCoordinateSystem(mainView, containingView, absPaddingQuad, coordinateSystem); 212 contentsQuadToCoordinateSystem(mainView, containingView, absBorderQuad, coordinateSystem); 213 contentsQuadToCoordinateSystem(mainView, containingView, absMarginQuad, coordinateSystem); 214 215 highlight->type = HighlightTypeNode; 216 highlight->quads.append(absMarginQuad); 217 highlight->quads.append(absBorderQuad); 218 highlight->quads.append(absPaddingQuad); 219 highlight->quads.append(absContentQuad); 220 } 221} 222 223static void buildNodeHighlight(Node* node, RenderRegion* region, const HighlightConfig& highlightConfig, Highlight* highlight, InspectorOverlay::CoordinateSystem coordinateSystem) 224{ 225 RenderObject* renderer = node->renderer(); 226 if (!renderer) 227 return; 228 buildRendererHighlight(renderer, region, highlightConfig, highlight, coordinateSystem); 229} 230 231static void buildQuadHighlight(const FloatQuad& quad, const HighlightConfig& highlightConfig, Highlight *highlight) 232{ 233 highlight->setDataFromConfig(highlightConfig); 234 highlight->type = HighlightTypeRects; 235 highlight->quads.append(quad); 236} 237 238InspectorOverlay::InspectorOverlay(Page& page, InspectorClient* client) 239 : m_page(page) 240 , m_client(client) 241 , m_indicating(false) 242{ 243} 244 245InspectorOverlay::~InspectorOverlay() 246{ 247} 248 249void InspectorOverlay::paint(GraphicsContext& context) 250{ 251 if (!shouldShowOverlay()) 252 return; 253 254 GraphicsContextStateSaver stateSaver(context); 255 FrameView* view = overlayPage()->mainFrame().view(); 256 view->updateLayoutAndStyleIfNeededRecursive(); 257 view->paint(&context, IntRect(0, 0, view->width(), view->height())); 258} 259 260void InspectorOverlay::drawOutline(GraphicsContext* context, const LayoutRect& rect, const Color& color) 261{ 262 FloatRect outlineRect = rect; 263 drawOutlinedQuad(context, outlineRect, Color(), color); 264} 265 266void InspectorOverlay::getHighlight(Highlight* highlight, InspectorOverlay::CoordinateSystem coordinateSystem) const 267{ 268 if (!m_highlightNode && !m_highlightQuad) 269 return; 270 271 highlight->type = HighlightTypeRects; 272 if (m_highlightNode) 273 buildNodeHighlight(m_highlightNode.get(), nullptr, m_nodeHighlightConfig, highlight, coordinateSystem); 274 else 275 buildQuadHighlight(*m_highlightQuad, m_quadHighlightConfig, highlight); 276} 277 278void InspectorOverlay::setPausedInDebuggerMessage(const String* message) 279{ 280 m_pausedInDebuggerMessage = message ? *message : String(); 281 update(); 282} 283 284void InspectorOverlay::hideHighlight() 285{ 286 m_highlightNode.clear(); 287 m_highlightQuad.reset(); 288 update(); 289} 290 291void InspectorOverlay::highlightNode(Node* node, const HighlightConfig& highlightConfig) 292{ 293 m_nodeHighlightConfig = highlightConfig; 294 m_highlightNode = node; 295 update(); 296} 297 298void InspectorOverlay::highlightQuad(std::unique_ptr<FloatQuad> quad, const HighlightConfig& highlightConfig) 299{ 300 if (m_quadHighlightConfig.usePageCoordinates) 301 *quad -= m_page.mainFrame().view()->scrollOffset(); 302 303 m_quadHighlightConfig = highlightConfig; 304 m_highlightQuad = WTF::move(quad); 305 update(); 306} 307 308Node* InspectorOverlay::highlightedNode() const 309{ 310 return m_highlightNode.get(); 311} 312 313void InspectorOverlay::didSetSearchingForNode(bool enabled) 314{ 315 m_client->didSetSearchingForNode(enabled); 316} 317 318void InspectorOverlay::setIndicating(bool indicating) 319{ 320 m_indicating = indicating; 321 322 if (m_indicating) 323 evaluateInOverlay(ASCIILiteral("showPageIndication")); 324 else 325 evaluateInOverlay(ASCIILiteral("hidePageIndication")); 326 327 update(); 328} 329 330bool InspectorOverlay::shouldShowOverlay() const 331{ 332 return m_highlightNode || m_highlightNode || m_indicating || !m_pausedInDebuggerMessage.isNull(); 333} 334 335void InspectorOverlay::update() 336{ 337 if (!shouldShowOverlay()) { 338 m_client->hideHighlight(); 339 return; 340 } 341 342 FrameView* view = m_page.mainFrame().view(); 343 if (!view) 344 return; 345 346 FrameView* overlayView = overlayPage()->mainFrame().view(); 347 IntSize viewportSize = view->unscaledVisibleContentSizeIncludingObscuredArea(); 348 IntSize frameViewFullSize = view->unscaledVisibleContentSizeIncludingObscuredArea(ScrollableArea::IncludeScrollbars); 349 overlayPage()->setPageScaleFactor(m_page.pageScaleFactor(), IntPoint()); 350 frameViewFullSize.scale(m_page.pageScaleFactor()); 351 overlayView->resize(frameViewFullSize); 352 353 // Clear canvas and paint things. 354 // FIXME: Remove extra parameter? 355 reset(viewportSize, IntSize()); 356 357 // Include scrollbars to avoid masking them by the gutter. 358 drawGutter(); 359 drawNodeHighlight(); 360 drawQuadHighlight(); 361 drawPausedInDebuggerMessage(); 362 363 // Position DOM elements. 364 overlayPage()->mainFrame().document()->recalcStyle(Style::Force); 365 if (overlayView->needsLayout()) 366 overlayView->layout(); 367 368 // Kick paint. 369 m_client->highlight(); 370} 371 372static PassRefPtr<InspectorObject> buildObjectForPoint(const FloatPoint& point) 373{ 374 RefPtr<InspectorObject> object = InspectorObject::create(); 375 object->setNumber("x", point.x()); 376 object->setNumber("y", point.y()); 377 return object.release(); 378} 379 380static PassRefPtr<InspectorArray> buildArrayForQuad(const FloatQuad& quad) 381{ 382 RefPtr<InspectorArray> array = InspectorArray::create(); 383 array->pushObject(buildObjectForPoint(quad.p1())); 384 array->pushObject(buildObjectForPoint(quad.p2())); 385 array->pushObject(buildObjectForPoint(quad.p3())); 386 array->pushObject(buildObjectForPoint(quad.p4())); 387 return array.release(); 388} 389 390static PassRefPtr<InspectorObject> buildObjectForHighlight(const Highlight& highlight) 391{ 392 RefPtr<InspectorObject> object = InspectorObject::create(); 393 RefPtr<InspectorArray> array = InspectorArray::create(); 394 for (size_t i = 0; i < highlight.quads.size(); ++i) 395 array->pushArray(buildArrayForQuad(highlight.quads[i])); 396 object->setArray("quads", array.release()); 397 object->setString("contentColor", highlight.contentColor.serialized()); 398 object->setString("contentOutlineColor", highlight.contentOutlineColor.serialized()); 399 object->setString("paddingColor", highlight.paddingColor.serialized()); 400 object->setString("borderColor", highlight.borderColor.serialized()); 401 object->setString("marginColor", highlight.marginColor.serialized()); 402 return object.release(); 403} 404 405static PassRefPtr<InspectorObject> buildObjectForRegionHighlight(FrameView* mainView, RenderRegion* region) 406{ 407 FrameView* containingView = region->frame().view(); 408 if (!containingView) 409 return nullptr; 410 411 RenderBlockFlow* regionContainer = toRenderBlockFlow(region->parent()); 412 LayoutRect borderBox = regionContainer->borderBoxRect(); 413 borderBox.setWidth(borderBox.width() + regionContainer->verticalScrollbarWidth()); 414 borderBox.setHeight(borderBox.height() + regionContainer->horizontalScrollbarHeight()); 415 416 // Create incoming and outgoing boxes that we use to chain the regions toghether. 417 const LayoutSize linkBoxSize(10, 10); 418 const LayoutSize linkBoxMidpoint(linkBoxSize.width() / 2, linkBoxSize.height() / 2); 419 420 LayoutRect incomingRectBox = LayoutRect(borderBox.location() - linkBoxMidpoint, linkBoxSize); 421 LayoutRect outgoingRectBox = LayoutRect(borderBox.location() - linkBoxMidpoint + borderBox.size(), linkBoxSize); 422 423 // Move the link boxes slightly inside the region border box. 424 LayoutUnit maxUsableHeight = std::max(LayoutUnit(), borderBox.height() - linkBoxMidpoint.height()); 425 LayoutUnit linkBoxVerticalOffset = std::min(LayoutUnit::fromPixel(15), maxUsableHeight); 426 incomingRectBox.move(0, linkBoxVerticalOffset); 427 outgoingRectBox.move(0, -linkBoxVerticalOffset); 428 429 FloatQuad borderRectQuad = regionContainer->localToAbsoluteQuad(FloatRect(borderBox)); 430 FloatQuad incomingRectQuad = regionContainer->localToAbsoluteQuad(FloatRect(incomingRectBox)); 431 FloatQuad outgoingRectQuad = regionContainer->localToAbsoluteQuad(FloatRect(outgoingRectBox)); 432 433 contentsQuadToPage(mainView, containingView, borderRectQuad); 434 contentsQuadToPage(mainView, containingView, incomingRectQuad); 435 contentsQuadToPage(mainView, containingView, outgoingRectQuad); 436 437 RefPtr<InspectorObject> regionObject = InspectorObject::create(); 438 439 regionObject->setArray("borderQuad", buildArrayForQuad(borderRectQuad)); 440 regionObject->setArray("incomingQuad", buildArrayForQuad(incomingRectQuad)); 441 regionObject->setArray("outgoingQuad", buildArrayForQuad(outgoingRectQuad)); 442 443 return regionObject.release(); 444} 445 446static PassRefPtr<InspectorArray> buildObjectForCSSRegionsHighlight(RenderRegion* region, RenderFlowThread* flowThread) 447{ 448 FrameView* mainFrameView = region->document().page()->mainFrame().view(); 449 450 RefPtr<InspectorArray> array = InspectorArray::create(); 451 452 const RenderRegionList& regionList = flowThread->renderRegionList(); 453 for (auto& iterRegion : regionList) { 454 if (!iterRegion->isValid()) 455 continue; 456 RefPtr<InspectorObject> regionHighlightObject = buildObjectForRegionHighlight(mainFrameView, iterRegion); 457 if (!regionHighlightObject) 458 continue; 459 if (region == iterRegion) { 460 // Let the script know that this is the currently highlighted node. 461 regionHighlightObject->setBoolean("isHighlighted", true); 462 } 463 array->pushObject(regionHighlightObject.release()); 464 } 465 466 return array.release(); 467} 468 469static PassRefPtr<InspectorObject> buildObjectForSize(const IntSize& size) 470{ 471 RefPtr<InspectorObject> result = InspectorObject::create(); 472 result->setNumber("width", size.width()); 473 result->setNumber("height", size.height()); 474 return result.release(); 475} 476 477static PassRefPtr<InspectorObject> buildObjectForCSSRegionContentClip(RenderRegion* region) 478{ 479 Frame* containingFrame = region->document().frame(); 480 if (!containingFrame) 481 return nullptr; 482 483 FrameView* containingView = containingFrame->view(); 484 FrameView* mainView = containingFrame->page()->mainFrame().view(); 485 RenderFlowThread* flowThread = region->flowThread(); 486 487 // Get the clip box of the current region and covert it into an absolute quad. 488 LayoutRect flippedRegionRect(region->flowThreadPortionOverflowRect()); 489 flowThread->flipForWritingMode(flippedRegionRect); 490 491 // Apply any border or padding of the region. 492 flippedRegionRect.setLocation(region->contentBoxRect().location()); 493 494 FloatQuad clipQuad = region->localToAbsoluteQuad(FloatRect(flippedRegionRect)); 495 contentsQuadToPage(mainView, containingView, clipQuad); 496 497 RefPtr<InspectorObject> regionObject = InspectorObject::create(); 498 regionObject->setArray("quad", buildArrayForQuad(clipQuad)); 499 return regionObject.release(); 500} 501 502void InspectorOverlay::drawGutter() 503{ 504 evaluateInOverlay("drawGutter"); 505} 506 507static PassRefPtr<InspectorArray> buildObjectForRendererFragments(RenderObject* renderer, const HighlightConfig& config) 508{ 509 RefPtr<InspectorArray> fragmentsArray = InspectorArray::create(); 510 511 RenderFlowThread* containingFlowThread = renderer->flowThreadContainingBlock(); 512 if (!containingFlowThread) { 513 Highlight highlight; 514 buildRendererHighlight(renderer, nullptr, config, &highlight, InspectorOverlay::CoordinateSystem::View); 515 fragmentsArray->pushObject(buildObjectForHighlight(highlight)); 516 } else { 517 RenderRegion* startRegion = nullptr; 518 RenderRegion* endRegion = nullptr; 519 if (!containingFlowThread->getRegionRangeForBox(&renderer->enclosingBox(), startRegion, endRegion)) { 520 // The flow has no visible regions. The renderer is not visible on screen. 521 return nullptr; 522 } 523 524 const RenderRegionList& regionList = containingFlowThread->renderRegionList(); 525 for (RenderRegionList::const_iterator iter = regionList.find(startRegion); iter != regionList.end(); ++iter) { 526 RenderRegion* region = *iter; 527 if (region->isValid()) { 528 // Compute the highlight of the fragment inside the current region. 529 Highlight highlight; 530 buildRendererHighlight(renderer, region, config, &highlight, InspectorOverlay::CoordinateSystem::View); 531 RefPtr<InspectorObject> fragmentObject = buildObjectForHighlight(highlight); 532 533 // Compute the clipping area of the region. 534 fragmentObject->setObject("region", buildObjectForCSSRegionContentClip(region)); 535 fragmentsArray->pushObject(fragmentObject.release()); 536 } 537 if (region == endRegion) 538 break; 539 } 540 } 541 542 return fragmentsArray.release(); 543} 544 545#if ENABLE(CSS_SHAPES) 546static FloatPoint localPointToRoot(RenderObject* renderer, const FrameView* mainView, const FrameView* view, const FloatPoint& point) 547{ 548 FloatPoint result = renderer->localToAbsolute(point); 549 result = view->contentsToRootView(roundedIntPoint(result)); 550 result += mainView->scrollOffset(); 551 return result; 552} 553 554struct PathApplyInfo { 555 FrameView* rootView; 556 FrameView* view; 557 InspectorArray* array; 558 RenderObject* renderer; 559 const ShapeOutsideInfo* shapeOutsideInfo; 560}; 561 562static void appendPathCommandAndPoints(PathApplyInfo* info, const String& command, const FloatPoint points[], unsigned length) 563{ 564 FloatPoint point; 565 info->array->pushString(command); 566 for (unsigned i = 0; i < length; i++) { 567 point = info->shapeOutsideInfo->shapeToRendererPoint(points[i]); 568 point = localPointToRoot(info->renderer, info->rootView, info->view, point); 569 info->array->pushNumber(point.x()); 570 info->array->pushNumber(point.y()); 571 } 572} 573 574static void appendPathSegment(void* info, const PathElement* pathElement) 575{ 576 PathApplyInfo* pathApplyInfo = static_cast<PathApplyInfo*>(info); 577 FloatPoint point; 578 switch (pathElement->type) { 579 // The points member will contain 1 value. 580 case PathElementMoveToPoint: 581 appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("M"), pathElement->points, 1); 582 break; 583 // The points member will contain 1 value. 584 case PathElementAddLineToPoint: 585 appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("L"), pathElement->points, 1); 586 break; 587 // The points member will contain 3 values. 588 case PathElementAddCurveToPoint: 589 appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("C"), pathElement->points, 3); 590 break; 591 // The points member will contain 2 values. 592 case PathElementAddQuadCurveToPoint: 593 appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("Q"), pathElement->points, 2); 594 break; 595 // The points member will contain no values. 596 case PathElementCloseSubpath: 597 appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("Z"), nullptr, 0); 598 break; 599 } 600} 601 602static PassRefPtr<InspectorObject> buildObjectForShapeOutside(Frame* containingFrame, RenderBox* renderer) 603{ 604 const ShapeOutsideInfo* shapeOutsideInfo = renderer->shapeOutsideInfo(); 605 if (!shapeOutsideInfo) 606 return nullptr; 607 608 RefPtr<InspectorObject> shapeObject = InspectorObject::create(); 609 LayoutRect shapeBounds = shapeOutsideInfo->computedShapePhysicalBoundingBox(); 610 FloatQuad shapeQuad = renderer->localToAbsoluteQuad(FloatRect(shapeBounds)); 611 contentsQuadToPage(containingFrame->page()->mainFrame().view(), containingFrame->view(), shapeQuad); 612 shapeObject->setArray(ASCIILiteral("bounds"), buildArrayForQuad(shapeQuad)); 613 614 Shape::DisplayPaths paths; 615 shapeOutsideInfo->computedShape().buildDisplayPaths(paths); 616 617 if (paths.shape.length()) { 618 RefPtr<InspectorArray> shapePath = InspectorArray::create(); 619 PathApplyInfo info; 620 info.rootView = containingFrame->page()->mainFrame().view(); 621 info.view = containingFrame->view(); 622 info.array = shapePath.get(); 623 info.renderer = renderer; 624 info.shapeOutsideInfo = shapeOutsideInfo; 625 626 paths.shape.apply(&info, &appendPathSegment); 627 628 shapeObject->setArray(ASCIILiteral("shape"), shapePath.release()); 629 630 if (paths.marginShape.length()) { 631 shapePath = InspectorArray::create(); 632 info.array = shapePath.get(); 633 634 paths.marginShape.apply(&info, &appendPathSegment); 635 636 shapeObject->setArray(ASCIILiteral("marginShape"), shapePath.release()); 637 } 638 } 639 640 return shapeObject.release(); 641} 642#endif 643 644static PassRefPtr<InspectorObject> buildObjectForElementInfo(Node* node) 645{ 646 if (!node->isElementNode() || !node->document().frame()) 647 return nullptr; 648 649 RefPtr<InspectorObject> elementInfo = InspectorObject::create(); 650 651 Element* element = toElement(node); 652 bool isXHTML = element->document().isXHTMLDocument(); 653 elementInfo->setString("tagName", isXHTML ? element->nodeName() : element->nodeName().lower()); 654 elementInfo->setString("idValue", element->getIdAttribute()); 655 HashSet<AtomicString> usedClassNames; 656 if (element->hasClass() && element->isStyledElement()) { 657 StringBuilder classNames; 658 const SpaceSplitString& classNamesString = toStyledElement(element)->classNames(); 659 size_t classNameCount = classNamesString.size(); 660 for (size_t i = 0; i < classNameCount; ++i) { 661 const AtomicString& className = classNamesString[i]; 662 if (usedClassNames.contains(className)) 663 continue; 664 usedClassNames.add(className); 665 classNames.append('.'); 666 classNames.append(className); 667 } 668 elementInfo->setString("className", classNames.toString()); 669 } 670 671 RenderElement* renderer = element->renderer(); 672 Frame* containingFrame = node->document().frame(); 673 FrameView* containingView = containingFrame->view(); 674 IntRect boundingBox = pixelSnappedIntRect(containingView->contentsToRootView(renderer->absoluteBoundingBoxRect())); 675 RenderBoxModelObject* modelObject = renderer->isBoxModelObject() ? toRenderBoxModelObject(renderer) : nullptr; 676 elementInfo->setString("nodeWidth", String::number(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetWidth(), *modelObject) : boundingBox.width())); 677 elementInfo->setString("nodeHeight", String::number(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetHeight(), *modelObject) : boundingBox.height())); 678 679 if (renderer->isRenderNamedFlowFragmentContainer()) { 680 RenderNamedFlowFragment* region = toRenderBlockFlow(renderer)->renderNamedFlowFragment(); 681 if (region->isValid()) { 682 RenderFlowThread* flowThread = region->flowThread(); 683 ASSERT(flowThread && flowThread->isRenderNamedFlowThread()); 684 RefPtr<InspectorObject> regionFlowInfo = InspectorObject::create(); 685 regionFlowInfo->setString("name", toRenderNamedFlowThread(flowThread)->flowThreadName()); 686 regionFlowInfo->setArray("regions", buildObjectForCSSRegionsHighlight(region, flowThread)); 687 elementInfo->setObject("regionFlowInfo", regionFlowInfo.release()); 688 } 689 } 690 691 RenderFlowThread* containingFlowThread = renderer->flowThreadContainingBlock(); 692 if (containingFlowThread && containingFlowThread->isRenderNamedFlowThread()) { 693 RefPtr<InspectorObject> contentFlowInfo = InspectorObject::create(); 694 contentFlowInfo->setString("name", toRenderNamedFlowThread(containingFlowThread)->flowThreadName()); 695 elementInfo->setObject("contentFlowInfo", contentFlowInfo.release()); 696 } 697 698#if ENABLE(CSS_SHAPES) 699 if (renderer->isBox()) { 700 RenderBox* renderBox = toRenderBox(renderer); 701 if (RefPtr<InspectorObject> shapeObject = buildObjectForShapeOutside(containingFrame, renderBox)) 702 elementInfo->setObject("shapeOutsideInfo", shapeObject.release()); 703 } 704#endif 705 706 // Need to enable AX to get the computed role. 707 if (!WebCore::AXObjectCache::accessibilityEnabled()) 708 WebCore::AXObjectCache::enableAccessibility(); 709 710 if (AXObjectCache* axObjectCache = node->document().axObjectCache()) { 711 if (AccessibilityObject* axObject = axObjectCache->getOrCreate(node)) 712 elementInfo->setString("role", axObject->computedRoleString()); 713 } 714 715 return elementInfo.release(); 716} 717 718PassRefPtr<InspectorObject> InspectorOverlay::buildObjectForHighlightedNode() const 719{ 720 if (!m_highlightNode) 721 return nullptr; 722 723 Node* node = m_highlightNode.get(); 724 RenderObject* renderer = node->renderer(); 725 if (!renderer) 726 return nullptr; 727 728 RefPtr<InspectorArray> highlightFragments = buildObjectForRendererFragments(renderer, m_nodeHighlightConfig); 729 if (!highlightFragments) 730 return nullptr; 731 732 RefPtr<InspectorObject> highlightObject = InspectorObject::create(); 733 734 // The main view's scroll offset is shared across all quads. 735 FrameView* mainView = m_page.mainFrame().view(); 736 highlightObject->setObject("scroll", buildObjectForPoint(!mainView->delegatesScrolling() ? mainView->visibleContentRect().location() : FloatPoint())); 737 738 highlightObject->setArray("fragments", highlightFragments.release()); 739 740 if (m_nodeHighlightConfig.showInfo) { 741 RefPtr<InspectorObject> elementInfo = buildObjectForElementInfo(node); 742 if (elementInfo) 743 highlightObject->setObject("elementInfo", elementInfo.release()); 744 } 745 746 return highlightObject.release(); 747} 748 749void InspectorOverlay::drawNodeHighlight() 750{ 751 RefPtr<InspectorObject> highlightObject = buildObjectForHighlightedNode(); 752 if (!highlightObject) 753 return; 754 evaluateInOverlay("drawNodeHighlight", highlightObject); 755} 756 757void InspectorOverlay::drawQuadHighlight() 758{ 759 if (!m_highlightQuad) 760 return; 761 762 Highlight highlight; 763 buildQuadHighlight(*m_highlightQuad, m_quadHighlightConfig, &highlight); 764 evaluateInOverlay("drawQuadHighlight", buildObjectForHighlight(highlight)); 765} 766 767void InspectorOverlay::drawPausedInDebuggerMessage() 768{ 769 if (!m_pausedInDebuggerMessage.isNull()) 770 evaluateInOverlay("drawPausedInDebuggerMessage", m_pausedInDebuggerMessage); 771} 772 773Page* InspectorOverlay::overlayPage() 774{ 775 if (m_overlayPage) 776 return m_overlayPage.get(); 777 778 Page::PageClients pageClients; 779 fillWithEmptyClients(pageClients); 780 m_overlayPage = std::make_unique<Page>(pageClients); 781 782 Settings& settings = m_page.settings(); 783 Settings& overlaySettings = m_overlayPage->settings(); 784 785 overlaySettings.setStandardFontFamily(settings.standardFontFamily()); 786 overlaySettings.setSerifFontFamily(settings.serifFontFamily()); 787 overlaySettings.setSansSerifFontFamily(settings.sansSerifFontFamily()); 788 overlaySettings.setCursiveFontFamily(settings.cursiveFontFamily()); 789 overlaySettings.setFantasyFontFamily(settings.fantasyFontFamily()); 790 overlaySettings.setPictographFontFamily(settings.pictographFontFamily()); 791 overlaySettings.setMinimumFontSize(settings.minimumFontSize()); 792 overlaySettings.setMinimumLogicalFontSize(settings.minimumLogicalFontSize()); 793 overlaySettings.setMediaEnabled(false); 794 overlaySettings.setScriptEnabled(true); 795 overlaySettings.setPluginsEnabled(false); 796 797 Frame& frame = m_overlayPage->mainFrame(); 798 frame.setView(FrameView::create(frame)); 799 frame.init(); 800 FrameLoader& loader = frame.loader(); 801 frame.view()->setCanHaveScrollbars(false); 802 frame.view()->setTransparent(true); 803 ASSERT(loader.activeDocumentLoader()); 804 loader.activeDocumentLoader()->writer().setMIMEType("text/html"); 805 loader.activeDocumentLoader()->writer().begin(); 806 loader.activeDocumentLoader()->writer().addData(reinterpret_cast<const char*>(InspectorOverlayPage_html), sizeof(InspectorOverlayPage_html)); 807 loader.activeDocumentLoader()->writer().end(); 808 809#if OS(WINDOWS) 810 evaluateInOverlay("setPlatform", "windows"); 811#elif OS(MAC_OS_X) 812 evaluateInOverlay("setPlatform", "mac"); 813#elif OS(UNIX) 814 evaluateInOverlay("setPlatform", "linux"); 815#endif 816 817 return m_overlayPage.get(); 818} 819 820void InspectorOverlay::reset(const IntSize& viewportSize, const IntSize& frameViewFullSize) 821{ 822 RefPtr<InspectorObject> resetData = InspectorObject::create(); 823 resetData->setNumber("deviceScaleFactor", m_page.deviceScaleFactor()); 824 resetData->setObject("viewportSize", buildObjectForSize(viewportSize)); 825 resetData->setObject("frameViewFullSize", buildObjectForSize(frameViewFullSize)); 826 evaluateInOverlay("reset", resetData.release()); 827} 828 829void InspectorOverlay::evaluateInOverlay(const String& method) 830{ 831 RefPtr<InspectorArray> command = InspectorArray::create(); 832 command->pushString(method); 833 overlayPage()->mainFrame().script().evaluate(ScriptSourceCode(makeString("dispatch(", command->toJSONString(), ")"))); 834} 835 836void InspectorOverlay::evaluateInOverlay(const String& method, const String& argument) 837{ 838 RefPtr<InspectorArray> command = InspectorArray::create(); 839 command->pushString(method); 840 command->pushString(argument); 841 overlayPage()->mainFrame().script().evaluate(ScriptSourceCode(makeString("dispatch(", command->toJSONString(), ")"))); 842} 843 844void InspectorOverlay::evaluateInOverlay(const String& method, PassRefPtr<InspectorValue> argument) 845{ 846 RefPtr<InspectorArray> command = InspectorArray::create(); 847 command->pushString(method); 848 command->pushValue(argument); 849 overlayPage()->mainFrame().script().evaluate(ScriptSourceCode(makeString("dispatch(", command->toJSONString(), ")"))); 850} 851 852void InspectorOverlay::freePage() 853{ 854 m_overlayPage.reset(); 855} 856 857} // namespace WebCore 858 859#endif // ENABLE(INSPECTOR) 860