Chromium Code Reviews| 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 f7c0ea902854a0b9e9833a5072a400d7df61814c..88a4cb7a29daa116b7364b933161e319b712aa7e 100644 |
| --- a/third_party/WebKit/Source/core/dom/IntersectionObserver.cpp |
| +++ b/third_party/WebKit/Source/core/dom/IntersectionObserver.cpp |
| @@ -22,6 +22,42 @@ |
| namespace blink { |
| +static void parseRootMargin(String rootMarginParameter, Vector<Length>& rootMargin, ExceptionState& exceptionState) |
|
ojan
2016/01/12 01:08:49
TODO: We need to make sure this exact algorithm is
szager1
2016/01/12 19:24:43
Added TODO.
|
| +{ |
| + // 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() < 5 && 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 +93,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 +107,39 @@ 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) |
| { |
| + 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); |
| } |
| @@ -125,6 +192,10 @@ void IntersectionObserver::observe(Element* target, ExceptionState& exceptionSta |
| } |
| 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."); |
|
ojan
2016/01/12 01:08:49
TODO: I wonder if we should have a TODO or somethi
szager1
2016/01/12 19:24:43
Added a TODO.
|
| + return; |
| + } |
| if (target->ensureIntersectionObserverData().getObservationFor(*this)) |
| return; |
| @@ -182,6 +253,26 @@ 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)); |
| + return LayoutUnit(length.intValue()); |
|
ojan
2016/01/12 01:08:49
ASSERT(length.type() == Fixed); ?
szager1
2016/01/12 19:24:43
Done.
|
| +} |
| + |
| +void IntersectionObserver::applyRootMargin(LayoutRect& rect) const |
| +{ |
| + LayoutUnit topMargin = computeMargin(m_topMargin, rect.height()); |
| + LayoutUnit rightMargin = computeMargin(m_rightMargin, rect.width()); |
|
ojan
2016/01/12 01:08:49
TODO: We should make sure the spec is clear that t
szager1
2016/01/12 19:24:43
Added TODO.
|
| + 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; |
| @@ -209,6 +300,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) |