Title: [235358] trunk
Revision
235358
Author
[email protected]
Date
2018-08-27 04:13:51 -0700 (Mon, 27 Aug 2018)

Log Message

[IntersectionObserver] Implement intersection logic for the explicit root case
https://bugs.webkit.org/show_bug.cgi?id=188809

Reviewed by Simon Fraser.

LayoutTests/imported/w3c:

Rebaseline tests now that some intersection logic has been implemented.

* web-platform-tests/intersection-observer/bounding-box-expected.txt:
* web-platform-tests/intersection-observer/containing-block-expected.txt:
* web-platform-tests/intersection-observer/edge-inclusive-intersection-expected.txt:
* web-platform-tests/intersection-observer/isIntersecting-change-events-expected.txt:
* web-platform-tests/intersection-observer/remove-element-expected.txt:
* web-platform-tests/intersection-observer/same-document-root-expected.txt:
* web-platform-tests/intersection-observer/unclipped-root-expected.txt:

Source/WebCore:

Add logic to Document::updateIntersectionObservations to compute the intersection
between the target and root elements, for the case where an IntersectionObserver
has a root element.

There are no changes to the scheduling of intersection observations in this patch,
so observations are still only computed once for each observer.

* dom/Document.cpp:
(WebCore::computeIntersectionRects):
(WebCore::Document::updateIntersectionObservations):
* page/FrameView.cpp:
(WebCore::FrameView::absoluteToClientRect const):
* page/FrameView.h:
* page/IntersectionObserver.cpp:
(WebCore::IntersectionObserver::IntersectionObserver):
(WebCore::IntersectionObserver::createTimestamp const):
* page/IntersectionObserver.h:
* platform/graphics/FloatRect.h:
(WebCore::FloatRect::area const):
* rendering/RenderBlock.cpp:
(WebCore::RenderBlock::isContainingBlockAncestorFor const):
* rendering/RenderBlock.h:

Modified Paths

Diff

Modified: trunk/LayoutTests/imported/w3c/ChangeLog (235357 => 235358)


--- trunk/LayoutTests/imported/w3c/ChangeLog	2018-08-27 08:52:40 UTC (rev 235357)
+++ trunk/LayoutTests/imported/w3c/ChangeLog	2018-08-27 11:13:51 UTC (rev 235358)
@@ -1,3 +1,20 @@
+2018-08-27  Ali Juma  <[email protected]>
+
+        [IntersectionObserver] Implement intersection logic for the explicit root case
+        https://bugs.webkit.org/show_bug.cgi?id=188809
+
+        Reviewed by Simon Fraser.
+
+        Rebaseline tests now that some intersection logic has been implemented.
+
+        * web-platform-tests/intersection-observer/bounding-box-expected.txt:
+        * web-platform-tests/intersection-observer/containing-block-expected.txt:
+        * web-platform-tests/intersection-observer/edge-inclusive-intersection-expected.txt:
+        * web-platform-tests/intersection-observer/isIntersecting-change-events-expected.txt:
+        * web-platform-tests/intersection-observer/remove-element-expected.txt:
+        * web-platform-tests/intersection-observer/same-document-root-expected.txt:
+        * web-platform-tests/intersection-observer/unclipped-root-expected.txt:
+
 2018-08-27  Youenn Fablet  <[email protected]>
 
         Update WPT XHR tests to 87329a1

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/bounding-box-expected.txt (235357 => 235358)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/bounding-box-expected.txt	2018-08-27 08:52:40 UTC (rev 235357)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/bounding-box-expected.txt	2018-08-27 11:13:51 UTC (rev 235358)
@@ -1,5 +1,5 @@
 
 PASS Test that the target's border bounding box is used to calculate intersection. 
-FAIL First rAF. assert_equals: entries[0].boundingClientRect.left expected 25 but got 0
+PASS First rAF. 
 FAIL target.style.transform = 'translateY(195px)' assert_equals: entries.length expected 2 but got 1
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/containing-block-expected.txt (235357 => 235358)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/containing-block-expected.txt	2018-08-27 08:52:40 UTC (rev 235357)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/containing-block-expected.txt	2018-08-27 11:13:51 UTC (rev 235358)
@@ -1,6 +1,6 @@
 
 PASS IntersectionObserver should only report intersections if root is a containing block ancestor of target. 
-FAIL In containing block and intersecting. assert_equals: entries[0].boundingClientRect.left expected 58 but got 0
+FAIL In containing block and intersecting. assert_equals: entries[0].boundingClientRect.top expected 18 but got 258
 FAIL In containing block and not intersecting. assert_equals: entries.length expected 2 but got 1
 FAIL Not in containing block and intersecting. assert_equals: entries.length expected 2 but got 1
 FAIL Not in containing block and not intersecting. assert_equals: entries.length expected 2 but got 1

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/edge-inclusive-intersection-expected.txt (235357 => 235358)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/edge-inclusive-intersection-expected.txt	2018-08-27 08:52:40 UTC (rev 235357)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/edge-inclusive-intersection-expected.txt	2018-08-27 11:13:51 UTC (rev 235358)
@@ -1,6 +1,6 @@
 
 PASS IntersectionObserver should detect and report edge-adjacent and zero-area intersections. 
-FAIL First rAF. assert_equals: entries[0].boundingClientRect.left expected 8 but got 0
+PASS First rAF. 
 FAIL Set transform=translateY(200px) on target. assert_equals: entries.length expected 2 but got 1
 FAIL Set transform=translateY(201px) on target. assert_equals: entries.length expected 3 but got 1
 FAIL Set transform=translateY(185px) on target. assert_equals: entries.length expected 4 but got 1

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/isIntersecting-change-events-expected.txt (235357 => 235358)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/isIntersecting-change-events-expected.txt	2018-08-27 08:52:40 UTC (rev 235357)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/isIntersecting-change-events-expected.txt	2018-08-27 11:13:51 UTC (rev 235358)
@@ -1,4 +1,4 @@
 
 PASS isIntersecting changes should trigger notifications. 
-FAIL Rects in initial notifications should report initial positions. assert_equals: Check 1st entry rect.right expected 100 but got 0
+FAIL Rects in initial notifications should report initial positions. assert_equals: entries[2].target.isIntersecting equals true expected true but got false
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/remove-element-expected.txt (235357 => 235358)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/remove-element-expected.txt	2018-08-27 08:52:40 UTC (rev 235357)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/remove-element-expected.txt	2018-08-27 11:13:51 UTC (rev 235358)
@@ -1,6 +1,6 @@
 
 PASS Verify that not-intersecting notifications are sent when a target is removed from the DOM tree. 
-FAIL First rAF assert_equals: entries[0].boundingClientRect.left expected 11 but got 0
+PASS First rAF 
 FAIL root.scrollTop = 150 assert_equals: entries.length expected 2 but got 1
 FAIL root.removeChild(target). assert_equals: entries.length expected 3 but got 1
 FAIL root.insertBefore(target, trailingSpace). assert_equals: entries.length expected 3 but got 1

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/same-document-root-expected.txt (235357 => 235358)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/same-document-root-expected.txt	2018-08-27 08:52:40 UTC (rev 235357)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/same-document-root-expected.txt	2018-08-27 11:13:51 UTC (rev 235358)
@@ -1,6 +1,6 @@
 
 PASS IntersectionObserver in a single document with explicit root. 
-FAIL First rAF assert_equals: entries[0].boundingClientRect.left expected 11 but got 0
+PASS First rAF 
 PASS document.scrollingElement.scrollTop = window.innerHeight. 
 FAIL root.scrollTop = 150 with root scrolled into view. assert_equals: entries.length expected 2 but got 1
 FAIL document.scrollingElement.scrollTop = 0. assert_equals: entries.length expected 2 but got 1

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/unclipped-root-expected.txt (235357 => 235358)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/unclipped-root-expected.txt	2018-08-27 08:52:40 UTC (rev 235357)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/unclipped-root-expected.txt	2018-08-27 11:13:51 UTC (rev 235358)
@@ -1,5 +1,5 @@
 
 PASS Test that border bounding box is used to calculate intersection with a non-scrolling root. 
-FAIL First rAF. assert_equals: entries[0].boundingClientRect.left expected 15 but got 0
+PASS First rAF. 
 FAIL target.style.transform = 'translateY(195px)' assert_equals: entries.length expected 2 but got 1
 

Modified: trunk/Source/WebCore/ChangeLog (235357 => 235358)


--- trunk/Source/WebCore/ChangeLog	2018-08-27 08:52:40 UTC (rev 235357)
+++ trunk/Source/WebCore/ChangeLog	2018-08-27 11:13:51 UTC (rev 235358)
@@ -1,3 +1,33 @@
+2018-08-27  Ali Juma  <[email protected]>
+
+        [IntersectionObserver] Implement intersection logic for the explicit root case
+        https://bugs.webkit.org/show_bug.cgi?id=188809
+
+        Reviewed by Simon Fraser.
+
+        Add logic to Document::updateIntersectionObservations to compute the intersection
+        between the target and root elements, for the case where an IntersectionObserver
+        has a root element.
+
+        There are no changes to the scheduling of intersection observations in this patch,
+        so observations are still only computed once for each observer.
+
+        * dom/Document.cpp:
+        (WebCore::computeIntersectionRects):
+        (WebCore::Document::updateIntersectionObservations):
+        * page/FrameView.cpp:
+        (WebCore::FrameView::absoluteToClientRect const):
+        * page/FrameView.h:
+        * page/IntersectionObserver.cpp:
+        (WebCore::IntersectionObserver::IntersectionObserver):
+        (WebCore::IntersectionObserver::createTimestamp const):
+        * page/IntersectionObserver.h:
+        * platform/graphics/FloatRect.h:
+        (WebCore::FloatRect::area const):
+        * rendering/RenderBlock.cpp:
+        (WebCore::RenderBlock::isContainingBlockAncestorFor const):
+        * rendering/RenderBlock.h:
+
 2018-08-25  Yusuke Suzuki  <[email protected]>
 
         Shrink size of HTMLCollection

Modified: trunk/Source/WebCore/dom/Document.cpp (235357 => 235358)


--- trunk/Source/WebCore/dom/Document.cpp	2018-08-27 08:52:40 UTC (rev 235357)
+++ trunk/Source/WebCore/dom/Document.cpp	2018-08-27 11:13:51 UTC (rev 235358)
@@ -152,7 +152,9 @@
 #include "PublicSuffix.h"
 #include "RealtimeMediaSourceCenter.h"
 #include "RenderChildIterator.h"
+#include "RenderInline.h"
 #include "RenderLayerCompositor.h"
+#include "RenderLineBreak.h"
 #include "RenderTreeUpdater.h"
 #include "RenderView.h"
 #include "RenderWidget.h"
@@ -7438,10 +7440,63 @@
     return observerRef;
 }
 
+static void computeIntersectionRects(IntersectionObserver& observer, Element& target, FloatRect& absTargetRect, FloatRect& absIntersectionRect, FloatRect& absRootBounds)
+{
+    // FIXME: Implement intersection computation for the case of an implicit root.
+    if (!observer.root())
+        return;
+
+    if (observer.root()->document() != target.document())
+        return;
+
+    if (!observer.root()->renderer() || !is<RenderBlock>(observer.root()->renderer()))
+        return;
+
+    RenderBlock* rootRenderer = downcast<RenderBlock>(observer.root()->renderer());
+
+    auto* targetRenderer = target.renderer();
+    if (!targetRenderer)
+        return;
+
+    if (!rootRenderer->isContainingBlockAncestorFor(*targetRenderer))
+        return;
+
+    // FIXME: Expand localRootBounds using the observer's rootMargin.
+    FloatRect localRootBounds;
+    if (rootRenderer->hasOverflowClip())
+        localRootBounds = rootRenderer->contentBoxRect();
+    else
+        localRootBounds = { FloatPoint(), rootRenderer->size() };
+
+    LayoutRect localTargetBounds;
+    if (is<RenderBox>(*targetRenderer))
+        localTargetBounds = downcast<RenderBox>(targetRenderer)->borderBoundingBox();
+    else if (is<RenderInline>(targetRenderer))
+        localTargetBounds = downcast<RenderInline>(targetRenderer)->linesBoundingBox();
+    else if (is<RenderLineBreak>(targetRenderer))
+        localTargetBounds = downcast<RenderLineBreak>(targetRenderer)->linesBoundingBox();
+
+    FloatRect rootLocalIntersectionRect = targetRenderer->computeRectForRepaint(localTargetBounds, rootRenderer);
+    rootLocalIntersectionRect.intersect(localRootBounds);
+
+    if (!rootLocalIntersectionRect.isEmpty())
+        absIntersectionRect = rootRenderer->localToAbsoluteQuad(rootLocalIntersectionRect).boundingBox();
+
+    absTargetRect = targetRenderer->localToAbsoluteQuad(FloatRect(localTargetBounds)).boundingBox();
+    absRootBounds = rootRenderer->localToAbsoluteQuad(localRootBounds).boundingBox();
+}
+
 void Document::updateIntersectionObservations()
 {
+    auto* frameView = view();
+    if (!frameView)
+        return;
+
     for (auto observer : m_intersectionObservers) {
         bool needNotify = false;
+        DOMHighResTimeStamp timestamp;
+        if (!observer->createTimestamp(timestamp))
+            continue;
         for (Element* target : observer->observationTargets()) {
             auto& targetRegistrations = target->intersectionObserverData()->registrations;
             auto index = targetRegistrations.findMatching([observer](auto& registration) {
@@ -7450,20 +7505,40 @@
             ASSERT(index != notFound);
             auto& registration = targetRegistrations[index];
 
-            // FIXME: Compute intersection of target and observer's root.
+            FloatRect absTargetRect;
+            FloatRect absIntersectionRect;
+            FloatRect absRootBounds;
+            computeIntersectionRects(*observer, *target, absTargetRect, absIntersectionRect, absRootBounds);
+
+            // FIXME: Handle zero-area intersections (e.g., intersections involving zero-area targets).
+            bool isIntersecting = absIntersectionRect.area();
+            float intersectionRatio = isIntersecting ? absIntersectionRect.area() / absTargetRect.area() : 0;
             size_t thresholdIndex = 0;
-            double timestamp = 0;
-            std::optional<DOMRectInit> rootBounds;
-            DOMRectInit targetBoundingClientRect;
-            DOMRectInit intersectionRect;
-            double intersectionRatio = 0;
-            bool isIntersecting = false;
+            if (isIntersecting) {
+                auto& thresholds = observer->thresholds();
+                while (thresholdIndex < thresholds.size() && thresholds[thresholdIndex] <= intersectionRatio)
+                    ++thresholdIndex;
+            }
+
             if (!registration.previousThresholdIndex || thresholdIndex != registration.previousThresholdIndex) {
+                FloatRect targetBoundingClientRect = frameView->absoluteToClientRect(absTargetRect);
+                FloatRect clientIntersectionRect = frameView->absoluteToClientRect(absIntersectionRect);
+
+                // FIXME: Once cross-document observation is implemented, only report root bounds if the target document and
+                // the root document are similar-origin.
+                FloatRect clientRootBounds = frameView->absoluteToClientRect(absRootBounds);
+                std::optional<DOMRectInit> reportedRootBounds = DOMRectInit({
+                    clientRootBounds.x(),
+                    clientRootBounds.y(),
+                    clientRootBounds.width(),
+                    clientRootBounds.height()
+                });
+
                 observer->appendQueuedEntry(IntersectionObserverEntry::create({
                     timestamp,
-                    rootBounds,
-                    targetBoundingClientRect,
-                    intersectionRect,
+                    reportedRootBounds,
+                    { targetBoundingClientRect.x(), targetBoundingClientRect.y(), targetBoundingClientRect.width(), targetBoundingClientRect.height() },
+                    { clientIntersectionRect.x(), clientIntersectionRect.y(), clientIntersectionRect.width(), clientIntersectionRect.height() },
                     intersectionRatio,
                     target,
                     isIntersecting,

Modified: trunk/Source/WebCore/page/FrameView.cpp (235357 => 235358)


--- trunk/Source/WebCore/page/FrameView.cpp	2018-08-27 08:52:40 UTC (rev 235357)
+++ trunk/Source/WebCore/page/FrameView.cpp	2018-08-27 11:13:51 UTC (rev 235358)
@@ -4657,6 +4657,11 @@
     return p.scaled(absoluteToDocumentScaleFactor(effectiveZoom));
 }
 
+FloatRect FrameView::absoluteToClientRect(FloatRect rect, std::optional<float> effectiveZoom) const
+{
+    return documentToClientRect(absoluteToDocumentRect(rect, effectiveZoom));
+}
+
 FloatSize FrameView::documentToClientOffset() const
 {
     FloatSize clientOrigin = -toFloatSize(visibleContentRect().location());

Modified: trunk/Source/WebCore/page/FrameView.h (235357 => 235358)


--- trunk/Source/WebCore/page/FrameView.h	2018-08-27 08:52:40 UTC (rev 235357)
+++ trunk/Source/WebCore/page/FrameView.h	2018-08-27 11:13:51 UTC (rev 235358)
@@ -473,6 +473,8 @@
     FloatRect absoluteToDocumentRect(FloatRect, std::optional<float> effectiveZoom = std::nullopt) const;
     FloatPoint absoluteToDocumentPoint(FloatPoint, std::optional<float> effectiveZoom = std::nullopt) const;
 
+    FloatRect absoluteToClientRect(FloatRect, std::optional<float> effectiveZoom = std::nullopt) const;
+
     FloatSize documentToClientOffset() const;
     FloatRect documentToClientRect(FloatRect) const;
     FloatPoint documentToClientPoint(FloatPoint) const;

Modified: trunk/Source/WebCore/page/IntersectionObserver.cpp (235357 => 235358)


--- trunk/Source/WebCore/page/IntersectionObserver.cpp	2018-08-27 08:52:40 UTC (rev 235357)
+++ trunk/Source/WebCore/page/IntersectionObserver.cpp	2018-08-27 11:13:51 UTC (rev 235358)
@@ -34,6 +34,7 @@
 #include "Element.h"
 #include "IntersectionObserverCallback.h"
 #include "IntersectionObserverEntry.h"
+#include "Performance.h"
 #include <wtf/Vector.h>
 
 namespace WebCore {
@@ -114,6 +115,8 @@
         observerData.observers.append(makeWeakPtr(this));
     } else if (auto* frame = document.frame())
         m_implicitRootDocument = makeWeakPtr(frame->mainFrame().document());
+
+    std::sort(m_thresholds.begin(), m_thresholds.end());
 }
 
 IntersectionObserver::~IntersectionObserver()
@@ -226,6 +229,22 @@
     }
 }
 
+bool IntersectionObserver::createTimestamp(DOMHighResTimeStamp& timestamp) const
+{
+    auto* context = m_callback->scriptExecutionContext();
+    if (!context)
+        return false;
+    ASSERT(context->isDocument());
+    auto& document = downcast<Document>(*context);
+    if (auto* window = document.domWindow()) {
+        if (auto* performance = window->performance()) {
+            timestamp = performance->now();
+            return true;
+        }
+    }
+    return false;
+}
+
 void IntersectionObserver::appendQueuedEntry(Ref<IntersectionObserverEntry>&& entry)
 {
     m_queuedEntries.append(WTFMove(entry));

Modified: trunk/Source/WebCore/page/IntersectionObserver.h (235357 => 235358)


--- trunk/Source/WebCore/page/IntersectionObserver.h	2018-08-27 08:52:40 UTC (rev 235357)
+++ trunk/Source/WebCore/page/IntersectionObserver.h	2018-08-27 11:13:51 UTC (rev 235358)
@@ -84,6 +84,8 @@
     bool hasObservationTargets() const { return m_observationTargets.size(); }
     void rootDestroyed();
 
+    bool createTimestamp(DOMHighResTimeStamp&) const;
+
     void appendQueuedEntry(Ref<IntersectionObserverEntry>&&);
     void notify();
 

Modified: trunk/Source/WebCore/platform/graphics/FloatRect.h (235357 => 235358)


--- trunk/Source/WebCore/platform/graphics/FloatRect.h	2018-08-27 08:52:40 UTC (rev 235357)
+++ trunk/Source/WebCore/platform/graphics/FloatRect.h	2018-08-27 11:13:51 UTC (rev 235358)
@@ -89,6 +89,8 @@
     float width() const { return m_size.width(); }
     float height() const { return m_size.height(); }
 
+    float area() const { return m_size.area(); }
+
     void setX(float x) { m_location.setX(x); }
     void setY(float y) { m_location.setY(y); }
     void setWidth(float width) { m_size.setWidth(width); }

Modified: trunk/Source/WebCore/rendering/RenderBlock.cpp (235357 => 235358)


--- trunk/Source/WebCore/rendering/RenderBlock.cpp	2018-08-27 08:52:40 UTC (rev 235357)
+++ trunk/Source/WebCore/rendering/RenderBlock.cpp	2018-08-27 11:13:51 UTC (rev 235358)
@@ -1828,6 +1828,15 @@
     }
 }
 
+bool RenderBlock::isContainingBlockAncestorFor(RenderObject& renderer) const
+{
+    for (const auto* ancestor = renderer.containingBlock(); ancestor; ancestor = ancestor->containingBlock()) {
+        if (ancestor == this)
+            return true;
+    }
+    return false;
+}
+
 LayoutUnit RenderBlock::textIndentOffset() const
 {
     LayoutUnit cw = 0;

Modified: trunk/Source/WebCore/rendering/RenderBlock.h (235357 => 235358)


--- trunk/Source/WebCore/rendering/RenderBlock.h	2018-08-27 08:52:40 UTC (rev 235357)
+++ trunk/Source/WebCore/rendering/RenderBlock.h	2018-08-27 11:13:51 UTC (rev 235358)
@@ -101,6 +101,8 @@
     static void clearPercentHeightDescendantsFrom(RenderBox&);
     static void removePercentHeightDescendantIfNeeded(RenderBox&);
 
+    bool isContainingBlockAncestorFor(RenderObject&) const;
+
     void setHasMarginBeforeQuirk(bool b) { setRenderBlockHasMarginBeforeQuirk(b); }
     void setHasMarginAfterQuirk(bool b) { setRenderBlockHasMarginAfterQuirk(b); }
     void setShouldForceRelayoutChildren(bool b) { setRenderBlockShouldForceRelayoutChildren(b); }
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to