Web Inspector: [CSS Shapes] Highlight shape-outside when its element is selected...
authorbetravis@adobe.com <betravis@adobe.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Nov 2013 22:25:26 +0000 (22:25 +0000)
committerbetravis@adobe.com <betravis@adobe.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Nov 2013 22:25:26 +0000 (22:25 +0000)
https://bugs.webkit.org/show_bug.cgi?id=124071

Reviewed by Timothy Hatcher.

Source/WebCore:

Adding code to pass computed shape information (path and bounds) to the Inspector overlay
canvas, and the code to display it. The code creates a path based on ShapeInfo's computed
shape. The shape highlight draws whenever an element is highlighted, via selection in
the Inspector elements view.

Test: inspector-protocol/model/highlight-shape-outside.html

* inspector/InspectorOverlay.cpp:
(WebCore::localPointToRoot): Convert a local point to be relative to the root view.
(WebCore::appendPathCommandAndPoints): Helper for building a single segment's worth
of the overall path.
(WebCore::appendPathSegment): Build a single segment's worth of the overall path.
(WebCore::buildObjectForShapeOutside): Build an object to pass to the Inspector overlay
that represents the shape.
(WebCore::buildObjectForElementInfo): Call buildObjectForShapeOutside and pass the
resulting object along.
* inspector/InspectorOverlayPage.js:
(pathCommand): Draw a single path command.
(drawPath): Draw the overall path.
(_drawShapeHighlight): Draw the highlight for the given shapeInfo.
(drawNodeHighlight): Call _drawShapeHighlight.
* rendering/shapes/PolygonShape.h:
(WebCore::PolygonShape::polygon): Expose the underlying vertex information for a
PolygonShape.
* rendering/shapes/RasterShape.h:
* rendering/shapes/RectangleShape.h:
(WebCore::RectangleShape::logicalRx): Expose the logical radii for a shape.
(WebCore::RectangleShape::logicalRy): Ditto.
* rendering/shapes/Shape.h:
* rendering/shapes/ShapeInfo.h:
(WebCore::ShapeInfo::computedShapePhysicalBoundingBox): The physical bounds of a
shape in renderer coordinates.
(WebCore::ShapeInfo::shapeToRendererPoint): Convert shape coordinates to renderer
coordinates.
(WebCore::ShapeInfo::shapeToRendererSize): Ditto.
(WebCore::ShapeInfo::ShapeInfo):

LayoutTests:

Test that the information passed to the Inspector overlay properly corresponds to the
underlying shape. Retrieving the shape highlight information involves a series of
steps and has been abstracted into shape-info-helper.js, which supplies
Inspector.shapeOutsideInfo inside an Inspector test, and the ShapeInfoHelper.runShapesTest
method to begin a test.

* inspector-protocol/model/highlight-shape-outside-expected.txt: Added.
* inspector-protocol/model/highlight-shape-outside.html: Added.
* inspector-protocol/resources/shape-info-helper.js: Added.
(window.ShapeInfoHelper): Exposes ShapeInfoHelper.runShapesTest.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@159070 268f45cc-cd09-0410-ab3c-d52691b4dbfc

12 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector-protocol/model/highlight-shape-outside-expected.txt [new file with mode: 0644]
LayoutTests/inspector-protocol/model/highlight-shape-outside.html [new file with mode: 0644]
LayoutTests/inspector-protocol/resources/shape-info-helper.js [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/inspector/InspectorOverlay.cpp
Source/WebCore/inspector/InspectorOverlayPage.js
Source/WebCore/rendering/shapes/PolygonShape.h
Source/WebCore/rendering/shapes/RasterShape.h
Source/WebCore/rendering/shapes/RectangleShape.h
Source/WebCore/rendering/shapes/Shape.h
Source/WebCore/rendering/shapes/ShapeInfo.h

index 40b0a60..44c2f32 100644 (file)
@@ -1,3 +1,21 @@
+2013-11-11  Bear Travis  <betravis@adobe.com>
+
+        Web Inspector: [CSS Shapes] Highlight shape-outside when its element is selected in the Web Inspector
+        https://bugs.webkit.org/show_bug.cgi?id=124071
+
+        Reviewed by Timothy Hatcher.
+
+        Test that the information passed to the Inspector overlay properly corresponds to the
+        underlying shape. Retrieving the shape highlight information involves a series of
+        steps and has been abstracted into shape-info-helper.js, which supplies
+        Inspector.shapeOutsideInfo inside an Inspector test, and the ShapeInfoHelper.runShapesTest
+        method to begin a test.
+
+        * inspector-protocol/model/highlight-shape-outside-expected.txt: Added.
+        * inspector-protocol/model/highlight-shape-outside.html: Added.
+        * inspector-protocol/resources/shape-info-helper.js: Added.
+        (window.ShapeInfoHelper): Exposes ShapeInfoHelper.runShapesTest.
+
 2013-11-11  Samuel White  <samuel_white@apple.com>
 
         AX: support helpText() in DumpRenderTree
diff --git a/LayoutTests/inspector-protocol/model/highlight-shape-outside-expected.txt b/LayoutTests/inspector-protocol/model/highlight-shape-outside-expected.txt
new file mode 100644 (file)
index 0000000..8a81d89
--- /dev/null
@@ -0,0 +1,9 @@
+PASS: Actual [M 50 50 L 150 50 L 150 100 L 50 100 L 50 50 Z] Expected [M 50 50 L 150 50 L 150 100 L 50 100 L 50 50 Z]
+PASS: Actual [M 50 50 L 100 50 L 100 100 L 50 100 Z] Expected [M 50 50 L 100 50 L 100 100 L 50 100 Z]
+PASS: Actual [M 75 75 L 175 75 L 175 125 L 75 125 L 75 75 Z] Expected [M 75 75 L 175 75 L 175 125 L 75 125 L 75 75 Z]
+PASS: Actual [M 50 50 L 150 50 L 150 100 L 50 100 L 50 50 Z] Expected [M 50 50 L 150 50 L 150 100 L 50 100 L 50 50 Z]
+PASS: Actual [M 50 50 L 150 50 L 150 100 L 50 100 L 50 50 Z] Expected [M 50 50 L 150 50 L 150 100 L 50 100 L 50 50 Z]
+PASS: Actual [M 50 50 L 150 50 L 150 100 L 50 100 L 50 50 Z] Expected [M 50 50 L 150 50 L 150 100 L 50 100 L 50 50 Z]
+PASS: Actual [M 75 75 L 175 75 L 175 125 L 75 125 L 75 75 Z] Expected [M 75 75 L 175 75 L 175 125 L 75 125 L 75 75 Z]
+PASS: Actual [M 50 50 L 150 50 L 150 100 L 50 100 L 50 50 Z] Expected [M 50 50 L 150 50 L 150 100 L 50 100 L 50 50 Z]
+
diff --git a/LayoutTests/inspector-protocol/model/highlight-shape-outside.html b/LayoutTests/inspector-protocol/model/highlight-shape-outside.html
new file mode 100644 (file)
index 0000000..e153562
--- /dev/null
@@ -0,0 +1,103 @@
+<html>
+<head>
+<style>
+body {
+    margin: 0;
+}
+.shape {
+    float: left;
+    width: 200px;
+    height: 200px;
+    margin-right: -200px;
+}
+.padded {
+    width: 100px;
+    height: 100px;
+    padding: 25px 75px 75px 25px;
+}
+.border-box {
+    box-sizing: border-box;
+}
+.vertical-lr {
+    -webkit-writing-mode: vertical-lr;
+}
+.vertical-rl {
+    -webkit-writing-mode: vertical-rl;
+}
+.rectangle {
+    -webkit-shape-outside: rectangle(50px, 50px, 100px, 50px);
+}
+.polygon {
+    -webkit-shape-outside: polygon(50px 50px, 100px 50px, 100px 100px, 50px 100px);
+}
+</style>
+<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/protocol-test.js"></script>
+<script type="text/javascript" src="../resources/shape-info-helper.js"></script>
+<script>
+function shapeTests() {
+    var tests = [
+    {
+        'selector' : '.rectangle',
+        'path' : ['M', 50, 50, 'L', 150, 50, 'L', 150, 100, 'L', 50, 100, 'L', 50, 50, 'Z']
+    },
+    {
+        'selector' : '.polygon',
+        'path' : ['M', 50, 50, 'L', 100, 50, 'L', 100, 100, 'L', 50, 100, 'Z']
+    },
+    {
+        'selector' : '.rectangle.padded',
+        'path' : ['M', 75, 75, 'L', 175, 75, 'L', 175, 125, 'L', 75, 125, 'L', 75, 75, 'Z']
+    },
+    {
+        'selector' : '.rectangle.padded.border-box',
+        'path' : ['M', 50, 50, 'L', 150, 50, 'L', 150, 100, 'L', 50, 100, 'L', 50, 50, 'Z']
+    },
+    {
+        'selector' : '.rectangle.vertical-lr',
+        'path' : ['M', 50, 50, 'L', 150, 50, 'L', 150, 100, 'L', 50, 100, 'L', 50, 50, 'Z']
+    },
+    {
+        'selector' : '.rectangle.vertical-rl',
+        'path' : ['M', 50, 50, 'L', 150, 50, 'L', 150, 100, 'L', 50, 100, 'L', 50, 50, 'Z']
+    },
+    {
+        'selector' : '.rectangle.padded.vertical-rl',
+        'path' : ['M', 75, 75, 'L', 175, 75, 'L', 175, 125, 'L', 75, 125, 'L', 75, 75, 'Z']
+    },
+    {
+        'selector' : '.rectangle.padded.vertical-rl.border-box',
+        'path' : ['M', 50, 50, 'L', 150, 50, 'L', 150, 100, 'L', 50, 100, 'L', 50, 50, 'Z']
+    }
+    ];
+
+    function doTests(tests) {
+        if (!tests.length) {
+            InspectorTest.completeTest();
+            return;
+        }
+        var test = tests[0];
+        tests = tests.splice(1);
+        InspectorTest.shapeOutsideInfo(test.selector, function(shapeOutside) {
+            var actual = shapeOutside.path.join(' ');
+            var expected = test.path.join(' ');
+            InspectorTest.assert(actual === expected, "Actual [" + actual + "] Expected [" + expected + "]");
+            doTests(tests);
+        });
+    }
+
+    doTests(tests);
+}
+window.onload = function() { ShapeInfoHelper.runShapeTest(shapeTests); }
+</script>
+</head>
+<body>
+    <div class='shape rectangle'></div>
+    <div class='shape polygon'></div>
+    <div class='shape padded rectangle'></div>
+    <div class='shape padded rectangle border-box'></div>
+    <div class='shape rectangle vertical-lr'></div>
+    <div class='shape rectangle vertical-rl'></div>
+    <div class='shape padded rectangle vertical-rl'></div>
+    <div class='shape padded rectangle vertical-rl border-box'></div>
+</body>
+</html>
diff --git a/LayoutTests/inspector-protocol/resources/shape-info-helper.js b/LayoutTests/inspector-protocol/resources/shape-info-helper.js
new file mode 100644 (file)
index 0000000..dbe1d48
--- /dev/null
@@ -0,0 +1,64 @@
+window.ShapeInfoHelper = (function() {
+    function shapeOutsideInfo(selector, callback) {
+        InspectorTest.sendCommand("DOM.getDocument", {}, onGotDocument);
+
+        function onGotDocument(msg) {
+            InspectorTest.checkForError(msg);
+            var node = msg.result.root;
+            InspectorTest.sendCommand("DOM.querySelector",
+            {
+                "nodeId": node.nodeId,
+                "selector": selector
+            },
+            onQuerySelector);
+        }
+
+        function onQuerySelector(msg) {
+            InspectorTest.checkForError(msg);
+            var node = msg.result;
+            var highlightConfig = {
+                showInfo: true,
+                contentColor: {r: 255, g: 255, b: 255},
+                paddingColor: {r: 255, g: 255, b: 255},
+                borderColor: {r: 255, g: 255, b: 255},
+                marginColor: {r: 255, g: 255, b: 255},
+            };
+            InspectorTest.sendCommand("DOM.highlightNode",
+            {
+                "nodeId": node.nodeId,
+                "highlightConfig": highlightConfig
+            },
+            onHighlightNode);
+        }
+
+        function onHighlightNode(msg) {
+            InspectorTest.checkForError(msg);
+            InspectorTest.sendCommand("Runtime.evaluate",
+            {
+                "expression": "window.internals.inspectorHighlightObject()"
+            },
+            onRuntimeEvaluate);
+        }
+
+        function onRuntimeEvaluate(msg) {
+            InspectorTest.checkForError(msg);
+            var value = JSON.parse(msg.result.result.value);
+            callback(value.elementInfo.shapeOutsideInfo);
+        }
+    }
+
+    function runShapeTest(testfn) {
+        var body = [
+            "InspectorTest.shapeOutsideInfo = " + shapeOutsideInfo.toString(),
+            "(" + testfn.toString() + ")()"
+        ];
+        window.test = new Function(
+            body.join(';\n')
+        );
+        runTest();
+    }
+
+    return {
+        'runShapeTest' : runShapeTest
+    }
+})();
index 81101af..4d036ea 100644 (file)
@@ -1,3 +1,47 @@
+2013-11-11  Bear Travis  <betravis@adobe.com>
+
+        Web Inspector: [CSS Shapes] Highlight shape-outside when its element is selected in the Web Inspector
+        https://bugs.webkit.org/show_bug.cgi?id=124071
+
+        Reviewed by Timothy Hatcher.
+
+        Adding code to pass computed shape information (path and bounds) to the Inspector overlay
+        canvas, and the code to display it. The code creates a path based on ShapeInfo's computed
+        shape. The shape highlight draws whenever an element is highlighted, via selection in
+        the Inspector elements view.
+
+        Test: inspector-protocol/model/highlight-shape-outside.html
+
+        * inspector/InspectorOverlay.cpp:
+        (WebCore::localPointToRoot): Convert a local point to be relative to the root view.
+        (WebCore::appendPathCommandAndPoints): Helper for building a single segment's worth
+        of the overall path.
+        (WebCore::appendPathSegment): Build a single segment's worth of the overall path.
+        (WebCore::buildObjectForShapeOutside): Build an object to pass to the Inspector overlay
+        that represents the shape.
+        (WebCore::buildObjectForElementInfo): Call buildObjectForShapeOutside and pass the
+        resulting object along.
+        * inspector/InspectorOverlayPage.js:
+        (pathCommand): Draw a single path command.
+        (drawPath): Draw the overall path.
+        (_drawShapeHighlight): Draw the highlight for the given shapeInfo.
+        (drawNodeHighlight): Call _drawShapeHighlight.
+        * rendering/shapes/PolygonShape.h:
+        (WebCore::PolygonShape::polygon): Expose the underlying vertex information for a
+        PolygonShape.
+        * rendering/shapes/RasterShape.h:
+        * rendering/shapes/RectangleShape.h:
+        (WebCore::RectangleShape::logicalRx): Expose the logical radii for a shape.
+        (WebCore::RectangleShape::logicalRy): Ditto.
+        * rendering/shapes/Shape.h:
+        * rendering/shapes/ShapeInfo.h:
+        (WebCore::ShapeInfo::computedShapePhysicalBoundingBox): The physical bounds of a
+        shape in renderer coordinates.
+        (WebCore::ShapeInfo::shapeToRendererPoint): Convert shape coordinates to renderer
+        coordinates.
+        (WebCore::ShapeInfo::shapeToRendererSize): Ditto.
+        (WebCore::ShapeInfo::ShapeInfo):
+
 2013-11-11  Alexey Proskuryakov  <ap@apple.com>
 
         Support WebCrypto KeyPair interface
index af3f26e..4e827f1 100644 (file)
@@ -43,6 +43,8 @@
 #include "MainFrame.h"
 #include "Node.h"
 #include "Page.h"
+#include "PolygonShape.h"
+#include "RectangleShape.h"
 #include "RenderBoxModelObject.h"
 #include "RenderElement.h"
 #include "RenderFlowThread.h"
@@ -532,6 +534,125 @@ static PassRefPtr<InspectorArray> buildObjectForRendererFragments(RenderObject*
     return fragmentsArray.release();
 }
 
+#if ENABLE(CSS_SHAPES)
+static FloatPoint localPointToRoot(RenderObject* renderer, const FrameView* mainView, const FrameView* view, const FloatPoint& point)
+{
+    FloatPoint result = renderer->localToAbsolute(point);
+    result = view->contentsToRootView(roundedIntPoint(result));
+    result += mainView->scrollOffset();
+    return result;
+}
+
+struct PathApplyInfo {
+    FrameView* rootView;
+    FrameView* view;
+    InspectorArray* array;
+    RenderObject* renderer;
+};
+
+static void appendPathCommandAndPoints(PathApplyInfo* info, const String& command, const FloatPoint points[], unsigned length)
+{
+    FloatPoint point;
+    info->array->pushString(command);
+    for (unsigned i = 0; i < length; i++) {
+        point = localPointToRoot(info->renderer, info->rootView, info->view, points[i]);
+        info->array->pushNumber(point.x());
+        info->array->pushNumber(point.y());
+    }
+}
+
+static void appendPathSegment(void* info, const PathElement* pathElement)
+{
+    PathApplyInfo* pathApplyInfo = static_cast<PathApplyInfo*>(info);
+    FloatPoint point;
+    switch (pathElement->type) {
+    // The points member will contain 1 value.
+    case PathElementMoveToPoint:
+        appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("M"), pathElement->points, 1);
+        break;
+    // The points member will contain 1 value.
+    case PathElementAddLineToPoint:
+        appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("L"), pathElement->points, 1);
+        break;
+    // The points member will contain 3 values.
+    case PathElementAddCurveToPoint:
+        appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("C"), pathElement->points, 3);
+        break;
+    // The points member will contain 2 values.
+    case PathElementAddQuadCurveToPoint:
+        appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("Q"), pathElement->points, 2);
+        break;
+    // The points member will contain no values.
+    case PathElementCloseSubpath:
+        appendPathCommandAndPoints(pathApplyInfo, ASCIILiteral("Z"), nullptr, 0);
+        break;
+    }
+}
+
+static PassRefPtr<InspectorObject> buildObjectForShapeOutside(Frame* containingFrame, RenderBox* renderer)
+{
+    const ShapeOutsideInfo* shapeOutsideInfo = renderer->shapeOutsideInfo();
+    if (!shapeOutsideInfo)
+        return nullptr;
+
+    RefPtr<InspectorObject> shapeObject = InspectorObject::create();
+    LayoutRect shapeBounds = shapeOutsideInfo->computedShapePhysicalBoundingBox();
+    FloatQuad shapeQuad = renderer->localToAbsoluteQuad(FloatRect(shapeBounds));
+    contentsQuadToPage(containingFrame->page()->mainFrame().view(), containingFrame->view(), shapeQuad);
+    shapeObject->setArray("bounds", buildArrayForQuad(shapeQuad));
+
+    Path path;
+    switch (shapeOutsideInfo->computedShape()->type()) {
+    case Shape::RoundedRectangleType: {
+        const RectangleShape* shape = static_cast<const RectangleShape*>(shapeOutsideInfo->computedShape());
+        FloatSize radii(shape->logicalRx(), shape->logicalRy());
+        radii = shapeOutsideInfo->shapeToRendererSize(radii);
+        path.addRoundedRect(shapeBounds, radii, Path::PreferBezierRoundedRect);
+        break;
+    }
+
+    case Shape::PolygonType: {
+        const PolygonShape* shape = static_cast<const PolygonShape*>(shapeOutsideInfo->computedShape());
+        const FloatPolygon& polygon = shape->polygon();
+        FloatPoint vertex;
+
+        if (polygon.numberOfVertices()) {
+            vertex = shapeOutsideInfo->shapeToRendererPoint(polygon.vertexAt(0));
+            path.moveTo(vertex);
+        }
+
+        for (size_t i = 1; i < polygon.numberOfVertices(); i++) {
+            FloatPoint vertex = shapeOutsideInfo->shapeToRendererPoint(polygon.vertexAt(i));
+            path.addLineTo(vertex);
+        }
+
+        if (polygon.numberOfVertices())
+            path.closeSubpath();
+        break;
+    }
+
+    case Shape::RasterType:
+        // FIXME: Bug 124080 - RasterShapes are not yet supported and only display their shape bounds
+        break;
+    }
+
+    if (path.length()) {
+        RefPtr<InspectorArray> shapePath = InspectorArray::create();
+        PathApplyInfo info;
+        info.rootView = containingFrame->page()->mainFrame().view();
+        info.view = containingFrame->view();
+        info.array = shapePath.get();
+        info.renderer = renderer;
+
+        path.apply(&info, &appendPathSegment);
+
+        shapeObject->setArray("path", shapePath.release());
+    }
+
+    return shapeObject.release();
+}
+#endif
+
 static PassRefPtr<InspectorObject> buildObjectForElementInfo(Node* node)
 {
     if (!node->isElementNode() || !node->document().frame())
@@ -585,6 +706,14 @@ static PassRefPtr<InspectorObject> buildObjectForElementInfo(Node* node)
         elementInfo->setObject("contentFlowInfo", contentFlowInfo.release());
     }
 
+#if ENABLE(CSS_SHAPES)
+    if (renderer->isBox()) {
+        RenderBox* renderBox = toRenderBox(renderer);
+        if (RefPtr<InspectorObject> shapeObject = buildObjectForShapeOutside(containingFrame, renderBox))
+            elementInfo->setObject("shapeOutsideInfo", shapeObject.release());
+    }
+#endif
+
     return elementInfo.release();
 }
 
index 94f2862..f0473e2 100644 (file)
@@ -18,6 +18,9 @@ const regionNumberFont = "bold 40pt sans-serif";
 const regionNumberFillColor = "rgba(255, 255, 255, 0.9)";
 const regionNumberStrokeColor = "rgb(61, 127, 204)";
 
+// CSS Shapes highlight colors
+const shapeHighlightColor = "rgb(255, 105, 180)";
+
 function drawPausedInDebuggerMessage(message)
 {
     var pausedInDebugger = document.getElementById("paused-in-debugger");
@@ -184,6 +187,58 @@ function drawOutlinedQuad(quad, fillColor, outlineColor)
     context.restore();
 }
 
+function pathCommand(context, commands, name, index, length) {
+    context[name].apply(context, commands.slice(index + 1, index + length + 1));
+    return index + length + 1;
+}
+
+function drawPath(context, commands, fillColor, outlineColor)
+{
+    context.save();
+    context.beginPath();
+
+    var commandsIndex = 0;
+    var commandsLength = commands.length;
+    while(commandsIndex < commandsLength) {
+        switch(commands[commandsIndex]) {
+        // 1 point
+        case 'M':
+            commandsIndex = pathCommand(context, commands, "moveTo", commandsIndex, 2);
+            break;
+        // 1 point
+        case 'L':
+            commandsIndex = pathCommand(context, commands, "lineTo", commandsIndex, 2);
+            break;
+        // 3 points
+        case 'C':
+            commandsIndex = pathCommand(context, commands, "bezierCurveTo", commandsIndex, 6);
+            break;
+        // 2 points
+        case 'Q':
+            commandsIndex = pathCommand(context, commands, "quadraticCurveTo", commandsIndex, 2);
+            break;
+        // 0 points
+        case 'Z':
+            commandsIndex = pathCommand(context, commands, "closePath", commandsIndex, 0);
+            break;
+        default:
+            commandsIndex++;
+        }
+    }
+
+    context.closePath();
+    context.fillStyle = fillColor;
+    context.fill();
+
+    if (outlineColor) {
+        context.lineWidth = 2;
+        context.strokeStyle = outlineColor;
+        context.stroke();
+    }
+
+    context.restore();
+}
+
 function drawOutlinedQuadWithClip(quad, clipQuad, fillColor)
 {
     var canvas = document.getElementById("canvas");
@@ -506,6 +561,13 @@ function _drawRegionsHighlight(regions)
     }
 }
 
+function _drawShapeHighlight(shapeInfo) {
+    if (shapeInfo.path)
+        drawPath(context, shapeInfo.path, shapeHighlightColor);
+    else
+        drawOutlinedQuad(shapeInfo.bounds, shapeHighlightColor, shapeHighlightColor);
+}
+
 function _drawFragmentHighlight(highlight)
 {
     if (!highlight.quads.length) {
@@ -580,6 +642,9 @@ function drawNodeHighlight(highlight)
     if (highlight.elementInfo && highlight.elementInfo.regionFlowInfo)
         _drawRegionsHighlight(highlight.elementInfo.regionFlowInfo.regions, highlight);
 
+    if (highlight.elementInfo && highlight.elementInfo.shapeOutsideInfo)
+        _drawShapeHighlight(highlight.elementInfo.shapeOutsideInfo);
+
     context.restore();
 
     var elementTitleContainer = document.getElementById("element-title-container");
index 266825c..e1a6575 100644 (file)
@@ -97,6 +97,10 @@ public:
     virtual void getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const OVERRIDE;
     virtual bool firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const LayoutSize& minLogicalIntervalSize, LayoutUnit&) const OVERRIDE;
 
+    virtual ShapeType type() const OVERRIDE { return Shape::PolygonType; }
+
+    const FloatPolygon& polygon() const { return m_polygon; }
+
 private:
     const FloatPolygon& shapeMarginBounds() const;
     const FloatPolygon& shapePaddingBounds() const;
index db2bfc7..f0f8cb3 100644 (file)
@@ -101,6 +101,8 @@ public:
     virtual void getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const OVERRIDE;
     virtual bool firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const LayoutSize& minLogicalIntervalSize, LayoutUnit&) const OVERRIDE;
 
+    virtual ShapeType type() const OVERRIDE { return Shape::RasterType; }
+
 private:
     const RasterShapeIntervals& marginIntervals() const;
     const RasterShapeIntervals& paddingIntervals() const;
index e9ecf05..00078b8 100644 (file)
@@ -75,6 +75,11 @@ public:
     virtual void getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const OVERRIDE;
     virtual bool firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const LayoutSize& minLogicalIntervalSize, LayoutUnit&) const OVERRIDE;
 
+    LayoutUnit logicalRx() const { return m_bounds.rx(); }
+    LayoutUnit logicalRy() const { return m_bounds.ry(); }
+
+    virtual ShapeType type() const OVERRIDE { return Shape::RoundedRectangleType; }
+
 private:
     FloatRoundedRect shapeMarginBounds() const;
     FloatRoundedRect shapePaddingBounds() const;
index f775e5e..bd2d296 100644 (file)
@@ -60,6 +60,12 @@ typedef Vector<LineSegment> SegmentList;
 
 class Shape {
 public:
+    enum ShapeType {
+        RoundedRectangleType,
+        PolygonType,
+        RasterType
+    };
+
     static PassOwnPtr<Shape> createShape(const BasicShape*, const LayoutSize& logicalBoxSize, WritingMode, Length margin, Length padding);
     static PassOwnPtr<Shape> createShape(const StyleImage*, float threshold, const LayoutSize& logicalBoxSize, WritingMode, Length margin, Length padding);
 
@@ -72,6 +78,8 @@ public:
     virtual void getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList&) const = 0;
     virtual bool firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const LayoutSize& minLogicalIntervalSize, LayoutUnit& result) const = 0;
 
+    virtual ShapeType type() const = 0;
+
 protected:
     float shapeMargin() const { return m_margin; }
     float shapePadding() const { return m_padding; }
index 979daf4..6b34c9c 100644 (file)
@@ -105,11 +105,40 @@ public:
     const RenderType* owner() const { return m_renderer; }
     LayoutSize shapeSize() const { return m_shapeLogicalSize; }
 
-protected:
-    ShapeInfo(const RenderType* renderer): m_renderer(renderer) { }
+    LayoutRect computedShapePhysicalBoundingBox() const
+    {
+        LayoutRect physicalBoundingBox = computedShapeLogicalBoundingBox();
+        physicalBoundingBox.setX(physicalBoundingBox.x() + logicalLeftOffset());
+        physicalBoundingBox.setY(physicalBoundingBox.y() + logicalTopOffset());
+        if (m_renderer->style().isFlippedBlocksWritingMode())
+            physicalBoundingBox.setY(m_renderer->logicalHeight() - physicalBoundingBox.maxY());
+        if (!m_renderer->style().isHorizontalWritingMode())
+            physicalBoundingBox = physicalBoundingBox.transposedRect();
+        return physicalBoundingBox;
+    }
+
+    FloatPoint shapeToRendererPoint(FloatPoint point) const
+    {
+        FloatPoint result = FloatPoint(point.x() + logicalLeftOffset(), point.y() + logicalTopOffset());
+        if (m_renderer->style().isFlippedBlocksWritingMode())
+            result.setY(m_renderer->logicalHeight() - result.y());
+        if (!m_renderer->style().isHorizontalWritingMode())
+            result = result.transposedPoint();
+        return result;
+    }
+
+    FloatSize shapeToRendererSize(FloatSize size) const
+    {
+        if (!m_renderer->style().isHorizontalWritingMode())
+            return size.transposedSize();
+        return size;
+    }
 
     const Shape* computedShape() const;
 
+protected:
+    explicit ShapeInfo(const RenderType* renderer): m_renderer(renderer) { }
+
     virtual LayoutRect computedShapeLogicalBoundingBox() const = 0;
     virtual ShapeValue* shapeValue() const = 0;
     virtual void getIntervals(LayoutUnit, LayoutUnit, SegmentList&) const = 0;