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