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