Web Inspector: AXI: Include Role as an extra attribute in the page overlay.
[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 Computer, 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
33 #include "InspectorOverlay.h"
34
35 #include "DocumentLoader.h"
36 #include "Element.h"
37 #include "EmptyClients.h"
38 #include "FrameView.h"
39 #include "GraphicsContext.h"
40 #include "InspectorClient.h"
41 #include "InspectorOverlayPage.h"
42 #include "MainFrame.h"
43 #include "Node.h"
44 #include "Page.h"
45 #include "PolygonShape.h"
46 #include "RectangleShape.h"
47 #include "RenderBoxModelObject.h"
48 #include "RenderElement.h"
49 #include "RenderFlowThread.h"
50 #include "RenderInline.h"
51 #include "RenderNamedFlowFragment.h"
52 #include "RenderNamedFlowThread.h"
53 #include "RenderRegion.h"
54 #include "RenderView.h"
55 #include "ScriptController.h"
56 #include "ScriptSourceCode.h"
57 #include "Settings.h"
58 #include "StyledElement.h"
59 #include <bindings/ScriptValue.h>
60 #include <inspector/InspectorValues.h>
61 #include <wtf/text/StringBuilder.h>
62
63 using namespace Inspector;
64
65 namespace WebCore {
66
67 namespace {
68
69 Path quadToPath(const FloatQuad& quad)
70 {
71     Path quadPath;
72     quadPath.moveTo(quad.p1());
73     quadPath.addLineTo(quad.p2());
74     quadPath.addLineTo(quad.p3());
75     quadPath.addLineTo(quad.p4());
76     quadPath.closeSubpath();
77     return quadPath;
78 }
79
80 void drawOutlinedQuad(GraphicsContext* context, const FloatQuad& quad, const Color& fillColor, const Color& outlineColor)
81 {
82     static const int outlineThickness = 2;
83
84     Path quadPath = quadToPath(quad);
85
86     // Clip out the quad, then draw with a 2px stroke to get a pixel
87     // of outline (because inflating a quad is hard)
88     {
89         context->save();
90         context->clipOut(quadPath);
91
92         context->setStrokeThickness(outlineThickness);
93         context->setStrokeColor(outlineColor, ColorSpaceDeviceRGB);
94         context->strokePath(quadPath);
95
96         context->restore();
97     }
98
99     // Now do the fill
100     context->setFillColor(fillColor, ColorSpaceDeviceRGB);
101     context->fillPath(quadPath);
102 }
103
104 static void contentsQuadToPage(const FrameView* mainView, const FrameView* view, FloatQuad& quad)
105 {
106     quad.setP1(view->contentsToRootView(roundedIntPoint(quad.p1())));
107     quad.setP2(view->contentsToRootView(roundedIntPoint(quad.p2())));
108     quad.setP3(view->contentsToRootView(roundedIntPoint(quad.p3())));
109     quad.setP4(view->contentsToRootView(roundedIntPoint(quad.p4())));
110     quad += mainView->scrollOffset();
111 }
112
113 static void buildRendererHighlight(RenderObject* renderer, RenderRegion* region, const HighlightConfig& highlightConfig, Highlight* highlight)
114 {
115     Frame* containingFrame = renderer->document().frame();
116     if (!containingFrame)
117         return;
118
119     highlight->setDataFromConfig(highlightConfig);
120     FrameView* containingView = containingFrame->view();
121     FrameView* mainView = containingFrame->page()->mainFrame().view();
122
123     // RenderSVGRoot should be highlighted through the isBox() code path, all other SVG elements should just dump their absoluteQuads().
124     bool isSVGRenderer = renderer->node() && renderer->node()->isSVGElement() && !renderer->isSVGRoot();
125
126     if (isSVGRenderer) {
127         highlight->type = HighlightTypeRects;
128         renderer->absoluteQuads(highlight->quads);
129         for (size_t i = 0; i < highlight->quads.size(); ++i)
130             contentsQuadToPage(mainView, containingView, highlight->quads[i]);
131     } else if (renderer->isBox() || renderer->isRenderInline()) {
132         LayoutRect contentBox;
133         LayoutRect paddingBox;
134         LayoutRect borderBox;
135         LayoutRect marginBox;
136
137         if (renderer->isBox()) {
138             RenderBox* renderBox = toRenderBox(renderer);
139
140             LayoutBoxExtent margins(renderBox->marginTop(), renderBox->marginRight(), renderBox->marginBottom(), renderBox->marginLeft());
141
142             if (!renderBox->isOutOfFlowPositioned() && region) {
143                 RenderBox::LogicalExtentComputedValues computedValues;
144                 renderBox->computeLogicalWidthInRegion(computedValues, region);
145                 margins.mutableLogicalLeft(renderBox->style().writingMode()) = computedValues.m_margins.m_start;
146                 margins.mutableLogicalRight(renderBox->style().writingMode()) = computedValues.m_margins.m_end;
147             }
148
149             paddingBox = renderBox->clientBoxRectInRegion(region);
150             contentBox = LayoutRect(paddingBox.x() + renderBox->paddingLeft(), paddingBox.y() + renderBox->paddingTop(),
151                 paddingBox.width() - renderBox->paddingLeft() - renderBox->paddingRight(), paddingBox.height() - renderBox->paddingTop() - renderBox->paddingBottom());
152             borderBox = LayoutRect(paddingBox.x() - renderBox->borderLeft(), paddingBox.y() - renderBox->borderTop(),
153                 paddingBox.width() + renderBox->borderLeft() + renderBox->borderRight(), paddingBox.height() + renderBox->borderTop() + renderBox->borderBottom());
154             marginBox = LayoutRect(borderBox.x() - margins.left(), borderBox.y() - margins.top(),
155                 borderBox.width() + margins.left() + margins.right(), borderBox.height() + margins.top() + margins.bottom());
156         } else {
157             RenderInline* renderInline = toRenderInline(renderer);
158
159             // RenderInline's bounding box includes paddings and borders, excludes margins.
160             borderBox = renderInline->linesBoundingBox();
161             paddingBox = LayoutRect(borderBox.x() + renderInline->borderLeft(), borderBox.y() + renderInline->borderTop(),
162                 borderBox.width() - renderInline->borderLeft() - renderInline->borderRight(), borderBox.height() - renderInline->borderTop() - renderInline->borderBottom());
163             contentBox = LayoutRect(paddingBox.x() + renderInline->paddingLeft(), paddingBox.y() + renderInline->paddingTop(),
164                 paddingBox.width() - renderInline->paddingLeft() - renderInline->paddingRight(), paddingBox.height() - renderInline->paddingTop() - renderInline->paddingBottom());
165             // Ignore marginTop and marginBottom for inlines.
166             marginBox = LayoutRect(borderBox.x() - renderInline->marginLeft(), borderBox.y(),
167                 borderBox.width() + renderInline->horizontalMarginExtent(), borderBox.height());
168         }
169
170         FloatQuad absContentQuad;
171         FloatQuad absPaddingQuad;
172         FloatQuad absBorderQuad;
173         FloatQuad absMarginQuad;
174
175         if (region) {
176             RenderFlowThread* flowThread = region->flowThread();
177
178             // Figure out the quads in the space of the RenderFlowThread.
179             absContentQuad = renderer->localToContainerQuad(FloatRect(contentBox), flowThread);
180             absPaddingQuad = renderer->localToContainerQuad(FloatRect(paddingBox), flowThread);
181             absBorderQuad = renderer->localToContainerQuad(FloatRect(borderBox), flowThread);
182             absMarginQuad = renderer->localToContainerQuad(FloatRect(marginBox), flowThread);
183
184             // Move the quad relative to the space of the current region.
185             LayoutRect flippedRegionRect(region->flowThreadPortionRect());
186             flowThread->flipForWritingMode(flippedRegionRect);
187
188             FloatSize delta = region->contentBoxRect().location() - flippedRegionRect.location();
189             absContentQuad.move(delta);
190             absPaddingQuad.move(delta);
191             absBorderQuad.move(delta);
192             absMarginQuad.move(delta);
193
194             // Resolve the absolute quads starting from the current region.
195             absContentQuad = region->localToAbsoluteQuad(absContentQuad);
196             absPaddingQuad = region->localToAbsoluteQuad(absPaddingQuad);
197             absBorderQuad = region->localToAbsoluteQuad(absBorderQuad);
198             absMarginQuad = region->localToAbsoluteQuad(absMarginQuad);
199         } else {
200             absContentQuad = renderer->localToAbsoluteQuad(FloatRect(contentBox));
201             absPaddingQuad = renderer->localToAbsoluteQuad(FloatRect(paddingBox));
202             absBorderQuad = renderer->localToAbsoluteQuad(FloatRect(borderBox));
203             absMarginQuad = renderer->localToAbsoluteQuad(FloatRect(marginBox));
204         }
205
206         contentsQuadToPage(mainView, containingView, absContentQuad);
207         contentsQuadToPage(mainView, containingView, absPaddingQuad);
208         contentsQuadToPage(mainView, containingView, absBorderQuad);
209         contentsQuadToPage(mainView, containingView, absMarginQuad);
210
211         highlight->type = HighlightTypeNode;
212         highlight->quads.append(absMarginQuad);
213         highlight->quads.append(absBorderQuad);
214         highlight->quads.append(absPaddingQuad);
215         highlight->quads.append(absContentQuad);
216     }
217 }
218
219 static void buildNodeHighlight(Node* node, RenderRegion* region, const HighlightConfig& highlightConfig, Highlight* highlight)
220 {
221     RenderObject* renderer = node->renderer();
222     if (!renderer)
223         return;
224     buildRendererHighlight(renderer, region, highlightConfig, highlight);
225 }
226
227 static void buildQuadHighlight(const FloatQuad& quad, const HighlightConfig& highlightConfig, Highlight *highlight)
228 {
229     highlight->setDataFromConfig(highlightConfig);
230     highlight->type = HighlightTypeRects;
231     highlight->quads.append(quad);
232 }
233
234 } // anonymous namespace
235
236 InspectorOverlay::InspectorOverlay(Page& page, InspectorClient* client)
237     : m_page(page)
238     , m_client(client)
239     , m_indicating(false)
240 {
241 }
242
243 InspectorOverlay::~InspectorOverlay()
244 {
245 }
246
247 void InspectorOverlay::paint(GraphicsContext& context)
248 {
249     if (!shouldShowOverlay())
250         return;
251
252     GraphicsContextStateSaver stateSaver(context);
253     FrameView* view = overlayPage()->mainFrame().view();
254     view->updateLayoutAndStyleIfNeededRecursive();
255     view->paint(&context, IntRect(0, 0, view->width(), view->height()));
256 }
257
258 void InspectorOverlay::drawOutline(GraphicsContext* context, const LayoutRect& rect, const Color& color)
259 {
260     FloatRect outlineRect = rect;
261     drawOutlinedQuad(context, outlineRect, Color(), color);
262 }
263
264 void InspectorOverlay::getHighlight(Highlight* highlight) const
265 {
266     if (!m_highlightNode && !m_highlightQuad)
267         return;
268
269     highlight->type = HighlightTypeRects;
270     if (m_highlightNode)
271         buildNodeHighlight(m_highlightNode.get(), nullptr, m_nodeHighlightConfig, highlight);
272     else
273         buildQuadHighlight(*m_highlightQuad, m_quadHighlightConfig, highlight);
274 }
275
276 void InspectorOverlay::setPausedInDebuggerMessage(const String* message)
277 {
278     m_pausedInDebuggerMessage = message ? *message : String();
279     update();
280 }
281
282 void InspectorOverlay::hideHighlight()
283 {
284     m_highlightNode.clear();
285     m_highlightQuad.reset();
286     update();
287 }
288
289 void InspectorOverlay::highlightNode(Node* node, const HighlightConfig& highlightConfig)
290 {
291     m_nodeHighlightConfig = highlightConfig;
292     m_highlightNode = node;
293     update();
294 }
295
296 void InspectorOverlay::highlightQuad(std::unique_ptr<FloatQuad> quad, const HighlightConfig& highlightConfig)
297 {
298     if (m_quadHighlightConfig.usePageCoordinates)
299         *quad -= m_page.mainFrame().view()->scrollOffset();
300
301     m_quadHighlightConfig = highlightConfig;
302     m_highlightQuad = std::move(quad);
303     update();
304 }
305
306 Node* InspectorOverlay::highlightedNode() const
307 {
308     return m_highlightNode.get();
309 }
310
311 void InspectorOverlay::didSetSearchingForNode(bool enabled)
312 {
313     m_client->didSetSearchingForNode(enabled);
314 }
315
316 void InspectorOverlay::setIndicating(bool indicating)
317 {
318     m_indicating = indicating;
319
320     if (m_indicating)
321         evaluateInOverlay(ASCIILiteral("showPageIndication"));
322     else
323         evaluateInOverlay(ASCIILiteral("hidePageIndication"));
324
325     update();
326 }
327
328 bool InspectorOverlay::shouldShowOverlay() const
329 {
330     return m_highlightNode || m_highlightNode || m_indicating || !m_pausedInDebuggerMessage.isNull();
331 }
332
333 void InspectorOverlay::update()
334 {
335     if (!shouldShowOverlay()) {
336         m_client->hideHighlight();
337         return;
338     }
339
340     FrameView* view = m_page.mainFrame().view();
341     if (!view)
342         return;
343
344     FrameView* overlayView = overlayPage()->mainFrame().view();
345     IntSize viewportSize = view->visibleContentRect().size();
346     IntSize frameViewFullSize = view->visibleContentRectIncludingScrollbars().size();
347     overlayPage()->setPageScaleFactor(m_page.pageScaleFactor(), IntPoint());
348     frameViewFullSize.scale(m_page.pageScaleFactor());
349     overlayView->resize(frameViewFullSize);
350
351     // Clear canvas and paint things.
352     // FIXME: Remove extra parameter?
353     reset(viewportSize, IntSize());
354
355     // Include scrollbars to avoid masking them by the gutter.
356     drawGutter();
357     drawNodeHighlight();
358     drawQuadHighlight();
359     drawPausedInDebuggerMessage();
360
361     // Position DOM elements.
362     overlayPage()->mainFrame().document()->recalcStyle(Style::Force);
363     if (overlayView->needsLayout())
364         overlayView->layout();
365
366     // Kick paint.
367     m_client->highlight();
368 }
369
370 static PassRefPtr<InspectorObject> buildObjectForPoint(const FloatPoint& point)
371 {
372     RefPtr<InspectorObject> object = InspectorObject::create();
373     object->setNumber("x", point.x());
374     object->setNumber("y", point.y());
375     return object.release();
376 }
377
378 static PassRefPtr<InspectorArray> buildArrayForQuad(const FloatQuad& quad)
379 {
380     RefPtr<InspectorArray> array = InspectorArray::create();
381     array->pushObject(buildObjectForPoint(quad.p1()));
382     array->pushObject(buildObjectForPoint(quad.p2()));
383     array->pushObject(buildObjectForPoint(quad.p3()));
384     array->pushObject(buildObjectForPoint(quad.p4()));
385     return array.release();
386 }
387
388 static PassRefPtr<InspectorObject> buildObjectForHighlight(const Highlight& highlight)
389 {
390     RefPtr<InspectorObject> object = InspectorObject::create();
391     RefPtr<InspectorArray> array = InspectorArray::create();
392     for (size_t i = 0; i < highlight.quads.size(); ++i)
393         array->pushArray(buildArrayForQuad(highlight.quads[i]));
394     object->setArray("quads", array.release());
395     object->setBoolean("showRulers", highlight.showRulers);
396     object->setString("contentColor", highlight.contentColor.serialized());
397     object->setString("contentOutlineColor", highlight.contentOutlineColor.serialized());
398     object->setString("paddingColor", highlight.paddingColor.serialized());
399     object->setString("borderColor", highlight.borderColor.serialized());
400     object->setString("marginColor", highlight.marginColor.serialized());
401     return object.release();
402 }
403
404 static PassRefPtr<InspectorObject> buildObjectForRegionHighlight(FrameView* mainView, RenderRegion* region)
405 {
406     FrameView* containingView = region->frame().view();
407     if (!containingView)
408         return nullptr;
409
410     RenderBlockFlow* regionContainer = toRenderBlockFlow(region->parent());
411     LayoutRect borderBox = regionContainer->borderBoxRect();
412     borderBox.setWidth(borderBox.width() + regionContainer->verticalScrollbarWidth());
413     borderBox.setHeight(borderBox.height() + regionContainer->horizontalScrollbarHeight());
414
415     // Create incoming and outgoing boxes that we use to chain the regions toghether.
416     const LayoutSize linkBoxSize(10, 10);
417     const LayoutSize linkBoxMidpoint(linkBoxSize.width() / 2, linkBoxSize.height() / 2);
418     
419     LayoutRect incomingRectBox = LayoutRect(borderBox.location() - linkBoxMidpoint, linkBoxSize);
420     LayoutRect outgoingRectBox = LayoutRect(borderBox.location() - linkBoxMidpoint + borderBox.size(), linkBoxSize);
421
422     // Move the link boxes slightly inside the region border box.
423     LayoutUnit maxUsableHeight = std::max(LayoutUnit(), borderBox.height() - linkBoxMidpoint.height());
424     LayoutUnit linkBoxVerticalOffset = std::min(LayoutUnit::fromPixel(15), maxUsableHeight);
425     incomingRectBox.move(0, linkBoxVerticalOffset);
426     outgoingRectBox.move(0, -linkBoxVerticalOffset);
427
428     FloatQuad borderRectQuad = regionContainer->localToAbsoluteQuad(FloatRect(borderBox));
429     FloatQuad incomingRectQuad = regionContainer->localToAbsoluteQuad(FloatRect(incomingRectBox));
430     FloatQuad outgoingRectQuad = regionContainer->localToAbsoluteQuad(FloatRect(outgoingRectBox));
431
432     contentsQuadToPage(mainView, containingView, borderRectQuad);
433     contentsQuadToPage(mainView, containingView, incomingRectQuad);
434     contentsQuadToPage(mainView, containingView, outgoingRectQuad);
435
436     RefPtr<InspectorObject> regionObject = InspectorObject::create();
437
438     regionObject->setArray("borderQuad", buildArrayForQuad(borderRectQuad));
439     regionObject->setArray("incomingQuad", buildArrayForQuad(incomingRectQuad));
440     regionObject->setArray("outgoingQuad", buildArrayForQuad(outgoingRectQuad));
441
442     return regionObject.release();
443 }
444
445 static PassRefPtr<InspectorArray> buildObjectForCSSRegionsHighlight(RenderRegion* region, RenderFlowThread* flowThread)
446 {
447     FrameView* mainFrameView = region->document().page()->mainFrame().view();
448
449     RefPtr<InspectorArray> array = InspectorArray::create();
450
451     const RenderRegionList& regionList = flowThread->renderRegionList();
452     for (auto& iterRegion : regionList) {
453         if (!iterRegion->isValid())
454             continue;
455         RefPtr<InspectorObject> regionHighlightObject = buildObjectForRegionHighlight(mainFrameView, iterRegion);
456         if (!regionHighlightObject)
457             continue;
458         if (region == iterRegion) {
459             // Let the script know that this is the currently highlighted node.
460             regionHighlightObject->setBoolean("isHighlighted", true);
461         }
462         array->pushObject(regionHighlightObject.release());
463     }
464
465     return array.release();
466 }
467
468 static PassRefPtr<InspectorObject> buildObjectForSize(const IntSize& size)
469 {
470     RefPtr<InspectorObject> result = InspectorObject::create();
471     result->setNumber("width", size.width());
472     result->setNumber("height", size.height());
473     return result.release();
474 }
475
476 static PassRefPtr<InspectorObject> buildObjectForCSSRegionContentClip(RenderRegion* region)
477 {
478     Frame* containingFrame = region->document().frame();
479     if (!containingFrame)
480         return nullptr;
481
482     FrameView* containingView = containingFrame->view();
483     FrameView* mainView = containingFrame->page()->mainFrame().view();
484     RenderFlowThread* flowThread = region->flowThread();
485
486     // Get the clip box of the current region and covert it into an absolute quad.
487     LayoutRect flippedRegionRect(region->flowThreadPortionOverflowRect());
488     flowThread->flipForWritingMode(flippedRegionRect);
489
490     // Apply any border or padding of the region.
491     flippedRegionRect.setLocation(region->contentBoxRect().location());
492     
493     FloatQuad clipQuad = region->localToAbsoluteQuad(FloatRect(flippedRegionRect));
494     contentsQuadToPage(mainView, containingView, clipQuad);
495
496     RefPtr<InspectorObject> regionObject = InspectorObject::create();
497     regionObject->setArray("quad", buildArrayForQuad(clipQuad));
498     return regionObject.release();
499 }
500
501 void InspectorOverlay::drawGutter()
502 {
503     evaluateInOverlay("drawGutter");
504 }
505
506 static PassRefPtr<InspectorArray> buildObjectForRendererFragments(RenderObject* renderer, const HighlightConfig& config)
507 {
508     RefPtr<InspectorArray> fragmentsArray = InspectorArray::create();
509
510     RenderFlowThread* containingFlowThread = renderer->flowThreadContainingBlock();
511     if (!containingFlowThread) {
512         Highlight highlight;
513         buildRendererHighlight(renderer, nullptr, config, &highlight);
514         fragmentsArray->pushObject(buildObjectForHighlight(highlight));
515     } else {
516         RenderBox* enclosingBox = renderer->enclosingBox();
517         RenderRegion* startRegion = nullptr;
518         RenderRegion* endRegion = nullptr;
519         containingFlowThread->getRegionRangeForBox(enclosingBox, startRegion, endRegion);
520         if (!startRegion) {
521             // The flow has no visible regions. The renderer is not visible on screen.
522             return nullptr;
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);
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)
546 static 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
554 struct PathApplyInfo {
555     FrameView* rootView;
556     FrameView* view;
557     InspectorArray* array;
558     RenderObject* renderer;
559     const ShapeOutsideInfo* shapeOutsideInfo;
560 };
561
562 static 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
574 static 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
602 static 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
644 static 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         RenderFlowThread* flowThread = region->flowThread();
682         if (flowThread && flowThread->isRenderNamedFlowThread()) {
683             RefPtr<InspectorObject> regionFlowInfo = InspectorObject::create();
684             regionFlowInfo->setString("name", toRenderNamedFlowThread(flowThread)->flowThreadName());
685             regionFlowInfo->setArray("regions", buildObjectForCSSRegionsHighlight(region, flowThread));
686             elementInfo->setObject("regionFlowInfo", regionFlowInfo.release());
687         }
688     }
689
690     RenderFlowThread* containingFlowThread = renderer->flowThreadContainingBlock();
691     if (containingFlowThread && containingFlowThread->isRenderNamedFlowThread()) {
692         RefPtr<InspectorObject> contentFlowInfo = InspectorObject::create();
693         contentFlowInfo->setString("name", toRenderNamedFlowThread(containingFlowThread)->flowThreadName());
694         elementInfo->setObject("contentFlowInfo", contentFlowInfo.release());
695     }
696
697 #if ENABLE(CSS_SHAPES)
698     if (renderer->isBox()) {
699         RenderBox* renderBox = toRenderBox(renderer);
700         if (RefPtr<InspectorObject> shapeObject = buildObjectForShapeOutside(containingFrame, renderBox))
701             elementInfo->setObject("shapeOutsideInfo", shapeObject.release());
702     }
703 #endif
704
705     // Need to enable AX to get the computed role.
706     if (!WebCore::AXObjectCache::accessibilityEnabled())
707         WebCore::AXObjectCache::enableAccessibility();
708
709     if (AXObjectCache* axObjectCache = node->document().axObjectCache()) {
710         if (AccessibilityObject* axObject = axObjectCache->getOrCreate(node))
711             elementInfo->setString("role", axObject->computedRoleString());
712     }
713
714     return elementInfo.release();
715 }
716
717 PassRefPtr<InspectorObject> InspectorOverlay::buildObjectForHighlightedNode() const
718 {
719     if (!m_highlightNode)
720         return nullptr;
721
722     Node* node = m_highlightNode.get();
723     RenderObject* renderer = node->renderer();
724     if (!renderer)
725         return nullptr;
726
727     RefPtr<InspectorArray> highlightFragments = buildObjectForRendererFragments(renderer, m_nodeHighlightConfig);
728     if (!highlightFragments)
729         return nullptr;
730
731     RefPtr<InspectorObject> highlightObject = InspectorObject::create();
732
733     // The main view's scroll offset is shared across all quads.
734     FrameView* mainView = m_page.mainFrame().view();
735     highlightObject->setObject("scroll", buildObjectForPoint(!mainView->delegatesScrolling() ? mainView->visibleContentRect().location() : FloatPoint()));
736
737     highlightObject->setArray("fragments", highlightFragments.release());
738
739     if (m_nodeHighlightConfig.showInfo) {
740         RefPtr<InspectorObject> elementInfo = buildObjectForElementInfo(node);
741         if (elementInfo)
742             highlightObject->setObject("elementInfo", elementInfo.release());
743     }
744         
745     return highlightObject.release();
746 }
747
748 void InspectorOverlay::drawNodeHighlight()
749 {
750     RefPtr<InspectorObject> highlightObject = buildObjectForHighlightedNode();
751     if (!highlightObject)
752         return;
753     evaluateInOverlay("drawNodeHighlight", highlightObject);
754 }
755
756 void InspectorOverlay::drawQuadHighlight()
757 {
758     if (!m_highlightQuad)
759         return;
760
761     Highlight highlight;
762     buildQuadHighlight(*m_highlightQuad, m_quadHighlightConfig, &highlight);
763     evaluateInOverlay("drawQuadHighlight", buildObjectForHighlight(highlight));
764 }
765
766 void InspectorOverlay::drawPausedInDebuggerMessage()
767 {
768     if (!m_pausedInDebuggerMessage.isNull())
769         evaluateInOverlay("drawPausedInDebuggerMessage", m_pausedInDebuggerMessage);
770 }
771
772 Page* InspectorOverlay::overlayPage()
773 {
774     if (m_overlayPage)
775         return m_overlayPage.get();
776
777     Page::PageClients pageClients;
778     fillWithEmptyClients(pageClients);
779     m_overlayPage = std::make_unique<Page>(pageClients);
780
781     Settings& settings = m_page.settings();
782     Settings& overlaySettings = m_overlayPage->settings();
783
784     overlaySettings.setStandardFontFamily(settings.standardFontFamily());
785     overlaySettings.setSerifFontFamily(settings.serifFontFamily());
786     overlaySettings.setSansSerifFontFamily(settings.sansSerifFontFamily());
787     overlaySettings.setCursiveFontFamily(settings.cursiveFontFamily());
788     overlaySettings.setFantasyFontFamily(settings.fantasyFontFamily());
789     overlaySettings.setPictographFontFamily(settings.pictographFontFamily());
790     overlaySettings.setMinimumFontSize(settings.minimumFontSize());
791     overlaySettings.setMinimumLogicalFontSize(settings.minimumLogicalFontSize());
792     overlaySettings.setMediaEnabled(false);
793     overlaySettings.setScriptEnabled(true);
794     overlaySettings.setPluginsEnabled(false);
795
796     Frame& frame = m_overlayPage->mainFrame();
797     frame.setView(FrameView::create(frame));
798     frame.init();
799     FrameLoader& loader = frame.loader();
800     frame.view()->setCanHaveScrollbars(false);
801     frame.view()->setTransparent(true);
802     ASSERT(loader.activeDocumentLoader());
803     loader.activeDocumentLoader()->writer().setMIMEType("text/html");
804     loader.activeDocumentLoader()->writer().begin();
805     loader.activeDocumentLoader()->writer().addData(reinterpret_cast<const char*>(InspectorOverlayPage_html), sizeof(InspectorOverlayPage_html));
806     loader.activeDocumentLoader()->writer().end();
807
808 #if OS(WINDOWS)
809     evaluateInOverlay("setPlatform", "windows");
810 #elif OS(MAC_OS_X)
811     evaluateInOverlay("setPlatform", "mac");
812 #elif OS(UNIX)
813     evaluateInOverlay("setPlatform", "linux");
814 #endif
815
816     return m_overlayPage.get();
817 }
818
819 void InspectorOverlay::reset(const IntSize& viewportSize, const IntSize& frameViewFullSize)
820 {
821     RefPtr<InspectorObject> resetData = InspectorObject::create();
822     resetData->setNumber("deviceScaleFactor", m_page.deviceScaleFactor());
823     resetData->setObject("viewportSize", buildObjectForSize(viewportSize));
824     resetData->setObject("frameViewFullSize", buildObjectForSize(frameViewFullSize));
825     evaluateInOverlay("reset", resetData.release());
826 }
827
828 void InspectorOverlay::evaluateInOverlay(const String& method)
829 {
830     RefPtr<InspectorArray> command = InspectorArray::create();
831     command->pushString(method);
832     overlayPage()->mainFrame().script().evaluate(ScriptSourceCode(makeString("dispatch(", command->toJSONString(), ")")));
833 }
834
835 void InspectorOverlay::evaluateInOverlay(const String& method, const String& argument)
836 {
837     RefPtr<InspectorArray> command = InspectorArray::create();
838     command->pushString(method);
839     command->pushString(argument);
840     overlayPage()->mainFrame().script().evaluate(ScriptSourceCode(makeString("dispatch(", command->toJSONString(), ")")));
841 }
842
843 void InspectorOverlay::evaluateInOverlay(const String& method, PassRefPtr<InspectorValue> argument)
844 {
845     RefPtr<InspectorArray> command = InspectorArray::create();
846     command->pushString(method);
847     command->pushValue(argument);
848     overlayPage()->mainFrame().script().evaluate(ScriptSourceCode(makeString("dispatch(", command->toJSONString(), ")")));
849 }
850
851 void InspectorOverlay::freePage()
852 {
853     m_overlayPage.reset();
854 }
855
856 } // namespace WebCore
857
858 #endif // ENABLE(INSPECTOR)