REGRESSION (r203535): Web Inspector: Inspector overlay node info has disappeared
[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 "PseudoElement.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/InspectorProtocolObjects.h>
60 #include <inspector/InspectorValues.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 += toIntSize(mainView->scrollPosition());
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 (auto& quad : highlight.quads)
99             contentsQuadToCoordinateSystem(mainView, containingView, quad, 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 && !m_highlightNodeList)
229         return;
230
231     highlight.type = HighlightType::Rects;
232     if (m_highlightNode)
233         buildNodeHighlight(*m_highlightNode, nullptr, m_nodeHighlightConfig, highlight, coordinateSystem);
234     else if (m_highlightNodeList) {
235         highlight.setDataFromConfig(m_nodeHighlightConfig);
236         for (unsigned i = 0; i < m_highlightNodeList->length(); ++i) {
237             Highlight nodeHighlight;
238             buildNodeHighlight(*(m_highlightNodeList->item(i)), nullptr, m_nodeHighlightConfig, nodeHighlight, coordinateSystem);
239             if (nodeHighlight.type == HighlightType::Node)
240                 highlight.quads.appendVector(nodeHighlight.quads);
241         }
242         highlight.type = HighlightType::NodeList;
243     } else
244         buildQuadHighlight(*m_highlightQuad, m_quadHighlightConfig, highlight);
245 }
246
247 void InspectorOverlay::setPausedInDebuggerMessage(const String* message)
248 {
249     m_pausedInDebuggerMessage = message ? *message : String();
250     update();
251 }
252
253 void InspectorOverlay::hideHighlight()
254 {
255     m_highlightNode = nullptr;
256     m_highlightNodeList = nullptr;
257     m_highlightQuad = nullptr;
258     update();
259 }
260
261 void InspectorOverlay::highlightNodeList(RefPtr<NodeList>&& nodes, const HighlightConfig& highlightConfig)
262 {
263     m_nodeHighlightConfig = highlightConfig;
264     m_highlightNodeList = WTFMove(nodes);
265     m_highlightNode = nullptr;
266     update();
267 }
268
269 void InspectorOverlay::highlightNode(Node* node, const HighlightConfig& highlightConfig)
270 {
271     m_nodeHighlightConfig = highlightConfig;
272     m_highlightNode = node;
273     m_highlightNodeList = nullptr;
274     update();
275 }
276
277 void InspectorOverlay::highlightQuad(std::unique_ptr<FloatQuad> quad, const HighlightConfig& highlightConfig)
278 {
279     if (highlightConfig.usePageCoordinates)
280         *quad -= toIntSize(m_page.mainFrame().view()->scrollPosition());
281
282     m_quadHighlightConfig = highlightConfig;
283     m_highlightQuad = WTFMove(quad);
284     update();
285 }
286
287 Node* InspectorOverlay::highlightedNode() const
288 {
289     return m_highlightNode.get();
290 }
291
292 void InspectorOverlay::didSetSearchingForNode(bool enabled)
293 {
294     m_client->didSetSearchingForNode(enabled);
295 }
296
297 void InspectorOverlay::setIndicating(bool indicating)
298 {
299     m_indicating = indicating;
300
301     if (m_indicating)
302         evaluateInOverlay(ASCIILiteral("showPageIndication"));
303     else
304         evaluateInOverlay(ASCIILiteral("hidePageIndication"));
305
306     update();
307 }
308
309 bool InspectorOverlay::shouldShowOverlay() const
310 {
311     return m_highlightNode || m_highlightNodeList || m_highlightQuad || m_indicating || m_showingPaintRects || !m_pausedInDebuggerMessage.isNull();
312 }
313
314 void InspectorOverlay::update()
315 {
316     if (!shouldShowOverlay()) {
317         m_client->hideHighlight();
318         return;
319     }
320
321     FrameView* view = m_page.mainFrame().view();
322     if (!view)
323         return;
324
325     FrameView* overlayView = overlayPage()->mainFrame().view();
326     IntSize viewportSize = view->unscaledVisibleContentSizeIncludingObscuredArea();
327     IntSize frameViewFullSize = view->unscaledVisibleContentSizeIncludingObscuredArea(ScrollableArea::IncludeScrollbars);
328     overlayView->resize(frameViewFullSize);
329
330     // Clear canvas and paint things.
331     // FIXME: Remove extra parameter?
332     reset(viewportSize, IntSize());
333
334     // Include scrollbars to avoid masking them by the gutter.
335     drawGutter();
336     drawNodeHighlight();
337     drawQuadHighlight();
338     drawPausedInDebuggerMessage();
339     drawPaintRects();
340
341     // Position DOM elements.
342     overlayPage()->mainFrame().document()->recalcStyle(Style::Force);
343     if (overlayView->needsLayout())
344         overlayView->layout();
345
346     forcePaint();
347 }
348
349 static Ref<Inspector::Protocol::OverlayTypes::Point> buildObjectForPoint(const FloatPoint& point)
350 {
351     return Inspector::Protocol::OverlayTypes::Point::create()
352         .setX(point.x())
353         .setY(point.y())
354         .release();
355 }
356
357 static Ref<Inspector::Protocol::OverlayTypes::Rect> buildObjectForRect(const FloatRect& rect)
358 {
359     return Inspector::Protocol::OverlayTypes::Rect::create()
360         .setX(rect.x())
361         .setY(rect.y())
362         .setWidth(rect.width())
363         .setHeight(rect.height())
364         .release();
365 }
366
367 static Ref<Inspector::Protocol::OverlayTypes::Quad> buildArrayForQuad(const FloatQuad& quad)
368 {
369     auto array = Inspector::Protocol::OverlayTypes::Quad::create();
370     array->addItem(buildObjectForPoint(quad.p1()));
371     array->addItem(buildObjectForPoint(quad.p2()));
372     array->addItem(buildObjectForPoint(quad.p3()));
373     array->addItem(buildObjectForPoint(quad.p4()));
374     return array;
375 }
376
377 static Ref<Inspector::Protocol::OverlayTypes::FragmentHighlightData> buildObjectForHighlight(const Highlight& highlight)
378 {
379     auto arrayOfQuads = Inspector::Protocol::Array<Inspector::Protocol::OverlayTypes::Quad>::create();
380     for (auto& quad : highlight.quads)
381         arrayOfQuads->addItem(buildArrayForQuad(quad));
382
383     return Inspector::Protocol::OverlayTypes::FragmentHighlightData::create()
384         .setQuads(WTFMove(arrayOfQuads))
385         .setContentColor(highlight.contentColor.serialized())
386         .setContentOutlineColor(highlight.contentOutlineColor.serialized())
387         .setPaddingColor(highlight.paddingColor.serialized())
388         .setBorderColor(highlight.borderColor.serialized())
389         .setMarginColor(highlight.marginColor.serialized())
390         .release();
391 }
392
393 static RefPtr<Inspector::Protocol::OverlayTypes::Region> buildObjectForRegion(FrameView* mainView, RenderRegion* region)
394 {
395     FrameView* containingView = region->frame().view();
396     if (!containingView)
397         return nullptr;
398
399     RenderBlockFlow& regionContainer = downcast<RenderBlockFlow>(*region->parent());
400     LayoutRect borderBox = regionContainer.borderBoxRect();
401     borderBox.setWidth(borderBox.width() + regionContainer.verticalScrollbarWidth());
402     borderBox.setHeight(borderBox.height() + regionContainer.horizontalScrollbarHeight());
403
404     // Create incoming and outgoing boxes that we use to chain the regions toghether.
405     const LayoutSize linkBoxSize(10, 10);
406     const LayoutSize linkBoxMidpoint(linkBoxSize.width() / 2, linkBoxSize.height() / 2);
407
408     LayoutRect incomingRectBox = LayoutRect(borderBox.location() - linkBoxMidpoint, linkBoxSize);
409     LayoutRect outgoingRectBox = LayoutRect(borderBox.location() - linkBoxMidpoint + borderBox.size(), linkBoxSize);
410
411     // Move the link boxes slightly inside the region border box.
412     LayoutUnit maxUsableHeight = std::max(LayoutUnit(), borderBox.height() - linkBoxMidpoint.height());
413     LayoutUnit linkBoxVerticalOffset = std::min(LayoutUnit::fromPixel(15), maxUsableHeight);
414     incomingRectBox.move(0, linkBoxVerticalOffset);
415     outgoingRectBox.move(0, -linkBoxVerticalOffset);
416
417     FloatQuad borderRectQuad = regionContainer.localToAbsoluteQuad(FloatRect(borderBox));
418     FloatQuad incomingRectQuad = regionContainer.localToAbsoluteQuad(FloatRect(incomingRectBox));
419     FloatQuad outgoingRectQuad = regionContainer.localToAbsoluteQuad(FloatRect(outgoingRectBox));
420
421     contentsQuadToPage(mainView, containingView, borderRectQuad);
422     contentsQuadToPage(mainView, containingView, incomingRectQuad);
423     contentsQuadToPage(mainView, containingView, outgoingRectQuad);
424
425     return Inspector::Protocol::OverlayTypes::Region::create()
426         .setBorderQuad(buildArrayForQuad(borderRectQuad))
427         .setIncomingQuad(buildArrayForQuad(incomingRectQuad))
428         .setOutgoingQuad(buildArrayForQuad(outgoingRectQuad))
429         .release();
430 }
431
432 static Ref<Inspector::Protocol::Array<Inspector::Protocol::OverlayTypes::Region>> buildObjectForFlowRegions(RenderRegion* region, RenderFlowThread* flowThread)
433 {
434     FrameView* mainFrameView = region->document().page()->mainFrame().view();
435
436     auto arrayOfRegions = Inspector::Protocol::Array<Inspector::Protocol::OverlayTypes::Region>::create();
437
438     const RenderRegionList& regionList = flowThread->renderRegionList();
439     for (auto& iterRegion : regionList) {
440         if (!iterRegion->isValid())
441             continue;
442         RefPtr<Inspector::Protocol::OverlayTypes::Region> regionObject = buildObjectForRegion(mainFrameView, iterRegion);
443         if (!regionObject)
444             continue;
445         if (region == iterRegion) {
446             // Let the script know that this is the currently highlighted node.
447             regionObject->setIsHighlighted(true);
448         }
449         arrayOfRegions->addItem(WTFMove(regionObject));
450     }
451
452     return arrayOfRegions;
453 }
454
455 static Ref<Inspector::Protocol::OverlayTypes::Size> buildObjectForSize(const IntSize& size)
456 {
457     return Inspector::Protocol::OverlayTypes::Size::create()
458         .setWidth(size.width())
459         .setHeight(size.height())
460         .release();
461 }
462
463 static RefPtr<Inspector::Protocol::OverlayTypes::Quad> buildQuadObjectForCSSRegionContentClip(RenderRegion* region)
464 {
465     Frame* containingFrame = region->document().frame();
466     if (!containingFrame)
467         return nullptr;
468
469     FrameView* containingView = containingFrame->view();
470     FrameView* mainView = containingFrame->page()->mainFrame().view();
471     RenderFlowThread* flowThread = region->flowThread();
472
473     // Get the clip box of the current region and covert it into an absolute quad.
474     LayoutRect flippedRegionRect(region->flowThreadPortionOverflowRect());
475     flowThread->flipForWritingMode(flippedRegionRect);
476
477     // Apply any border or padding of the region.
478     flippedRegionRect.setLocation(region->contentBoxRect().location());
479     
480     FloatQuad clipQuad = region->localToAbsoluteQuad(FloatRect(flippedRegionRect));
481     contentsQuadToPage(mainView, containingView, clipQuad);
482
483     return buildArrayForQuad(clipQuad);
484 }
485
486 void InspectorOverlay::setShowingPaintRects(bool showingPaintRects)
487 {
488     if (m_showingPaintRects == showingPaintRects)
489         return;
490
491     m_showingPaintRects = showingPaintRects;
492     if (!m_showingPaintRects) {
493         m_paintRects.clear();
494         m_paintRectUpdateTimer.stop();
495         drawPaintRects();
496         forcePaint();
497     }
498 }
499
500 void InspectorOverlay::showPaintRect(const FloatRect& rect)
501 {
502     if (!m_showingPaintRects)
503         return;
504
505     IntRect rootRect = m_page.mainFrame().view()->contentsToRootView(enclosingIntRect(rect));
506
507     const auto removeDelay = 250ms;
508
509     std::chrono::steady_clock::time_point removeTime = std::chrono::steady_clock::now() + removeDelay;
510     m_paintRects.append(TimeRectPair(removeTime, rootRect));
511
512     if (!m_paintRectUpdateTimer.isActive()) {
513         const double paintRectsUpdateIntervalSeconds = 0.032;
514         m_paintRectUpdateTimer.startRepeating(paintRectsUpdateIntervalSeconds);
515     }
516
517     drawPaintRects();
518     forcePaint();
519 }
520
521 void InspectorOverlay::updatePaintRectsTimerFired()
522 {
523     std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
524     bool rectsChanged = false;
525     while (!m_paintRects.isEmpty() && m_paintRects.first().first < now) {
526         m_paintRects.removeFirst();
527         rectsChanged = true;
528     }
529
530     if (m_paintRects.isEmpty())
531         m_paintRectUpdateTimer.stop();
532
533     if (rectsChanged) {
534         drawPaintRects();
535         forcePaint();
536     }
537 }
538
539 void InspectorOverlay::drawPaintRects()
540 {
541     auto arrayOfRects = Inspector::Protocol::Array<Inspector::Protocol::OverlayTypes::Rect>::create();
542     for (const auto& pair : m_paintRects)
543         arrayOfRects->addItem(buildObjectForRect(pair.second));
544
545     evaluateInOverlay(ASCIILiteral("updatePaintRects"), WTFMove(arrayOfRects));
546 }
547
548 void InspectorOverlay::drawGutter()
549 {
550     evaluateInOverlay(ASCIILiteral("drawGutter"));
551 }
552
553 static RefPtr<Inspector::Protocol::Array<Inspector::Protocol::OverlayTypes::FragmentHighlightData>> buildArrayForRendererFragments(RenderObject* renderer, const HighlightConfig& config)
554 {
555     auto arrayOfFragments = Inspector::Protocol::Array<Inspector::Protocol::OverlayTypes::FragmentHighlightData>::create();
556
557     RenderFlowThread* containingFlowThread = renderer->flowThreadContainingBlock();
558     if (!containingFlowThread) {
559         Highlight highlight;
560         buildRendererHighlight(renderer, nullptr, config, highlight, InspectorOverlay::CoordinateSystem::View);
561         arrayOfFragments->addItem(buildObjectForHighlight(highlight));
562     } else {
563         RenderRegion* startRegion = nullptr;
564         RenderRegion* endRegion = nullptr;
565         if (!containingFlowThread->getRegionRangeForBox(&renderer->enclosingBox(), startRegion, endRegion)) {
566             // The flow has no visible regions. The renderer is not visible on screen.
567             return nullptr;
568         }
569
570         const RenderRegionList& regionList = containingFlowThread->renderRegionList();
571         for (RenderRegionList::const_iterator iter = regionList.find(startRegion); iter != regionList.end(); ++iter) {
572             RenderRegion* region = *iter;
573             if (region->isValid()) {
574                 // Compute the highlight of the fragment inside the current region.
575                 Highlight highlight;
576                 buildRendererHighlight(renderer, region, config, highlight, InspectorOverlay::CoordinateSystem::View);
577                 Ref<Inspector::Protocol::OverlayTypes::FragmentHighlightData> fragmentHighlight = buildObjectForHighlight(highlight);
578
579                 // Compute the clipping area of the region.
580                 fragmentHighlight->setRegionClippingArea(buildQuadObjectForCSSRegionContentClip(region));
581                 arrayOfFragments->addItem(WTFMove(fragmentHighlight));
582             }
583             if (region == endRegion)
584                 break;
585         }
586     }
587
588     return WTFMove(arrayOfFragments);
589 }
590
591 #if ENABLE(CSS_SHAPES)
592 static FloatPoint localPointToRoot(RenderObject* renderer, const FrameView* mainView, const FrameView* view, const FloatPoint& point)
593 {
594     FloatPoint result = renderer->localToAbsolute(point);
595     result = view->contentsToRootView(roundedIntPoint(result));
596     result += toIntSize(mainView->scrollPosition());
597     return result;
598 }
599
600 struct PathApplyInfo {
601     FrameView* rootView;
602     FrameView* view;
603     Inspector::Protocol::OverlayTypes::DisplayPath* pathArray;
604     RenderObject* renderer;
605     const ShapeOutsideInfo* shapeOutsideInfo;
606 };
607
608 static void appendPathCommandAndPoints(PathApplyInfo& info, const String& command, const FloatPoint points[], unsigned length)
609 {
610     FloatPoint point;
611     info.pathArray->addItem(command);
612     for (unsigned i = 0; i < length; i++) {
613         point = info.shapeOutsideInfo->shapeToRendererPoint(points[i]);
614         point = localPointToRoot(info.renderer, info.rootView, info.view, point);
615         info.pathArray->addItem(point.x());
616         info.pathArray->addItem(point.y());
617     }
618 }
619
620 static void appendPathSegment(PathApplyInfo& pathApplyInfo, const PathElement& pathElement)
621 {
622     FloatPoint point;
623     switch (pathElement.type) {
624     // The points member will contain 1 value.
625     case PathElementMoveToPoint:
626         appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("M"), pathElement.points, 1);
627         break;
628     // The points member will contain 1 value.
629     case PathElementAddLineToPoint:
630         appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("L"), pathElement.points, 1);
631         break;
632     // The points member will contain 3 values.
633     case PathElementAddCurveToPoint:
634         appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("C"), pathElement.points, 3);
635         break;
636     // The points member will contain 2 values.
637     case PathElementAddQuadCurveToPoint:
638         appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("Q"), pathElement.points, 2);
639         break;
640     // The points member will contain no values.
641     case PathElementCloseSubpath:
642         appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("Z"), nullptr, 0);
643         break;
644     }
645 }
646
647 static RefPtr<Inspector::Protocol::OverlayTypes::ShapeOutsideData> buildObjectForShapeOutside(Frame* containingFrame, RenderBox* renderer)
648 {
649     const ShapeOutsideInfo* shapeOutsideInfo = renderer->shapeOutsideInfo();
650     if (!shapeOutsideInfo)
651         return nullptr;
652
653     LayoutRect shapeBounds = shapeOutsideInfo->computedShapePhysicalBoundingBox();
654     FloatQuad shapeQuad = renderer->localToAbsoluteQuad(FloatRect(shapeBounds));
655     contentsQuadToPage(containingFrame->page()->mainFrame().view(), containingFrame->view(), shapeQuad);
656
657     auto shapeObject = Inspector::Protocol::OverlayTypes::ShapeOutsideData::create()
658         .setBounds(buildArrayForQuad(shapeQuad))
659         .release();
660
661     Shape::DisplayPaths paths;
662     shapeOutsideInfo->computedShape().buildDisplayPaths(paths);
663
664     if (paths.shape.length()) {
665         auto shapePath = Inspector::Protocol::OverlayTypes::DisplayPath::create();
666         PathApplyInfo info;
667         info.rootView = containingFrame->page()->mainFrame().view();
668         info.view = containingFrame->view();
669         info.pathArray = &shapePath.get();
670         info.renderer = renderer;
671         info.shapeOutsideInfo = shapeOutsideInfo;
672
673         paths.shape.apply([&info](const PathElement& pathElement) {
674             appendPathSegment(info, pathElement);
675         });
676
677         shapeObject->setShape(shapePath.copyRef());
678
679         if (paths.marginShape.length()) {
680             auto marginShapePath = Inspector::Protocol::OverlayTypes::DisplayPath::create();
681             info.pathArray = &marginShapePath.get();
682
683             paths.marginShape.apply([&info](const PathElement& pathElement) {
684                 appendPathSegment(info, pathElement);
685             });
686
687             shapeObject->setMarginShape(marginShapePath.copyRef());
688         }
689     }
690
691     return WTFMove(shapeObject);
692 }
693 #endif
694
695 static RefPtr<Inspector::Protocol::OverlayTypes::ElementData> buildObjectForElementData(Node* node, HighlightType type)
696 {
697     if (!is<Element>(node) || !node->document().frame())
698         return nullptr;
699
700     Element* effectiveElement = downcast<Element>(node);
701     if (node->isPseudoElement()) {
702         Element* hostElement = downcast<PseudoElement>(*node).hostElement();
703         if (!hostElement)
704             return nullptr;
705         effectiveElement = hostElement;
706     }
707
708     Element& element = *effectiveElement;
709     bool isXHTML = element.document().isXHTMLDocument();
710     auto elementData = Inspector::Protocol::OverlayTypes::ElementData::create()
711         .setTagName(isXHTML ? element.nodeName() : element.nodeName().convertToASCIILowercase())
712         .setIdValue(element.getIdAttribute())
713         .release();
714
715     if (element.hasClass() && is<StyledElement>(element)) {
716         auto classes = Inspector::Protocol::Array<String>::create();
717         HashSet<AtomicString> usedClassNames;
718         const SpaceSplitString& classNamesString = downcast<StyledElement>(element).classNames();
719         for (size_t i = 0; i < classNamesString.size(); ++i) {
720             const AtomicString& className = classNamesString[i];
721             if (usedClassNames.contains(className))
722                 continue;
723
724             usedClassNames.add(className);
725             classes->addItem(className);
726         }
727         elementData->setClasses(WTFMove(classes));
728     }
729
730     if (node->isPseudoElement()) {
731         if (node->pseudoId() == BEFORE)
732             elementData->setPseudoElement("before");
733         else if (node->pseudoId() == AFTER)
734             elementData->setPseudoElement("after");
735     }
736
737     RenderElement* renderer = element.renderer();
738     if (!renderer)
739         return nullptr;
740
741     Frame* containingFrame = node->document().frame();
742     FrameView* containingView = containingFrame->view();
743     IntRect boundingBox = snappedIntRect(containingView->contentsToRootView(renderer->absoluteBoundingBoxRect()));
744     RenderBoxModelObject* modelObject = is<RenderBoxModelObject>(*renderer) ? downcast<RenderBoxModelObject>(renderer) : nullptr;
745     auto sizeObject = Inspector::Protocol::OverlayTypes::Size::create()
746         .setWidth(modelObject ? adjustForAbsoluteZoom(roundToInt(modelObject->offsetWidth()), *modelObject) : boundingBox.width())
747         .setHeight(modelObject ? adjustForAbsoluteZoom(roundToInt(modelObject->offsetHeight()), *modelObject) : boundingBox.height())
748         .release();
749     elementData->setSize(WTFMove(sizeObject));
750
751     if (type != HighlightType::NodeList && renderer->isRenderNamedFlowFragmentContainer()) {
752         RenderNamedFlowFragment& region = *downcast<RenderBlockFlow>(*renderer).renderNamedFlowFragment();
753         if (region.isValid()) {
754             RenderFlowThread* flowThread = region.flowThread();
755             auto regionFlowData = Inspector::Protocol::OverlayTypes::RegionFlowData::create()
756                 .setName(downcast<RenderNamedFlowThread>(*flowThread).flowThreadName())
757                 .setRegions(buildObjectForFlowRegions(&region, flowThread))
758                 .release();
759             elementData->setRegionFlowData(WTFMove(regionFlowData));
760         }
761     }
762
763     RenderFlowThread* containingFlowThread = renderer->flowThreadContainingBlock();
764     if (is<RenderNamedFlowThread>(containingFlowThread)) {
765         auto contentFlowData = Inspector::Protocol::OverlayTypes::ContentFlowData::create()
766             .setName(downcast<RenderNamedFlowThread>(*containingFlowThread).flowThreadName())
767             .release();
768
769         elementData->setContentFlowData(WTFMove(contentFlowData));
770     }
771
772 #if ENABLE(CSS_SHAPES)
773     if (is<RenderBox>(*renderer)) {
774         auto& renderBox = downcast<RenderBox>(*renderer);
775         if (RefPtr<Inspector::Protocol::OverlayTypes::ShapeOutsideData> shapeObject = buildObjectForShapeOutside(containingFrame, &renderBox))
776             elementData->setShapeOutsideData(WTFMove(shapeObject));
777     }
778 #endif
779
780     // Need to enable AX to get the computed role.
781     if (!WebCore::AXObjectCache::accessibilityEnabled())
782         WebCore::AXObjectCache::enableAccessibility();
783
784     if (AXObjectCache* axObjectCache = node->document().axObjectCache()) {
785         if (AccessibilityObject* axObject = axObjectCache->getOrCreate(node))
786             elementData->setRole(axObject->computedRoleString());
787     }
788
789     return WTFMove(elementData);
790 }
791
792 RefPtr<Inspector::Protocol::OverlayTypes::NodeHighlightData> InspectorOverlay::buildHighlightObjectForNode(Node* node, HighlightType type) const
793 {
794     if (!node)
795         return nullptr;
796
797     RenderObject* renderer = node->renderer();
798     if (!renderer)
799         return nullptr;
800
801     RefPtr<Inspector::Protocol::Array<Inspector::Protocol::OverlayTypes::FragmentHighlightData>> arrayOfFragmentHighlights = buildArrayForRendererFragments(renderer, m_nodeHighlightConfig);
802     if (!arrayOfFragmentHighlights)
803         return nullptr;
804
805     // The main view's scroll offset is shared across all quads.
806     FrameView* mainView = m_page.mainFrame().view();
807
808     auto nodeHighlightObject = Inspector::Protocol::OverlayTypes::NodeHighlightData::create()
809         .setScrollOffset(buildObjectForPoint(!mainView->delegatesScrolling() ? mainView->visibleContentRect().location() : FloatPoint()))
810         .setFragments(WTFMove(arrayOfFragmentHighlights))
811         .release();
812
813     if (m_nodeHighlightConfig.showInfo) {
814         if (RefPtr<Inspector::Protocol::OverlayTypes::ElementData> elementData = buildObjectForElementData(node, type))
815             nodeHighlightObject->setElementData(WTFMove(elementData));
816     }
817
818     return WTFMove(nodeHighlightObject);
819 }
820
821 Ref<Inspector::Protocol::Array<Inspector::Protocol::OverlayTypes::NodeHighlightData>> InspectorOverlay::buildObjectForHighlightedNodes() const
822 {
823     auto highlights = Inspector::Protocol::Array<Inspector::Protocol::OverlayTypes::NodeHighlightData>::create();
824
825     if (m_highlightNode) {
826         if (RefPtr<Inspector::Protocol::OverlayTypes::NodeHighlightData> nodeHighlightData = buildHighlightObjectForNode(m_highlightNode.get(), HighlightType::Node))
827             highlights->addItem(WTFMove(nodeHighlightData));
828     } else if (m_highlightNodeList) {
829         for (unsigned i = 0; i < m_highlightNodeList->length(); ++i) {
830             if (RefPtr<Inspector::Protocol::OverlayTypes::NodeHighlightData> nodeHighlightData = buildHighlightObjectForNode(m_highlightNodeList->item(i), HighlightType::NodeList))
831                 highlights->addItem(WTFMove(nodeHighlightData));
832         }
833     }
834
835     return highlights;
836 }
837
838 void InspectorOverlay::drawNodeHighlight()
839 {
840     if (m_highlightNode || m_highlightNodeList)
841         evaluateInOverlay("drawNodeHighlight", buildObjectForHighlightedNodes());
842 }
843
844 void InspectorOverlay::drawQuadHighlight()
845 {
846     if (!m_highlightQuad)
847         return;
848
849     Highlight highlight;
850     buildQuadHighlight(*m_highlightQuad, m_quadHighlightConfig, highlight);
851     evaluateInOverlay("drawQuadHighlight", buildObjectForHighlight(highlight));
852 }
853
854 void InspectorOverlay::drawPausedInDebuggerMessage()
855 {
856     if (!m_pausedInDebuggerMessage.isNull())
857         evaluateInOverlay("drawPausedInDebuggerMessage", m_pausedInDebuggerMessage);
858 }
859
860 Page* InspectorOverlay::overlayPage()
861 {
862     if (m_overlayPage)
863         return m_overlayPage.get();
864
865     PageConfiguration pageConfiguration(makeUniqueRef<EmptyEditorClient>(), SocketProvider::create());
866     fillWithEmptyClients(pageConfiguration);
867     m_overlayPage = std::make_unique<Page>(WTFMove(pageConfiguration));
868     m_overlayPage->setDeviceScaleFactor(m_page.deviceScaleFactor());
869
870     Settings& settings = m_page.settings();
871     Settings& overlaySettings = m_overlayPage->settings();
872
873     overlaySettings.setStandardFontFamily(settings.standardFontFamily());
874     overlaySettings.setSerifFontFamily(settings.serifFontFamily());
875     overlaySettings.setSansSerifFontFamily(settings.sansSerifFontFamily());
876     overlaySettings.setCursiveFontFamily(settings.cursiveFontFamily());
877     overlaySettings.setFantasyFontFamily(settings.fantasyFontFamily());
878     overlaySettings.setPictographFontFamily(settings.pictographFontFamily());
879     overlaySettings.setMinimumFontSize(settings.minimumFontSize());
880     overlaySettings.setMinimumLogicalFontSize(settings.minimumLogicalFontSize());
881     overlaySettings.setMediaEnabled(false);
882     overlaySettings.setScriptEnabled(true);
883     overlaySettings.setPluginsEnabled(false);
884
885     Frame& frame = m_overlayPage->mainFrame();
886     frame.setView(FrameView::create(frame));
887     frame.init();
888     FrameLoader& loader = frame.loader();
889     frame.view()->setCanHaveScrollbars(false);
890     frame.view()->setTransparent(true);
891     ASSERT(loader.activeDocumentLoader());
892     loader.activeDocumentLoader()->writer().setMIMEType("text/html");
893     loader.activeDocumentLoader()->writer().begin();
894     loader.activeDocumentLoader()->writer().addData(reinterpret_cast<const char*>(InspectorOverlayPage_html), sizeof(InspectorOverlayPage_html));
895     loader.activeDocumentLoader()->writer().end();
896
897 #if OS(WINDOWS)
898     evaluateInOverlay("setPlatform", "windows");
899 #elif OS(MAC_OS_X)
900     evaluateInOverlay("setPlatform", "mac");
901 #elif OS(UNIX)
902     evaluateInOverlay("setPlatform", "linux");
903 #endif
904
905     return m_overlayPage.get();
906 }
907
908 void InspectorOverlay::forcePaint()
909 {
910     // This overlay page is very weird and doesn't automatically paint. We have to force paints manually.
911     m_client->highlight();
912 }
913
914 void InspectorOverlay::reset(const IntSize& viewportSize, const IntSize& frameViewFullSize)
915 {
916     auto configObject = Inspector::Protocol::OverlayTypes::OverlayConfiguration::create()
917         .setDeviceScaleFactor(m_page.deviceScaleFactor())
918         .setViewportSize(buildObjectForSize(viewportSize))
919         .setFrameViewFullSize(buildObjectForSize(frameViewFullSize))
920         .release();
921     evaluateInOverlay("reset", WTFMove(configObject));
922 }
923
924 static void evaluateCommandInOverlay(Page* page, Ref<InspectorArray>&& command)
925 {
926     JSC::JSValue result = page->mainFrame().script().evaluate(ScriptSourceCode(makeString("dispatch(", command->toJSONString(), ')')));
927     ASSERT_UNUSED(result, result); // There should never be exceptions when evaluating in the overlay page.
928 }
929
930 void InspectorOverlay::evaluateInOverlay(const String& method)
931 {
932     Ref<InspectorArray> command = InspectorArray::create();
933     command->pushString(method);
934
935     evaluateCommandInOverlay(overlayPage(), WTFMove(command));
936 }
937
938 void InspectorOverlay::evaluateInOverlay(const String& method, const String& argument)
939 {
940     Ref<InspectorArray> command = InspectorArray::create();
941     command->pushString(method);
942     command->pushString(argument);
943
944     evaluateCommandInOverlay(overlayPage(), WTFMove(command));
945 }
946
947 void InspectorOverlay::evaluateInOverlay(const String& method, RefPtr<InspectorValue>&& argument)
948 {
949     Ref<InspectorArray> command = InspectorArray::create();
950     command->pushString(method);
951     command->pushValue(WTFMove(argument));
952
953     evaluateCommandInOverlay(overlayPage(), WTFMove(command));
954 }
955
956 void InspectorOverlay::freePage()
957 {
958     m_overlayPage = nullptr;
959 }
960
961 } // namespace WebCore