Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(184)

Unified Diff: third_party/WebKit/Source/core/dom/IntersectionObserver.cpp

Issue 1559593002: Add root margin support for IntersectionObserver. (Closed) Base URL: https://chromium.googlesource.com/chromium/src@intersection-observer-no-root-margin
Patch Set: Initialize margin members to Fixed Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/core/dom/IntersectionObserver.cpp
diff --git a/third_party/WebKit/Source/core/dom/IntersectionObserver.cpp b/third_party/WebKit/Source/core/dom/IntersectionObserver.cpp
index 7a84be31d400be073323f8e0940e8abe7d31ab55..5758a6b37458696db61bd5631da75b8bb6608c87 100644
--- a/third_party/WebKit/Source/core/dom/IntersectionObserver.cpp
+++ b/third_party/WebKit/Source/core/dom/IntersectionObserver.cpp
@@ -22,6 +22,44 @@
namespace blink {
+static void parseRootMargin(String rootMarginParameter, Vector<Length>& rootMargin, ExceptionState& exceptionState)
+{
+ // TODO(szager): Make sure this exact syntax and behavior is spec-ed somewhere.
+
+ // The root margin argument accepts syntax similar to that for CSS margin:
+ //
+ // "1px" = top/right/bottom/left
+ // "1px 2px" = top/bottom left/right
+ // "1px 2px 3px" = top left/right bottom
+ // "1px 2px 3px 4px" = top left right bottom
+ //
+ // Any extra stuff after the first four tokens is ignored.
+ CSSTokenizer::Scope tokenizerScope(rootMarginParameter);
+ CSSParserTokenRange tokenRange = tokenizerScope.tokenRange();
+ while (rootMargin.size() < 4 && tokenRange.peek().type() != EOFToken && !exceptionState.hadException()) {
+ const CSSParserToken& token = tokenRange.consumeIncludingWhitespace();
+ switch (token.type()) {
+ case PercentageToken:
+ rootMargin.append(Length(token.numericValue(), Percent));
+ break;
+ case DimensionToken:
+ switch (token.unitType()) {
+ case CSSPrimitiveValue::UnitType::Pixels:
+ rootMargin.append(Length(static_cast<int>(floor(token.numericValue())), Fixed));
+ break;
+ case CSSPrimitiveValue::UnitType::Percentage:
+ rootMargin.append(Length(token.numericValue(), Percent));
+ break;
+ default:
+ exceptionState.throwTypeError("rootMargin must be specified in pixels or percent.");
+ }
+ break;
+ default:
+ exceptionState.throwTypeError("rootMargin must be specified in pixels or percent.");
+ }
+ }
+}
+
static void parseThresholds(const DoubleOrDoubleArray& thresholdParameter, Vector<float>& thresholds, ExceptionState& exceptionState)
{
if (thresholdParameter.isDouble()) {
@@ -57,6 +95,12 @@ IntersectionObserver* IntersectionObserver::create(const IntersectionObserverIni
return nullptr;
}
+ Vector<Length> rootMargin;
+ if (observerInit.hasRootMargin())
+ parseRootMargin(observerInit.rootMargin(), rootMargin, exceptionState);
+ if (exceptionState.hadException())
+ return nullptr;
+
Vector<float> thresholds;
if (observerInit.hasThreshold())
parseThresholds(observerInit.threshold(), thresholds, exceptionState);
@@ -65,14 +109,43 @@ IntersectionObserver* IntersectionObserver::create(const IntersectionObserverIni
if (exceptionState.hadException())
return nullptr;
- return new IntersectionObserver(callback, *root, thresholds);
+ return new IntersectionObserver(callback, *root, rootMargin, thresholds);
}
-IntersectionObserver::IntersectionObserver(IntersectionObserverCallback& callback, Element& root, const Vector<float>& thresholds)
+IntersectionObserver::IntersectionObserver(IntersectionObserverCallback& callback, Element& root, const Vector<Length>& rootMargin, const Vector<float>& thresholds)
: m_callback(&callback)
, m_root(root.ensureIntersectionObserverData().createWeakPtr(&root))
, m_thresholds(thresholds)
+ , m_topMargin(Fixed)
+ , m_rightMargin(Fixed)
+ , m_bottomMargin(Fixed)
+ , m_leftMargin(Fixed)
{
+ switch (rootMargin.size()) {
+ case 0:
+ break;
+ case 1:
+ m_topMargin = m_rightMargin = m_bottomMargin = m_leftMargin = rootMargin[0];
+ break;
+ case 2:
+ m_topMargin = m_bottomMargin = rootMargin[0];
+ m_rightMargin = m_leftMargin = rootMargin[1];
+ break;
+ case 3:
+ m_topMargin = rootMargin[0];
+ m_rightMargin = m_leftMargin = rootMargin[1];
+ m_bottomMargin = rootMargin[2];
+ break;
+ case 4:
+ m_topMargin = rootMargin[0];
+ m_rightMargin = rootMargin[1];
+ m_bottomMargin = rootMargin[2];
+ m_leftMargin = rootMargin[3];
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
root.document().ensureIntersectionObserverController().addTrackedObserver(*this);
}
@@ -124,7 +197,12 @@ void IntersectionObserver::observe(Element* target, ExceptionState& exceptionSta
return;
}
+ // TODO(szager): Add a pointer to the spec that describes this policy.
bool shouldReportRootBounds = target->document().frame()->securityContext()->securityOrigin()->canAccess(root()->document().frame()->securityContext()->securityOrigin());
+ if (!shouldReportRootBounds && hasPercentMargin()) {
+ exceptionState.throwDOMException(HierarchyRequestError, "Cannot observe a cross-origin target because the observer has a root margin value specified as a percent.");
+ return;
+ }
if (target->ensureIntersectionObserverData().getObservationFor(*this))
return;
@@ -181,6 +259,29 @@ void IntersectionObserver::enqueueIntersectionObserverEntry(IntersectionObserver
toDocument(m_callback->executionContext())->ensureIntersectionObserverController().scheduleIntersectionObserverForDelivery(*this);
}
+static LayoutUnit computeMargin(const Length& length, LayoutUnit referenceLength)
+{
+ if (length.type() == Percent)
+ return LayoutUnit(static_cast<int>(referenceLength.toFloat() * length.percent() / 100.0));
+ ASSERT(length.type() == Fixed);
+ return LayoutUnit(length.intValue());
+}
+
+void IntersectionObserver::applyRootMargin(LayoutRect& rect) const
+{
+ // TODO(szager): Make sure the spec is clear that left/right margins are resolved against
+ // width and not height.
+ LayoutUnit topMargin = computeMargin(m_topMargin, rect.height());
+ LayoutUnit rightMargin = computeMargin(m_rightMargin, rect.width());
+ LayoutUnit bottomMargin = computeMargin(m_bottomMargin, rect.height());
+ LayoutUnit leftMargin = computeMargin(m_leftMargin, rect.width());
+
+ rect.setX(rect.x() - leftMargin);
+ rect.setWidth(rect.width() + leftMargin + rightMargin);
+ rect.setY(rect.y() - topMargin);
+ rect.setHeight(rect.height() + topMargin + bottomMargin);
+}
+
unsigned IntersectionObserver::firstThresholdGreaterThan(float ratio) const
{
unsigned result = 0;
@@ -208,6 +309,14 @@ void IntersectionObserver::setActive(bool active)
observation->setActive(m_root && active && isDescendantOfRoot(observation->target()));
}
+bool IntersectionObserver::hasPercentMargin() const
+{
+ return (m_topMargin.type() == Percent
+ || m_rightMargin.type() == Percent
+ || m_bottomMargin.type() == Percent
+ || m_leftMargin.type() == Percent);
+}
+
void IntersectionObserver::checkRootAndDetachIfNeeded()
{
#if ENABLE(OILPAN)

Powered by Google App Engine
This is Rietveld 408576698