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#include "core/inspector/InspectorOverlay.h"
31
32#include "bindings/core/v8/ScriptController.h"
33#include "bindings/core/v8/ScriptSourceCode.h"
34#include "bindings/core/v8/V8InspectorOverlayHost.h"
35#include "core/dom/Element.h"
36#include "core/dom/Node.h"
37#include "core/dom/PseudoElement.h"
38#include "core/frame/FrameView.h"
39#include "core/frame/LocalFrame.h"
40#include "core/frame/Settings.h"
41#include "core/inspector/InspectorClient.h"
42#include "core/inspector/InspectorOverlayHost.h"
43#include "core/loader/EmptyClients.h"
44#include "core/loader/FrameLoadRequest.h"
45#include "core/page/Chrome.h"
46#include "core/page/EventHandler.h"
47#include "core/page/Page.h"
48#include "core/rendering/RenderBox.h"
49#include "core/rendering/RenderBoxModelObject.h"
50#include "core/rendering/RenderInline.h"
51#include "core/rendering/RenderObject.h"
52#include "core/rendering/shapes/ShapeOutsideInfo.h"
53#include "core/rendering/style/RenderStyleConstants.h"
54#include "platform/JSONValues.h"
55#include "platform/PlatformMouseEvent.h"
56#include "platform/ScriptForbiddenScope.h"
57#include "platform/graphics/GraphicsContextStateSaver.h"
58#include "public/platform/Platform.h"
59#include "public/platform/WebData.h"
60#include "wtf/Vector.h"
61#include "wtf/text/StringBuilder.h"
62#include <v8.h>
63
64namespace blink {
65
66namespace {
67
68class PathBuilder {
69    WTF_MAKE_NONCOPYABLE(PathBuilder);
70public:
71    PathBuilder() : m_path(TypeBuilder::Array<JSONValue>::create()) { }
72    virtual ~PathBuilder() { }
73
74    PassRefPtr<TypeBuilder::Array<JSONValue> > path() const { return m_path; }
75    void appendPath(const Path& path)
76    {
77        path.apply(this, &PathBuilder::appendPathElement);
78    }
79
80protected:
81    virtual FloatPoint translatePoint(const FloatPoint& point) { return point; }
82
83private:
84    static void appendPathElement(void* pathBuilder, const PathElement* pathElement)
85    {
86        static_cast<PathBuilder*>(pathBuilder)->appendPathElement(pathElement);
87    }
88
89    void appendPathElement(const PathElement*);
90    void appendPathCommandAndPoints(const char* command, const FloatPoint points[], size_t length);
91
92    RefPtr<TypeBuilder::Array<JSONValue> > m_path;
93};
94
95class ShapePathBuilder : public PathBuilder {
96public:
97    ShapePathBuilder(FrameView& view, RenderObject& renderer, const ShapeOutsideInfo& shapeOutsideInfo)
98        : m_view(view)
99        , m_renderer(renderer)
100        , m_shapeOutsideInfo(shapeOutsideInfo) { }
101
102    static PassRefPtr<TypeBuilder::Array<JSONValue> > buildPath(FrameView& view, RenderObject& renderer, const ShapeOutsideInfo& shapeOutsideInfo, const Path& path)
103    {
104        ShapePathBuilder builder(view, renderer, shapeOutsideInfo);
105        builder.appendPath(path);
106        return builder.path();
107    }
108
109protected:
110    virtual FloatPoint translatePoint(const FloatPoint& point)
111    {
112        FloatPoint rendererPoint = m_shapeOutsideInfo.shapeToRendererPoint(point);
113        return m_view.contentsToRootView(roundedIntPoint(m_renderer.localToAbsolute(rendererPoint)));
114    }
115
116private:
117    FrameView& m_view;
118    RenderObject& m_renderer;
119    const ShapeOutsideInfo& m_shapeOutsideInfo;
120};
121
122class InspectorOverlayChromeClient FINAL: public EmptyChromeClient {
123public:
124    InspectorOverlayChromeClient(ChromeClient& client, InspectorOverlay* overlay)
125        : m_client(client)
126        , m_overlay(overlay)
127    { }
128
129    virtual void setCursor(const Cursor& cursor) OVERRIDE
130    {
131        m_client.setCursor(cursor);
132    }
133
134    virtual void setToolTip(const String& tooltip, TextDirection direction) OVERRIDE
135    {
136        m_client.setToolTip(tooltip, direction);
137    }
138
139    virtual void invalidateContentsAndRootView(const IntRect&) OVERRIDE
140    {
141        m_overlay->invalidate();
142    }
143
144    virtual void invalidateContentsForSlowScroll(const IntRect&) OVERRIDE
145    {
146        m_overlay->invalidate();
147    }
148
149private:
150    ChromeClient& m_client;
151    InspectorOverlay* m_overlay;
152};
153
154static Path quadToPath(const FloatQuad& quad)
155{
156    Path quadPath;
157    quadPath.moveTo(quad.p1());
158    quadPath.addLineTo(quad.p2());
159    quadPath.addLineTo(quad.p3());
160    quadPath.addLineTo(quad.p4());
161    quadPath.closeSubpath();
162    return quadPath;
163}
164
165void drawOutlinedQuad(GraphicsContext* context, const FloatQuad& quad, const Color& fillColor, const Color& outlineColor)
166{
167    static const int outlineThickness = 2;
168
169    Path quadPath = quadToPath(quad);
170
171    // Clip out the quad, then draw with a 2px stroke to get a pixel
172    // of outline (because inflating a quad is hard)
173    {
174        context->save();
175        context->clipOut(quadPath);
176
177        context->setStrokeThickness(outlineThickness);
178        context->setStrokeColor(outlineColor);
179        context->strokePath(quadPath);
180
181        context->restore();
182    }
183
184    // Now do the fill
185    context->setFillColor(fillColor);
186    context->fillPath(quadPath);
187}
188
189class Highlight {
190public:
191    Highlight()
192        : m_showRulers(false)
193        , m_showExtensionLines(false)
194        , m_highlightPaths(JSONArray::create())
195    { }
196
197    void setDataFromConfig(const HighlightConfig& highlightConfig)
198    {
199        m_showRulers = highlightConfig.showRulers;
200        m_showExtensionLines = highlightConfig.showExtensionLines;
201    }
202
203    void setElementInfo(PassRefPtr<JSONObject> elementInfo)
204    {
205        m_elementInfo = elementInfo;
206    }
207
208    void appendQuad(const FloatQuad& quad, const Color& fillColor, const Color& outlineColor = Color::transparent)
209    {
210        Path path = quadToPath(quad);
211        PathBuilder builder;
212        builder.appendPath(path);
213        appendPath(builder.path(), fillColor, outlineColor);
214    }
215
216    void appendPath(PassRefPtr<JSONArrayBase> path, const Color& fillColor, const Color& outlineColor)
217    {
218        RefPtr<JSONObject> object = JSONObject::create();
219        object->setValue("path", path);
220        object->setString("fillColor", fillColor.serialized());
221        if (outlineColor != Color::transparent)
222            object->setString("outlineColor", outlineColor.serialized());
223        m_highlightPaths->pushObject(object.release());
224    }
225
226    PassRefPtr<JSONObject> asJSONObject() const
227    {
228        RefPtr<JSONObject> object = JSONObject::create();
229        object->setArray("paths", m_highlightPaths);
230        object->setBoolean("showRulers", m_showRulers);
231        object->setBoolean("showExtensionLines", m_showExtensionLines);
232        if (m_elementInfo)
233            object->setObject("elementInfo", m_elementInfo);
234        return object.release();
235    }
236
237private:
238    bool m_showRulers;
239    bool m_showExtensionLines;
240    RefPtr<JSONObject> m_elementInfo;
241    RefPtr<JSONArray> m_highlightPaths;
242};
243
244static void contentsQuadToScreen(const FrameView* view, FloatQuad& quad)
245{
246    quad.setP1(view->contentsToRootView(roundedIntPoint(quad.p1())));
247    quad.setP2(view->contentsToRootView(roundedIntPoint(quad.p2())));
248    quad.setP3(view->contentsToRootView(roundedIntPoint(quad.p3())));
249    quad.setP4(view->contentsToRootView(roundedIntPoint(quad.p4())));
250}
251
252static bool buildNodeQuads(RenderObject* renderer, FloatQuad* content, FloatQuad* padding, FloatQuad* border, FloatQuad* margin)
253{
254    FrameView* containingView = renderer->frameView();
255    if (!containingView)
256        return false;
257    if (!renderer->isBox() && !renderer->isRenderInline())
258        return false;
259
260    LayoutRect contentBox;
261    LayoutRect paddingBox;
262    LayoutRect borderBox;
263    LayoutRect marginBox;
264
265    if (renderer->isBox()) {
266        RenderBox* renderBox = toRenderBox(renderer);
267
268        // RenderBox returns the "pure" content area box, exclusive of the scrollbars (if present), which also count towards the content area in CSS.
269        contentBox = renderBox->contentBoxRect();
270        contentBox.setWidth(contentBox.width() + renderBox->verticalScrollbarWidth());
271        contentBox.setHeight(contentBox.height() + renderBox->horizontalScrollbarHeight());
272
273        paddingBox = LayoutRect(contentBox.x() - renderBox->paddingLeft(), contentBox.y() - renderBox->paddingTop(),
274            contentBox.width() + renderBox->paddingLeft() + renderBox->paddingRight(), contentBox.height() + renderBox->paddingTop() + renderBox->paddingBottom());
275        borderBox = LayoutRect(paddingBox.x() - renderBox->borderLeft(), paddingBox.y() - renderBox->borderTop(),
276            paddingBox.width() + renderBox->borderLeft() + renderBox->borderRight(), paddingBox.height() + renderBox->borderTop() + renderBox->borderBottom());
277        marginBox = LayoutRect(borderBox.x() - renderBox->marginLeft(), borderBox.y() - renderBox->marginTop(),
278            borderBox.width() + renderBox->marginWidth(), borderBox.height() + renderBox->marginHeight());
279    } else {
280        RenderInline* renderInline = toRenderInline(renderer);
281
282        // RenderInline's bounding box includes paddings and borders, excludes margins.
283        borderBox = renderInline->linesBoundingBox();
284        paddingBox = LayoutRect(borderBox.x() + renderInline->borderLeft(), borderBox.y() + renderInline->borderTop(),
285            borderBox.width() - renderInline->borderLeft() - renderInline->borderRight(), borderBox.height() - renderInline->borderTop() - renderInline->borderBottom());
286        contentBox = LayoutRect(paddingBox.x() + renderInline->paddingLeft(), paddingBox.y() + renderInline->paddingTop(),
287            paddingBox.width() - renderInline->paddingLeft() - renderInline->paddingRight(), paddingBox.height() - renderInline->paddingTop() - renderInline->paddingBottom());
288        // Ignore marginTop and marginBottom for inlines.
289        marginBox = LayoutRect(borderBox.x() - renderInline->marginLeft(), borderBox.y(),
290            borderBox.width() + renderInline->marginWidth(), borderBox.height());
291    }
292
293    *content = renderer->localToAbsoluteQuad(FloatRect(contentBox));
294    *padding = renderer->localToAbsoluteQuad(FloatRect(paddingBox));
295    *border = renderer->localToAbsoluteQuad(FloatRect(borderBox));
296    *margin = renderer->localToAbsoluteQuad(FloatRect(marginBox));
297
298    contentsQuadToScreen(containingView, *content);
299    contentsQuadToScreen(containingView, *padding);
300    contentsQuadToScreen(containingView, *border);
301    contentsQuadToScreen(containingView, *margin);
302
303    return true;
304}
305
306static void buildNodeHighlight(Node& node, const HighlightConfig& highlightConfig, Highlight* highlight)
307{
308    RenderObject* renderer = node.renderer();
309    if (!renderer)
310        return;
311
312    highlight->setDataFromConfig(highlightConfig);
313
314    // RenderSVGRoot should be highlighted through the isBox() code path, all other SVG elements should just dump their absoluteQuads().
315    if (renderer->node() && renderer->node()->isSVGElement() && !renderer->isSVGRoot()) {
316        Vector<FloatQuad> quads;
317        renderer->absoluteQuads(quads);
318        for (size_t i = 0; i < quads.size(); ++i)
319            highlight->appendQuad(quads[i], highlightConfig.content, highlightConfig.contentOutline);
320        return;
321    }
322
323    FloatQuad content, padding, border, margin;
324    if (!buildNodeQuads(renderer, &content, &padding, &border, &margin))
325        return;
326    highlight->appendQuad(content, highlightConfig.content, highlightConfig.contentOutline);
327    highlight->appendQuad(padding, highlightConfig.padding);
328    highlight->appendQuad(border, highlightConfig.border);
329    highlight->appendQuad(margin, highlightConfig.margin);
330}
331
332} // anonymous namespace
333
334InspectorOverlay::InspectorOverlay(Page* page, InspectorClient* client)
335    : m_page(page)
336    , m_client(client)
337    , m_inspectModeEnabled(false)
338    , m_overlayHost(InspectorOverlayHost::create())
339    , m_drawViewSize(false)
340    , m_drawViewSizeWithGrid(false)
341    , m_omitTooltip(false)
342    , m_timer(this, &InspectorOverlay::onTimer)
343    , m_activeProfilerCount(0)
344{
345}
346
347InspectorOverlay::~InspectorOverlay()
348{
349    ASSERT(!m_overlayPage);
350}
351
352void InspectorOverlay::paint(GraphicsContext& context)
353{
354    if (isEmpty())
355        return;
356    GraphicsContextStateSaver stateSaver(context);
357    FrameView* view = toLocalFrame(overlayPage()->mainFrame())->view();
358    ASSERT(!view->needsLayout());
359    view->paint(&context, IntRect(0, 0, view->width(), view->height()));
360}
361
362void InspectorOverlay::invalidate()
363{
364    m_client->highlight();
365}
366
367bool InspectorOverlay::handleGestureEvent(const PlatformGestureEvent& event)
368{
369    if (isEmpty())
370        return false;
371
372    return toLocalFrame(overlayPage()->mainFrame())->eventHandler().handleGestureEvent(event);
373}
374
375bool InspectorOverlay::handleMouseEvent(const PlatformMouseEvent& event)
376{
377    if (isEmpty())
378        return false;
379
380    EventHandler& eventHandler = toLocalFrame(overlayPage()->mainFrame())->eventHandler();
381    switch (event.type()) {
382    case PlatformEvent::MouseMoved:
383        return eventHandler.handleMouseMoveEvent(event);
384    case PlatformEvent::MousePressed:
385        return eventHandler.handleMousePressEvent(event);
386    case PlatformEvent::MouseReleased:
387        return eventHandler.handleMouseReleaseEvent(event);
388    default:
389        return false;
390    }
391}
392
393bool InspectorOverlay::handleTouchEvent(const PlatformTouchEvent& event)
394{
395    if (isEmpty())
396        return false;
397
398    return toLocalFrame(overlayPage()->mainFrame())->eventHandler().handleTouchEvent(event);
399}
400
401bool InspectorOverlay::handleKeyboardEvent(const PlatformKeyboardEvent& event)
402{
403    if (isEmpty())
404        return false;
405
406    return toLocalFrame(overlayPage()->mainFrame())->eventHandler().keyEvent(event);
407}
408
409void InspectorOverlay::drawOutline(GraphicsContext* context, const LayoutRect& rect, const Color& color)
410{
411    FloatRect outlineRect = rect;
412    drawOutlinedQuad(context, outlineRect, Color(), color);
413}
414
415void InspectorOverlay::setPausedInDebuggerMessage(const String* message)
416{
417    m_pausedInDebuggerMessage = message ? *message : String();
418    update();
419}
420
421void InspectorOverlay::setInspectModeEnabled(bool enabled)
422{
423    m_inspectModeEnabled = enabled;
424    update();
425}
426
427void InspectorOverlay::hideHighlight()
428{
429    m_highlightNode.clear();
430    m_eventTargetNode.clear();
431    m_highlightQuad.clear();
432    update();
433}
434
435void InspectorOverlay::highlightNode(Node* node, Node* eventTarget, const HighlightConfig& highlightConfig, bool omitTooltip)
436{
437    m_nodeHighlightConfig = highlightConfig;
438    m_highlightNode = node;
439    m_eventTargetNode = eventTarget;
440    m_omitTooltip = omitTooltip;
441    update();
442}
443
444void InspectorOverlay::highlightQuad(PassOwnPtr<FloatQuad> quad, const HighlightConfig& highlightConfig)
445{
446    m_quadHighlightConfig = highlightConfig;
447    m_highlightQuad = quad;
448    m_omitTooltip = false;
449    update();
450}
451
452void InspectorOverlay::showAndHideViewSize(bool showGrid)
453{
454    m_drawViewSize = true;
455    m_drawViewSizeWithGrid = showGrid;
456    update();
457    m_timer.startOneShot(1, FROM_HERE);
458}
459
460Node* InspectorOverlay::highlightedNode() const
461{
462    return m_highlightNode.get();
463}
464
465bool InspectorOverlay::isEmpty()
466{
467    if (m_activeProfilerCount)
468        return true;
469    bool hasAlwaysVisibleElements = m_highlightNode || m_eventTargetNode || m_highlightQuad  || m_drawViewSize;
470    bool hasInvisibleInInspectModeElements = !m_pausedInDebuggerMessage.isNull();
471    return !(hasAlwaysVisibleElements || (hasInvisibleInInspectModeElements && !m_inspectModeEnabled));
472}
473
474void InspectorOverlay::update()
475{
476    if (isEmpty()) {
477        m_client->hideHighlight();
478        return;
479    }
480
481    FrameView* view = m_page->deprecatedLocalMainFrame()->view();
482    if (!view)
483        return;
484
485    // Include scrollbars to avoid masking them by the gutter.
486    IntSize size = view->unscaledVisibleContentSize(IncludeScrollbars);
487    toLocalFrame(overlayPage()->mainFrame())->view()->resize(size);
488
489    // Clear canvas and paint things.
490    IntRect viewRect = view->visibleContentRect();
491    reset(size, viewRect.x(), viewRect.y());
492
493    drawNodeHighlight();
494    drawQuadHighlight();
495    if (!m_inspectModeEnabled)
496        drawPausedInDebuggerMessage();
497    drawViewSize();
498
499    toLocalFrame(overlayPage()->mainFrame())->view()->updateLayoutAndStyleForPainting();
500
501    m_client->highlight();
502}
503
504void InspectorOverlay::hide()
505{
506    m_timer.stop();
507    m_highlightNode.clear();
508    m_eventTargetNode.clear();
509    m_highlightQuad.clear();
510    m_pausedInDebuggerMessage = String();
511    m_drawViewSize = false;
512    m_drawViewSizeWithGrid = false;
513    update();
514}
515
516static PassRefPtr<JSONObject> buildObjectForSize(const IntSize& size)
517{
518    RefPtr<JSONObject> result = JSONObject::create();
519    result->setNumber("width", size.width());
520    result->setNumber("height", size.height());
521    return result.release();
522}
523
524void PathBuilder::appendPathCommandAndPoints(const char* command, const FloatPoint points[], size_t length)
525{
526    m_path->addItem(JSONString::create(command));
527    for (size_t i = 0; i < length; i++) {
528        FloatPoint point = translatePoint(points[i]);
529        m_path->addItem(JSONBasicValue::create(point.x()));
530        m_path->addItem(JSONBasicValue::create(point.y()));
531    }
532}
533
534void PathBuilder::appendPathElement(const PathElement* pathElement)
535{
536    switch (pathElement->type) {
537    // The points member will contain 1 value.
538    case PathElementMoveToPoint:
539        appendPathCommandAndPoints("M", pathElement->points, 1);
540        break;
541    // The points member will contain 1 value.
542    case PathElementAddLineToPoint:
543        appendPathCommandAndPoints("L", pathElement->points, 1);
544        break;
545    // The points member will contain 3 values.
546    case PathElementAddCurveToPoint:
547        appendPathCommandAndPoints("C", pathElement->points, 3);
548        break;
549    // The points member will contain 2 values.
550    case PathElementAddQuadCurveToPoint:
551        appendPathCommandAndPoints("Q", pathElement->points, 2);
552        break;
553    // The points member will contain no values.
554    case PathElementCloseSubpath:
555        appendPathCommandAndPoints("Z", 0, 0);
556        break;
557    }
558}
559
560static RefPtr<TypeBuilder::Array<double> > buildArrayForQuad(const FloatQuad& quad)
561{
562    RefPtr<TypeBuilder::Array<double> > array = TypeBuilder::Array<double>::create();
563    array->addItem(quad.p1().x());
564    array->addItem(quad.p1().y());
565    array->addItem(quad.p2().x());
566    array->addItem(quad.p2().y());
567    array->addItem(quad.p3().x());
568    array->addItem(quad.p3().y());
569    array->addItem(quad.p4().x());
570    array->addItem(quad.p4().y());
571    return array.release();
572}
573
574static const ShapeOutsideInfo* shapeOutsideInfoForNode(Node* node, Shape::DisplayPaths* paths, FloatQuad* bounds)
575{
576    RenderObject* renderer = node->renderer();
577    if (!renderer || !renderer->isBox() || !toRenderBox(renderer)->shapeOutsideInfo())
578        return 0;
579
580    FrameView* containingView = node->document().view();
581    RenderBox* renderBox = toRenderBox(renderer);
582    const ShapeOutsideInfo* shapeOutsideInfo = renderBox->shapeOutsideInfo();
583
584    shapeOutsideInfo->computedShape().buildDisplayPaths(*paths);
585
586    LayoutRect shapeBounds = shapeOutsideInfo->computedShapePhysicalBoundingBox();
587    *bounds = renderBox->localToAbsoluteQuad(FloatRect(shapeBounds));
588    contentsQuadToScreen(containingView, *bounds);
589
590    return shapeOutsideInfo;
591}
592
593static void appendPathsForShapeOutside(Highlight& highlight, const HighlightConfig& config, Node* node)
594{
595    Shape::DisplayPaths paths;
596    FloatQuad boundsQuad;
597
598    const ShapeOutsideInfo* shapeOutsideInfo = shapeOutsideInfoForNode(node, &paths, &boundsQuad);
599    if (!shapeOutsideInfo)
600        return;
601
602    if (!paths.shape.length()) {
603        highlight.appendQuad(boundsQuad, config.shape);
604        return;
605    }
606
607    highlight.appendPath(ShapePathBuilder::buildPath(*node->document().view(), *node->renderer(), *shapeOutsideInfo, paths.shape), config.shape, Color::transparent);
608    if (paths.marginShape.length())
609        highlight.appendPath(ShapePathBuilder::buildPath(*node->document().view(), *node->renderer(), *shapeOutsideInfo, paths.marginShape), config.shapeMargin, Color::transparent);
610}
611
612PassRefPtr<JSONObject> buildElementInfo(Element* element)
613{
614    RefPtr<JSONObject> elementInfo = JSONObject::create();
615    Element* realElement = element;
616    PseudoElement* pseudoElement = 0;
617    if (element->isPseudoElement()) {
618        pseudoElement = toPseudoElement(element);
619        realElement = element->parentOrShadowHostElement();
620    }
621    bool isXHTML = realElement->document().isXHTMLDocument();
622    elementInfo->setString("tagName", isXHTML ? realElement->nodeName() : realElement->nodeName().lower());
623    elementInfo->setString("idValue", realElement->getIdAttribute());
624    StringBuilder classNames;
625    if (realElement->hasClass() && realElement->isStyledElement()) {
626        HashSet<AtomicString> usedClassNames;
627        const SpaceSplitString& classNamesString = realElement->classNames();
628        size_t classNameCount = classNamesString.size();
629        for (size_t i = 0; i < classNameCount; ++i) {
630            const AtomicString& className = classNamesString[i];
631            if (!usedClassNames.add(className).isNewEntry)
632                continue;
633            classNames.append('.');
634            classNames.append(className);
635        }
636    }
637    if (pseudoElement) {
638        if (pseudoElement->pseudoId() == BEFORE)
639            classNames.appendLiteral("::before");
640        else if (pseudoElement->pseudoId() == AFTER)
641            classNames.appendLiteral("::after");
642    }
643    if (!classNames.isEmpty())
644        elementInfo->setString("className", classNames.toString());
645
646    RenderObject* renderer = element->renderer();
647    FrameView* containingView = element->document().view();
648    if (!renderer || !containingView)
649        return elementInfo;
650
651    IntRect boundingBox = pixelSnappedIntRect(containingView->contentsToRootView(renderer->absoluteBoundingBoxRect()));
652    RenderBoxModelObject* modelObject = renderer->isBoxModelObject() ? toRenderBoxModelObject(renderer) : 0;
653    elementInfo->setString("nodeWidth", String::number(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetWidth(), modelObject) : boundingBox.width()));
654    elementInfo->setString("nodeHeight", String::number(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetHeight(), modelObject) : boundingBox.height()));
655
656    return elementInfo;
657}
658
659void InspectorOverlay::drawNodeHighlight()
660{
661    if (!m_highlightNode)
662        return;
663
664    Highlight highlight;
665    appendPathsForShapeOutside(highlight, m_nodeHighlightConfig, m_highlightNode.get());
666    buildNodeHighlight(*m_highlightNode, m_nodeHighlightConfig, &highlight);
667
668    if (m_eventTargetNode && m_eventTargetNode->renderer()) {
669        FloatQuad border, unused;
670        if (buildNodeQuads(m_eventTargetNode->renderer(), &unused, &unused, &border, &unused))
671            highlight.appendQuad(border, m_nodeHighlightConfig.eventTarget);
672    }
673
674    if (m_highlightNode->isElementNode() && !m_omitTooltip && m_nodeHighlightConfig.showInfo && m_highlightNode->renderer() && m_highlightNode->document().frame())
675        highlight.setElementInfo(buildElementInfo(toElement(m_highlightNode.get())));
676
677    evaluateInOverlay("drawHighlight", highlight.asJSONObject());
678}
679
680void InspectorOverlay::drawQuadHighlight()
681{
682    if (!m_highlightQuad)
683        return;
684
685    Highlight highlight;
686    highlight.appendQuad(*m_highlightQuad, m_quadHighlightConfig.content, m_quadHighlightConfig.contentOutline);
687    evaluateInOverlay("drawHighlight", highlight.asJSONObject());
688}
689
690void InspectorOverlay::drawPausedInDebuggerMessage()
691{
692    if (!m_pausedInDebuggerMessage.isNull())
693        evaluateInOverlay("drawPausedInDebuggerMessage", m_pausedInDebuggerMessage);
694}
695
696void InspectorOverlay::drawViewSize()
697{
698    if (m_drawViewSize)
699        evaluateInOverlay("drawViewSize", m_drawViewSizeWithGrid ? "true" : "false");
700}
701
702Page* InspectorOverlay::overlayPage()
703{
704    if (m_overlayPage)
705        return m_overlayPage.get();
706
707    ScriptForbiddenScope::AllowUserAgentScript allowScript;
708
709    static FrameLoaderClient* dummyFrameLoaderClient =  new EmptyFrameLoaderClient;
710    Page::PageClients pageClients;
711    fillWithEmptyClients(pageClients);
712    ASSERT(!m_overlayChromeClient);
713    m_overlayChromeClient = adoptPtr(new InspectorOverlayChromeClient(m_page->chrome().client(), this));
714    pageClients.chromeClient = m_overlayChromeClient.get();
715    m_overlayPage = adoptPtrWillBeNoop(new Page(pageClients));
716
717    Settings& settings = m_page->settings();
718    Settings& overlaySettings = m_overlayPage->settings();
719
720    overlaySettings.genericFontFamilySettings().updateStandard(settings.genericFontFamilySettings().standard());
721    overlaySettings.genericFontFamilySettings().updateSerif(settings.genericFontFamilySettings().serif());
722    overlaySettings.genericFontFamilySettings().updateSansSerif(settings.genericFontFamilySettings().sansSerif());
723    overlaySettings.genericFontFamilySettings().updateCursive(settings.genericFontFamilySettings().cursive());
724    overlaySettings.genericFontFamilySettings().updateFantasy(settings.genericFontFamilySettings().fantasy());
725    overlaySettings.genericFontFamilySettings().updatePictograph(settings.genericFontFamilySettings().pictograph());
726    overlaySettings.setMinimumFontSize(settings.minimumFontSize());
727    overlaySettings.setMinimumLogicalFontSize(settings.minimumLogicalFontSize());
728    overlaySettings.setScriptEnabled(true);
729    overlaySettings.setPluginsEnabled(false);
730    overlaySettings.setLoadsImagesAutomatically(true);
731    // FIXME: http://crbug.com/363843. Inspector should probably create its
732    // own graphics layers and attach them to the tree rather than going
733    // through some non-composited paint function.
734    overlaySettings.setAcceleratedCompositingEnabled(false);
735
736    RefPtrWillBeRawPtr<LocalFrame> frame = LocalFrame::create(dummyFrameLoaderClient, &m_overlayPage->frameHost(), 0);
737    frame->setView(FrameView::create(frame.get()));
738    frame->init();
739    FrameLoader& loader = frame->loader();
740    frame->view()->setCanHaveScrollbars(false);
741    frame->view()->setTransparent(true);
742
743    const blink::WebData& overlayPageHTMLResource = blink::Platform::current()->loadResource("InspectorOverlayPage.html");
744    RefPtr<SharedBuffer> data = SharedBuffer::create(overlayPageHTMLResource.data(), overlayPageHTMLResource.size());
745    loader.load(FrameLoadRequest(0, blankURL(), SubstituteData(data, "text/html", "UTF-8", KURL(), ForceSynchronousLoad)));
746    v8::Isolate* isolate = toIsolate(frame.get());
747    ScriptState* scriptState = ScriptState::forMainWorld(frame.get());
748    ASSERT(!scriptState->contextIsValid());
749    ScriptState::Scope scope(scriptState);
750    v8::Handle<v8::Object> global = scriptState->context()->Global();
751    v8::Handle<v8::Value> overlayHostObj = toV8(m_overlayHost.get(), global, isolate);
752    global->Set(v8::String::NewFromUtf8(isolate, "InspectorOverlayHost"), overlayHostObj);
753
754#if OS(WIN)
755    evaluateInOverlay("setPlatform", "windows");
756#elif OS(MACOSX)
757    evaluateInOverlay("setPlatform", "mac");
758#elif OS(POSIX)
759    evaluateInOverlay("setPlatform", "linux");
760#endif
761
762    return m_overlayPage.get();
763}
764
765void InspectorOverlay::reset(const IntSize& viewportSize, int scrollX, int scrollY)
766{
767    RefPtr<JSONObject> resetData = JSONObject::create();
768    resetData->setNumber("pageScaleFactor", m_page->settings().pinchVirtualViewportEnabled() ? 1 : m_page->pageScaleFactor());
769    resetData->setNumber("deviceScaleFactor", m_page->deviceScaleFactor());
770    resetData->setObject("viewportSize", buildObjectForSize(viewportSize));
771    resetData->setNumber("pageZoomFactor", m_page->deprecatedLocalMainFrame()->pageZoomFactor());
772    resetData->setNumber("scrollX", scrollX);
773    resetData->setNumber("scrollY", scrollY);
774    evaluateInOverlay("reset", resetData.release());
775}
776
777void InspectorOverlay::evaluateInOverlay(const String& method, const String& argument)
778{
779    ScriptForbiddenScope::AllowUserAgentScript allowScript;
780    RefPtr<JSONArray> command = JSONArray::create();
781    command->pushString(method);
782    command->pushString(argument);
783    toLocalFrame(overlayPage()->mainFrame())->script().executeScriptInMainWorld("dispatch(" + command->toJSONString() + ")", ScriptController::ExecuteScriptWhenScriptsDisabled);
784}
785
786void InspectorOverlay::evaluateInOverlay(const String& method, PassRefPtr<JSONValue> argument)
787{
788    ScriptForbiddenScope::AllowUserAgentScript allowScript;
789    RefPtr<JSONArray> command = JSONArray::create();
790    command->pushString(method);
791    command->pushValue(argument);
792    toLocalFrame(overlayPage()->mainFrame())->script().executeScriptInMainWorld("dispatch(" + command->toJSONString() + ")", ScriptController::ExecuteScriptWhenScriptsDisabled);
793}
794
795void InspectorOverlay::onTimer(Timer<InspectorOverlay>*)
796{
797    m_drawViewSize = false;
798    update();
799}
800
801bool InspectorOverlay::getBoxModel(Node* node, RefPtr<TypeBuilder::DOM::BoxModel>& model)
802{
803    RenderObject* renderer = node->renderer();
804    FrameView* view = node->document().view();
805    if (!renderer || !view)
806        return false;
807
808    FloatQuad content, padding, border, margin;
809    if (!buildNodeQuads(node->renderer(), &content, &padding, &border, &margin))
810        return false;
811
812    IntRect boundingBox = pixelSnappedIntRect(view->contentsToRootView(renderer->absoluteBoundingBoxRect()));
813    RenderBoxModelObject* modelObject = renderer->isBoxModelObject() ? toRenderBoxModelObject(renderer) : 0;
814
815    model = TypeBuilder::DOM::BoxModel::create()
816        .setContent(buildArrayForQuad(content))
817        .setPadding(buildArrayForQuad(padding))
818        .setBorder(buildArrayForQuad(border))
819        .setMargin(buildArrayForQuad(margin))
820        .setWidth(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetWidth(), modelObject) : boundingBox.width())
821        .setHeight(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetHeight(), modelObject) : boundingBox.height());
822
823    Shape::DisplayPaths paths;
824    FloatQuad boundsQuad;
825    if (const ShapeOutsideInfo* shapeOutsideInfo = shapeOutsideInfoForNode(node, &paths, &boundsQuad)) {
826        RefPtr<TypeBuilder::DOM::ShapeOutsideInfo> shapeTypeBuilder = TypeBuilder::DOM::ShapeOutsideInfo::create()
827            .setBounds(buildArrayForQuad(boundsQuad))
828            .setShape(ShapePathBuilder::buildPath(*view, *renderer, *shapeOutsideInfo, paths.shape))
829            .setMarginShape(ShapePathBuilder::buildPath(*view, *renderer, *shapeOutsideInfo, paths.marginShape));
830        model->setShapeOutside(shapeTypeBuilder);
831    }
832
833    return true;
834}
835
836void InspectorOverlay::freePage()
837{
838    if (m_overlayPage) {
839        m_overlayPage->willBeDestroyed();
840        m_overlayPage.clear();
841    }
842    m_overlayChromeClient.clear();
843    m_timer.stop();
844
845    // This will clear internal structures and issue update to the client. Safe to call last.
846    hideHighlight();
847}
848
849void InspectorOverlay::startedRecordingProfile()
850{
851    if (!m_activeProfilerCount++)
852        freePage();
853}
854
855} // namespace blink
856