RenderSVGModelObject::checkIntersection triggers layout
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 25 Oct 2017 01:35:45 +0000 (01:35 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 25 Oct 2017 01:35:45 +0000 (01:35 +0000)
https://bugs.webkit.org/show_bug.cgi?id=178710

Reviewed by Simon Fraser.

Source/WebCore:

Fixed the bug that checkIntersection and checkEnclosure no longer updates the layout after r223882.

Test: svg/custom/check-intersection-basic.svg

* svg/SVGSVGElement.cpp:
(WebCore::SVGSVGElement::collectIntersectionOrEnclosureList):
(WebCore::SVGSVGElement::checkIntersection):
(WebCore::SVGSVGElement::checkEnclosure):
* svg/SVGSVGElement.h:

LayoutTests:

Added the support for SVG documents to js-test.js, and added a basic test for checkIntersection
and checkEnclosure using it.

* resources/js-test.js:
(ensureRootElement): Added. Creates a foreignObject element inside a SVG document.
(moveForeignObjectToTopIfNeeded): Added. In SVG, z-index order is determined by the element order.
In order to make the results visible, we move the foreignObject to the top by appending to the end
of the document element.
(getOrCreate):
(debug): Run innerHTML before appendChild as setting namespaceURI before running innerHTML would
result in span's being parsed as SVG elements.
(insertStyleSheet):
(finishJSTest):
* svg/custom/check-intersection-basic-expected.txt: Added.
* svg/custom/check-intersection-basic.svg: Added.

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

LayoutTests/ChangeLog
LayoutTests/resources/js-test.js
LayoutTests/svg/custom/check-intersection-basic-expected.txt [new file with mode: 0644]
LayoutTests/svg/custom/check-intersection-basic.svg [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/svg/SVGSVGElement.cpp
Source/WebCore/svg/SVGSVGElement.h

index 36690ed..95b6566 100644 (file)
@@ -1,3 +1,26 @@
+2017-10-24  Ryosuke Niwa  <rniwa@webkit.org>
+
+        RenderSVGModelObject::checkIntersection triggers layout
+        https://bugs.webkit.org/show_bug.cgi?id=178710
+
+        Reviewed by Simon Fraser.
+
+        Added the support for SVG documents to js-test.js, and added a basic test for checkIntersection
+        and checkEnclosure using it.
+
+        * resources/js-test.js:
+        (ensureRootElement): Added. Creates a foreignObject element inside a SVG document.
+        (moveForeignObjectToTopIfNeeded): Added. In SVG, z-index order is determined by the element order.
+        In order to make the results visible, we move the foreignObject to the top by appending to the end
+        of the document element.
+        (getOrCreate):
+        (debug): Run innerHTML before appendChild as setting namespaceURI before running innerHTML would
+        result in span's being parsed as SVG elements.
+        (insertStyleSheet):
+        (finishJSTest):
+        * svg/custom/check-intersection-basic-expected.txt: Added.
+        * svg/custom/check-intersection-basic.svg: Added.
+
 2017-10-24  Andy Estes  <aestes@apple.com>
 
         [Apple Pay] Implement a paymentmethodselected event for PaymentRequest
index 30f26b0..c93d436 100644 (file)
@@ -31,6 +31,31 @@ var unexpectedErrorMessage; // set by onerror when expectingError is not true
         return document.createElement(tagName);
     }
 
+    var rootElement = null;
+    function ensureRootElement()
+    {
+        if (!rootElement || !rootElement.isConnected) {
+            rootElement = document.body || document.documentElement;
+            if (document.documentElement.namespaceURI == 'http://www.w3.org/2000/svg') {
+                // FIXME: Make the test harness use SVG elements naively.
+                var foreignObject = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
+                foreignObject.setAttribute('x', '0px');
+                foreignObject.setAttribute('y', '0px');
+                foreignObject.setAttribute('width', '100%');
+                foreignObject.setAttribute('height', '100%');
+                foreignObject.setAttribute('style', 'padding: 10px; background-color: rgba(255, 255, 255, 0.5)');
+                document.documentElement.appendChild(foreignObject);
+                rootElement = foreignObject;
+            }
+        }
+        return rootElement;
+    }
+
+    moveForeignObjectToTopIfNeeded = function () {
+        if (rootElement && rootElement.localName == 'foreignObject')
+            document.documentElement.appendChild(rootElement);
+    }
+
     function getOrCreate(id, tagName)
     {
         var element = document.getElementById(id);
@@ -40,7 +65,8 @@ var unexpectedErrorMessage; // set by onerror when expectingError is not true
         element = createHTMLElement(tagName);
         element.id = id;
         var refNode;
-        var parent = document.body || document.documentElement;
+        var parent = ensureRootElement();
+
         if (id == "description")
             refNode = getOrCreate("console", "div");
         else
@@ -69,8 +95,8 @@ var unexpectedErrorMessage; // set by onerror when expectingError is not true
     debug = function debug(msg)
     {
         var span = createHTMLElement("span");
-        getOrCreate("console", "div").appendChild(span); // insert it first so XHTML knows the namespace
         span.innerHTML = msg + '<br />';
+        getOrCreate("console", "div").appendChild(span);
     };
 
     var css =
@@ -91,7 +117,7 @@ var unexpectedErrorMessage; // set by onerror when expectingError is not true
     {
         var styleElement = createHTMLElement("style");
         styleElement.textContent = css;
-        (document.head || document.documentElement).appendChild(styleElement);
+        (document.head || ensureRootElement()).appendChild(styleElement);
     }
 
     function handleTestFinished()
@@ -748,6 +774,7 @@ function finishJSTest()
     if (!self.wasPostTestScriptParsed)
         return;
     isSuccessfullyParsed();
+    moveForeignObjectToTopIfNeeded();
     if (self.jsTestIsAsync && self.testRunner)
         testRunner.notifyDone();
 }
diff --git a/LayoutTests/svg/custom/check-intersection-basic-expected.txt b/LayoutTests/svg/custom/check-intersection-basic-expected.txt
new file mode 100644 (file)
index 0000000..0ac4123
--- /dev/null
@@ -0,0 +1,45 @@
+Tests SVGSVGElement.checkIntersection
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS rectOfBox = createRect(10, 10, 50, 50); document.documentElement.checkIntersection(box, rectOfBox) is true
+PASS document.documentElement.checkEnclosure(box, rectOfBox) is true
+PASS rectInBox = createRect(20, 20, 20, 20); document.documentElement.checkIntersection(box, rectInBox) is true
+PASS document.documentElement.checkEnclosure(box, rectInBox) is false
+PASS boxExtendingUp = createRect(10, 0, 50, 60); document.documentElement.checkIntersection(box, boxExtendingUp) is true
+PASS document.documentElement.checkEnclosure(box, boxExtendingUp) is true
+PASS boxExtendingDown = createRect(10, 10, 50, 60); document.documentElement.checkIntersection(box, boxExtendingDown) is true
+PASS document.documentElement.checkEnclosure(box, boxExtendingDown) is true
+PASS boxExtendingLeft = createRect(0, 10, 60, 50); document.documentElement.checkIntersection(box, boxExtendingLeft) is true
+PASS document.documentElement.checkEnclosure(box, boxExtendingLeft) is true
+PASS boxExtendingRight = createRect(10, 10, 60, 50); document.documentElement.checkIntersection(box, boxExtendingRight) is true
+PASS document.documentElement.checkEnclosure(box, boxExtendingRight) is true
+PASS boxShrinkingTop = createRect(10, 20, 50, 40); document.documentElement.checkIntersection(box, boxShrinkingTop) is true
+PASS document.documentElement.checkEnclosure(box, boxShrinkingTop) is false
+PASS boxShrinkingBottom = createRect(10, 10, 50, 40); document.documentElement.checkIntersection(box, boxShrinkingBottom) is true
+PASS document.documentElement.checkEnclosure(box, boxShrinkingBottom) is false
+PASS boxShrinkingLeft = createRect(20, 10, 40, 50); document.documentElement.checkIntersection(box, boxShrinkingLeft) is true
+PASS document.documentElement.checkEnclosure(box, boxShrinkingLeft) is false
+PASS boxShrinkingRight = createRect(10, 10, 40, 50); document.documentElement.checkIntersection(box, boxShrinkingRight) is true
+PASS document.documentElement.checkEnclosure(box, boxShrinkingRight) is false
+PASS upperLeftCornerBox = createRect(5, 5, 20, 20); document.documentElement.checkIntersection(box, upperLeftCornerBox) is true
+PASS document.documentElement.checkEnclosure(box, upperLeftCornerBox) is false
+PASS upperRightCornerBox = createRect(45, 5, 20, 20); document.documentElement.checkIntersection(box, upperRightCornerBox) is true
+PASS document.documentElement.checkEnclosure(box, upperRightCornerBox) is false
+PASS boxOnTop = createRect(10, 5, 50, 5); document.documentElement.checkIntersection(box, boxOnTop) is false
+PASS document.documentElement.checkEnclosure(box, boxOnTop) is false
+PASS boxOnBottom = createRect(60, 5, 50, 5); document.documentElement.checkIntersection(box, boxOnBottom) is false
+PASS document.documentElement.checkEnclosure(box, boxOnBottom) is false
+PASS boxOnLeft = createRect(5, 10, 5, 50); document.documentElement.checkIntersection(box, boxOnLeft) is false
+PASS document.documentElement.checkEnclosure(box, boxOnLeft) is false
+PASS boxOnRight = createRect(60, 10, 5, 50); document.documentElement.checkIntersection(box, boxOnRight) is false
+PASS document.documentElement.checkEnclosure(box, boxOnRight) is false
+PASS box.setAttribute("class", "width20"); document.documentElement.checkIntersection(box, upperRightCornerBox) is false
+PASS narrowBox = createRect(10, 10, 20, 50); document.documentElement.checkEnclosure(box, narrowBox) is true
+PASS box.setAttribute("class", "width50"); document.documentElement.checkIntersection(box, upperRightCornerBox) is true
+PASS document.documentElement.checkEnclosure(box, narrowBox) is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/svg/custom/check-intersection-basic.svg b/LayoutTests/svg/custom/check-intersection-basic.svg
new file mode 100644 (file)
index 0000000..5dcd0c9
--- /dev/null
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" onload="runTest()">
+<script xlink:href="../../resources/js-test.js"/>
+<rect id="box" class="width50" x="10" y="10" height="50" fill="blue" />
+<text x="10" y="60"></text>
+<style>
+.width20 { width: 20px; }
+.width50 { width: 50px; }
+</style>
+<script>
+
+description('Tests SVGSVGElement.checkIntersection');
+
+function createRect(x, y, width, height) {
+    const rect = document.documentElement.createSVGRect();
+    rect.x = x;
+    rect.y = y;
+    rect.width = width;
+    rect.height = height;
+    return rect;
+}
+
+function runTest() {
+    window.box = document.getElementById("box");
+
+    shouldBeTrue('rectOfBox = createRect(10, 10, 50, 50); document.documentElement.checkIntersection(box, rectOfBox)');
+    shouldBeTrue('document.documentElement.checkEnclosure(box, rectOfBox)');
+
+    shouldBeTrue('rectInBox = createRect(20, 20, 20, 20); document.documentElement.checkIntersection(box, rectInBox)');
+    shouldBeFalse('document.documentElement.checkEnclosure(box, rectInBox)');
+
+    shouldBeTrue('boxExtendingUp = createRect(10, 0, 50, 60); document.documentElement.checkIntersection(box, boxExtendingUp)');
+    shouldBeTrue('document.documentElement.checkEnclosure(box, boxExtendingUp)');
+
+    shouldBeTrue('boxExtendingDown = createRect(10, 10, 50, 60); document.documentElement.checkIntersection(box, boxExtendingDown)');
+    shouldBeTrue('document.documentElement.checkEnclosure(box, boxExtendingDown)');
+
+    shouldBeTrue('boxExtendingLeft = createRect(0, 10, 60, 50); document.documentElement.checkIntersection(box, boxExtendingLeft)');
+    shouldBeTrue('document.documentElement.checkEnclosure(box, boxExtendingLeft)');
+
+    shouldBeTrue('boxExtendingRight = createRect(10, 10, 60, 50); document.documentElement.checkIntersection(box, boxExtendingRight)');
+    shouldBeTrue('document.documentElement.checkEnclosure(box, boxExtendingRight)');
+
+    shouldBeTrue('boxShrinkingTop = createRect(10, 20, 50, 40); document.documentElement.checkIntersection(box, boxShrinkingTop)');
+    shouldBeFalse('document.documentElement.checkEnclosure(box, boxShrinkingTop)');
+
+    shouldBeTrue('boxShrinkingBottom = createRect(10, 10, 50, 40); document.documentElement.checkIntersection(box, boxShrinkingBottom)');
+    shouldBeFalse('document.documentElement.checkEnclosure(box, boxShrinkingBottom)');
+
+    shouldBeTrue('boxShrinkingLeft = createRect(20, 10, 40, 50); document.documentElement.checkIntersection(box, boxShrinkingLeft)');
+    shouldBeFalse('document.documentElement.checkEnclosure(box, boxShrinkingLeft)');
+
+    shouldBeTrue('boxShrinkingRight = createRect(10, 10, 40, 50); document.documentElement.checkIntersection(box, boxShrinkingRight)');
+    shouldBeFalse('document.documentElement.checkEnclosure(box, boxShrinkingRight)');
+
+    shouldBeTrue('upperLeftCornerBox = createRect(5, 5, 20, 20); document.documentElement.checkIntersection(box, upperLeftCornerBox)');
+    shouldBeFalse('document.documentElement.checkEnclosure(box, upperLeftCornerBox)');
+
+    shouldBeTrue('upperRightCornerBox = createRect(45, 5, 20, 20); document.documentElement.checkIntersection(box, upperRightCornerBox)');
+    shouldBeFalse('document.documentElement.checkEnclosure(box, upperRightCornerBox)');
+
+    shouldBeFalse('boxOnTop = createRect(10, 5, 50, 5); document.documentElement.checkIntersection(box, boxOnTop)');
+    shouldBeFalse('document.documentElement.checkEnclosure(box, boxOnTop)');
+
+    shouldBeFalse('boxOnBottom = createRect(60, 5, 50, 5); document.documentElement.checkIntersection(box, boxOnBottom)');
+    shouldBeFalse('document.documentElement.checkEnclosure(box, boxOnBottom)');
+
+    shouldBeFalse('boxOnLeft = createRect(5, 10, 5, 50); document.documentElement.checkIntersection(box, boxOnLeft)');
+    shouldBeFalse('document.documentElement.checkEnclosure(box, boxOnLeft)');
+
+    shouldBeFalse('boxOnRight = createRect(60, 10, 5, 50); document.documentElement.checkIntersection(box, boxOnRight)');
+    shouldBeFalse('document.documentElement.checkEnclosure(box, boxOnRight)');
+
+    shouldBeFalse('box.setAttribute("class", "width20"); document.documentElement.checkIntersection(box, upperRightCornerBox)');
+    shouldBeTrue('narrowBox = createRect(10, 10, 20, 50); document.documentElement.checkEnclosure(box, narrowBox)');
+
+    shouldBeTrue('box.setAttribute("class", "width50"); document.documentElement.checkIntersection(box, upperRightCornerBox)');
+    shouldBeFalse('document.documentElement.checkEnclosure(box, narrowBox)');
+
+    finishJSTest()
+}
+
+var successfullyParsed = true;
+
+</script>
+</svg>
+
index cf56288..810a2f0 100644 (file)
@@ -1,3 +1,20 @@
+2017-10-24  Ryosuke Niwa  <rniwa@webkit.org>
+
+        RenderSVGModelObject::checkIntersection triggers layout
+        https://bugs.webkit.org/show_bug.cgi?id=178710
+
+        Reviewed by Simon Fraser.
+
+        Fixed the bug that checkIntersection and checkEnclosure no longer updates the layout after r223882.
+
+        Test: svg/custom/check-intersection-basic.svg
+
+        * svg/SVGSVGElement.cpp:
+        (WebCore::SVGSVGElement::collectIntersectionOrEnclosureList):
+        (WebCore::SVGSVGElement::checkIntersection):
+        (WebCore::SVGSVGElement::checkEnclosure):
+        * svg/SVGSVGElement.h:
+
 2017-10-24  Andy Estes  <aestes@apple.com>
 
         [Apple Pay] Implement a paymentmethodselected event for PaymentRequest
index 522821e..81add34 100644 (file)
@@ -323,7 +323,7 @@ void SVGSVGElement::forceRedraw()
 {
 }
 
-Ref<NodeList> SVGSVGElement::collectIntersectionOrEnclosureList(SVGRect& rect, SVGElement* referenceElement, bool (*checkFunction)(const SVGElement*, SVGRect&))
+Ref<NodeList> SVGSVGElement::collectIntersectionOrEnclosureList(SVGRect& rect, SVGElement* referenceElement, bool (*checkFunction)(RefPtr<SVGElement>&&, SVGRect&))
 {
     Vector<Ref<Element>> elements;
     for (auto& element : descendantsOfType<SVGElement>(referenceElement ? *referenceElement : *this)) {
@@ -345,14 +345,20 @@ Ref<NodeList> SVGSVGElement::getEnclosureList(SVGRect& rect, SVGElement* referen
     return collectIntersectionOrEnclosureList(rect, referenceElement, checkEnclosure);
 }
 
-bool SVGSVGElement::checkIntersection(const SVGElement* element, SVGRect& rect)
+bool SVGSVGElement::checkIntersection(RefPtr<SVGElement>&& element, SVGRect& rect)
 {
-    return element && RenderSVGModelObject::checkIntersection(element->renderer(), rect.propertyReference());
+    if (!element)
+        return false;
+    element->document().updateLayoutIgnorePendingStylesheets();
+    return RenderSVGModelObject::checkIntersection(element->renderer(), rect.propertyReference());
 }
 
-bool SVGSVGElement::checkEnclosure(const SVGElement* element, SVGRect& rect)
+bool SVGSVGElement::checkEnclosure(RefPtr<SVGElement>&& element, SVGRect& rect)
 {
-    return element && RenderSVGModelObject::checkEnclosure(element->renderer(), rect.propertyReference());
+    if (!element)
+        return false;
+    element->document().updateLayoutIgnorePendingStylesheets();
+    return RenderSVGModelObject::checkEnclosure(element->renderer(), rect.propertyReference());
 }
 
 void SVGSVGElement::deselectAll()
index 20ae178..eac309a 100644 (file)
@@ -92,8 +92,8 @@ public: // DOM
 
     Ref<NodeList> getIntersectionList(SVGRect&, SVGElement* referenceElement);
     Ref<NodeList> getEnclosureList(SVGRect&, SVGElement* referenceElement);
-    static bool checkIntersection(const SVGElement*, SVGRect&);
-    static bool checkEnclosure(const SVGElement*, SVGRect&);
+    static bool checkIntersection(RefPtr<SVGElement>&&, SVGRect&);
+    static bool checkEnclosure(RefPtr<SVGElement>&&, SVGRect&);
     void deselectAll();
 
     static Ref<SVGNumber> createSVGNumber();
@@ -153,7 +153,7 @@ private:
 
     Frame* frameForCurrentScale() const;
     void inheritViewAttributes(const SVGViewElement&);
-    Ref<NodeList> collectIntersectionOrEnclosureList(SVGRect&, SVGElement*, bool (*checkFunction)(const SVGElement*, SVGRect&));
+    Ref<NodeList> collectIntersectionOrEnclosureList(SVGRect&, SVGElement*, bool (*checkFunction)(RefPtr<SVGElement>&&, SVGRect&));
 
     bool m_useCurrentView { false };
     SVGZoomAndPanType m_zoomAndPan { SVGZoomAndPanMagnify };