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