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); }