Commit 80f26da6 authored by szager's avatar szager Committed by Commit bot

Refactor IntersectionGeometry class and move it to core/layout.

IntersectionGeometry is an ephemeral object, so make it stack
allocated, let it use raw pointers to LayoutObject, and move it to
core/layout.

Also, be explicit about the implicit root by introducing
rootIsImplicit.

R=ojan@chromium.org,xjz@chromium.org
BUG=

Review-Url: https://codereview.chromium.org/2553103004
Cr-Commit-Position: refs/heads/master@{#437949}
parent 0cfffd46
......@@ -155,8 +155,6 @@ blink_core_sources("dom") {
"IgnoreDestructiveWriteCountIncrementer.h",
"IncrementLoadEventDelayCount.cpp",
"IncrementLoadEventDelayCount.h",
"IntersectionGeometry.cpp",
"IntersectionGeometry.h",
"IntersectionObservation.cpp",
"IntersectionObservation.h",
"IntersectionObserver.cpp",
......
......@@ -6,18 +6,16 @@
#include "core/dom/ElementRareData.h"
#include "core/dom/IntersectionObserver.h"
#include "core/layout/IntersectionGeometry.h"
namespace blink {
IntersectionObservation::IntersectionObservation(
IntersectionObserver& observer,
Element& target,
IntersectionGeometry::ReportRootBounds shouldReportRootBounds)
IntersectionObservation::IntersectionObservation(IntersectionObserver& observer,
Element& target,
bool shouldReportRootBounds)
: m_observer(observer),
m_target(&target),
m_shouldReportRootBounds(
shouldReportRootBounds ==
IntersectionGeometry::ReportRootBounds::kShouldReportRootBounds),
m_shouldReportRootBounds(shouldReportRootBounds),
m_lastThresholdIndex(0) {}
void IntersectionObservation::computeIntersectionObservations(
......@@ -29,11 +27,10 @@ void IntersectionObservation::computeIntersectionObservations(
rootMargin[1] = m_observer->rightMargin();
rootMargin[2] = m_observer->bottomMargin();
rootMargin[3] = m_observer->leftMargin();
Node* rootNode = m_observer->rootNode();
IntersectionGeometry geometry(
m_observer->rootNode(), target(), rootMargin,
m_shouldReportRootBounds
? IntersectionGeometry::ReportRootBounds::kShouldReportRootBounds
: IntersectionGeometry::ReportRootBounds::kShouldNotReportRootBounds);
rootNode && !rootNode->isDocumentNode() ? toElement(rootNode) : nullptr,
*target(), rootMargin, m_shouldReportRootBounds);
geometry.computeGeometry();
// Some corner cases for threshold index:
......
......@@ -6,7 +6,6 @@
#define IntersectionObservation_h
#include "core/dom/DOMHighResTimeStamp.h"
#include "core/dom/IntersectionGeometry.h"
#include "platform/heap/Handle.h"
namespace blink {
......@@ -17,10 +16,9 @@ class IntersectionObserver;
class IntersectionObservation final
: public GarbageCollected<IntersectionObservation> {
public:
IntersectionObservation(
IntersectionObserver&,
Element&,
IntersectionGeometry::ReportRootBounds shouldReportRootBounds);
IntersectionObservation(IntersectionObserver&,
Element&,
bool shouldReportRootBounds);
IntersectionObserver& observer() const { return *m_observer; }
Element* target() const { return m_target; }
......
......@@ -270,11 +270,8 @@ void IntersectionObserver::observe(Element* target,
isDOMDescendant = (targetFrame->tree().top() == rootFrame);
}
IntersectionObservation* observation = new IntersectionObservation(
*this, *target,
shouldReportRootBounds
? IntersectionGeometry::ReportRootBounds::kShouldReportRootBounds
: IntersectionGeometry::ReportRootBounds::kShouldNotReportRootBounds);
IntersectionObservation* observation =
new IntersectionObservation(*this, *target, shouldReportRootBounds);
target->ensureIntersectionObserverData().addObservation(*observation);
m_observations.add(observation);
......
......@@ -39,7 +39,6 @@
#include "core/dom/ElementTraversal.h"
#include "core/dom/ElementVisibilityObserver.h"
#include "core/dom/Fullscreen.h"
#include "core/dom/IntersectionGeometry.h"
#include "core/dom/TaskRunnerHelper.h"
#include "core/dom/shadow/ShadowRoot.h"
#include "core/events/Event.h"
......@@ -66,6 +65,7 @@
#include "core/html/track/VideoTrack.h"
#include "core/html/track/VideoTrackList.h"
#include "core/inspector/ConsoleMessage.h"
#include "core/layout/IntersectionGeometry.h"
#include "core/layout/LayoutMedia.h"
#include "core/layout/api/LayoutViewItem.h"
#include "core/layout/compositing/PaintLayerCompositor.h"
......@@ -4069,9 +4069,9 @@ DEFINE_TRACE(HTMLMediaElement::AudioSourceProviderImpl) {
void HTMLMediaElement::checkViewportIntersectionChanged() {
// TODO(xjz): Early return if we not in tab mirroring.
IntersectionGeometry geometry(
document().frame()->localFrameRoot()->document(), this, Vector<Length>(),
IntersectionGeometry::ReportRootBounds::kShouldReportRootBounds);
bool shouldReportRootBounds = true;
IntersectionGeometry geometry(nullptr, *this, Vector<Length>(),
shouldReportRootBounds);
geometry.computeGeometry();
IntRect intersectRect = geometry.intersectionIntRect();
if (m_currentIntersectRect == intersectRect)
......
......@@ -39,6 +39,8 @@ blink_core_sources("layout") {
"HitTestingTransformState.h",
"ImageQualityController.cpp",
"ImageQualityController.h",
"IntersectionGeometry.cpp",
"IntersectionGeometry.h",
"LayoutAnalyzer.cpp",
"LayoutAnalyzer.h",
"LayoutBR.cpp",
......
......@@ -2,12 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/dom/IntersectionGeometry.h"
#include "core/layout/IntersectionGeometry.h"
#include "core/dom/Element.h"
#include "core/dom/ElementRareData.h"
#include "core/frame/FrameView.h"
#include "core/frame/LocalFrame.h"
#include "core/html/HTMLFrameOwnerElement.h"
#include "core/layout/LayoutBox.h"
#include "core/layout/LayoutView.h"
#include "core/layout/api/LayoutAPIShim.h"
......@@ -22,10 +21,6 @@ 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;
......@@ -35,18 +30,19 @@ bool isContainingBlockChainDescendant(LayoutObject* descendant,
}
void mapRectUpToDocument(LayoutRect& rect,
const LayoutObject& layoutObject,
const LayoutObject& descendant,
const Document& document) {
FloatQuad mappedQuad = layoutObject.localToAbsoluteQuad(
FloatQuad(FloatRect(rect)), UseTransforms | ApplyContainerFlip);
FloatQuad mappedQuad = descendant.localToAncestorQuad(
FloatQuad(FloatRect(rect)), document.layoutView(),
UseTransforms | ApplyContainerFlip);
rect = LayoutRect(mappedQuad.boundingBox());
}
void mapRectDownToDocument(LayoutRect& rect,
LayoutBoxModelObject& layoutObject,
LayoutBoxModelObject* ancestor,
const Document& document) {
FloatQuad mappedQuad = document.layoutView()->ancestorToLocalQuad(
&layoutObject, FloatQuad(FloatRect(rect)),
ancestor, FloatQuad(FloatRect(rect)),
UseTransforms | ApplyContainerFlip | TraverseDocumentBoundaries);
rect = LayoutRect(mappedQuad.boundingBox());
}
......@@ -60,62 +56,71 @@ LayoutUnit computeMargin(const Length& length, LayoutUnit referenceLength) {
return LayoutUnit(length.intValue());
}
LayoutView* localRootView(Element& element) {
LocalFrame* frame = element.document().frame();
LocalFrame* frameRoot = frame ? frame->localFrameRoot() : nullptr;
return frameRoot ? frameRoot->contentLayoutObject() : nullptr;
}
} // namespace
IntersectionGeometry::IntersectionGeometry(
Node* root,
Element* target,
const Vector<Length>& rootMargin,
ReportRootBounds shouldReportRootBounds)
: m_root(root),
m_target(target),
IntersectionGeometry::IntersectionGeometry(Element* root,
Element& target,
const Vector<Length>& rootMargin,
bool shouldReportRootBounds)
: m_root(root ? root->layoutObject() : localRootView(target)),
m_target(target.layoutObject()),
m_rootMargin(rootMargin),
m_shouldReportRootBounds(shouldReportRootBounds) {
DCHECK(m_target);
DCHECK(rootMargin.isEmpty() || rootMargin.size() == 4);
m_doesIntersect(0),
m_shouldReportRootBounds(shouldReportRootBounds),
m_rootIsImplicit(!root),
m_canComputeGeometry(initializeCanComputeGeometry(root, target)) {
if (m_canComputeGeometry)
initializeGeometry();
}
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();
bool IntersectionGeometry::initializeCanComputeGeometry(Element* root,
Element& target) const {
DCHECK(m_rootMargin.isEmpty() || m_rootMargin.size() == 4);
if (root && !root->isConnected())
return false;
if (!m_root || !m_root->isBox())
return false;
if (!target.isConnected())
return false;
if (!m_target || (!m_target->isBoxModelObject() && !m_target->isText()))
return false;
if (root && !isContainingBlockChainDescendant(m_target, m_root))
return false;
return true;
}
void IntersectionGeometry::initializeGeometry() {
initializeTargetRect();
m_intersectionRect = m_targetRect;
initializeRootRect();
m_doesIntersect = true;
}
void IntersectionGeometry::initializeTargetRect() {
LayoutObject* targetLayoutObject = m_target->layoutObject();
DCHECK(targetLayoutObject && targetLayoutObject->isBoxModelObject());
m_targetRect = LayoutRect(
toLayoutBoxModelObject(targetLayoutObject)->borderBoundingBox());
m_targetRect =
LayoutRect(toLayoutBoxModelObject(target())->borderBoundingBox());
}
void IntersectionGeometry::initializeRootRect() {
LayoutObject* rootLayoutObject = getRootLayoutObject();
if (rootLayoutObject->isLayoutView()) {
m_rootRect = LayoutRect(
toLayoutView(rootLayoutObject)->frameView()->visibleContentRect());
} else if (rootLayoutObject->isBox() && rootLayoutObject->hasOverflowClip()) {
m_rootRect = LayoutRect(toLayoutBox(rootLayoutObject)->contentBoxRect());
// TODO(szager): In OOPIF, m_root will be the LayoutView of the
// localFrameRoot(). Once viewport intersection support lands,
// add a call to mapToVisualRectInAncestorSpace to map the rect up to
// top-level frame coordinates.
if (m_root->isLayoutView()) {
m_rootRect =
LayoutRect(toLayoutView(m_root)->frameView()->visibleContentRect());
} else if (m_root->isBox() && m_root->hasOverflowClip()) {
m_rootRect = LayoutRect(toLayoutBox(m_root)->contentBoxRect());
} else {
m_rootRect = LayoutRect(
toLayoutBoxModelObject(rootLayoutObject)->borderBoundingBox());
m_rootRect =
LayoutRect(toLayoutBoxModelObject(m_root)->borderBoundingBox());
}
applyRootMargin();
}
......@@ -140,102 +145,71 @@ void IntersectionGeometry::applyRootMargin() {
void IntersectionGeometry::clipToRoot() {
// Map and clip rect into root element coordinates.
// TODO(szager): the writing mode flipping needs a test.
LayoutBox* rootLayoutObject = toLayoutBox(getRootLayoutObject());
LayoutObject* targetLayoutObject = m_target->layoutObject();
m_doesIntersect = targetLayoutObject->mapToVisualRectInAncestorSpace(
rootLayoutObject, m_intersectionRect, EdgeInclusive);
if (rootLayoutObject->hasOverflowClip())
m_intersectionRect.move(-rootLayoutObject->scrolledContentOffset());
// TODO(szager): Once the OOPIF viewport intersection code lands,
// use nullptr for ancestor to map to the top frame.
LayoutBox* ancestor = toLayoutBox(m_root);
m_doesIntersect = m_target->mapToVisualRectInAncestorSpace(
ancestor, m_intersectionRect, EdgeInclusive);
if (ancestor && ancestor->hasOverflowClip())
m_intersectionRect.move(-ancestor->scrolledContentOffset());
if (!m_doesIntersect)
return;
LayoutRect rootClipRect(m_rootRect);
rootLayoutObject->flipForWritingMode(rootClipRect);
if (ancestor)
ancestor->flipForWritingMode(rootClipRect);
m_doesIntersect &= m_intersectionRect.inclusiveIntersect(rootClipRect);
}
void IntersectionGeometry::mapTargetRectToTargetFrameCoordinates() {
LayoutObject& targetLayoutObject = *m_target->layoutObject();
Document& targetDocument = m_target->document();
LayoutSize scrollPosition =
LayoutSize(targetDocument.view()->getScrollOffset());
mapRectUpToDocument(m_targetRect, targetLayoutObject, targetDocument);
mapRectUpToDocument(m_targetRect, *m_target, targetDocument);
m_targetRect.move(-scrollPosition);
}
void IntersectionGeometry::mapRootRectToRootFrameCoordinates() {
LayoutObject& rootLayoutObject = *getRootLayoutObject();
Document& rootDocument = rootLayoutObject.document();
Document& rootDocument = m_root->document();
if (!rootIsImplicit())
mapRectUpToDocument(m_rootRect, *m_root, rootDocument);
// TODO(szager): When OOPIF support lands, this scroll offset adjustment
// will probably be wrong.
LayoutSize scrollPosition =
LayoutSize(rootDocument.view()->getScrollOffset());
mapRectUpToDocument(m_rootRect, rootLayoutObject,
rootLayoutObject.document());
m_rootRect.move(-scrollPosition);
}
void IntersectionGeometry::mapRootRectToTargetFrameCoordinates() {
LayoutObject& rootLayoutObject = *getRootLayoutObject();
void IntersectionGeometry::mapIntersectionRectToTargetFrameCoordinates() {
Document& targetDocument = m_target->document();
LayoutSize scrollPosition =
LayoutSize(targetDocument.view()->getScrollOffset());
if (&targetDocument == &rootLayoutObject.document()) {
mapRectUpToDocument(m_intersectionRect, rootLayoutObject, targetDocument);
if (rootIsImplicit()) {
LocalFrame* targetFrame = targetDocument.frame();
Frame* rootFrame = targetFrame->tree().top();
LayoutSize scrollPosition =
LayoutSize(targetDocument.view()->getScrollOffset());
if (targetFrame != rootFrame)
mapRectDownToDocument(m_intersectionRect, nullptr, targetDocument);
m_intersectionRect.move(-scrollPosition);
} else {
mapRectDownToDocument(m_intersectionRect,
toLayoutBoxModelObject(rootLayoutObject),
targetDocument);
LayoutSize scrollPosition =
LayoutSize(targetDocument.view()->getScrollOffset());
mapRectUpToDocument(m_intersectionRect, *m_root, m_root->document());
m_intersectionRect.move(-scrollPosition);
}
m_intersectionRect.move(-scrollPosition);
}
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.
if (!m_target->isConnected())
return;
Element* rootElement = root();
if (rootElement && !rootElement->isConnected())
return;
LayoutObject* rootLayoutObject = getRootLayoutObject();
if (!rootLayoutObject || !rootLayoutObject->isBoxModelObject())
return;
// TODO(szager): Support SVG
LayoutObject* targetLayoutObject = m_target->layoutObject();
if (!targetLayoutObject)
return;
if (!targetLayoutObject->isBoxModelObject() && !targetLayoutObject->isText())
return;
if (!isContainingBlockChainDescendant(targetLayoutObject, rootLayoutObject))
if (!canComputeGeometry())
return;
initializeGeometry();
clipToRoot();
mapTargetRectToTargetFrameCoordinates();
if (m_doesIntersect)
mapRootRectToTargetFrameCoordinates();
mapIntersectionRectToTargetFrameCoordinates();
else
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 == ReportRootBounds::kShouldReportRootBounds)
if (shouldReportRootBounds())
mapRootRectToRootFrameCoordinates();
}
DEFINE_TRACE(IntersectionGeometry) {
visitor->trace(m_root);
visitor->trace(m_target);
}
} // namespace blink
......@@ -12,28 +12,38 @@
namespace blink {
class Node;
class Element;
class LayoutObject;
class IntersectionGeometry final
: public GarbageCollectedFinalized<IntersectionGeometry> {
// Computes the intersection between an ancestor (root) element and a
// descendant (target) element, with overflow and CSS clipping applied, but not
// paint occlusion.
//
// If the root argument to the constructor is null, computes the intersection
// of the target with the top-level frame viewport (AKA the "implicit root").
class IntersectionGeometry {
STACK_ALLOCATED()
public:
enum ReportRootBounds {
kShouldReportRootBounds,
kShouldNotReportRootBounds,
};
IntersectionGeometry(Node* root,
Element* target,
IntersectionGeometry(Element* root,
Element& target,
const Vector<Length>& rootMargin,
ReportRootBounds shouldReportRootBounds);
bool shouldReportRootBounds);
~IntersectionGeometry();
void computeGeometry();
LayoutObject* root() const { return m_root; }
LayoutObject* target() const { return m_target; }
// Client rect in the coordinate system of the frame containing target.
LayoutRect targetRect() const { return m_targetRect; }
// Client rect in the coordinate system of the frame containing target.
LayoutRect intersectionRect() const { return m_intersectionRect; }
// Client rect in the coordinate system of the frame containing root.
LayoutRect rootRect() const { return m_rootRect; }
bool doesIntersect() const { return m_doesIntersect; }
IntRect intersectionIntRect() const {
......@@ -44,28 +54,33 @@ class IntersectionGeometry final
IntRect rootIntRect() const { return pixelSnappedIntRect(m_rootRect); }
DECLARE_TRACE();
private:
bool initializeCanComputeGeometry(Element* root, Element& target) const;
void initializeGeometry();
void initializeTargetRect();
void initializeRootRect();
void clipToRoot();
void mapTargetRectToTargetFrameCoordinates();
void mapRootRectToRootFrameCoordinates();
void mapRootRectToTargetFrameCoordinates();
Element* root() const;
LayoutObject* getRootLayoutObject() const;
void mapIntersectionRectToTargetFrameCoordinates();
void applyRootMargin();
Member<Node> m_root;
Member<Element> m_target;
// Returns true iff it's possible to compute an intersection between root
// and target.
bool canComputeGeometry() const { return m_canComputeGeometry; }
bool rootIsImplicit() const { return m_rootIsImplicit; }
bool shouldReportRootBounds() const { return m_shouldReportRootBounds; }
LayoutObject* m_root;
LayoutObject* m_target;
const Vector<Length> m_rootMargin;
const ReportRootBounds m_shouldReportRootBounds;
LayoutRect m_targetRect;
LayoutRect m_intersectionRect;
LayoutRect m_rootRect;
bool m_doesIntersect = false;
unsigned m_doesIntersect : 1;
const unsigned m_shouldReportRootBounds : 1;
const unsigned m_rootIsImplicit : 1;
const unsigned m_canComputeGeometry : 1;
};
} // namespace blink
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment