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

Side by Side Diff: third_party/WebKit/Source/core/dom/IntersectionObservation.cpp

Issue 2511143006: Detect change on the intersection of video and viewport. (Closed)
Patch Set: Addressed miu's comments. Created 4 years, 1 month 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 unified diff | Download patch
OLDNEW
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/IntersectionGeometry.h"
8 #include "core/dom/IntersectionObserver.h" 9 #include "core/dom/IntersectionObserver.h"
9 #include "core/frame/FrameView.h"
10 #include "core/frame/LocalFrame.h"
11 #include "core/layout/LayoutBox.h"
12 #include "core/layout/LayoutView.h"
13 #include "core/paint/PaintLayer.h"
14 10
15 namespace blink { 11 namespace blink {
16 12
17 IntersectionObservation::IntersectionObservation(IntersectionObserver& observer, 13 IntersectionObservation::IntersectionObservation(IntersectionObserver& observer,
18 Element& target, 14 Element& target,
19 bool shouldReportRootBounds) 15 bool shouldReportRootBounds)
20 : m_observer(observer), 16 : m_observer(observer),
21 m_target(&target), 17 m_target(&target),
22 m_shouldReportRootBounds(shouldReportRootBounds), 18 m_shouldReportRootBounds(shouldReportRootBounds),
23 m_lastThresholdIndex(0) {} 19 m_lastThresholdIndex(0) {}
24 20
25 void IntersectionObservation::applyRootMargin(LayoutRect& rect) const {
26 if (m_shouldReportRootBounds)
27 m_observer->applyRootMargin(rect);
28 }
29
30 void IntersectionObservation::initializeGeometry(
31 IntersectionGeometry& geometry) const {
32 initializeTargetRect(geometry.targetRect);
33 geometry.intersectionRect = geometry.targetRect;
34 initializeRootRect(geometry.rootRect);
35 geometry.doesIntersect = true;
36 }
37
38 void IntersectionObservation::initializeTargetRect(LayoutRect& rect) const {
39 DCHECK(m_target);
40 LayoutObject* targetLayoutObject = target()->layoutObject();
41 DCHECK(targetLayoutObject && targetLayoutObject->isBoxModelObject());
42 rect = LayoutRect(
43 toLayoutBoxModelObject(targetLayoutObject)->borderBoundingBox());
44 }
45
46 void IntersectionObservation::initializeRootRect(LayoutRect& rect) const {
47 LayoutObject* rootLayoutObject = m_observer->rootLayoutObject();
48 if (rootLayoutObject->isLayoutView())
49 rect = LayoutRect(
50 toLayoutView(rootLayoutObject)->frameView()->visibleContentRect());
51 else if (rootLayoutObject->isBox() && rootLayoutObject->hasOverflowClip())
52 rect = LayoutRect(toLayoutBox(rootLayoutObject)->contentBoxRect());
53 else
54 rect = LayoutRect(
55 toLayoutBoxModelObject(rootLayoutObject)->borderBoundingBox());
56 applyRootMargin(rect);
57 }
58
59 void IntersectionObservation::clipToRoot(IntersectionGeometry& geometry) const {
60 // Map and clip rect into root element coordinates.
61 // TODO(szager): the writing mode flipping needs a test.
62 DCHECK(m_target);
63 LayoutBox* rootLayoutObject = toLayoutBox(m_observer->rootLayoutObject());
64 LayoutObject* targetLayoutObject = target()->layoutObject();
65
66 geometry.doesIntersect = targetLayoutObject->mapToVisualRectInAncestorSpace(
67 rootLayoutObject, geometry.intersectionRect, EdgeInclusive);
68 if (rootLayoutObject->hasOverflowClip())
69 geometry.intersectionRect.move(-rootLayoutObject->scrolledContentOffset());
70
71 if (!geometry.doesIntersect)
72 return;
73 LayoutRect rootClipRect(geometry.rootRect);
74 rootLayoutObject->flipForWritingMode(rootClipRect);
75 geometry.doesIntersect &=
76 geometry.intersectionRect.inclusiveIntersect(rootClipRect);
77 }
78
79 static void mapRectUpToDocument(LayoutRect& rect,
80 const LayoutObject& layoutObject,
81 const Document& document) {
82 FloatQuad mappedQuad = layoutObject.localToAbsoluteQuad(
83 FloatQuad(FloatRect(rect)), UseTransforms | ApplyContainerFlip);
84 rect = LayoutRect(mappedQuad.boundingBox());
85 }
86
87 static void mapRectDownToDocument(LayoutRect& rect,
88 LayoutBoxModelObject& layoutObject,
89 const Document& document) {
90 FloatQuad mappedQuad = document.layoutView()->ancestorToLocalQuad(
91 &layoutObject, FloatQuad(FloatRect(rect)),
92 UseTransforms | ApplyContainerFlip | TraverseDocumentBoundaries);
93 rect = LayoutRect(mappedQuad.boundingBox());
94 }
95
96 void IntersectionObservation::mapTargetRectToTargetFrameCoordinates(
97 LayoutRect& rect) const {
98 LayoutObject& targetLayoutObject = *target()->layoutObject();
99 Document& targetDocument = target()->document();
100 LayoutSize scrollPosition = LayoutSize(targetDocument.view()->scrollOffset());
101 mapRectUpToDocument(rect, targetLayoutObject, targetDocument);
102 rect.move(-scrollPosition);
103 }
104
105 void IntersectionObservation::mapRootRectToRootFrameCoordinates(
106 LayoutRect& rect) const {
107 LayoutObject& rootLayoutObject = *m_observer->rootLayoutObject();
108 Document& rootDocument = rootLayoutObject.document();
109 LayoutSize scrollPosition = LayoutSize(rootDocument.view()->scrollOffset());
110 mapRectUpToDocument(rect, rootLayoutObject, rootLayoutObject.document());
111 rect.move(-scrollPosition);
112 }
113
114 void IntersectionObservation::mapRootRectToTargetFrameCoordinates(
115 LayoutRect& rect) const {
116 LayoutObject& rootLayoutObject = *m_observer->rootLayoutObject();
117 Document& targetDocument = target()->document();
118 LayoutSize scrollPosition = LayoutSize(targetDocument.view()->scrollOffset());
119
120 if (&targetDocument == &rootLayoutObject.document())
121 mapRectUpToDocument(rect, rootLayoutObject, targetDocument);
122 else
123 mapRectDownToDocument(rect, toLayoutBoxModelObject(rootLayoutObject),
124 targetDocument);
125
126 rect.move(-scrollPosition);
127 }
128
129 static bool isContainingBlockChainDescendant(LayoutObject* descendant,
130 LayoutObject* ancestor) {
131 LocalFrame* ancestorFrame = ancestor->document().frame();
132 LocalFrame* descendantFrame = descendant->document().frame();
133
134 if (ancestor->isLayoutView())
135 return descendantFrame && descendantFrame->tree().top() == ancestorFrame;
136
137 if (ancestorFrame != descendantFrame)
138 return false;
139
140 while (descendant && descendant != ancestor)
141 descendant = descendant->containingBlock();
142 return descendant;
143 }
144
145 bool IntersectionObservation::computeGeometry(
146 IntersectionGeometry& geometry) const {
147 // In the first few lines here, before initializeGeometry is called, "return
148 // true" effectively means "if the previous observed state was that root and
149 // target were intersecting, then generate a notification indicating that they
150 // are no longer intersecting." This happens, for example, when root or
151 // target is removed from the DOM tree and not reinserted before the next
152 // frame is generated, or display:none is set on the root or target.
153 Element* targetElement = target();
154 if (!targetElement)
155 return false;
156 if (!targetElement->isConnected())
157 return true;
158 DCHECK(m_observer);
159 Element* rootElement = m_observer->root();
160 if (rootElement && !rootElement->isConnected())
161 return true;
162
163 LayoutObject* rootLayoutObject = m_observer->rootLayoutObject();
164 if (!rootLayoutObject || !rootLayoutObject->isBoxModelObject())
165 return true;
166 // TODO(szager): Support SVG
167 LayoutObject* targetLayoutObject = targetElement->layoutObject();
168 if (!targetLayoutObject)
169 return true;
170 if (!targetLayoutObject->isBoxModelObject() && !targetLayoutObject->isText())
171 return true;
172 if (!isContainingBlockChainDescendant(targetLayoutObject, rootLayoutObject))
173 return true;
174
175 initializeGeometry(geometry);
176
177 clipToRoot(geometry);
178
179 mapTargetRectToTargetFrameCoordinates(geometry.targetRect);
180
181 if (geometry.doesIntersect)
182 mapRootRectToTargetFrameCoordinates(geometry.intersectionRect);
183 else
184 geometry.intersectionRect = LayoutRect();
185
186 // Small optimization: if we're not going to report root bounds, don't bother
187 // transforming them to the frame.
188 if (m_shouldReportRootBounds)
189 mapRootRectToRootFrameCoordinates(geometry.rootRect);
190
191 return true;
192 }
193
194 void IntersectionObservation::computeIntersectionObservations( 21 void IntersectionObservation::computeIntersectionObservations(
195 DOMHighResTimeStamp timestamp) { 22 DOMHighResTimeStamp timestamp) {
196 IntersectionGeometry geometry; 23 if (!m_target || !m_observer->rootNode())
szager1 2016/11/23 17:44:58 The rootNode() check is a behavior change; previou
xjz 2016/11/23 23:43:25 Done.
197 if (!computeGeometry(geometry))
198 return; 24 return;
25 Vector<Length> rootMargin(4);
26 rootMargin[0] = m_observer->topMargin();
27 rootMargin[1] = m_observer->rightMargin();
28 rootMargin[2] = m_observer->bottomMargin();
29 rootMargin[3] = m_observer->leftMargin();
30 IntersectionGeometry geometry(m_observer->rootNode(), target(), rootMargin,
31 m_shouldReportRootBounds);
32 geometry.computeGeometry();
199 33
200 // Some corner cases for threshold index: 34 // Some corner cases for threshold index:
201 // - If target rect is zero area, because it has zero width and/or zero 35 // - If target rect is zero area, because it has zero width and/or zero
202 // height, 36 // height,
203 // only two states are recognized: 37 // only two states are recognized:
204 // - 0 means not intersecting. 38 // - 0 means not intersecting.
205 // - 1 means intersecting. 39 // - 1 means intersecting.
206 // No other threshold crossings are possible. 40 // No other threshold crossings are possible.
207 // - Otherwise: 41 // - Otherwise:
208 // - If root and target do not intersect, the threshold index is 0. 42 // - If root and target do not intersect, the threshold index is 0.
209 // - If root and target intersect but the intersection has zero-area 43 // - If root and target intersect but the intersection has zero-area
210 // (i.e., they have a coincident edge or corner), we consider the 44 // (i.e., they have a coincident edge or corner), we consider the
211 // intersection to have "crossed" a zero threshold, but not crossed 45 // intersection to have "crossed" a zero threshold, but not crossed
212 // any non-zero threshold. 46 // any non-zero threshold.
213 unsigned newThresholdIndex; 47 unsigned newThresholdIndex;
214 float newVisibleRatio = 0; 48 float newVisibleRatio = 0;
215 if (geometry.targetRect.isEmpty()) { 49 if (geometry.targetRect().isEmpty()) {
216 newThresholdIndex = geometry.doesIntersect ? 1 : 0; 50 newThresholdIndex = geometry.doesIntersect() ? 1 : 0;
217 } else if (!geometry.doesIntersect) { 51 } else if (!geometry.doesIntersect()) {
218 newThresholdIndex = 0; 52 newThresholdIndex = 0;
219 } else { 53 } else {
220 float intersectionArea = 54 float intersectionArea =
221 geometry.intersectionRect.size().width().toFloat() * 55 geometry.intersectionRect().size().width().toFloat() *
222 geometry.intersectionRect.size().height().toFloat(); 56 geometry.intersectionRect().size().height().toFloat();
223 float targetArea = geometry.targetRect.size().width().toFloat() * 57 float targetArea = geometry.targetRect().size().width().toFloat() *
224 geometry.targetRect.size().height().toFloat(); 58 geometry.targetRect().size().height().toFloat();
225 newVisibleRatio = intersectionArea / targetArea; 59 newVisibleRatio = intersectionArea / targetArea;
226 newThresholdIndex = observer().firstThresholdGreaterThan(newVisibleRatio); 60 newThresholdIndex = observer().firstThresholdGreaterThan(newVisibleRatio);
227 } 61 }
228 if (m_lastThresholdIndex != newThresholdIndex) { 62 if (m_lastThresholdIndex != newThresholdIndex) {
229 IntRect snappedRootBounds = pixelSnappedIntRect(geometry.rootRect); 63 IntRect snappedRootBounds = pixelSnappedIntRect(geometry.rootRect());
szager1 2016/11/23 17:44:58 geometry.rootIntRect()
xjz 2016/11/23 23:43:25 Done.
230 IntRect* rootBoundsPointer = 64 IntRect* rootBoundsPointer =
231 m_shouldReportRootBounds ? &snappedRootBounds : nullptr; 65 m_shouldReportRootBounds ? &snappedRootBounds : nullptr;
232 IntersectionObserverEntry* newEntry = new IntersectionObserverEntry( 66 IntersectionObserverEntry* newEntry = new IntersectionObserverEntry(
233 timestamp, newVisibleRatio, pixelSnappedIntRect(geometry.targetRect), 67 timestamp, newVisibleRatio, pixelSnappedIntRect(geometry.targetRect()),
szager1 2016/11/23 17:44:58 geometry.targetIntRect()
xjz 2016/11/23 23:43:25 Done.
234 rootBoundsPointer, pixelSnappedIntRect(geometry.intersectionRect), 68 rootBoundsPointer, pixelSnappedIntRect(geometry.intersectionRect()),
szager1 2016/11/23 17:44:58 geometry.intersectionIntRect()
xjz 2016/11/23 23:43:25 Done.
235 target()); 69 target());
236 observer().enqueueIntersectionObserverEntry(*newEntry); 70 observer().enqueueIntersectionObserverEntry(*newEntry);
237 setLastThresholdIndex(newThresholdIndex); 71 setLastThresholdIndex(newThresholdIndex);
238 } 72 }
239 } 73 }
240 74
241 void IntersectionObservation::disconnect() { 75 void IntersectionObservation::disconnect() {
242 IntersectionObserver* observer = m_observer; 76 IntersectionObserver* observer = m_observer;
243 clearRootAndRemoveFromTarget(); 77 clearRootAndRemoveFromTarget();
244 observer->removeObservation(*this); 78 observer->removeObservation(*this);
245 } 79 }
246 80
247 void IntersectionObservation::clearRootAndRemoveFromTarget() { 81 void IntersectionObservation::clearRootAndRemoveFromTarget() {
248 if (m_target) 82 if (m_target)
249 target()->ensureIntersectionObserverData().removeObservation(observer()); 83 target()->ensureIntersectionObserverData().removeObservation(observer());
250 m_observer.clear(); 84 m_observer.clear();
251 } 85 }
252 86
253 DEFINE_TRACE(IntersectionObservation) { 87 DEFINE_TRACE(IntersectionObservation) {
254 visitor->trace(m_observer); 88 visitor->trace(m_observer);
255 visitor->trace(m_target); 89 visitor->trace(m_target);
256 } 90 }
257 91
258 } // namespace blink 92 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698