Home | History | Annotate | Download | only in inspector
      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 
     64 namespace blink {
     65 
     66 namespace {
     67 
     68 class PathBuilder {
     69     WTF_MAKE_NONCOPYABLE(PathBuilder);
     70 public:
     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 
     80 protected:
     81     virtual FloatPoint translatePoint(const FloatPoint& point) { return point; }
     82 
     83 private:
     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 
     95 class ShapePathBuilder : public PathBuilder {
     96 public:
     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 
    109 protected:
    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 
    116 private:
    117     FrameView& m_view;
    118     RenderObject& m_renderer;
    119     const ShapeOutsideInfo& m_shapeOutsideInfo;
    120 };
    121 
    122 class InspectorOverlayChromeClient FINAL: public EmptyChromeClient {
    123 public:
    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 
    149 private:
    150     ChromeClient& m_client;
    151     InspectorOverlay* m_overlay;
    152 };
    153 
    154 static 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 
    165 void 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 
    189 class Highlight {
    190 public:
    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 
    237 private:
    238     bool m_showRulers;
    239     bool m_showExtensionLines;
    240     RefPtr<JSONObject> m_elementInfo;
    241     RefPtr<JSONArray> m_highlightPaths;
    242 };
    243 
    244 static 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 
    252 static 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 
    306 static 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 
    334 InspectorOverlay::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 
    347 InspectorOverlay::~InspectorOverlay()
    348 {
    349     ASSERT(!m_overlayPage);
    350 }
    351 
    352 void 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 
    362 void InspectorOverlay::invalidate()
    363 {
    364     m_client->highlight();
    365 }
    366 
    367 bool InspectorOverlay::handleGestureEvent(const PlatformGestureEvent& event)
    368 {
    369     if (isEmpty())
    370         return false;
    371 
    372     return toLocalFrame(overlayPage()->mainFrame())->eventHandler().handleGestureEvent(event);
    373 }
    374 
    375 bool 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 
    393 bool InspectorOverlay::handleTouchEvent(const PlatformTouchEvent& event)
    394 {
    395     if (isEmpty())
    396         return false;
    397 
    398     return toLocalFrame(overlayPage()->mainFrame())->eventHandler().handleTouchEvent(event);
    399 }
    400 
    401 bool InspectorOverlay::handleKeyboardEvent(const PlatformKeyboardEvent& event)
    402 {
    403     if (isEmpty())
    404         return false;
    405 
    406     return toLocalFrame(overlayPage()->mainFrame())->eventHandler().keyEvent(event);
    407 }
    408 
    409 void InspectorOverlay::drawOutline(GraphicsContext* context, const LayoutRect& rect, const Color& color)
    410 {
    411     FloatRect outlineRect = rect;
    412     drawOutlinedQuad(context, outlineRect, Color(), color);
    413 }
    414 
    415 void InspectorOverlay::setPausedInDebuggerMessage(const String* message)
    416 {
    417     m_pausedInDebuggerMessage = message ? *message : String();
    418     update();
    419 }
    420 
    421 void InspectorOverlay::setInspectModeEnabled(bool enabled)
    422 {
    423     m_inspectModeEnabled = enabled;
    424     update();
    425 }
    426 
    427 void InspectorOverlay::hideHighlight()
    428 {
    429     m_highlightNode.clear();
    430     m_eventTargetNode.clear();
    431     m_highlightQuad.clear();
    432     update();
    433 }
    434 
    435 void 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 
    444 void 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 
    452 void InspectorOverlay::showAndHideViewSize(bool showGrid)
    453 {
    454     m_drawViewSize = true;
    455     m_drawViewSizeWithGrid = showGrid;
    456     update();
    457     m_timer.startOneShot(1, FROM_HERE);
    458 }
    459 
    460 Node* InspectorOverlay::highlightedNode() const
    461 {
    462     return m_highlightNode.get();
    463 }
    464 
    465 bool 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 
    474 void 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 
    504 void 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 
    516 static 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 
    524 void 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 
    534 void 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 
    560 static 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 
    574 static 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 
    593 static 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 
    612 PassRefPtr<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 
    659 void 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 
    680 void 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 
    690 void InspectorOverlay::drawPausedInDebuggerMessage()
    691 {
    692     if (!m_pausedInDebuggerMessage.isNull())
    693         evaluateInOverlay("drawPausedInDebuggerMessage", m_pausedInDebuggerMessage);
    694 }
    695 
    696 void InspectorOverlay::drawViewSize()
    697 {
    698     if (m_drawViewSize)
    699         evaluateInOverlay("drawViewSize", m_drawViewSizeWithGrid ? "true" : "false");
    700 }
    701 
    702 Page* 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 
    765 void 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 
    777 void 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 
    786 void 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 
    795 void InspectorOverlay::onTimer(Timer<InspectorOverlay>*)
    796 {
    797     m_drawViewSize = false;
    798     update();
    799 }
    800 
    801 bool 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 
    836 void 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 
    849 void InspectorOverlay::startedRecordingProfile()
    850 {
    851     if (!m_activeProfilerCount++)
    852         freePage();
    853 }
    854 
    855 } // namespace blink
    856