Web Inspector: Highlight currently edited CSS selector
[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 #include "InspectorOverlay.h"
31
32 #include "DocumentLoader.h"
33 #include "Element.h"
34 #include "EmptyClients.h"
35 #include "FrameView.h"
36 #include "GraphicsContext.h"
37 #include "InspectorClient.h"
38 #include "InspectorOverlayPage.h"
39 #include "MainFrame.h"
40 #include "Node.h"
41 #include "Page.h"
42 #include "PageConfiguration.h"
43 #include "PolygonShape.h"
44 #include "RectangleShape.h"
45 #include "RenderBoxModelObject.h"
46 #include "RenderElement.h"
47 #include "RenderFlowThread.h"
48 #include "RenderInline.h"
49 #include "RenderNamedFlowFragment.h"
50 #include "RenderNamedFlowThread.h"
51 #include "RenderRegion.h"
52 #include "RenderView.h"
53 #include "ScriptController.h"
54 #include "ScriptSourceCode.h"
55 #include "Settings.h"
56 #include "StyledElement.h"
57 #include <bindings/ScriptValue.h>
58 #include <inspector/InspectorProtocolObjects.h>
59 #include <inspector/InspectorValues.h>
60 #include <wtf/text/StringBuilder.h>
61
62 using namespace Inspector;
63
64 namespace WebCore {
65
66 static void contentsQuadToCoordinateSystem(const FrameView* mainView, const FrameView* view, FloatQuad& quad, InspectorOverlay::CoordinateSystem coordinateSystem)
67 {
68     quad.setP1(view->contentsToRootView(roundedIntPoint(quad.p1())));
69     quad.setP2(view->contentsToRootView(roundedIntPoint(quad.p2())));
70     quad.setP3(view->contentsToRootView(roundedIntPoint(quad.p3())));
71     quad.setP4(view->contentsToRootView(roundedIntPoint(quad.p4())));
72
73     if (coordinateSystem == InspectorOverlay::CoordinateSystem::View)
74         quad += mainView->scrollOffset();
75 }
76
77 static void contentsQuadToPage(const FrameView* mainView, const FrameView* view, FloatQuad& quad)
78 {
79     contentsQuadToCoordinateSystem(mainView, view, quad, InspectorOverlay::CoordinateSystem::View);
80 }
81
82 static void buildRendererHighlight(RenderObject* renderer, RenderRegion* region, const HighlightConfig& highlightConfig, Highlight& highlight, InspectorOverlay::CoordinateSystem coordinateSystem)
83 {
84     Frame* containingFrame = renderer->document().frame();
85     if (!containingFrame)
86         return;
87
88     highlight.setDataFromConfig(highlightConfig);
89     FrameView* containingView = containingFrame->view();
90     FrameView* mainView = containingFrame->page()->mainFrame().view();
91
92     // RenderSVGRoot should be highlighted through the isBox() code path, all other SVG elements should just dump their absoluteQuads().
93     bool isSVGRenderer = renderer->node() && renderer->node()->isSVGElement() && !renderer->isSVGRoot();
94
95     if (isSVGRenderer) {
96         highlight.type = HighlightType::Rects;
97         renderer->absoluteQuads(highlight.quads);
98         for (size_t i = 0; i < highlight.quads.size(); ++i)
99             contentsQuadToCoordinateSystem(mainView, containingView, highlight.quads[i], coordinateSystem);
100     } else if (is<RenderBox>(*renderer) || is<RenderInline>(*renderer)) {
101         LayoutRect contentBox;
102         LayoutRect paddingBox;
103         LayoutRect borderBox;
104         LayoutRect marginBox;
105
106         if (is<RenderBox>(*renderer)) {
107             auto& renderBox = downcast<RenderBox>(*renderer);
108
109             LayoutBoxExtent margins(renderBox.marginTop(), renderBox.marginRight(), renderBox.marginBottom(), renderBox.marginLeft());
110
111             if (!renderBox.isOutOfFlowPositioned() && region) {
112                 RenderBox::LogicalExtentComputedValues computedValues;
113                 renderBox.computeLogicalWidthInRegion(computedValues, region);
114                 margins.start(renderBox.style().writingMode()) = computedValues.m_margins.m_start;
115                 margins.end(renderBox.style().writingMode()) = computedValues.m_margins.m_end;
116             }
117
118             paddingBox = renderBox.clientBoxRectInRegion(region);
119             contentBox = LayoutRect(paddingBox.x() + renderBox.paddingLeft(), paddingBox.y() + renderBox.paddingTop(),
120                 paddingBox.width() - renderBox.paddingLeft() - renderBox.paddingRight(), paddingBox.height() - renderBox.paddingTop() - renderBox.paddingBottom());
121             borderBox = LayoutRect(paddingBox.x() - renderBox.borderLeft(), paddingBox.y() - renderBox.borderTop(),
122                 paddingBox.width() + renderBox.borderLeft() + renderBox.borderRight(), paddingBox.height() + renderBox.borderTop() + renderBox.borderBottom());
123             marginBox = LayoutRect(borderBox.x() - margins.left(), borderBox.y() - margins.top(),
124                 borderBox.width() + margins.left() + margins.right(), borderBox.height() + margins.top() + margins.bottom());
125         } else {
126             auto& renderInline = downcast<RenderInline>(*renderer);
127
128             // RenderInline's bounding box includes paddings and borders, excludes margins.
129             borderBox = renderInline.linesBoundingBox();
130             paddingBox = LayoutRect(borderBox.x() + renderInline.borderLeft(), borderBox.y() + renderInline.borderTop(),
131                 borderBox.width() - renderInline.borderLeft() - renderInline.borderRight(), borderBox.height() - renderInline.borderTop() - renderInline.borderBottom());
132             contentBox = LayoutRect(paddingBox.x() + renderInline.paddingLeft(), paddingBox.y() + renderInline.paddingTop(),
133                 paddingBox.width() - renderInline.paddingLeft() - renderInline.paddingRight(), paddingBox.height() - renderInline.paddingTop() - renderInline.paddingBottom());
134             // Ignore marginTop and marginBottom for inlines.
135             marginBox = LayoutRect(borderBox.x() - renderInline.marginLeft(), borderBox.y(),
136                 borderBox.width() + renderInline.horizontalMarginExtent(), borderBox.height());
137         }
138
139         FloatQuad absContentQuad;
140         FloatQuad absPaddingQuad;
141         FloatQuad absBorderQuad;
142         FloatQuad absMarginQuad;
143
144         if (region) {
145             RenderFlowThread* flowThread = region->flowThread();
146
147             // Figure out the quads in the space of the RenderFlowThread.
148             absContentQuad = renderer->localToContainerQuad(FloatRect(contentBox), flowThread);
149             absPaddingQuad = renderer->localToContainerQuad(FloatRect(paddingBox), flowThread);
150             absBorderQuad = renderer->localToContainerQuad(FloatRect(borderBox), flowThread);
151             absMarginQuad = renderer->localToContainerQuad(FloatRect(marginBox), flowThread);
152
153             // Move the quad relative to the space of the current region.
154             LayoutRect flippedRegionRect(region->flowThreadPortionRect());
155             flowThread->flipForWritingMode(flippedRegionRect);
156
157             FloatSize delta = region->contentBoxRect().location() - flippedRegionRect.location();
158             absContentQuad.move(delta);
159             absPaddingQuad.move(delta);
160             absBorderQuad.move(delta);
161             absMarginQuad.move(delta);
162
163             // Resolve the absolute quads starting from the current region.
164             absContentQuad = region->localToAbsoluteQuad(absContentQuad);
165             absPaddingQuad = region->localToAbsoluteQuad(absPaddingQuad);
166             absBorderQuad = region->localToAbsoluteQuad(absBorderQuad);
167             absMarginQuad = region->localToAbsoluteQuad(absMarginQuad);
168         } else {
169             absContentQuad = renderer->localToAbsoluteQuad(FloatRect(contentBox));
170             absPaddingQuad = renderer->localToAbsoluteQuad(FloatRect(paddingBox));
171             absBorderQuad = renderer->localToAbsoluteQuad(FloatRect(borderBox));
172             absMarginQuad = renderer->localToAbsoluteQuad(FloatRect(marginBox));
173         }
174
175         contentsQuadToCoordinateSystem(mainView, containingView, absContentQuad, coordinateSystem);
176         contentsQuadToCoordinateSystem(mainView, containingView, absPaddingQuad, coordinateSystem);
177         contentsQuadToCoordinateSystem(mainView, containingView, absBorderQuad, coordinateSystem);
178         contentsQuadToCoordinateSystem(mainView, containingView, absMarginQuad, coordinateSystem);
179
180         highlight.type = HighlightType::Node;
181         highlight.quads.append(absMarginQuad);
182         highlight.quads.append(absBorderQuad);
183         highlight.quads.append(absPaddingQuad);
184         highlight.quads.append(absContentQuad);
185     }
186 }
187
188 static void buildNodeHighlight(Node& node, RenderRegion* region, const HighlightConfig& highlightConfig, Highlight& highlight, InspectorOverlay::CoordinateSystem coordinateSystem)
189 {
190     RenderObject* renderer = node.renderer();
191     if (!renderer)
192         return;
193
194     buildRendererHighlight(renderer, region, highlightConfig, highlight, coordinateSystem);
195 }
196
197 static void buildQuadHighlight(const FloatQuad& quad, const HighlightConfig& highlightConfig, Highlight& highlight)
198 {
199     highlight.setDataFromConfig(highlightConfig);
200     highlight.type = HighlightType::Rects;
201     highlight.quads.append(quad);
202 }
203
204 InspectorOverlay::InspectorOverlay(Page& page, InspectorClient* client)
205     : m_page(page)
206     , m_client(client)
207     , m_paintRectUpdateTimer(*this, &InspectorOverlay::updatePaintRectsTimerFired)
208 {
209 }
210
211 InspectorOverlay::~InspectorOverlay()
212 {
213 }
214
215 void InspectorOverlay::paint(GraphicsContext& context)
216 {
217     if (!shouldShowOverlay())
218         return;
219
220     GraphicsContextStateSaver stateSaver(context);
221     FrameView* view = overlayPage()->mainFrame().view();
222     view->updateLayoutAndStyleIfNeededRecursive();
223     view->paint(&context, IntRect(0, 0, view->width(), view->height()));
224 }
225
226 void InspectorOverlay::getHighlight(Highlight& highlight, InspectorOverlay::CoordinateSystem coordinateSystem) const
227 {
228     if (!m_highlightNode && !m_highlightQuad)
229         return;
230
231     highlight.type = HighlightType::Rects;
232     if (m_highlightNode)
233         buildNodeHighlight(*m_highlightNode, nullptr, m_nodeHighlightConfig, highlight, coordinateSystem);
234     else
235         buildQuadHighlight(*m_highlightQuad, m_quadHighlightConfig, highlight);
236 }
237
238 void InspectorOverlay::setPausedInDebuggerMessage(const String* message)
239 {
240     m_pausedInDebuggerMessage = message ? *message : String();
241     update();
242 }
243
244 void InspectorOverlay::hideHighlight()
245 {
246     m_highlightNode.clear();
247     m_highlightNodeList.clear();
248     m_highlightQuad.reset();
249     update();
250 }
251
252 void InspectorOverlay::highlightNodeList(PassRefPtr<NodeList> nodes, const HighlightConfig& highlightConfig)
253 {
254     m_nodeHighlightConfig = highlightConfig;
255     m_highlightNodeList = nodes;
256     m_highlightNode.clear();
257     update();
258 }
259
260 void InspectorOverlay::highlightNode(Node* node, const HighlightConfig& highlightConfig)
261 {
262     m_nodeHighlightConfig = highlightConfig;
263     m_highlightNode = node;
264     m_highlightNodeList.clear();
265     update();
266 }
267
268 void InspectorOverlay::highlightQuad(std::unique_ptr<FloatQuad> quad, const HighlightConfig& highlightConfig)
269 {
270     if (m_quadHighlightConfig.usePageCoordinates)
271         *quad -= m_page.mainFrame().view()->scrollOffset();
272
273     m_quadHighlightConfig = highlightConfig;
274     m_highlightQuad = WTF::move(quad);
275     update();
276 }
277
278 Node* InspectorOverlay::highlightedNode() const
279 {
280     return m_highlightNode.get();
281 }
282
283 void InspectorOverlay::didSetSearchingForNode(bool enabled)
284 {
285     m_client->didSetSearchingForNode(enabled);
286 }
287
288 void InspectorOverlay::setIndicating(bool indicating)
289 {
290     m_indicating = indicating;
291
292     if (m_indicating)
293         evaluateInOverlay(ASCIILiteral("showPageIndication"));
294     else
295         evaluateInOverlay(ASCIILiteral("hidePageIndication"));
296
297     update();
298 }
299
300 bool InspectorOverlay::shouldShowOverlay() const
301 {
302     return m_highlightNode || m_highlightNodeList || m_highlightQuad || m_indicating || m_showingPaintRects || !m_pausedInDebuggerMessage.isNull();
303 }
304
305 void InspectorOverlay::update()
306 {
307     if (!shouldShowOverlay()) {
308         m_client->hideHighlight();
309         return;
310     }
311
312     FrameView* view = m_page.mainFrame().view();
313     if (!view)
314         return;
315
316     FrameView* overlayView = overlayPage()->mainFrame().view();
317     IntSize viewportSize = view->unscaledVisibleContentSizeIncludingObscuredArea();
318     IntSize frameViewFullSize = view->unscaledVisibleContentSizeIncludingObscuredArea(ScrollableArea::IncludeScrollbars);
319     overlayPage()->setPageScaleFactor(m_page.pageScaleFactor(), IntPoint());
320     frameViewFullSize.scale(m_page.pageScaleFactor());
321     overlayView->resize(frameViewFullSize);
322
323     // Clear canvas and paint things.
324     // FIXME: Remove extra parameter?
325     reset(viewportSize, IntSize());
326
327     // Include scrollbars to avoid masking them by the gutter.
328     drawGutter();
329     drawNodeHighlight();
330     drawQuadHighlight();
331     drawPausedInDebuggerMessage();
332     drawPaintRects();
333
334     // Position DOM elements.
335     overlayPage()->mainFrame().document()->recalcStyle(Style::Force);
336     if (overlayView->needsLayout())
337         overlayView->layout();
338
339     forcePaint();
340 }
341
342 static Ref<Inspector::Protocol::OverlayTypes::Point> buildObjectForPoint(const FloatPoint& point)
343 {
344     return Inspector::Protocol::OverlayTypes::Point::create()
345         .setX(point.x())
346         .setY(point.y())
347         .release();
348 }
349
350 static Ref<Inspector::Protocol::OverlayTypes::Rect> buildObjectForRect(const FloatRect& rect)
351 {
352     return Inspector::Protocol::OverlayTypes::Rect::create()
353         .setX(rect.x())
354         .setY(rect.y())
355         .setWidth(rect.width())
356         .setHeight(rect.height())
357         .release();
358 }
359
360 static Ref<Inspector::Protocol::OverlayTypes::Quad> buildArrayForQuad(const FloatQuad& quad)
361 {
362     auto array = Inspector::Protocol::OverlayTypes::Quad::create();
363     array->addItem(buildObjectForPoint(quad.p1()));
364     array->addItem(buildObjectForPoint(quad.p2()));
365     array->addItem(buildObjectForPoint(quad.p3()));
366     array->addItem(buildObjectForPoint(quad.p4()));
367     return WTF::move(array);
368 }
369
370 static Ref<Inspector::Protocol::OverlayTypes::FragmentHighlightData> buildObjectForHighlight(const Highlight& highlight)
371 {
372     auto arrayOfQuads = Inspector::Protocol::Array<Inspector::Protocol::OverlayTypes::Quad>::create();
373     for (size_t i = 0; i < highlight.quads.size(); ++i)
374         arrayOfQuads->addItem(buildArrayForQuad(highlight.quads[i]));
375
376     return Inspector::Protocol::OverlayTypes::FragmentHighlightData::create()
377         .setQuads(WTF::move(arrayOfQuads))
378         .setContentColor(highlight.contentColor.serialized())
379         .setContentOutlineColor(highlight.contentOutlineColor.serialized())
380         .setPaddingColor(highlight.paddingColor.serialized())
381         .setBorderColor(highlight.borderColor.serialized())
382         .setMarginColor(highlight.marginColor.serialized())
383         .release();
384 }
385
386 static RefPtr<Inspector::Protocol::OverlayTypes::Region> buildObjectForRegion(FrameView* mainView, RenderRegion* region)
387 {
388     FrameView* containingView = region->frame().view();
389     if (!containingView)
390         return nullptr;
391
392     RenderBlockFlow& regionContainer = downcast<RenderBlockFlow>(*region->parent());
393     LayoutRect borderBox = regionContainer.borderBoxRect();
394     borderBox.setWidth(borderBox.width() + regionContainer.verticalScrollbarWidth());
395     borderBox.setHeight(borderBox.height() + regionContainer.horizontalScrollbarHeight());
396
397     // Create incoming and outgoing boxes that we use to chain the regions toghether.
398     const LayoutSize linkBoxSize(10, 10);
399     const LayoutSize linkBoxMidpoint(linkBoxSize.width() / 2, linkBoxSize.height() / 2);
400
401     LayoutRect incomingRectBox = LayoutRect(borderBox.location() - linkBoxMidpoint, linkBoxSize);
402     LayoutRect outgoingRectBox = LayoutRect(borderBox.location() - linkBoxMidpoint + borderBox.size(), linkBoxSize);
403
404     // Move the link boxes slightly inside the region border box.
405     LayoutUnit maxUsableHeight = std::max(LayoutUnit(), borderBox.height() - linkBoxMidpoint.height());
406     LayoutUnit linkBoxVerticalOffset = std::min(LayoutUnit::fromPixel(15), maxUsableHeight);
407     incomingRectBox.move(0, linkBoxVerticalOffset);
408     outgoingRectBox.move(0, -linkBoxVerticalOffset);
409
410     FloatQuad borderRectQuad = regionContainer.localToAbsoluteQuad(FloatRect(borderBox));
411     FloatQuad incomingRectQuad = regionContainer.localToAbsoluteQuad(FloatRect(incomingRectBox));
412     FloatQuad outgoingRectQuad = regionContainer.localToAbsoluteQuad(FloatRect(outgoingRectBox));
413
414     contentsQuadToPage(mainView, containingView, borderRectQuad);
415     contentsQuadToPage(mainView, containingView, incomingRectQuad);
416     contentsQuadToPage(mainView, containingView, outgoingRectQuad);
417
418     return Inspector::Protocol::OverlayTypes::Region::create()
419         .setBorderQuad(buildArrayForQuad(borderRectQuad))
420         .setIncomingQuad(buildArrayForQuad(incomingRectQuad))
421         .setOutgoingQuad(buildArrayForQuad(outgoingRectQuad))
422         .release();
423 }
424
425 static Ref<Inspector::Protocol::Array<Inspector::Protocol::OverlayTypes::Region>> buildObjectForFlowRegions(RenderRegion* region, RenderFlowThread* flowThread)
426 {
427     FrameView* mainFrameView = region->document().page()->mainFrame().view();
428
429     auto arrayOfRegions = Inspector::Protocol::Array<Inspector::Protocol::OverlayTypes::Region>::create();
430
431     const RenderRegionList& regionList = flowThread->renderRegionList();
432     for (auto& iterRegion : regionList) {
433         if (!iterRegion->isValid())
434             continue;
435         RefPtr<Inspector::Protocol::OverlayTypes::Region> regionObject = buildObjectForRegion(mainFrameView, iterRegion);
436         if (!regionObject)
437             continue;
438         if (region == iterRegion) {
439             // Let the script know that this is the currently highlighted node.
440             regionObject->setIsHighlighted(true);
441         }
442         arrayOfRegions->addItem(WTF::move(regionObject));
443     }
444
445     return WTF::move(arrayOfRegions);
446 }
447
448 static Ref<Inspector::Protocol::OverlayTypes::Size> buildObjectForSize(const IntSize& size)
449 {
450     return Inspector::Protocol::OverlayTypes::Size::create()
451         .setWidth(size.width())
452         .setHeight(size.height())
453         .release();
454 }
455
456 static RefPtr<Inspector::Protocol::OverlayTypes::Quad> buildQuadObjectForCSSRegionContentClip(RenderRegion* region)
457 {
458     Frame* containingFrame = region->document().frame();
459     if (!containingFrame)
460         return nullptr;
461
462     FrameView* containingView = containingFrame->view();
463     FrameView* mainView = containingFrame->page()->mainFrame().view();
464     RenderFlowThread* flowThread = region->flowThread();
465
466     // Get the clip box of the current region and covert it into an absolute quad.
467     LayoutRect flippedRegionRect(region->flowThreadPortionOverflowRect());
468     flowThread->flipForWritingMode(flippedRegionRect);
469
470     // Apply any border or padding of the region.
471     flippedRegionRect.setLocation(region->contentBoxRect().location());
472     
473     FloatQuad clipQuad = region->localToAbsoluteQuad(FloatRect(flippedRegionRect));
474     contentsQuadToPage(mainView, containingView, clipQuad);
475
476     return buildArrayForQuad(clipQuad);
477 }
478
479 void InspectorOverlay::setShowingPaintRects(bool showingPaintRects)
480 {
481     if (m_showingPaintRects == showingPaintRects)
482         return;
483
484     m_showingPaintRects = showingPaintRects;
485     if (!m_showingPaintRects) {
486         m_paintRects.clear();
487         m_paintRectUpdateTimer.stop();
488         drawPaintRects();
489         forcePaint();
490     }
491 }
492
493 void InspectorOverlay::showPaintRect(const FloatRect& rect)
494 {
495     if (!m_showingPaintRects)
496         return;
497
498     IntRect rootRect = m_page.mainFrame().view()->contentsToRootView(enclosingIntRect(rect));
499
500     const std::chrono::milliseconds removeDelay = std::chrono::milliseconds(250);
501
502     std::chrono::steady_clock::time_point removeTime = std::chrono::steady_clock::now() + removeDelay;
503     m_paintRects.append(TimeRectPair(removeTime, rootRect));
504
505     if (!m_paintRectUpdateTimer.isActive()) {
506         const double paintRectsUpdateIntervalSeconds = 0.032;
507         m_paintRectUpdateTimer.startRepeating(paintRectsUpdateIntervalSeconds);
508     }
509
510     drawPaintRects();
511     forcePaint();
512 }
513
514 void InspectorOverlay::updatePaintRectsTimerFired()
515 {
516     std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
517     bool rectsChanged = false;
518     while (!m_paintRects.isEmpty() && m_paintRects.first().first < now) {
519         m_paintRects.removeFirst();
520         rectsChanged = true;
521     }
522
523     if (m_paintRects.isEmpty())
524         m_paintRectUpdateTimer.stop();
525
526     if (rectsChanged) {
527         drawPaintRects();
528         forcePaint();
529     }
530 }
531
532 void InspectorOverlay::drawPaintRects()
533 {
534     auto arrayOfRects = Inspector::Protocol::Array<Inspector::Protocol::OverlayTypes::Rect>::create();
535     for (const auto& pair : m_paintRects)
536         arrayOfRects->addItem(buildObjectForRect(pair.second));
537
538     evaluateInOverlay(ASCIILiteral("updatePaintRects"), WTF::move(arrayOfRects));
539 }
540
541 void InspectorOverlay::drawGutter()
542 {
543     evaluateInOverlay(ASCIILiteral("drawGutter"));
544 }
545
546 static RefPtr<Inspector::Protocol::Array<Inspector::Protocol::OverlayTypes::FragmentHighlightData>> buildArrayForRendererFragments(RenderObject* renderer, const HighlightConfig& config)
547 {
548     auto arrayOfFragments = Inspector::Protocol::Array<Inspector::Protocol::OverlayTypes::FragmentHighlightData>::create();
549
550     RenderFlowThread* containingFlowThread = renderer->flowThreadContainingBlock();
551     if (!containingFlowThread) {
552         Highlight highlight;
553         buildRendererHighlight(renderer, nullptr, config, highlight, InspectorOverlay::CoordinateSystem::View);
554         arrayOfFragments->addItem(buildObjectForHighlight(highlight));
555     } else {
556         RenderRegion* startRegion = nullptr;
557         RenderRegion* endRegion = nullptr;
558         if (!containingFlowThread->getRegionRangeForBox(&renderer->enclosingBox(), startRegion, endRegion)) {
559             // The flow has no visible regions. The renderer is not visible on screen.
560             return nullptr;
561         }
562
563         const RenderRegionList& regionList = containingFlowThread->renderRegionList();
564         for (RenderRegionList::const_iterator iter = regionList.find(startRegion); iter != regionList.end(); ++iter) {
565             RenderRegion* region = *iter;
566             if (region->isValid()) {
567                 // Compute the highlight of the fragment inside the current region.
568                 Highlight highlight;
569                 buildRendererHighlight(renderer, region, config, highlight, InspectorOverlay::CoordinateSystem::View);
570                 Ref<Inspector::Protocol::OverlayTypes::FragmentHighlightData> fragmentHighlight = buildObjectForHighlight(highlight);
571
572                 // Compute the clipping area of the region.
573                 fragmentHighlight->setRegionClippingArea(buildQuadObjectForCSSRegionContentClip(region));
574                 arrayOfFragments->addItem(WTF::move(fragmentHighlight));
575             }
576             if (region == endRegion)
577                 break;
578         }
579     }
580
581     return WTF::move(arrayOfFragments);
582 }
583
584 #if ENABLE(CSS_SHAPES)
585 static FloatPoint localPointToRoot(RenderObject* renderer, const FrameView* mainView, const FrameView* view, const FloatPoint& point)
586 {
587     FloatPoint result = renderer->localToAbsolute(point);
588     result = view->contentsToRootView(roundedIntPoint(result));
589     result += mainView->scrollOffset();
590     return result;
591 }
592
593 struct PathApplyInfo {
594     FrameView* rootView;
595     FrameView* view;
596     Inspector::Protocol::OverlayTypes::DisplayPath* pathArray;
597     RenderObject* renderer;
598     const ShapeOutsideInfo* shapeOutsideInfo;
599 };
600
601 static void appendPathCommandAndPoints(PathApplyInfo& info, const String& command, const FloatPoint points[], unsigned length)
602 {
603     FloatPoint point;
604     info.pathArray->addItem(command);
605     for (unsigned i = 0; i < length; i++) {
606         point = info.shapeOutsideInfo->shapeToRendererPoint(points[i]);
607         point = localPointToRoot(info.renderer, info.rootView, info.view, point);
608         info.pathArray->addItem(point.x());
609         info.pathArray->addItem(point.y());
610     }
611 }
612
613 // Used as a functor for Shape::apply, which has not been cleaned up to use modern C++.
614 static void appendPathSegment(void* info, const PathElement* pathElement)
615 {
616     PathApplyInfo& pathApplyInfo = *static_cast<PathApplyInfo*>(info);
617     FloatPoint point;
618     switch (pathElement->type) {
619     // The points member will contain 1 value.
620     case PathElementMoveToPoint:
621         appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("M"), pathElement->points, 1);
622         break;
623     // The points member will contain 1 value.
624     case PathElementAddLineToPoint:
625         appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("L"), pathElement->points, 1);
626         break;
627     // The points member will contain 3 values.
628     case PathElementAddCurveToPoint:
629         appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("C"), pathElement->points, 3);
630         break;
631     // The points member will contain 2 values.
632     case PathElementAddQuadCurveToPoint:
633         appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("Q"), pathElement->points, 2);
634         break;
635     // The points member will contain no values.
636     case PathElementCloseSubpath:
637         appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("Z"), nullptr, 0);
638         break;
639     }
640 }
641
642 static RefPtr<Inspector::Protocol::OverlayTypes::ShapeOutsideData> buildObjectForShapeOutside(Frame* containingFrame, RenderBox* renderer)
643 {
644     const ShapeOutsideInfo* shapeOutsideInfo = renderer->shapeOutsideInfo();
645     if (!shapeOutsideInfo)
646         return nullptr;
647
648     LayoutRect shapeBounds = shapeOutsideInfo->computedShapePhysicalBoundingBox();
649     FloatQuad shapeQuad = renderer->localToAbsoluteQuad(FloatRect(shapeBounds));
650     contentsQuadToPage(containingFrame->page()->mainFrame().view(), containingFrame->view(), shapeQuad);
651
652     auto shapeObject = Inspector::Protocol::OverlayTypes::ShapeOutsideData::create()
653         .setBounds(buildArrayForQuad(shapeQuad))
654         .release();
655
656     Shape::DisplayPaths paths;
657     shapeOutsideInfo->computedShape().buildDisplayPaths(paths);
658
659     if (paths.shape.length()) {
660         auto shapePath = Inspector::Protocol::OverlayTypes::DisplayPath::create();
661         PathApplyInfo info;
662         info.rootView = containingFrame->page()->mainFrame().view();
663         info.view = containingFrame->view();
664         info.pathArray = &shapePath.get();
665         info.renderer = renderer;
666         info.shapeOutsideInfo = shapeOutsideInfo;
667
668         paths.shape.apply(&info, &appendPathSegment);
669
670         shapeObject->setShape(shapePath.copyRef());
671
672         if (paths.marginShape.length()) {
673             auto marginShapePath = Inspector::Protocol::OverlayTypes::DisplayPath::create();
674             info.pathArray = &marginShapePath.get();
675
676             paths.marginShape.apply(&info, &appendPathSegment);
677
678             shapeObject->setMarginShape(shapePath.copyRef());
679         }
680     }
681
682     return WTF::move(shapeObject);
683 }
684 #endif
685
686 static RefPtr<Inspector::Protocol::OverlayTypes::ElementData> buildObjectForElementData(Node* node, HighlightType type)
687 {
688     if (!is<Element>(node) || !node->document().frame())
689         return nullptr;
690
691     Element& element = downcast<Element>(*node);
692     bool isXHTML = element.document().isXHTMLDocument();
693
694     auto elementData = Inspector::Protocol::OverlayTypes::ElementData::create()
695         .setTagName(isXHTML ? element.nodeName() : element.nodeName().lower())
696         .setIdValue(element.getIdAttribute())
697         .release();
698
699     HashSet<AtomicString> usedClassNames;
700     if (element.hasClass() && is<StyledElement>(element)) {
701         StringBuilder classNames;
702         const SpaceSplitString& classNamesString = downcast<StyledElement>(element).classNames();
703         size_t classNameCount = classNamesString.size();
704         for (size_t i = 0; i < classNameCount; ++i) {
705             const AtomicString& className = classNamesString[i];
706             if (usedClassNames.contains(className))
707                 continue;
708             usedClassNames.add(className);
709             classNames.append('.');
710             classNames.append(className);
711         }
712         elementData->setClassName(classNames.toString());
713     }
714
715     RenderElement* renderer = element.renderer();
716     if (!renderer)
717         return nullptr;
718
719     Frame* containingFrame = node->document().frame();
720     FrameView* containingView = containingFrame->view();
721     IntRect boundingBox = snappedIntRect(containingView->contentsToRootView(renderer->absoluteBoundingBoxRect()));
722     RenderBoxModelObject* modelObject = is<RenderBoxModelObject>(*renderer) ? downcast<RenderBoxModelObject>(renderer) : nullptr;
723     auto sizeObject = Inspector::Protocol::OverlayTypes::Size::create()
724         .setWidth(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetWidth(), *modelObject) : boundingBox.width())
725         .setHeight(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetHeight(), *modelObject) : boundingBox.height())
726         .release();
727     elementData->setSize(WTF::move(sizeObject));
728
729     if (type != HighlightType::NodeList && renderer->isRenderNamedFlowFragmentContainer()) {
730         RenderNamedFlowFragment& region = *downcast<RenderBlockFlow>(*renderer).renderNamedFlowFragment();
731         if (region.isValid()) {
732             RenderFlowThread* flowThread = region.flowThread();
733             auto regionFlowData = Inspector::Protocol::OverlayTypes::RegionFlowData::create()
734                 .setName(downcast<RenderNamedFlowThread>(*flowThread).flowThreadName())
735                 .setRegions(buildObjectForFlowRegions(&region, flowThread))
736                 .release();
737             elementData->setRegionFlowData(WTF::move(regionFlowData));
738         }
739     }
740
741     RenderFlowThread* containingFlowThread = renderer->flowThreadContainingBlock();
742     if (is<RenderNamedFlowThread>(containingFlowThread)) {
743         auto contentFlowData = Inspector::Protocol::OverlayTypes::ContentFlowData::create()
744             .setName(downcast<RenderNamedFlowThread>(*containingFlowThread).flowThreadName())
745             .release();
746
747         elementData->setContentFlowData(WTF::move(contentFlowData));
748     }
749
750 #if ENABLE(CSS_SHAPES)
751     if (is<RenderBox>(*renderer)) {
752         auto& renderBox = downcast<RenderBox>(*renderer);
753         if (RefPtr<Inspector::Protocol::OverlayTypes::ShapeOutsideData> shapeObject = buildObjectForShapeOutside(containingFrame, &renderBox))
754             elementData->setShapeOutsideData(WTF::move(shapeObject));
755     }
756 #endif
757
758     // Need to enable AX to get the computed role.
759     if (!WebCore::AXObjectCache::accessibilityEnabled())
760         WebCore::AXObjectCache::enableAccessibility();
761
762     if (AXObjectCache* axObjectCache = node->document().axObjectCache()) {
763         if (AccessibilityObject* axObject = axObjectCache->getOrCreate(node))
764             elementData->setRole(axObject->computedRoleString());
765     }
766
767     return WTF::move(elementData);
768 }
769
770 RefPtr<Inspector::Protocol::OverlayTypes::NodeHighlightData> InspectorOverlay::buildHighlightObjectForNode(Node* node, HighlightType type) const
771 {
772     if (!node)
773         return nullptr;
774
775     RenderObject* renderer = node->renderer();
776     if (!renderer)
777         return nullptr;
778
779     RefPtr<Inspector::Protocol::Array<Inspector::Protocol::OverlayTypes::FragmentHighlightData>> arrayOfFragmentHighlights = buildArrayForRendererFragments(renderer, m_nodeHighlightConfig);
780     if (!arrayOfFragmentHighlights)
781         return nullptr;
782
783     // The main view's scroll offset is shared across all quads.
784     FrameView* mainView = m_page.mainFrame().view();
785
786     auto nodeHighlightObject = Inspector::Protocol::OverlayTypes::NodeHighlightData::create()
787         .setScrollOffset(buildObjectForPoint(!mainView->delegatesScrolling() ? mainView->visibleContentRect().location() : FloatPoint()))
788         .setFragments(WTF::move(arrayOfFragmentHighlights))
789         .release();
790
791     if (m_nodeHighlightConfig.showInfo) {
792         if (RefPtr<Inspector::Protocol::OverlayTypes::ElementData> elementData = buildObjectForElementData(node, type))
793             nodeHighlightObject->setElementData(WTF::move(elementData));
794     }
795
796     return WTF::move(nodeHighlightObject);
797 }
798
799 Ref<Inspector::Protocol::Array<Inspector::Protocol::OverlayTypes::NodeHighlightData>> InspectorOverlay::buildObjectForHighlightedNodes() const
800 {
801     auto highlights = Inspector::Protocol::Array<Inspector::Protocol::OverlayTypes::NodeHighlightData>::create();
802
803     if (m_highlightNode) {
804         if (RefPtr<Inspector::Protocol::OverlayTypes::NodeHighlightData> nodeHighlightData = buildHighlightObjectForNode(m_highlightNode.get(), HighlightType::Node))
805             highlights->addItem(WTF::move(nodeHighlightData));
806     } else if (m_highlightNodeList) {
807         for (unsigned i = 0; i < m_highlightNodeList->length(); ++i) {
808             if (RefPtr<Inspector::Protocol::OverlayTypes::NodeHighlightData> nodeHighlightData = buildHighlightObjectForNode(m_highlightNodeList->item(i), HighlightType::NodeList))
809                 highlights->addItem(WTF::move(nodeHighlightData));
810         }
811     }
812
813     return WTF::move(highlights);
814 }
815
816 void InspectorOverlay::drawNodeHighlight()
817 {
818     if (m_highlightNode || m_highlightNodeList)
819         evaluateInOverlay("drawNodeHighlight", buildObjectForHighlightedNodes());
820 }
821
822 void InspectorOverlay::drawQuadHighlight()
823 {
824     if (!m_highlightQuad)
825         return;
826
827     Highlight highlight;
828     buildQuadHighlight(*m_highlightQuad, m_quadHighlightConfig, highlight);
829     evaluateInOverlay("drawQuadHighlight", buildObjectForHighlight(highlight));
830 }
831
832 void InspectorOverlay::drawPausedInDebuggerMessage()
833 {
834     if (!m_pausedInDebuggerMessage.isNull())
835         evaluateInOverlay("drawPausedInDebuggerMessage", m_pausedInDebuggerMessage);
836 }
837
838 Page* InspectorOverlay::overlayPage()
839 {
840     if (m_overlayPage)
841         return m_overlayPage.get();
842
843     PageConfiguration pageConfiguration;
844     fillWithEmptyClients(pageConfiguration);
845     m_overlayPage = std::make_unique<Page>(pageConfiguration);
846
847     Settings& settings = m_page.settings();
848     Settings& overlaySettings = m_overlayPage->settings();
849
850     overlaySettings.setStandardFontFamily(settings.standardFontFamily());
851     overlaySettings.setSerifFontFamily(settings.serifFontFamily());
852     overlaySettings.setSansSerifFontFamily(settings.sansSerifFontFamily());
853     overlaySettings.setCursiveFontFamily(settings.cursiveFontFamily());
854     overlaySettings.setFantasyFontFamily(settings.fantasyFontFamily());
855     overlaySettings.setPictographFontFamily(settings.pictographFontFamily());
856     overlaySettings.setMinimumFontSize(settings.minimumFontSize());
857     overlaySettings.setMinimumLogicalFontSize(settings.minimumLogicalFontSize());
858     overlaySettings.setMediaEnabled(false);
859     overlaySettings.setScriptEnabled(true);
860     overlaySettings.setPluginsEnabled(false);
861
862     Frame& frame = m_overlayPage->mainFrame();
863     frame.setView(FrameView::create(frame));
864     frame.init();
865     FrameLoader& loader = frame.loader();
866     frame.view()->setCanHaveScrollbars(false);
867     frame.view()->setTransparent(true);
868     ASSERT(loader.activeDocumentLoader());
869     loader.activeDocumentLoader()->writer().setMIMEType("text/html");
870     loader.activeDocumentLoader()->writer().begin();
871     loader.activeDocumentLoader()->writer().addData(reinterpret_cast<const char*>(InspectorOverlayPage_html), sizeof(InspectorOverlayPage_html));
872     loader.activeDocumentLoader()->writer().end();
873
874 #if OS(WINDOWS)
875     evaluateInOverlay("setPlatform", "windows");
876 #elif OS(MAC_OS_X)
877     evaluateInOverlay("setPlatform", "mac");
878 #elif OS(UNIX)
879     evaluateInOverlay("setPlatform", "linux");
880 #endif
881
882     return m_overlayPage.get();
883 }
884
885 void InspectorOverlay::forcePaint()
886 {
887     // This overlay page is very weird and doesn't automatically paint. We have to force paints manually.
888     m_client->highlight();
889 }
890
891 void InspectorOverlay::reset(const IntSize& viewportSize, const IntSize& frameViewFullSize)
892 {
893     auto configObject = Inspector::Protocol::OverlayTypes::OverlayConfiguration::create()
894         .setDeviceScaleFactor(m_page.deviceScaleFactor())
895         .setViewportSize(buildObjectForSize(viewportSize))
896         .setFrameViewFullSize(buildObjectForSize(frameViewFullSize))
897         .release();
898     evaluateInOverlay("reset", WTF::move(configObject));
899 }
900
901 void InspectorOverlay::evaluateInOverlay(const String& method)
902 {
903     Ref<InspectorArray> command = InspectorArray::create();
904     command->pushString(method);
905     overlayPage()->mainFrame().script().evaluate(ScriptSourceCode(makeString("dispatch(", command->toJSONString(), ')')));
906 }
907
908 void InspectorOverlay::evaluateInOverlay(const String& method, const String& argument)
909 {
910     Ref<InspectorArray> command = InspectorArray::create();
911     command->pushString(method);
912     command->pushString(argument);
913     overlayPage()->mainFrame().script().evaluate(ScriptSourceCode(makeString("dispatch(", command->toJSONString(), ')')));
914 }
915
916 void InspectorOverlay::evaluateInOverlay(const String& method, RefPtr<InspectorValue>&& argument)
917 {
918     Ref<InspectorArray> command = InspectorArray::create();
919     command->pushString(method);
920     command->pushValue(WTF::move(argument));
921     overlayPage()->mainFrame().script().evaluate(ScriptSourceCode(makeString("dispatch(", command->toJSONString(), ')')));
922 }
923
924 void InspectorOverlay::freePage()
925 {
926     m_overlayPage.reset();
927 }
928
929 } // namespace WebCore