Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "core/dom/IntersectionObservation.h" | 5 #include "core/dom/IntersectionObservation.h" |
| 6 | 6 |
| 7 #include "core/dom/ElementRareData.h" | 7 #include "core/dom/ElementRareData.h" |
| 8 #include "core/dom/IntersectionObserver.h" | 8 #include "core/dom/IntersectionObserver.h" |
| 9 #include "core/frame/FrameView.h" | 9 #include "core/frame/FrameView.h" |
| 10 #include "core/layout/LayoutBox.h" | 10 #include "core/layout/LayoutBox.h" |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 27 { | 27 { |
| 28 return toElement(m_target.get()); | 28 return toElement(m_target.get()); |
| 29 } | 29 } |
| 30 | 30 |
| 31 void IntersectionObservation::applyRootMargin(LayoutRect& rect) const | 31 void IntersectionObservation::applyRootMargin(LayoutRect& rect) const |
| 32 { | 32 { |
| 33 if (m_shouldReportRootBounds) | 33 if (m_shouldReportRootBounds) |
| 34 m_observer->applyRootMargin(rect); | 34 m_observer->applyRootMargin(rect); |
| 35 } | 35 } |
| 36 | 36 |
| 37 void IntersectionObservation::initializeGeometry(IntersectionGeometry& geometry) const | |
| 38 { | |
| 39 initializeTargetRect(geometry.targetRect); | |
| 40 geometry.intersectionRect = geometry.targetRect; | |
| 41 initializeRootRect(geometry.rootRect); | |
| 42 geometry.doesIntersect = true; | |
| 43 } | |
| 44 | |
| 37 void IntersectionObservation::initializeTargetRect(LayoutRect& rect) const | 45 void IntersectionObservation::initializeTargetRect(LayoutRect& rect) const |
| 38 { | 46 { |
| 39 ASSERT(m_target); | 47 ASSERT(m_target); |
| 40 LayoutObject* targetLayoutObject = target()->layoutObject(); | 48 LayoutObject* targetLayoutObject = target()->layoutObject(); |
| 41 ASSERT(targetLayoutObject && targetLayoutObject->isBoxModelObject()); | 49 ASSERT(targetLayoutObject && targetLayoutObject->isBoxModelObject()); |
| 42 rect = toLayoutBoxModelObject(targetLayoutObject)->visualOverflowRect(); | 50 rect = toLayoutBoxModelObject(targetLayoutObject)->visualOverflowRect(); |
| 43 | |
| 44 // TODO(szager): Properly support intersection observations for zero-area ta rgets | |
| 45 // by using edge-inclusive geometry. | |
| 46 if (!rect.size().width()) | |
| 47 rect.setWidth(LayoutUnit(1)); | |
| 48 if (!rect.size().height()) | |
| 49 rect.setHeight(LayoutUnit(1)); | |
| 50 } | 51 } |
| 51 | 52 |
| 52 void IntersectionObservation::initializeRootRect(LayoutRect& rect) const | 53 void IntersectionObservation::initializeRootRect(LayoutRect& rect) const |
| 53 { | 54 { |
| 54 LayoutObject* rootLayoutObject = m_observer->rootLayoutObject(); | 55 LayoutObject* rootLayoutObject = m_observer->rootLayoutObject(); |
| 55 if (rootLayoutObject->isLayoutView()) | 56 if (rootLayoutObject->isLayoutView()) |
| 56 rect = LayoutRect(toLayoutView(rootLayoutObject)->frameView()->visibleCo ntentRect()); | 57 rect = LayoutRect(toLayoutView(rootLayoutObject)->frameView()->visibleCo ntentRect()); |
| 57 // TODO(szager): Obey the spec -- use content box for a scrolling element, b order box otherwise. | 58 // TODO(szager): Obey the spec -- use content box for a scrolling element, b order box otherwise. |
| 58 else if (rootLayoutObject->isBox()) | 59 else if (rootLayoutObject->isBox()) |
| 59 rect = LayoutRect(toLayoutBox(rootLayoutObject)->contentBoxRect()); | 60 rect = LayoutRect(toLayoutBox(rootLayoutObject)->contentBoxRect()); |
| 60 else | 61 else |
| 61 rect = LayoutRect(toLayoutBoxModelObject(rootLayoutObject)->borderBoundi ngBox()); | 62 rect = LayoutRect(toLayoutBoxModelObject(rootLayoutObject)->borderBoundi ngBox()); |
| 62 applyRootMargin(rect); | 63 applyRootMargin(rect); |
| 63 } | 64 } |
| 64 | 65 |
| 65 void IntersectionObservation::clipToRoot(LayoutRect& rect, const LayoutRect& roo tRect) const | 66 void IntersectionObservation::clipToRoot(IntersectionGeometry& geometry) const |
| 66 { | 67 { |
| 67 // Map and clip rect into root element coordinates. | 68 // Map and clip rect into root element coordinates. |
| 68 // TODO(szager): the writing mode flipping needs a test. | 69 // TODO(szager): the writing mode flipping needs a test. |
| 69 ASSERT(m_target); | 70 ASSERT(m_target); |
| 70 LayoutObject* rootLayoutObject = m_observer->rootLayoutObject(); | 71 LayoutObject* rootLayoutObject = m_observer->rootLayoutObject(); |
| 71 LayoutObject* targetLayoutObject = target()->layoutObject(); | 72 LayoutObject* targetLayoutObject = target()->layoutObject(); |
| 72 | 73 |
| 73 targetLayoutObject->mapToVisibleRectInAncestorSpace(toLayoutBoxModelObject(r ootLayoutObject), rect, nullptr); | 74 geometry.doesIntersect = targetLayoutObject->mapToVisibleRectInAncestorSpace (toLayoutBoxModelObject(rootLayoutObject), geometry.intersectionRect, nullptr, E dgeInclusive); |
| 74 LayoutRect rootClipRect(rootRect); | 75 if (!geometry.doesIntersect) |
| 76 return; | |
| 77 LayoutRect rootClipRect(geometry.rootRect); | |
| 75 toLayoutBox(rootLayoutObject)->flipForWritingMode(rootClipRect); | 78 toLayoutBox(rootLayoutObject)->flipForWritingMode(rootClipRect); |
| 76 rect.intersect(rootClipRect); | 79 geometry.doesIntersect &= geometry.intersectionRect.inclusiveIntersect(rootC lipRect); |
| 77 } | 80 } |
| 78 | 81 |
| 79 static void mapRectUpToDocument(LayoutRect& rect, const LayoutObject& layoutObje ct, const Document& document) | 82 static void mapRectUpToDocument(LayoutRect& rect, const LayoutObject& layoutObje ct, const Document& document) |
| 80 { | 83 { |
| 81 FloatQuad mappedQuad = layoutObject.localToAbsoluteQuad( | 84 FloatQuad mappedQuad = layoutObject.localToAbsoluteQuad( |
| 82 FloatQuad(FloatRect(rect)), | 85 FloatQuad(FloatRect(rect)), |
| 83 UseTransforms | ApplyContainerFlip); | 86 UseTransforms | ApplyContainerFlip); |
| 84 rect = LayoutRect(mappedQuad.boundingBox()); | 87 rect = LayoutRect(mappedQuad.boundingBox()); |
| 85 } | 88 } |
| 86 | 89 |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 157 // TODO(szager): Support SVG | 160 // TODO(szager): Support SVG |
| 158 if (!targetLayoutObject) | 161 if (!targetLayoutObject) |
| 159 return false; | 162 return false; |
| 160 if (!targetLayoutObject->isBoxModelObject() && !targetLayoutObject->isText() ) | 163 if (!targetLayoutObject->isBoxModelObject() && !targetLayoutObject->isText() ) |
| 161 return false; | 164 return false; |
| 162 if (!rootLayoutObject || !rootLayoutObject->isBoxModelObject()) | 165 if (!rootLayoutObject || !rootLayoutObject->isBoxModelObject()) |
| 163 return false; | 166 return false; |
| 164 if (!isContainingBlockChainDescendant(targetLayoutObject, rootLayoutObject)) | 167 if (!isContainingBlockChainDescendant(targetLayoutObject, rootLayoutObject)) |
| 165 return false; | 168 return false; |
| 166 | 169 |
| 167 initializeTargetRect(geometry.targetRect); | 170 initializeGeometry(geometry); |
| 168 geometry.intersectionRect = geometry.targetRect; | |
| 169 initializeRootRect(geometry.rootRect); | |
| 170 | 171 |
| 171 clipToRoot(geometry.intersectionRect, geometry.rootRect); | 172 clipToRoot(geometry); |
| 172 | 173 |
| 173 // TODO(szager): there are some simple optimizations that can be done here: | |
| 174 // - Don't transform rootRect if it's not going to be reported | |
| 175 // - Don't transform intersectionRect if it's empty | |
| 176 mapTargetRectToTargetFrameCoordinates(geometry.targetRect); | 174 mapTargetRectToTargetFrameCoordinates(geometry.targetRect); |
| 177 mapRootRectToTargetFrameCoordinates(geometry.intersectionRect); | |
| 178 mapRootRectToRootFrameCoordinates(geometry.rootRect); | |
| 179 | 175 |
| 180 if (geometry.intersectionRect.size().isZero()) | 176 if (geometry.doesIntersect) |
| 177 mapRootRectToTargetFrameCoordinates(geometry.intersectionRect); | |
| 178 else | |
| 181 geometry.intersectionRect = LayoutRect(); | 179 geometry.intersectionRect = LayoutRect(); |
| 182 | 180 |
| 181 // Small optimization: if we're not going to report root bounds, don't bothe r | |
| 182 // transforming them to the frame. | |
| 183 if (m_shouldReportRootBounds) | |
| 184 mapRootRectToRootFrameCoordinates(geometry.rootRect); | |
| 185 | |
| 183 return true; | 186 return true; |
| 184 } | 187 } |
| 185 | 188 |
| 189 enum IntersectionState { | |
| 190 NotIntersecting = 0, | |
| 191 Intersecting = 1 | |
| 192 }; | |
| 193 | |
| 186 void IntersectionObservation::computeIntersectionObservations(DOMHighResTimeStam p timestamp) | 194 void IntersectionObservation::computeIntersectionObservations(DOMHighResTimeStam p timestamp) |
| 187 { | 195 { |
| 188 IntersectionGeometry geometry; | 196 IntersectionGeometry geometry; |
| 189 if (!computeGeometry(geometry)) | 197 if (!computeGeometry(geometry)) |
| 190 return; | 198 return; |
| 191 | 199 |
| 192 float intersectionArea = geometry.intersectionRect.size().width().toFloat() * geometry.intersectionRect.size().height().toFloat(); | 200 // Some corner cases for threshold index: |
| 193 float targetArea = geometry.targetRect.size().width().toFloat() * geometry.t argetRect.size().height().toFloat(); | 201 // - If target rect is zero area, because it has zero width and/or zero he ight, |
| 194 if (!targetArea) | 202 // only two states are recognized: |
| 195 return; | 203 // - 0 means not intersecting. |
| 196 float newVisibleRatio = intersectionArea / targetArea; | 204 // - 1 means intersecting. |
| 197 unsigned newThresholdIndex = observer().firstThresholdGreaterThan(newVisible Ratio); | 205 // No other threshold crossings are possible. |
| 198 IntRect snappedRootBounds = pixelSnappedIntRect(geometry.rootRect); | 206 // - Otherwise: |
| 199 IntRect* rootBoundsPointer = m_shouldReportRootBounds ? &snappedRootBounds : nullptr; | 207 // - If root and target do not intersect, the threshold index is 0. |
| 208 // - If root and target intersect but the intersection has zero-area (i. e., they | |
| 209 // have a coincident edge or corner), we consider the intersection to have | |
| 210 // "crossed" a zero threshold, but not crossed any non-zero threshold. | |
| 211 unsigned newThresholdIndex; | |
| 212 if (!geometry.doesIntersect) { | |
| 213 newThresholdIndex = NotIntersecting; | |
| 214 } else if (geometry.targetRect.isEmpty()) { | |
| 215 newThresholdIndex = Intersecting; | |
| 216 } else { | |
| 217 float intersectionArea = geometry.intersectionRect.size().width().toFloa t() * geometry.intersectionRect.size().height().toFloat(); | |
| 218 float targetArea = geometry.targetRect.size().width().toFloat() * geomet ry.targetRect.size().height().toFloat(); | |
| 219 float newVisibleRatio = intersectionArea / targetArea; | |
| 220 newThresholdIndex = observer().firstThresholdGreaterThan(newVisibleRatio ); | |
|
chrishtr
2016/03/25 14:54:47
Oh I see. It is weird to have an enum combined wit
szager1
2016/03/25 17:03:10
I think it's important to keep in mind that the pu
| |
| 221 } | |
| 200 if (m_lastThresholdIndex != newThresholdIndex) { | 222 if (m_lastThresholdIndex != newThresholdIndex) { |
| 223 IntRect snappedRootBounds = pixelSnappedIntRect(geometry.rootRect); | |
| 224 IntRect* rootBoundsPointer = m_shouldReportRootBounds ? &snappedRootBoun ds : nullptr; | |
| 201 IntersectionObserverEntry* newEntry = new IntersectionObserverEntry( | 225 IntersectionObserverEntry* newEntry = new IntersectionObserverEntry( |
| 202 timestamp, | 226 timestamp, |
| 203 pixelSnappedIntRect(geometry.targetRect), | 227 pixelSnappedIntRect(geometry.targetRect), |
| 204 rootBoundsPointer, | 228 rootBoundsPointer, |
| 205 pixelSnappedIntRect(geometry.intersectionRect), | 229 pixelSnappedIntRect(geometry.intersectionRect), |
| 206 target()); | 230 target()); |
| 207 observer().enqueueIntersectionObserverEntry(*newEntry); | 231 observer().enqueueIntersectionObserverEntry(*newEntry); |
| 208 } | 232 } |
| 209 setLastThresholdIndex(newThresholdIndex); | 233 setLastThresholdIndex(newThresholdIndex); |
| 210 } | 234 } |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 223 m_observer.clear(); | 247 m_observer.clear(); |
| 224 } | 248 } |
| 225 | 249 |
| 226 DEFINE_TRACE(IntersectionObservation) | 250 DEFINE_TRACE(IntersectionObservation) |
| 227 { | 251 { |
| 228 visitor->trace(m_observer); | 252 visitor->trace(m_observer); |
| 229 visitor->trace(m_target); | 253 visitor->trace(m_target); |
| 230 } | 254 } |
| 231 | 255 |
| 232 } // namespace blink | 256 } // namespace blink |
| OLD | NEW |