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

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

Powered by Google App Engine
This is Rietveld 408576698