Factor rect shrink-wrapping code out of RenderThemeMac for future reuse
authortimothy_horton@apple.com <timothy_horton@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 15 Jul 2015 21:19:29 +0000 (21:19 +0000)
committertimothy_horton@apple.com <timothy_horton@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 15 Jul 2015 21:19:29 +0000 (21:19 +0000)
https://bugs.webkit.org/show_bug.cgi?id=146973
<rdar://problem/21643094>

Reviewed by Anders Carlsson.

Test: fast/shrink-wrap/rect-shrink-wrap.html

* WebCore.xcodeproj/project.pbxproj:
Add DOMPath.cpp and PathUtilities.{h, cpp}.

* bindings/js/JSDOMBinding.h:
(WebCore::NativeValueTraits<double>::nativeValue):
Make it possible to use sequence<double> in IDL files.

* bindings/scripts/CodeGeneratorJS.pm:
Export JSDOMPath for use in Internals.

* html/canvas/DOMPath.cpp: Added.
(WebCore::DOMPath::~DOMPath):
* html/canvas/DOMPath.h:
Out-of-line the DOMPath destructor so as not to anger the bindings
integrity checker (otherwise, the address of the DOMPath destructor
is different in WebCoreTestSupport and WebCore, causing us to fail
the vtable equality test).

* platform/graphics/Path.h:
Forward declare FloatRect instead of including it unnecessarily.
Export ensurePlatformPath().

* platform/graphics/PathUtilities.cpp: Added.
(WebCore::addShrinkWrapRightCorner):
(WebCore::addShrinkWrapLeftCorner):
(WebCore::addShrinkWrappedPathForRects):
These parts are extracted from RenderThemeMac, with two changes:
    + support for arbitrarily-aligned rects
      (the other version assumed they were horizontally center-aligned)
    + support for overlapping rects
      (the other version assumed they touched but did not overlap)

There are still things missing:
    + support for a fallback when the shape is too hard to shrink-wrap

And things broken:
    + if the distance between two edges is smaller than the corner radius,
      we'll end up with a sharp edge in the path

Both of these cases are covered in the layout test and can be improved.

(WebCore::rectsIntersectOrTouch):
Rect intersection with <= instead of <.

(WebCore::contiguousRectGroupsFromRects):
Given a set of rects, find all of the contiguous regions. We'll
shrink-wrap each region independently.

(WebCore::PathUtilities::pathWithShrinkWrappedRects):
* platform/graphics/PathUtilities.h: Added.
Add PathUtilities, where the shrink-wrapping code lives.

* rendering/RenderThemeMac.mm:
(WebCore::paintAttachmentTitleBackground):
(WebCore::addAttachmentTitleBackgroundRightCorner): Deleted.
(WebCore::addAttachmentTitleBackgroundLeftCorner): Deleted.
Remove shrink-wrapping implementation and make use of the one in PathUtilities.

* testing/Internals.cpp:
(WebCore::Internals::pathWithShrinkWrappedRects):
* testing/Internals.h:
* testing/Internals.idl:
Expose pathWithShrinkWrappedRects to tests via Internals.
It takes a sequence<double> where every four values are the x, y, w, h
of a rect, and returns a DOMPath which can be used with Canvas.

* fast/shrink-wrap/rect-shrink-wrap-expected.png: Added.
* fast/shrink-wrap/rect-shrink-wrap-expected.txt: Added.
* fast/shrink-wrap/rect-shrink-wrap.html: Added.
Add a test of both working and broken (indicated by comments in the test)
shrink-wrapping cases.

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

18 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/shrink-wrap/rect-shrink-wrap-expected.png [new file with mode: 0644]
LayoutTests/fast/shrink-wrap/rect-shrink-wrap-expected.txt [new file with mode: 0644]
LayoutTests/fast/shrink-wrap/rect-shrink-wrap.html [new file with mode: 0644]
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/bindings/js/JSDOMBinding.h
Source/WebCore/bindings/scripts/CodeGeneratorJS.pm
Source/WebCore/html/canvas/DOMPath.cpp [new file with mode: 0644]
Source/WebCore/html/canvas/DOMPath.h
Source/WebCore/platform/graphics/Path.h
Source/WebCore/platform/graphics/PathUtilities.cpp [new file with mode: 0644]
Source/WebCore/platform/graphics/PathUtilities.h [new file with mode: 0644]
Source/WebCore/rendering/RenderThemeMac.mm
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl

index 4b98839..fe38124 100644 (file)
@@ -1,3 +1,17 @@
+2015-07-15  Tim Horton  <timothy_horton@apple.com>
+
+        Factor rect shrink-wrapping code out of RenderThemeMac for future reuse
+        https://bugs.webkit.org/show_bug.cgi?id=146973
+        <rdar://problem/21643094>
+
+        Reviewed by Anders Carlsson.
+
+        * fast/shrink-wrap/rect-shrink-wrap-expected.png: Added.
+        * fast/shrink-wrap/rect-shrink-wrap-expected.txt: Added.
+        * fast/shrink-wrap/rect-shrink-wrap.html: Added.
+        Add a test of both working and broken (indicated by comments in the test)
+        shrink-wrapping cases.
+
 2015-07-15  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         Negative scroll snap repeat values cause web process to hang indefinitely
diff --git a/LayoutTests/fast/shrink-wrap/rect-shrink-wrap-expected.png b/LayoutTests/fast/shrink-wrap/rect-shrink-wrap-expected.png
new file mode 100644 (file)
index 0000000..6cab818
Binary files /dev/null and b/LayoutTests/fast/shrink-wrap/rect-shrink-wrap-expected.png differ
diff --git a/LayoutTests/fast/shrink-wrap/rect-shrink-wrap-expected.txt b/LayoutTests/fast/shrink-wrap/rect-shrink-wrap-expected.txt
new file mode 100644 (file)
index 0000000..a989629
--- /dev/null
@@ -0,0 +1,8 @@
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderBlock {HTML} at (0,0) size 800x600
+    RenderBody {BODY} at (0,0) size 800x600
+      RenderText {#text} at (0,0) size 0x0
+layer at (0,0) size 800x600
+  RenderHTMLCanvas {CANVAS} at (0,0) size 800x600
diff --git a/LayoutTests/fast/shrink-wrap/rect-shrink-wrap.html b/LayoutTests/fast/shrink-wrap/rect-shrink-wrap.html
new file mode 100644 (file)
index 0000000..e613ab7
--- /dev/null
@@ -0,0 +1,154 @@
+<script>
+
+function testRects(rects) {
+    if (!window.internals)
+        document.write("This test must be run in a test runner.")
+
+    var concatRects = [];
+    for (var i in rects)
+        Array.prototype.push.apply(concatRects, rects[i]);
+
+    var path = undefined;
+    if (window.internals)
+        path = window.internals.pathWithShrinkWrappedRects(concatRects);
+
+    var canvas = document.getElementById("shrink");
+    var ctx = canvas.getContext("2d");
+
+    ctx.fillStyle = "rgba(0,0,0,0.2)";
+
+    for (var i in rects)
+        ctx.fillRect.apply(ctx, rects[i]);
+
+    ctx.strokeStyle = "rgba(0,0,0,0.5)";
+    ctx.lineWidth = 1;
+    for (var i in rects)
+        ctx.strokeRect.apply(ctx, rects[i]);
+
+    ctx.strokeStyle = "blue";
+    ctx.lineWidth = 3;
+    if (path)
+        ctx.stroke(path);
+}
+
+window.onload = function () {
+    // Right and left aligned, touching:
+
+    testRects([
+        [20, 20, 50, 20],
+        [20, 40, 35, 20],
+        [20, 60, 20, 20]]);
+
+    testRects([
+        [20, 90, 20, 20],
+        [20, 110, 35, 20],
+        [20, 130, 50, 20]]);
+
+    testRects([
+        [80, 20, 50, 20],
+        [95, 40, 35, 20],
+        [110, 60, 20, 20]]);
+
+    testRects([
+        [110, 90, 20, 20],
+        [95, 110, 35, 20],
+        [80, 130, 50, 20]]);
+
+    // Center aligned, touching:
+
+    testRects([
+        [170, 20, 100, 40],
+        [190, 60, 60, 40],
+        [205, 100, 30, 40]]);
+
+    testRects([
+        [305, 20, 30, 40],
+        [290, 60, 60, 40],
+        [270, 100, 100, 40]]);
+
+    testRects([
+        [370, 20, 100, 40],
+        [405, 60, 30, 40],
+        [390, 100, 60, 40]]);
+
+    // Other:
+
+    testRects([
+        [20, 200, 40, 40],
+        [40, 220, 40, 40],
+        [60, 240, 40, 40]]);
+
+    testRects([
+        [120, 200, 40, 40],
+        [120, 240, 40, 40],
+        [120, 280, 40, 40]]);
+
+    // Non-touching:
+
+    testRects([
+        [180, 200, 40, 60],
+        [180, 280, 40, 40]]);
+
+    // Combination of touching and non-touching:
+
+    testRects([
+        [280, 200, 30, 40],
+        [280, 280, 50, 40],
+        [340, 200, 40, 40],
+        [360, 240, 80, 40],
+        [380, 280, 40, 40],
+        [430, 200, 40, 20],
+        [450, 215, 40, 20],
+        [470, 230, 40, 20]]);
+
+    // Incorrectly sorted:
+
+    testRects([
+        [20, 380, 40, 40],
+        [40, 360, 40, 40],
+        [60, 340, 40, 40]]);
+
+    // Broken:
+
+    testRects([
+        [600+100, 90, 20, 20],
+        [600+95, 110, 35, 20],
+        [600+80, 130, 50, 20]]);
+
+    testRects([
+        [230+340, 20, 40, 40],
+        [230+360, 60, 65, 40],
+        [230+380, 100, 40, 40]]);
+
+    // These should fallback to a rounded bounding rect:
+
+    testRects([
+        [600+100, 190, 20, 20],
+        [600+95, 210, 35, 20],
+        [600+80, 210, 50, 20]]);
+
+    testRects([
+        [600+0, 250, 40, 40],
+        [600+40, 250, 40, 40],
+        [600+80, 250, 40, 40]]);
+
+    testRects([
+        [600, 300, 20, 40],
+        [600+20, 320, 20, 40],
+        [600, 340, 20, 40]]);
+
+    testRects([
+        [700, 300, 20, 40],
+        [700+20, 320, 20, 40],
+        [700+40, 300, 20, 40]]);
+}
+
+</script>
+
+<style>
+body {
+    margin: 0;
+}
+</style>
+
+<canvas id="shrink" width="800" height="600"></canvas>
index e3261f9..000f999 100644 (file)
@@ -1742,6 +1742,7 @@ set(WebCore_SOURCES
     html/canvas/CanvasRenderingContext.cpp
     html/canvas/CanvasRenderingContext2D.cpp
     html/canvas/CanvasStyle.cpp
+    html/canvas/DOMPath.cpp
     html/canvas/EXTBlendMinMax.cpp
     html/canvas/EXTFragDepth.cpp
     html/canvas/EXTShaderTextureLOD.cpp
@@ -2171,6 +2172,7 @@ set(WebCore_SOURCES
     platform/graphics/NamedImageGeneratedImage.cpp
     platform/graphics/Path.cpp
     platform/graphics/PathTraversalState.cpp
+    platform/graphics/PathUtilities.cpp
     platform/graphics/Pattern.cpp
     platform/graphics/PlatformTimeRanges.cpp
     platform/graphics/Region.cpp
index edf2d08..4b1f8e0 100644 (file)
@@ -1,3 +1,79 @@
+2015-07-15  Tim Horton  <timothy_horton@apple.com>
+
+        Factor rect shrink-wrapping code out of RenderThemeMac for future reuse
+        https://bugs.webkit.org/show_bug.cgi?id=146973
+        <rdar://problem/21643094>
+
+        Reviewed by Anders Carlsson.
+
+        Test: fast/shrink-wrap/rect-shrink-wrap.html
+
+        * WebCore.xcodeproj/project.pbxproj:
+        Add DOMPath.cpp and PathUtilities.{h, cpp}.
+
+        * bindings/js/JSDOMBinding.h:
+        (WebCore::NativeValueTraits<double>::nativeValue):
+        Make it possible to use sequence<double> in IDL files.
+
+        * bindings/scripts/CodeGeneratorJS.pm:
+        Export JSDOMPath for use in Internals.
+
+        * html/canvas/DOMPath.cpp: Added.
+        (WebCore::DOMPath::~DOMPath):
+        * html/canvas/DOMPath.h:
+        Out-of-line the DOMPath destructor so as not to anger the bindings
+        integrity checker (otherwise, the address of the DOMPath destructor
+        is different in WebCoreTestSupport and WebCore, causing us to fail
+        the vtable equality test).
+
+        * platform/graphics/Path.h:
+        Forward declare FloatRect instead of including it unnecessarily.
+        Export ensurePlatformPath().
+
+        * platform/graphics/PathUtilities.cpp: Added.
+        (WebCore::addShrinkWrapRightCorner):
+        (WebCore::addShrinkWrapLeftCorner):
+        (WebCore::addShrinkWrappedPathForRects):
+        These parts are extracted from RenderThemeMac, with two changes:
+            + support for arbitrarily-aligned rects
+              (the other version assumed they were horizontally center-aligned)
+            + support for overlapping rects
+              (the other version assumed they touched but did not overlap)
+
+        There are still things missing:
+            + support for a fallback when the shape is too hard to shrink-wrap
+
+        And things broken:
+            + if the distance between two edges is smaller than the corner radius,
+              we'll end up with a sharp edge in the path
+
+        Both of these cases are covered in the layout test and can be improved.
+
+        (WebCore::rectsIntersectOrTouch):
+        Rect intersection with <= instead of <.
+
+        (WebCore::contiguousRectGroupsFromRects):
+        Given a set of rects, find all of the contiguous regions. We'll
+        shrink-wrap each region independently.
+
+        (WebCore::PathUtilities::pathWithShrinkWrappedRects):
+        * platform/graphics/PathUtilities.h: Added.
+        Add PathUtilities, where the shrink-wrapping code lives.
+
+        * rendering/RenderThemeMac.mm:
+        (WebCore::paintAttachmentTitleBackground):
+        (WebCore::addAttachmentTitleBackgroundRightCorner): Deleted.
+        (WebCore::addAttachmentTitleBackgroundLeftCorner): Deleted.
+        Remove shrink-wrapping implementation and make use of the one in PathUtilities.
+
+        * testing/Internals.cpp:
+        (WebCore::Internals::pathWithShrinkWrappedRects):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+        Expose pathWithShrinkWrappedRects to tests via Internals.
+        It takes a sequence<double> where every four values are the x, y, w, h
+        of a rect, and returns a DOMPath which can be used with Canvas.
+
 2015-07-15  Enrica Casucci  <enrica@apple.com>
 
         [iOS] Should look for RTF and RTFD pasteboard types before plain text.
index acc93e9..37092ca 100644 (file)
                2D4F96F61A1ECC240098BF88 /* TextIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D4F96F21A1ECC240098BF88 /* TextIndicator.h */; settings = {ATTRIBUTES = (Private, ); }; };
                2D4F96F71A1ECC240098BF88 /* TextIndicatorWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D4F96F31A1ECC240098BF88 /* TextIndicatorWindow.h */; settings = {ATTRIBUTES = (Private, ); }; };
                2D4F96F81A1ECC240098BF88 /* TextIndicatorWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D4F96F41A1ECC240098BF88 /* TextIndicatorWindow.mm */; };
+               2D5002F81B56D7810020AAF7 /* DOMPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2D5002F71B56D7810020AAF7 /* DOMPath.cpp */; };
+               2D5002FB1B56D7990020AAF7 /* PathUtilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2D5002F91B56D7990020AAF7 /* PathUtilities.cpp */; };
+               2D5002FC1B56D7990020AAF7 /* PathUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D5002FA1B56D7990020AAF7 /* PathUtilities.h */; };
                2D58D8551A15F65F00A5F726 /* DataDetection.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D58D8531A15F65F00A5F726 /* DataDetection.h */; settings = {ATTRIBUTES = (Private, ); }; };
                2D58D8561A15F65F00A5F726 /* DataDetection.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D58D8541A15F65F00A5F726 /* DataDetection.mm */; };
                2D59F1BF1A0044C6001F3D29 /* DataDetectorsSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D59F1BE1A0044C6001F3D29 /* DataDetectorsSPI.h */; settings = {ATTRIBUTES = (Private, ); }; };
                2D4F96F21A1ECC240098BF88 /* TextIndicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextIndicator.h; sourceTree = "<group>"; };
                2D4F96F31A1ECC240098BF88 /* TextIndicatorWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextIndicatorWindow.h; sourceTree = "<group>"; };
                2D4F96F41A1ECC240098BF88 /* TextIndicatorWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TextIndicatorWindow.mm; sourceTree = "<group>"; };
+               2D5002F71B56D7810020AAF7 /* DOMPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DOMPath.cpp; sourceTree = "<group>"; };
+               2D5002F91B56D7990020AAF7 /* PathUtilities.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PathUtilities.cpp; sourceTree = "<group>"; };
+               2D5002FA1B56D7990020AAF7 /* PathUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PathUtilities.h; sourceTree = "<group>"; };
                2D58D8531A15F65F00A5F726 /* DataDetection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataDetection.h; sourceTree = "<group>"; };
                2D58D8541A15F65F00A5F726 /* DataDetection.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DataDetection.mm; sourceTree = "<group>"; };
                2D59F1BE1A0044C6001F3D29 /* DataDetectorsSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataDetectorsSPI.h; sourceTree = "<group>"; };
                                49484FBE102CF23C00187DD3 /* CanvasRenderingContext2D.idl */,
                                49484FBF102CF23C00187DD3 /* CanvasStyle.cpp */,
                                49484FC0102CF23C00187DD3 /* CanvasStyle.h */,
+                               2D5002F71B56D7810020AAF7 /* DOMPath.cpp */,
                                FB91392016AE4B0B001FE682 /* DOMPath.h */,
                                FB91392116AE4B0B001FE682 /* DOMPath.idl */,
                                724ED3291A3A7E5400F5F13C /* EXTBlendMinMax.cpp */,
                                B27535530B053814002CE64F /* Path.h */,
                                A88DD4880B4629B000C02990 /* PathTraversalState.cpp */,
                                A88DD4860B4629A300C02990 /* PathTraversalState.h */,
+                               2D5002F91B56D7990020AAF7 /* PathUtilities.cpp */,
+                               2D5002FA1B56D7990020AAF7 /* PathUtilities.h */,
                                A8FA6E5C0E4CFDED00D5CF49 /* Pattern.cpp */,
                                A8FA6E5B0E4CFDED00D5CF49 /* Pattern.h */,
                                0562F9601573F88F0031CA16 /* PlatformLayer.h */,
                                065AD4F70B0C2EDA005A2B1D /* ContextMenuController.h in Headers */,
                                06027CAD0B1CBFC000884B2D /* ContextMenuItem.h in Headers */,
                                7ADE722610CBBB9B006B3B3A /* ContextMenuProvider.h in Headers */,
+                               2D5002FC1B56D7990020AAF7 /* PathUtilities.h in Headers */,
                                759CB837192DA9190012BC64 /* ControlStates.h in Headers */,
                                FD31602912B0267600C1A359 /* ConvolverNode.h in Headers */,
                                D8B6152F1032495100C8554A /* Cookie.h in Headers */,
                                297BE3DA16C043D8003316BD /* PlatformSpeechSynthesizer.cpp in Sources */,
                                AA12DF491743DF83004DAFDF /* PlatformSpeechSynthesizerIOS.mm in Sources */,
                                297BE3D816C03CCE003316BD /* PlatformSpeechSynthesizerMac.mm in Sources */,
+                               2D5002FB1B56D7990020AAF7 /* PathUtilities.cpp in Sources */,
                                1AD8F81C11CAB9E900E93E54 /* PlatformStrategies.cpp in Sources */,
                                074E82BA18A69F0E007EF54C /* PlatformTimeRanges.cpp in Sources */,
                                A9C6E4F30D745E48006442E9 /* PluginData.cpp in Sources */,
                                7AF9B20518CFB2DF00C64BEF /* VTTRegionList.cpp in Sources */,
                                7A93868518DCC14500B8263D /* VTTScanner.cpp in Sources */,
                                A14832B1187F61E100DA63A6 /* WAKAppKitStubs.m in Sources */,
+                               2D5002F81B56D7810020AAF7 /* DOMPath.cpp in Sources */,
                                A14832B3187F629100DA63A6 /* WAKClipView.m in Sources */,
                                A14832B5187F62FC00DA63A6 /* WAKResponder.m in Sources */,
                                A14832B7187F636C00DA63A6 /* WAKScrollView.mm in Sources */,
index ae7c15f..0e133f9 100644 (file)
@@ -507,6 +507,14 @@ template<> struct NativeValueTraits<float> {
     }
 };
 
+template<> struct NativeValueTraits<double> {
+    static inline bool nativeValue(JSC::ExecState* exec, JSC::JSValue jsValue, double& indexedValue)
+    {
+        indexedValue = jsValue.toNumber(exec);
+        return !exec->hadException();
+    }
+};
+
 template<typename T, typename JST> Vector<RefPtr<T>> toRefPtrNativeArray(JSC::ExecState* exec, JSC::JSValue value, T* (*toT)(JSC::JSValue value))
 {
     if (!isJSArray(value))
index 62e4dc9..6153d68 100644 (file)
@@ -240,6 +240,7 @@ my %classesNeedingWebCoreExport = (
     "JSClientRectList" => 1,
     "JSCSSStyleDeclaration" => 1,
     "JSDocument" => 1,
+    "JSDOMPath" => 1,
     "JSDOMWindow" => 1,
     "JSElement" => 1,
     "JSFile" => 1,
diff --git a/Source/WebCore/html/canvas/DOMPath.cpp b/Source/WebCore/html/canvas/DOMPath.cpp
new file mode 100644 (file)
index 0000000..d51b78a
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DOMPath.h"
+
+namespace WebCore {
+
+DOMPath::~DOMPath()
+{
+}
+
+}
index eb4bd10..91d7b15 100644 (file)
 
 namespace WebCore {
 
-class DOMPath final : public RefCounted<DOMPath>, public CanvasPathMethods {
+class WEBCORE_EXPORT DOMPath final : public RefCounted<DOMPath>, public CanvasPathMethods {
     WTF_MAKE_FAST_ALLOCATED;
 public:
+    WEBCORE_EXPORT virtual ~DOMPath();
+
     static Ref<DOMPath> create() { return adoptRef(*new DOMPath); }
     static Ref<DOMPath> create(const Path& path) { return adoptRef(*new DOMPath(path)); }
     static Ref<DOMPath> create(const DOMPath* path) { return create(path->path()); }
index c3c4c07..08b6f50 100644 (file)
@@ -28,6 +28,7 @@
 #ifndef Path_h
 #define Path_h
 
+#include "FloatRect.h"
 #include "WindRule.h"
 #include <wtf/FastMalloc.h>
 #include <wtf/Forward.h>
@@ -56,7 +57,6 @@ namespace WebCore {
 
     class AffineTransform;
     class FloatPoint;
-    class FloatRect;
     class FloatRoundedRect;
     class FloatSize;
     class GraphicsContext;
@@ -144,7 +144,7 @@ namespace WebCore {
         // meaning Path::platformPath() can return null.
         PlatformPathPtr platformPath() const { return m_path; }
         // ensurePlatformPath() will allocate a PlatformPath if it has not yet been and will never return null.
-        PlatformPathPtr ensurePlatformPath();
+        WEBCORE_EXPORT PlatformPathPtr ensurePlatformPath();
 
         WEBCORE_EXPORT void apply(void* info, PathApplierFunction) const;
         void transform(const AffineTransform&);
diff --git a/Source/WebCore/platform/graphics/PathUtilities.cpp b/Source/WebCore/platform/graphics/PathUtilities.cpp
new file mode 100644 (file)
index 0000000..4960437
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2014-2015 Apple Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+
+#include "config.h"
+#include "PathUtilities.h"
+
+#include "FloatPoint.h"
+#include "FloatRect.h"
+#include <math.h>
+
+namespace WebCore {
+
+static void addShrinkWrapRightCorner(Path& path, const FloatRect* fromRect, const FloatRect* toRect, float radius)
+{
+    FloatSize horizontalRadius(radius, 0);
+    FloatSize verticalRadius(0, radius);
+
+    if (!fromRect) {
+        // For the first (top) rect:
+
+        path.moveTo(toRect->minXMinYCorner() + horizontalRadius);
+
+        // Across the top, towards the right.
+        path.addLineTo(toRect->maxXMinYCorner() - horizontalRadius);
+
+        // Arc the top corner.
+        path.addArcTo(toRect->maxXMinYCorner(), toRect->maxXMinYCorner() + verticalRadius, radius);
+    } else if (!toRect) {
+        // For the last rect:
+
+        // Down the right.
+        path.addLineTo(fromRect->maxXMaxYCorner() - verticalRadius);
+
+        // Arc the bottom corner.
+        path.addArcTo(fromRect->maxXMaxYCorner(), fromRect->maxXMaxYCorner() - horizontalRadius, radius);
+    } else {
+        // For middle rects:
+
+        float rightEdgeDifference = toRect->maxX() - fromRect->maxX();
+
+        // Skip over rects with equal edges, because we can't make
+        // sensible curves between them.
+        if (fabsf(rightEdgeDifference) < std::numeric_limits<float>::epsilon())
+            return;
+
+        if (rightEdgeDifference < 0) {
+            float effectiveY = std::max(toRect->y(), fromRect->maxY());
+            FloatPoint toRectMaxXMinYCorner = FloatPoint(toRect->maxX(), effectiveY);
+
+            // Down the right.
+            path.addLineTo(FloatPoint(fromRect->maxX(), effectiveY) - verticalRadius);
+
+            // Arc the outer corner.
+            path.addArcTo(FloatPoint(fromRect->maxX(), effectiveY), FloatPoint(fromRect->maxX(), effectiveY) - horizontalRadius, radius);
+
+            // Across the bottom, towards the left.
+            path.addLineTo(toRectMaxXMinYCorner + horizontalRadius);
+
+            // Arc the inner corner.
+            path.addArcTo(toRectMaxXMinYCorner, toRectMaxXMinYCorner + verticalRadius, radius);
+        } else {
+            float effectiveY = std::min(toRect->y(), fromRect->maxY());
+            FloatPoint toRectMaxXMinYCorner = FloatPoint(toRect->maxX(), effectiveY);
+
+            // Down the right.
+            path.addLineTo(FloatPoint(fromRect->maxX(), effectiveY) - verticalRadius);
+
+            // Arc the inner corner.
+            path.addArcTo(FloatPoint(fromRect->maxX(), effectiveY), FloatPoint(fromRect->maxX(), effectiveY) + horizontalRadius, radius);
+
+            // Across the bottom, towards the right.
+            path.addLineTo(toRectMaxXMinYCorner - horizontalRadius);
+
+            // Arc the outer corner.
+            path.addArcTo(toRectMaxXMinYCorner, toRectMaxXMinYCorner + verticalRadius, radius);
+        }
+    }
+}
+
+static void addShrinkWrapLeftCorner(Path& path, const FloatRect* fromRect, const FloatRect* toRect, float radius)
+{
+    FloatSize horizontalRadius(radius, 0);
+    FloatSize verticalRadius(0, radius);
+
+    if (!fromRect) {
+        // For the first (bottom) rect:
+
+        // Across the bottom, towards the left.
+        path.addLineTo(toRect->minXMaxYCorner() + horizontalRadius);
+
+        // Arc the bottom corner.
+        path.addArcTo(toRect->minXMaxYCorner(), toRect->minXMaxYCorner() - verticalRadius, radius);
+
+    } else if (!toRect) {
+        // For the last (top) rect:
+
+        // Up the left.
+        path.addLineTo(fromRect->minXMinYCorner() + verticalRadius);
+
+        // Arc the top corner.
+        path.addArcTo(fromRect->minXMinYCorner(), fromRect->minXMinYCorner() + horizontalRadius, radius);
+    } else {
+        // For middle rects:
+        float leftEdgeDifference = fromRect->x() - toRect->x();
+
+        // Skip over rects with equal edges, because we can't make
+        // sensible curves between them.
+        if (fabsf(leftEdgeDifference) < std::numeric_limits<float>::epsilon())
+            return;
+
+        if (leftEdgeDifference < 0) {
+            float effectiveY = std::min(toRect->maxY(), fromRect->y());
+            FloatPoint toRectMinXMaxYCorner = FloatPoint(toRect->x(), effectiveY);
+
+            // Up the right.
+            path.addLineTo(FloatPoint(fromRect->x(), effectiveY) + verticalRadius);
+
+            // Arc the inner corner.
+            path.addArcTo(FloatPoint(fromRect->x(), effectiveY), FloatPoint(fromRect->x(), effectiveY) + horizontalRadius, radius);
+
+            // Across the bottom, towards the right.
+            path.addLineTo(toRectMinXMaxYCorner - horizontalRadius);
+
+            // Arc the outer corner.
+            path.addArcTo(toRectMinXMaxYCorner, toRectMinXMaxYCorner - verticalRadius, radius);
+        } else {
+            float effectiveY = std::max(toRect->maxY(), fromRect->y());
+            FloatPoint toRectMinXMaxYCorner = FloatPoint(toRect->x(), effectiveY);
+
+            // Up the right.
+            path.addLineTo(FloatPoint(fromRect->x(), effectiveY) + verticalRadius);
+
+            // Arc the outer corner.
+            path.addArcTo(FloatPoint(fromRect->x(), effectiveY), FloatPoint(fromRect->x(), effectiveY) - horizontalRadius, radius);
+
+            // Across the bottom, towards the left.
+            path.addLineTo(toRectMinXMaxYCorner + horizontalRadius);
+
+            // Arc the inner corner.
+            path.addArcTo(toRectMinXMaxYCorner, toRectMinXMaxYCorner - verticalRadius, radius);
+        }
+    }
+}
+
+static void addShrinkWrappedPathForRects(Path& path, Vector<FloatRect>& rects, float radius)
+{
+    size_t rectCount = rects.size();
+
+    std::sort(rects.begin(), rects.end(), [](FloatRect a, FloatRect b) { return b.y() > a.y(); });
+
+    for (size_t i = 0; i <= rectCount; ++i)
+        addShrinkWrapRightCorner(path, i ? &rects[i - 1] : nullptr, i < rectCount ? &rects[i] : nullptr, radius);
+
+    for (size_t i = 0; i <= rectCount; ++i) {
+        size_t reverseIndex = rectCount - i;
+        addShrinkWrapLeftCorner(path, reverseIndex < rectCount ? &rects[reverseIndex] : nullptr, reverseIndex ? &rects[reverseIndex - 1] : nullptr, radius);
+    }
+
+    path.closeSubpath();
+}
+
+static bool rectsIntersectOrTouch(const FloatRect& a, const FloatRect& b)
+{
+    return !a.isEmpty() && !b.isEmpty()
+        && a.x() <= b.maxX() && b.x() <= a.maxX()
+        && a.y() <= b.maxY() && b.y() <= a.maxY();
+}
+
+static Vector<FloatRect>* findSetContainingRect(Vector<Vector<FloatRect>>& sets, FloatRect rect)
+{
+    for (auto& set : sets) {
+        if (set.contains(rect))
+            return &set;
+    }
+
+    return nullptr;
+}
+
+static Vector<Vector<FloatRect>> contiguousRectGroupsFromRects(const Vector<FloatRect>& rects)
+{
+    Vector<std::pair<FloatRect, FloatRect>> intersections;
+    Vector<FloatRect> soloRects = rects;
+
+    for (auto& rectA : rects) {
+        for (auto& rectB : rects) {
+            if (rectA == rectB)
+                continue;
+
+            if (rectsIntersectOrTouch(rectA, rectB)) {
+                intersections.append(std::make_pair(rectA, rectB));
+                soloRects.removeAllMatching([rectA, rectB](FloatRect q) { return q == rectA || q == rectB; });
+            }
+        }
+    }
+
+    Vector<Vector<FloatRect>> rectSets;
+
+    for (auto& intersectingPair : intersections) {
+        if (Vector<FloatRect>* rectContainingFirst = findSetContainingRect(rectSets, intersectingPair.first)) {
+            if (!rectContainingFirst->contains(intersectingPair.second))
+                rectContainingFirst->append(intersectingPair.second);
+            continue;
+        }
+
+        if (Vector<FloatRect>* rectContainingSecond = findSetContainingRect(rectSets, intersectingPair.second)) {
+            if (!rectContainingSecond->contains(intersectingPair.first))
+                rectContainingSecond->append(intersectingPair.first);
+            continue;
+        }
+
+        // We didn't find a set including either of our rects, so start a new one.
+        rectSets.append(Vector<FloatRect>({intersectingPair.first, intersectingPair.second}));
+
+        continue;
+    }
+
+    for (auto& rect : soloRects) {
+        ASSERT(!findSetContainingRect(rectSets, rect));
+        rectSets.append(Vector<FloatRect>({rect}));
+    }
+
+    return rectSets;
+}
+
+Path PathUtilities::pathWithShrinkWrappedRects(const Vector<FloatRect>& rects, float radius)
+{
+    Path path;
+
+    if (rects.isEmpty())
+        return path;
+
+    Vector<Vector<FloatRect>> rectSets = contiguousRectGroupsFromRects(rects);
+
+    for (auto& set : rectSets)
+        addShrinkWrappedPathForRects(path, set, radius);
+    
+    return path;
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/PathUtilities.h b/Source/WebCore/platform/graphics/PathUtilities.h
new file mode 100644 (file)
index 0000000..83f75b9
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014-2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef PathUtilities_h
+#define PathUtilities_h
+
+#include "Path.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class PathUtilities {
+public:
+    WEBCORE_EXPORT static Path pathWithShrinkWrappedRects(const Vector<FloatRect>& rects, float radius);
+};
+
+}
+
+#endif
index 5a12218..06e235f 100644 (file)
@@ -52,6 +52,7 @@
 #import "NSSharingServicePickerSPI.h"
 #import "Page.h"
 #import "PaintInfo.h"
+#import "PathUtilities.h"
 #import "RenderAttachment.h"
 #import "RenderLayer.h"
 #import "RenderMedia.h"
@@ -2379,135 +2380,15 @@ static void paintAttachmentIcon(const RenderAttachment& attachment, GraphicsCont
     icon->paint(context, layout.iconRect);
 }
 
-static void addAttachmentTitleBackgroundRightCorner(Path& path, const FloatRect* fromRect, const FloatRect* toRect)
-{
-    FloatSize horizontalRadius(attachmentTitleBackgroundRadius, 0);
-    FloatSize verticalRadius(0, attachmentTitleBackgroundRadius);
-
-    if (!fromRect) {
-        // For the first (top) rect:
-
-        path.moveTo(toRect->minXMinYCorner() + horizontalRadius);
-
-        // Across the top, towards the right.
-        path.addLineTo(toRect->maxXMinYCorner() - horizontalRadius);
-
-        // Arc the top corner.
-        path.addArcTo(toRect->maxXMinYCorner(), toRect->maxXMinYCorner() + verticalRadius, attachmentTitleBackgroundRadius);
-
-        // Down the right.
-        path.addLineTo(toRect->maxXMaxYCorner() - verticalRadius);
-    } else if (!toRect) {
-        // For the last rect:
-
-        // Arc the bottom corner.
-        path.addArcTo(fromRect->maxXMaxYCorner(), fromRect->maxXMaxYCorner() - horizontalRadius, attachmentTitleBackgroundRadius);
-    } else {
-        // For middle rects:
-
-        float widthDifference = toRect->width() - fromRect->width();
-
-        // Skip over very similar-width rects, because we can't make
-        // sensible curves between them.
-        if (fabs(widthDifference) < std::numeric_limits<float>::epsilon())
-            return;
-
-        if (widthDifference < 0) {
-            // Arc the outer corner.
-            path.addArcTo(FloatPoint(fromRect->maxX(), toRect->y()), FloatPoint(fromRect->maxX(), toRect->y()) - horizontalRadius, attachmentTitleBackgroundRadius);
-
-            // Across the bottom, towards the left.
-            path.addLineTo(toRect->maxXMinYCorner() + horizontalRadius);
-
-            // Arc the inner corner.
-            path.addArcTo(toRect->maxXMinYCorner(), toRect->maxXMinYCorner() + verticalRadius, attachmentTitleBackgroundRadius);
-        } else {
-            // Arc the inner corner.
-            path.addArcTo(FloatPoint(fromRect->maxX(), toRect->y()), FloatPoint(fromRect->maxX(), toRect->y()) + horizontalRadius, attachmentTitleBackgroundRadius);
-
-            // Across the bottom, towards the right.
-            path.addLineTo(toRect->maxXMinYCorner() - horizontalRadius);
-
-            // Arc the outer corner.
-            path.addArcTo(toRect->maxXMinYCorner(), toRect->maxXMinYCorner() + verticalRadius, attachmentTitleBackgroundRadius);
-        }
-
-        // Down the right.
-        path.addLineTo(toRect->maxXMaxYCorner() - verticalRadius);
-    }
-}
-
-static void addAttachmentTitleBackgroundLeftCorner(Path& path, const FloatRect* fromRect, const FloatRect* toRect)
-{
-    FloatSize horizontalRadius(attachmentTitleBackgroundRadius, 0);
-    FloatSize verticalRadius(0, attachmentTitleBackgroundRadius);
-
-    if (!fromRect) {
-        // For the first (bottom) rect:
-
-        // Across the bottom, towards the left.
-        path.addLineTo(toRect->minXMaxYCorner() + horizontalRadius);
-
-        // Arc the bottom corner.
-        path.addArcTo(toRect->minXMaxYCorner(), toRect->minXMaxYCorner() - verticalRadius, attachmentTitleBackgroundRadius);
-
-        // Up the left.
-        path.addLineTo(toRect->minXMinYCorner() + verticalRadius);
-    } else if (!toRect) {
-        // For the last (top) rect:
-
-        // Arc the top corner.
-        path.addArcTo(fromRect->minXMinYCorner(), fromRect->minXMinYCorner() + horizontalRadius, attachmentTitleBackgroundRadius);
-    } else {
-        // For middle rects:
-        float widthDifference = toRect->width() - fromRect->width();
-
-        // Skip over very similar-width rects, because we can't make
-        // sensible curves between them.
-        if (fabs(widthDifference) < std::numeric_limits<float>::epsilon())
-            return;
-
-        if (widthDifference < 0) {
-            // Arc the inner corner.
-            path.addArcTo(FloatPoint(fromRect->x(), toRect->maxY()), FloatPoint(fromRect->x(), toRect->maxY()) + horizontalRadius, attachmentTitleBackgroundRadius);
-
-            // Across the bottom, towards the right.
-            path.addLineTo(toRect->minXMaxYCorner() - horizontalRadius);
-
-            // Arc the outer corner.
-            path.addArcTo(toRect->minXMaxYCorner(), toRect->minXMaxYCorner() - verticalRadius, attachmentTitleBackgroundRadius);
-        } else {
-            // Arc the outer corner.
-            path.addArcTo(FloatPoint(fromRect->x(), toRect->maxY()), FloatPoint(fromRect->x(), toRect->maxY()) - horizontalRadius, attachmentTitleBackgroundRadius);
-
-            // Across the bottom, towards the left.
-            path.addLineTo(toRect->minXMaxYCorner() + horizontalRadius);
-
-            // Arc the inner corner.
-            path.addArcTo(toRect->minXMaxYCorner(), toRect->minXMaxYCorner() - verticalRadius, attachmentTitleBackgroundRadius);
-        }
-        
-        // Up the right.
-        path.addLineTo(toRect->minXMinYCorner() + verticalRadius);
-    }
-}
-
 static void paintAttachmentTitleBackground(const RenderAttachment& attachment, GraphicsContext& context, AttachmentLayout& layout)
 {
     if (layout.lines.isEmpty())
         return;
 
-    Path backgroundPath;
+    Vector<FloatRect> backgroundRects;
 
-    for (size_t i = 0; i <= layout.lines.size(); ++i)
-        addAttachmentTitleBackgroundRightCorner(backgroundPath, i ? &layout.lines[i - 1].backgroundRect : nullptr, i < layout.lines.size() ? &layout.lines[i].backgroundRect : nullptr);
-
-    for (size_t i = 0; i <= layout.lines.size(); ++i) {
-        size_t reverseIndex = layout.lines.size() - i;
-        addAttachmentTitleBackgroundLeftCorner(backgroundPath, reverseIndex < layout.lines.size() ? &layout.lines[reverseIndex].backgroundRect : nullptr, reverseIndex ? &layout.lines[reverseIndex - 1].backgroundRect : nullptr);
-    }
-
-    backgroundPath.closeSubpath();
+    for (size_t i = 0; i < layout.lines.size(); ++i)
+        backgroundRects.append(layout.lines[i].backgroundRect);
 
     Color backgroundColor;
     if (attachment.frame().selection().isFocusedAndActive())
@@ -2516,6 +2397,8 @@ static void paintAttachmentTitleBackground(const RenderAttachment& attachment, G
         backgroundColor = attachmentTitleInactiveBackgroundColor();
 
     context.setFillColor(backgroundColor, ColorSpaceDeviceRGB);
+
+    Path backgroundPath = PathUtilities::pathWithShrinkWrappedRects(backgroundRects, attachmentTitleBackgroundRadius);
     context.fillPath(backgroundPath);
 }
 
index 8bca77a..2cb3971 100644 (file)
@@ -39,6 +39,7 @@
 #include "ClientRectList.h"
 #include "ContentDistributor.h"
 #include "Cursor.h"
+#include "DOMPath.h"
 #include "DOMStringList.h"
 #include "DOMWindow.h"
 #include "Document.h"
@@ -87,6 +88,7 @@
 #include "Page.h"
 #include "PageCache.h"
 #include "PageOverlay.h"
+#include "PathUtilities.h"
 #include "PlatformMediaSessionManager.h"
 #include "PrintContext.h"
 #include "PseudoElement.h"
@@ -2919,4 +2921,28 @@ bool Internals::testPreloaderSettingViewport()
     return testPreloadScannerViewportSupport(contextDocument());
 }
 
+PassRefPtr<DOMPath> Internals::pathWithShrinkWrappedRects(Vector<double> rectComponents, ExceptionCode& ec)
+{
+    if (rectComponents.size() % 4) {
+        ec = INVALID_ACCESS_ERR;
+        return nullptr;
+    }
+
+    Vector<FloatRect> rects;
+    while (!rectComponents.isEmpty()) {
+        double height = rectComponents.takeLast();
+        double width = rectComponents.takeLast();
+        double y = rectComponents.takeLast();
+        double x = rectComponents.takeLast();
+
+        rects.append(FloatRect(x, y, width, height));
+    }
+
+    rects.reverse();
+
+    // FIXME: radius should be a parameter instead of fixed as 8.
+    Path path = PathUtilities::pathWithShrinkWrappedRects(rects, 8);
+    return DOMPath::create(path);
+}
+
 }
index 360f5d3..87bf6a9 100644 (file)
@@ -44,6 +44,7 @@ namespace WebCore {
 class AudioContext;
 class ClientRect;
 class ClientRectList;
+class DOMPath;
 class DOMStringList;
 class DOMWindow;
 class Document;
@@ -414,6 +415,8 @@ public:
     String scrollSnapOffsets(Element*, ExceptionCode&);
 #endif
 
+    PassRefPtr<DOMPath> pathWithShrinkWrappedRects(Vector<double>, ExceptionCode&);
+
 private:
     explicit Internals(Document*);
     Document* contextDocument() const;
index c3d8822..60e637d 100644 (file)
@@ -375,4 +375,6 @@ enum ResourceLoadPriority {
 #if defined(ENABLE_CSS_SCROLL_SNAP) && ENABLE_CSS_SCROLL_SNAP
     [RaisesException] DOMString scrollSnapOffsets(Element element);
 #endif
+
+    [RaisesException] DOMPath pathWithShrinkWrappedRects(sequence<double> rectComponents);
 };