| Index: third_party/WebKit/Source/core/dom/IntersectionGeometry.cpp
|
| diff --git a/third_party/WebKit/Source/core/dom/IntersectionObservation.cpp b/third_party/WebKit/Source/core/dom/IntersectionGeometry.cpp
|
| similarity index 31%
|
| copy from third_party/WebKit/Source/core/dom/IntersectionObservation.cpp
|
| copy to third_party/WebKit/Source/core/dom/IntersectionGeometry.cpp
|
| index 55d610af229a5ec1111ca814843f748ae86ae57e..1956b7b62aeda753729dc3e157b826fb8666bd11 100644
|
| --- a/third_party/WebKit/Source/core/dom/IntersectionObservation.cpp
|
| +++ b/third_party/WebKit/Source/core/dom/IntersectionGeometry.cpp
|
| @@ -2,259 +2,239 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| -#include "core/dom/IntersectionObservation.h"
|
| +#include "core/dom/IntersectionGeometry.h"
|
|
|
| +#include "core/dom/Element.h"
|
| #include "core/dom/ElementRareData.h"
|
| -#include "core/dom/IntersectionObserver.h"
|
| #include "core/frame/FrameView.h"
|
| #include "core/frame/LocalFrame.h"
|
| #include "core/layout/LayoutBox.h"
|
| #include "core/layout/LayoutView.h"
|
| +#include "core/layout/api/LayoutAPIShim.h"
|
| +#include "core/layout/api/LayoutViewItem.h"
|
| #include "core/paint/PaintLayer.h"
|
|
|
| namespace blink {
|
|
|
| -IntersectionObservation::IntersectionObservation(IntersectionObserver& observer,
|
| - Element& target,
|
| - bool shouldReportRootBounds)
|
| - : m_observer(observer),
|
| - m_target(&target),
|
| - m_shouldReportRootBounds(shouldReportRootBounds),
|
| - m_lastThresholdIndex(0) {}
|
| -
|
| -void IntersectionObservation::applyRootMargin(LayoutRect& rect) const {
|
| - if (m_shouldReportRootBounds)
|
| - m_observer->applyRootMargin(rect);
|
| +namespace {
|
| +
|
| +bool isContainingBlockChainDescendant(LayoutObject* descendant,
|
| + LayoutObject* ancestor) {
|
| + LocalFrame* ancestorFrame = ancestor->document().frame();
|
| + LocalFrame* descendantFrame = descendant->document().frame();
|
| +
|
| + if (ancestor->isLayoutView())
|
| + return descendantFrame && descendantFrame->tree().top() == ancestorFrame;
|
| +
|
| + if (ancestorFrame != descendantFrame)
|
| + return false;
|
| +
|
| + while (descendant && descendant != ancestor)
|
| + descendant = descendant->containingBlock();
|
| + return descendant;
|
| +}
|
| +
|
| +void mapRectUpToDocument(LayoutRect& rect,
|
| + const LayoutObject& layoutObject,
|
| + const Document& document) {
|
| + FloatQuad mappedQuad = layoutObject.localToAbsoluteQuad(
|
| + FloatQuad(FloatRect(rect)), UseTransforms | ApplyContainerFlip);
|
| + rect = LayoutRect(mappedQuad.boundingBox());
|
| +}
|
| +
|
| +void mapRectDownToDocument(LayoutRect& rect,
|
| + LayoutBoxModelObject& layoutObject,
|
| + const Document& document) {
|
| + FloatQuad mappedQuad = document.layoutView()->ancestorToLocalQuad(
|
| + &layoutObject, FloatQuad(FloatRect(rect)),
|
| + UseTransforms | ApplyContainerFlip | TraverseDocumentBoundaries);
|
| + rect = LayoutRect(mappedQuad.boundingBox());
|
| }
|
|
|
| -void IntersectionObservation::initializeGeometry(
|
| - IntersectionGeometry& geometry) const {
|
| - initializeTargetRect(geometry.targetRect);
|
| - geometry.intersectionRect = geometry.targetRect;
|
| - initializeRootRect(geometry.rootRect);
|
| - geometry.doesIntersect = true;
|
| +LayoutUnit computeMargin(const Length& length, LayoutUnit referenceLength) {
|
| + if (length.type() == Percent) {
|
| + return LayoutUnit(
|
| + static_cast<int>(referenceLength.toFloat() * length.percent() / 100.0));
|
| + }
|
| + DCHECK_EQ(length.type(), Fixed);
|
| + return LayoutUnit(length.intValue());
|
| }
|
|
|
| -void IntersectionObservation::initializeTargetRect(LayoutRect& rect) const {
|
| +} // namespace
|
| +
|
| +IntersectionGeometry::IntersectionGeometry(
|
| + Node* root,
|
| + Element* target,
|
| + const Vector<Length>& rootMargin,
|
| + ReportRootBounds shouldReportRootBounds)
|
| + : m_root(root),
|
| + m_target(target),
|
| + m_rootMargin(rootMargin),
|
| + m_shouldReportRootBounds(shouldReportRootBounds) {
|
| DCHECK(m_target);
|
| - LayoutObject* targetLayoutObject = target()->layoutObject();
|
| + DCHECK(rootMargin.isEmpty() || rootMargin.size() == 4);
|
| +}
|
| +
|
| +IntersectionGeometry::~IntersectionGeometry() {}
|
| +
|
| +Element* IntersectionGeometry::root() const {
|
| + if (m_root && !m_root->isDocumentNode())
|
| + return toElement(m_root);
|
| + return nullptr;
|
| +}
|
| +
|
| +LayoutObject* IntersectionGeometry::getRootLayoutObject() const {
|
| + DCHECK(m_root);
|
| + if (m_root->isDocumentNode()) {
|
| + return LayoutAPIShim::layoutObjectFrom(
|
| + toDocument(m_root)->layoutViewItem());
|
| + }
|
| + return toElement(m_root)->layoutObject();
|
| +}
|
| +
|
| +void IntersectionGeometry::initializeGeometry() {
|
| + initializeTargetRect();
|
| + m_intersectionRect = m_targetRect;
|
| + initializeRootRect();
|
| + m_doesIntersect = true;
|
| +}
|
| +
|
| +void IntersectionGeometry::initializeTargetRect() {
|
| + LayoutObject* targetLayoutObject = m_target->layoutObject();
|
| DCHECK(targetLayoutObject && targetLayoutObject->isBoxModelObject());
|
| - rect = LayoutRect(
|
| + m_targetRect = LayoutRect(
|
| toLayoutBoxModelObject(targetLayoutObject)->borderBoundingBox());
|
| }
|
|
|
| -void IntersectionObservation::initializeRootRect(LayoutRect& rect) const {
|
| - LayoutObject* rootLayoutObject = m_observer->rootLayoutObject();
|
| - if (rootLayoutObject->isLayoutView())
|
| - rect = LayoutRect(
|
| +void IntersectionGeometry::initializeRootRect() {
|
| + LayoutObject* rootLayoutObject = getRootLayoutObject();
|
| + if (rootLayoutObject->isLayoutView()) {
|
| + m_rootRect = LayoutRect(
|
| toLayoutView(rootLayoutObject)->frameView()->visibleContentRect());
|
| - else if (rootLayoutObject->isBox() && rootLayoutObject->hasOverflowClip())
|
| - rect = LayoutRect(toLayoutBox(rootLayoutObject)->contentBoxRect());
|
| - else
|
| - rect = LayoutRect(
|
| + } else if (rootLayoutObject->isBox() && rootLayoutObject->hasOverflowClip()) {
|
| + m_rootRect = LayoutRect(toLayoutBox(rootLayoutObject)->contentBoxRect());
|
| + } else {
|
| + m_rootRect = LayoutRect(
|
| toLayoutBoxModelObject(rootLayoutObject)->borderBoundingBox());
|
| - applyRootMargin(rect);
|
| + }
|
| + applyRootMargin();
|
| }
|
|
|
| -void IntersectionObservation::clipToRoot(IntersectionGeometry& geometry) const {
|
| +void IntersectionGeometry::applyRootMargin() {
|
| + if (m_rootMargin.isEmpty())
|
| + return;
|
| +
|
| + // TODO(szager): Make sure the spec is clear that left/right margins are
|
| + // resolved against width and not height.
|
| + LayoutUnit topMargin = computeMargin(m_rootMargin[0], m_rootRect.height());
|
| + LayoutUnit rightMargin = computeMargin(m_rootMargin[1], m_rootRect.width());
|
| + LayoutUnit bottomMargin = computeMargin(m_rootMargin[2], m_rootRect.height());
|
| + LayoutUnit leftMargin = computeMargin(m_rootMargin[3], m_rootRect.width());
|
| +
|
| + m_rootRect.setX(m_rootRect.x() - leftMargin);
|
| + m_rootRect.setWidth(m_rootRect.width() + leftMargin + rightMargin);
|
| + m_rootRect.setY(m_rootRect.y() - topMargin);
|
| + m_rootRect.setHeight(m_rootRect.height() + topMargin + bottomMargin);
|
| +}
|
| +
|
| +void IntersectionGeometry::clipToRoot() {
|
| // Map and clip rect into root element coordinates.
|
| // TODO(szager): the writing mode flipping needs a test.
|
| - DCHECK(m_target);
|
| - LayoutBox* rootLayoutObject = toLayoutBox(m_observer->rootLayoutObject());
|
| - LayoutObject* targetLayoutObject = target()->layoutObject();
|
| + LayoutBox* rootLayoutObject = toLayoutBox(getRootLayoutObject());
|
| + LayoutObject* targetLayoutObject = m_target->layoutObject();
|
|
|
| - geometry.doesIntersect = targetLayoutObject->mapToVisualRectInAncestorSpace(
|
| - rootLayoutObject, geometry.intersectionRect, EdgeInclusive);
|
| + m_doesIntersect = targetLayoutObject->mapToVisualRectInAncestorSpace(
|
| + rootLayoutObject, m_intersectionRect, EdgeInclusive);
|
| if (rootLayoutObject->hasOverflowClip())
|
| - geometry.intersectionRect.move(-rootLayoutObject->scrolledContentOffset());
|
| + m_intersectionRect.move(-rootLayoutObject->scrolledContentOffset());
|
|
|
| - if (!geometry.doesIntersect)
|
| + if (!m_doesIntersect)
|
| return;
|
| - LayoutRect rootClipRect(geometry.rootRect);
|
| + LayoutRect rootClipRect(m_rootRect);
|
| rootLayoutObject->flipForWritingMode(rootClipRect);
|
| - geometry.doesIntersect &=
|
| - geometry.intersectionRect.inclusiveIntersect(rootClipRect);
|
| -}
|
| -
|
| -static void mapRectUpToDocument(LayoutRect& rect,
|
| - const LayoutObject& layoutObject,
|
| - const Document& document) {
|
| - FloatQuad mappedQuad = layoutObject.localToAbsoluteQuad(
|
| - FloatQuad(FloatRect(rect)), UseTransforms | ApplyContainerFlip);
|
| - rect = LayoutRect(mappedQuad.boundingBox());
|
| -}
|
| -
|
| -static void mapRectDownToDocument(LayoutRect& rect,
|
| - LayoutBoxModelObject& layoutObject,
|
| - const Document& document) {
|
| - FloatQuad mappedQuad = document.layoutView()->ancestorToLocalQuad(
|
| - &layoutObject, FloatQuad(FloatRect(rect)),
|
| - UseTransforms | ApplyContainerFlip | TraverseDocumentBoundaries);
|
| - rect = LayoutRect(mappedQuad.boundingBox());
|
| + m_doesIntersect &= m_intersectionRect.inclusiveIntersect(rootClipRect);
|
| }
|
|
|
| -void IntersectionObservation::mapTargetRectToTargetFrameCoordinates(
|
| - LayoutRect& rect) const {
|
| - LayoutObject& targetLayoutObject = *target()->layoutObject();
|
| - Document& targetDocument = target()->document();
|
| +void IntersectionGeometry::mapTargetRectToTargetFrameCoordinates() {
|
| + LayoutObject& targetLayoutObject = *m_target->layoutObject();
|
| + Document& targetDocument = m_target->document();
|
| LayoutSize scrollPosition =
|
| LayoutSize(targetDocument.view()->getScrollOffset());
|
| - mapRectUpToDocument(rect, targetLayoutObject, targetDocument);
|
| - rect.move(-scrollPosition);
|
| + mapRectUpToDocument(m_targetRect, targetLayoutObject, targetDocument);
|
| + m_targetRect.move(-scrollPosition);
|
| }
|
|
|
| -void IntersectionObservation::mapRootRectToRootFrameCoordinates(
|
| - LayoutRect& rect) const {
|
| - LayoutObject& rootLayoutObject = *m_observer->rootLayoutObject();
|
| +void IntersectionGeometry::mapRootRectToRootFrameCoordinates() {
|
| + LayoutObject& rootLayoutObject = *getRootLayoutObject();
|
| Document& rootDocument = rootLayoutObject.document();
|
| LayoutSize scrollPosition =
|
| LayoutSize(rootDocument.view()->getScrollOffset());
|
| - mapRectUpToDocument(rect, rootLayoutObject, rootLayoutObject.document());
|
| - rect.move(-scrollPosition);
|
| + mapRectUpToDocument(m_rootRect, rootLayoutObject,
|
| + rootLayoutObject.document());
|
| + m_rootRect.move(-scrollPosition);
|
| }
|
|
|
| -void IntersectionObservation::mapRootRectToTargetFrameCoordinates(
|
| - LayoutRect& rect) const {
|
| - LayoutObject& rootLayoutObject = *m_observer->rootLayoutObject();
|
| - Document& targetDocument = target()->document();
|
| +void IntersectionGeometry::mapRootRectToTargetFrameCoordinates() {
|
| + LayoutObject& rootLayoutObject = *getRootLayoutObject();
|
| + Document& targetDocument = m_target->document();
|
| LayoutSize scrollPosition =
|
| LayoutSize(targetDocument.view()->getScrollOffset());
|
|
|
| - if (&targetDocument == &rootLayoutObject.document())
|
| - mapRectUpToDocument(rect, rootLayoutObject, targetDocument);
|
| - else
|
| - mapRectDownToDocument(rect, toLayoutBoxModelObject(rootLayoutObject),
|
| + if (&targetDocument == &rootLayoutObject.document()) {
|
| + mapRectUpToDocument(m_intersectionRect, rootLayoutObject, targetDocument);
|
| + } else {
|
| + mapRectDownToDocument(m_intersectionRect,
|
| + toLayoutBoxModelObject(rootLayoutObject),
|
| targetDocument);
|
| + }
|
|
|
| - rect.move(-scrollPosition);
|
| -}
|
| -
|
| -static bool isContainingBlockChainDescendant(LayoutObject* descendant,
|
| - LayoutObject* ancestor) {
|
| - LocalFrame* ancestorFrame = ancestor->document().frame();
|
| - LocalFrame* descendantFrame = descendant->document().frame();
|
| -
|
| - if (ancestor->isLayoutView())
|
| - return descendantFrame && descendantFrame->tree().top() == ancestorFrame;
|
| -
|
| - if (ancestorFrame != descendantFrame)
|
| - return false;
|
| -
|
| - while (descendant && descendant != ancestor)
|
| - descendant = descendant->containingBlock();
|
| - return descendant;
|
| + m_intersectionRect.move(-scrollPosition);
|
| }
|
|
|
| -bool IntersectionObservation::computeGeometry(
|
| - IntersectionGeometry& geometry) const {
|
| +void IntersectionGeometry::computeGeometry() {
|
| // In the first few lines here, before initializeGeometry is called, "return
|
| // true" effectively means "if the previous observed state was that root and
|
| // target were intersecting, then generate a notification indicating that they
|
| // are no longer intersecting." This happens, for example, when root or
|
| // target is removed from the DOM tree and not reinserted before the next
|
| // frame is generated, or display:none is set on the root or target.
|
| - Element* targetElement = target();
|
| - if (!targetElement)
|
| - return false;
|
| - if (!targetElement->isConnected())
|
| - return true;
|
| - DCHECK(m_observer);
|
| - Element* rootElement = m_observer->root();
|
| + if (!m_target->isConnected())
|
| + return;
|
| + Element* rootElement = root();
|
| if (rootElement && !rootElement->isConnected())
|
| - return true;
|
| + return;
|
|
|
| - LayoutObject* rootLayoutObject = m_observer->rootLayoutObject();
|
| + LayoutObject* rootLayoutObject = getRootLayoutObject();
|
| if (!rootLayoutObject || !rootLayoutObject->isBoxModelObject())
|
| - return true;
|
| + return;
|
| // TODO(szager): Support SVG
|
| - LayoutObject* targetLayoutObject = targetElement->layoutObject();
|
| + LayoutObject* targetLayoutObject = m_target->layoutObject();
|
| if (!targetLayoutObject)
|
| - return true;
|
| + return;
|
| if (!targetLayoutObject->isBoxModelObject() && !targetLayoutObject->isText())
|
| - return true;
|
| + return;
|
| if (!isContainingBlockChainDescendant(targetLayoutObject, rootLayoutObject))
|
| - return true;
|
| + return;
|
|
|
| - initializeGeometry(geometry);
|
| + initializeGeometry();
|
|
|
| - clipToRoot(geometry);
|
| + clipToRoot();
|
|
|
| - mapTargetRectToTargetFrameCoordinates(geometry.targetRect);
|
| + mapTargetRectToTargetFrameCoordinates();
|
|
|
| - if (geometry.doesIntersect)
|
| - mapRootRectToTargetFrameCoordinates(geometry.intersectionRect);
|
| + if (m_doesIntersect)
|
| + mapRootRectToTargetFrameCoordinates();
|
| else
|
| - geometry.intersectionRect = LayoutRect();
|
| + m_intersectionRect = LayoutRect();
|
|
|
| // Small optimization: if we're not going to report root bounds, don't bother
|
| // transforming them to the frame.
|
| - if (m_shouldReportRootBounds)
|
| - mapRootRectToRootFrameCoordinates(geometry.rootRect);
|
| -
|
| - return true;
|
| -}
|
| -
|
| -void IntersectionObservation::computeIntersectionObservations(
|
| - DOMHighResTimeStamp timestamp) {
|
| - IntersectionGeometry geometry;
|
| - if (!computeGeometry(geometry))
|
| - return;
|
| -
|
| - // Some corner cases for threshold index:
|
| - // - If target rect is zero area, because it has zero width and/or zero
|
| - // height,
|
| - // only two states are recognized:
|
| - // - 0 means not intersecting.
|
| - // - 1 means intersecting.
|
| - // No other threshold crossings are possible.
|
| - // - Otherwise:
|
| - // - If root and target do not intersect, the threshold index is 0.
|
| - // - If root and target intersect but the intersection has zero-area
|
| - // (i.e., they have a coincident edge or corner), we consider the
|
| - // intersection to have "crossed" a zero threshold, but not crossed
|
| - // any non-zero threshold.
|
| - unsigned newThresholdIndex;
|
| - float newVisibleRatio = 0;
|
| - if (geometry.targetRect.isEmpty()) {
|
| - newThresholdIndex = geometry.doesIntersect ? 1 : 0;
|
| - } else if (!geometry.doesIntersect) {
|
| - newThresholdIndex = 0;
|
| - } else {
|
| - float intersectionArea =
|
| - geometry.intersectionRect.size().width().toFloat() *
|
| - geometry.intersectionRect.size().height().toFloat();
|
| - float targetArea = geometry.targetRect.size().width().toFloat() *
|
| - geometry.targetRect.size().height().toFloat();
|
| - newVisibleRatio = intersectionArea / targetArea;
|
| - newThresholdIndex = observer().firstThresholdGreaterThan(newVisibleRatio);
|
| - }
|
| - if (m_lastThresholdIndex != newThresholdIndex) {
|
| - IntRect snappedRootBounds = pixelSnappedIntRect(geometry.rootRect);
|
| - IntRect* rootBoundsPointer =
|
| - m_shouldReportRootBounds ? &snappedRootBounds : nullptr;
|
| - IntersectionObserverEntry* newEntry = new IntersectionObserverEntry(
|
| - timestamp, newVisibleRatio, pixelSnappedIntRect(geometry.targetRect),
|
| - rootBoundsPointer, pixelSnappedIntRect(geometry.intersectionRect),
|
| - target());
|
| - observer().enqueueIntersectionObserverEntry(*newEntry);
|
| - setLastThresholdIndex(newThresholdIndex);
|
| - }
|
| -}
|
| -
|
| -void IntersectionObservation::disconnect() {
|
| - IntersectionObserver* observer = m_observer;
|
| - clearRootAndRemoveFromTarget();
|
| - observer->removeObservation(*this);
|
| -}
|
| -
|
| -void IntersectionObservation::clearRootAndRemoveFromTarget() {
|
| - if (m_target)
|
| - target()->ensureIntersectionObserverData().removeObservation(observer());
|
| - m_observer.clear();
|
| + if (m_shouldReportRootBounds == ReportRootBounds::kShouldReportRootBounds)
|
| + mapRootRectToRootFrameCoordinates();
|
| }
|
|
|
| -DEFINE_TRACE(IntersectionObservation) {
|
| - visitor->trace(m_observer);
|
| +DEFINE_TRACE(IntersectionGeometry) {
|
| + visitor->trace(m_root);
|
| visitor->trace(m_target);
|
| }
|
|
|
|
|