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