| Index: third_party/WebKit/Source/core/dom/IntersectionObservation.cpp
|
| diff --git a/third_party/WebKit/Source/core/dom/IntersectionObservation.cpp b/third_party/WebKit/Source/core/dom/IntersectionObservation.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4e761ef7c7ef245998f44e8fae7be0ee36e5ecf4
|
| --- /dev/null
|
| +++ b/third_party/WebKit/Source/core/dom/IntersectionObservation.cpp
|
| @@ -0,0 +1,162 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// 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/ElementRareData.h"
|
| +#include "core/dom/IntersectionObserver.h"
|
| +#include "core/frame/FrameView.h"
|
| +#include "core/layout/LayoutBox.h"
|
| +#include "core/layout/LayoutText.h"
|
| +#include "core/layout/LayoutView.h"
|
| +#include "core/paint/PaintLayer.h"
|
| +
|
| +namespace blink {
|
| +
|
| +IntersectionObservation::IntersectionObservation(IntersectionObserver& observer, Element& target, bool shouldReportRootBounds)
|
| + : m_observer(observer)
|
| + , m_target(target.ensureIntersectionObserverData().createWeakPtr(&target))
|
| + , m_active(true)
|
| + , m_shouldReportRootBounds(shouldReportRootBounds)
|
| + , m_lastThresholdIndex(0)
|
| +{
|
| +}
|
| +
|
| +void IntersectionObservation::initializeGeometry(IntersectionGeometry& geometry)
|
| +{
|
| + LayoutObject* targetLayoutObject = m_target->layoutObject();
|
| + if (targetLayoutObject->isBoxModelObject())
|
| + geometry.targetRect = toLayoutBoxModelObject(targetLayoutObject)->visualOverflowRect();
|
| + else
|
| + geometry.targetRect = toLayoutText(targetLayoutObject)->visualOverflowRect();
|
| + geometry.intersectionRect = geometry.targetRect;
|
| +}
|
| +
|
| +void IntersectionObservation::clipToRoot(LayoutRect& rect)
|
| +{
|
| + // Map and clip rect into root element coordinates.
|
| + // TODO(szager): the writing mode flipping needs a test.
|
| + LayoutObject* rootLayoutObject = m_observer->rootLayoutObject();
|
| + LayoutObject* targetLayoutObject = m_target->layoutObject();
|
| + targetLayoutObject->mapToVisibleRectInContainerSpace(toLayoutBoxModelObject(rootLayoutObject), rect, nullptr);
|
| + if (rootLayoutObject->hasOverflowClip()) {
|
| + LayoutBox* rootLayoutBox = toLayoutBox(rootLayoutObject);
|
| + LayoutRect clipRect(LayoutPoint(), LayoutSize(rootLayoutBox->layer()->size()));
|
| + m_observer->applyRootMargin(clipRect);
|
| + rootLayoutBox->flipForWritingMode(rect);
|
| + rect.intersect(clipRect);
|
| + rootLayoutBox->flipForWritingMode(rect);
|
| + }
|
| +}
|
| +
|
| +void IntersectionObservation::clipToFrameView(IntersectionGeometry& geometry)
|
| +{
|
| + Element* rootElement = m_observer->root();
|
| + LayoutObject* rootLayoutObject = m_observer->rootLayoutObject();
|
| + if (rootElement == rootElement->document().documentElement()) {
|
| + geometry.rootRect = LayoutRect(rootElement->document().view()->visibleContentRect());
|
| + m_observer->applyRootMargin(geometry.rootRect);
|
| + geometry.intersectionRect.intersect(geometry.rootRect);
|
| + } else {
|
| + if (rootLayoutObject->isBox())
|
| + geometry.rootRect = LayoutRect(toLayoutBox(rootLayoutObject)->absoluteContentBox());
|
| + else
|
| + geometry.rootRect = LayoutRect(rootLayoutObject->absoluteBoundingBoxRect());
|
| + m_observer->applyRootMargin(geometry.rootRect);
|
| + }
|
| +
|
| + LayoutPoint scrollPosition(rootElement->document().view()->scrollPosition());
|
| + geometry.targetRect.moveBy(-scrollPosition);
|
| + geometry.intersectionRect.moveBy(-scrollPosition);
|
| + geometry.rootRect.moveBy(-scrollPosition);
|
| +}
|
| +
|
| +static void mapRectToDocumentCoordinates(LayoutObject& layoutObject, LayoutRect& rect)
|
| +{
|
| + rect = LayoutRect(layoutObject.localToAbsoluteQuad(FloatQuad(FloatRect(rect)), UseTransforms | ApplyContainerFlip | TraverseDocumentBoundaries).boundingBox());
|
| +}
|
| +
|
| +bool IntersectionObservation::computeGeometry(IntersectionGeometry& geometry)
|
| +{
|
| + LayoutObject* rootLayoutObject = m_observer->rootLayoutObject();
|
| + LayoutObject* targetLayoutObject = m_target->layoutObject();
|
| + if (!rootLayoutObject->isBoxModelObject())
|
| + return false;
|
| + if (!targetLayoutObject->isBoxModelObject() && !targetLayoutObject->isText())
|
| + return false;
|
| +
|
| + // Initialize targetRect and intersectionRect to bounds of target, in target's coordinate space.
|
| + initializeGeometry(geometry);
|
| +
|
| + // TODO(szager): Support intersection observations for zero-area targets. For now, we just
|
| + // punt on the observation.
|
| + if (!geometry.targetRect.size().width() || !geometry.targetRect.size().height())
|
| + return false;
|
| +
|
| + // Clip intersectionRect to the root, and map it to root coordinates.
|
| + clipToRoot(geometry.intersectionRect);
|
| +
|
| + // Map targetRect into document coordinates.
|
| + mapRectToDocumentCoordinates(*targetLayoutObject, geometry.targetRect);
|
| +
|
| + // Map intersectionRect into document coordinates.
|
| + mapRectToDocumentCoordinates(*rootLayoutObject, geometry.intersectionRect);
|
| +
|
| + // Clip intersectionRect to FrameView visible area if necessary, and map all geometry to frame coordinates.
|
| + clipToFrameView(geometry);
|
| +
|
| + if (geometry.intersectionRect.size().isZero())
|
| + geometry.intersectionRect = LayoutRect();
|
| + if (!m_shouldReportRootBounds)
|
| + geometry.rootRect = LayoutRect();
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void IntersectionObservation::computeIntersectionObservations(double timestamp)
|
| +{
|
| + Element* targetElement = target();
|
| + if (!targetElement || !isActive())
|
| + return;
|
| + LayoutObject* targetLayoutObject = targetElement->layoutObject();
|
| + // TODO(szager): Support SVG
|
| + if (!targetLayoutObject || (!targetLayoutObject->isBox() && !targetLayoutObject->isInline()))
|
| + return;
|
| +
|
| + IntersectionGeometry geometry;
|
| + if (!computeGeometry(geometry))
|
| + return;
|
| +
|
| + float intersectionArea = geometry.intersectionRect.size().width().toFloat() * geometry.intersectionRect.size().height().toFloat();
|
| + float targetArea = geometry.targetRect.size().width().toFloat() * geometry.targetRect.size().height().toFloat();
|
| + if (!targetArea)
|
| + return;
|
| + float newVisibleRatio = intersectionArea / targetArea;
|
| + unsigned newThresholdIndex = observer().firstThresholdGreaterThan(newVisibleRatio);
|
| + if (m_lastThresholdIndex != newThresholdIndex) {
|
| + IntersectionObserverEntry* newEntry = new IntersectionObserverEntry(
|
| + timestamp / 1000.0,
|
| + pixelSnappedIntRect(geometry.targetRect),
|
| + pixelSnappedIntRect(geometry.rootRect),
|
| + pixelSnappedIntRect(geometry.intersectionRect),
|
| + targetElement);
|
| + observer().enqueueIntersectionObserverEntry(*newEntry);
|
| + }
|
| + setLastThresholdIndex(newThresholdIndex);
|
| +}
|
| +
|
| +void IntersectionObservation::disconnect()
|
| +{
|
| + observer().disconnect(*this);
|
| + if (m_target)
|
| + m_target->ensureIntersectionObserverData().removeObservation(this->observer());
|
| +}
|
| +
|
| +DEFINE_TRACE(IntersectionObservation)
|
| +{
|
| + visitor->trace(m_observer);
|
| + visitor->trace(m_target);
|
| +}
|
| +
|
| +} // namespace blink
|
|
|