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

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

Powered by Google App Engine
This is Rietveld 408576698