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

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

Powered by Google App Engine
This is Rietveld 408576698