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

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

Issue 1559593002: Add root margin support for IntersectionObserver. (Closed) Base URL: https://chromium.googlesource.com/chromium/src@intersection-observer-no-root-margin
Patch Set: rebaseline Created 4 years, 11 months 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/IntersectionObserver.h" 5 #include "core/dom/IntersectionObserver.h"
6 6
7 #include "bindings/core/v8/ExceptionState.h" 7 #include "bindings/core/v8/ExceptionState.h"
8 #include "core/css/parser/CSSParserTokenRange.h" 8 #include "core/css/parser/CSSParserTokenRange.h"
9 #include "core/css/parser/CSSTokenizer.h" 9 #include "core/css/parser/CSSTokenizer.h"
10 #include "core/dom/ElementIntersectionObserverData.h" 10 #include "core/dom/ElementIntersectionObserverData.h"
11 #include "core/dom/ExceptionCode.h" 11 #include "core/dom/ExceptionCode.h"
12 #include "core/dom/ExecutionContext.h" 12 #include "core/dom/ExecutionContext.h"
13 #include "core/dom/IntersectionObserverCallback.h" 13 #include "core/dom/IntersectionObserverCallback.h"
14 #include "core/dom/IntersectionObserverController.h" 14 #include "core/dom/IntersectionObserverController.h"
15 #include "core/dom/IntersectionObserverEntry.h" 15 #include "core/dom/IntersectionObserverEntry.h"
16 #include "core/dom/IntersectionObserverInit.h" 16 #include "core/dom/IntersectionObserverInit.h"
17 #include "core/html/HTMLFrameOwnerElement.h" 17 #include "core/html/HTMLFrameOwnerElement.h"
18 #include "core/layout/LayoutView.h" 18 #include "core/layout/LayoutView.h"
19 #include "platform/Timer.h" 19 #include "platform/Timer.h"
20 #include "wtf/MainThread.h" 20 #include "wtf/MainThread.h"
21 #include <algorithm> 21 #include <algorithm>
22 22
23 namespace blink { 23 namespace blink {
24 24
25 static void parseRootMargin(String rootMarginParameter, Vector<Length>& rootMarg in, ExceptionState& exceptionState)
ojan 2016/01/12 01:08:49 TODO: We need to make sure this exact algorithm is
szager1 2016/01/12 19:24:43 Added TODO.
26 {
27 // The root margin argument accepts syntax similar to that for CSS margin:
28 //
29 // "1px" = top/right/bottom/left
30 // "1px 2px" = top/bottom left/right
31 // "1px 2px 3px" = top left/right bottom
32 // "1px 2px 3px 4px" = top left right bottom
33 //
34 // Any extra stuff after the first four tokens is ignored.
35 CSSTokenizer::Scope tokenizerScope(rootMarginParameter);
36 CSSParserTokenRange tokenRange = tokenizerScope.tokenRange();
37 while (rootMargin.size() < 5 && tokenRange.peek().type() != EOFToken && !exc eptionState.hadException()) {
38 const CSSParserToken& token = tokenRange.consumeIncludingWhitespace();
39 switch (token.type()) {
40 case PercentageToken:
41 rootMargin.append(Length(token.numericValue(), Percent));
42 break;
43 case DimensionToken:
44 switch (token.unitType()) {
45 case CSSPrimitiveValue::UnitType::Pixels:
46 rootMargin.append(Length(static_cast<int>(floor(token.numericVal ue())), Fixed));
47 break;
48 case CSSPrimitiveValue::UnitType::Percentage:
49 rootMargin.append(Length(token.numericValue(), Percent));
50 break;
51 default:
52 exceptionState.throwTypeError("rootMargin must be specified in p ixels or percent.");
53 }
54 break;
55 default:
56 exceptionState.throwTypeError("rootMargin must be specified in pixel s or percent.");
57 }
58 }
59 }
60
25 static void parseThresholds(const DoubleOrDoubleArray& thresholdParameter, Vecto r<float>& thresholds, ExceptionState& exceptionState) 61 static void parseThresholds(const DoubleOrDoubleArray& thresholdParameter, Vecto r<float>& thresholds, ExceptionState& exceptionState)
26 { 62 {
27 if (thresholdParameter.isDouble()) { 63 if (thresholdParameter.isDouble()) {
28 thresholds.append(static_cast<float>(thresholdParameter.getAsDouble())); 64 thresholds.append(static_cast<float>(thresholdParameter.getAsDouble()));
29 } else { 65 } else {
30 for (auto thresholdValue : thresholdParameter.getAsDoubleArray()) 66 for (auto thresholdValue : thresholdParameter.getAsDoubleArray())
31 thresholds.append(static_cast<float>(thresholdValue)); 67 thresholds.append(static_cast<float>(thresholdValue));
32 } 68 }
33 69
34 for (auto thresholdValue : thresholds) { 70 for (auto thresholdValue : thresholds) {
(...skipping 15 matching lines...) Expand all
50 ASSERT(context->isDocument()); 86 ASSERT(context->isDocument());
51 Frame* mainFrame = toDocument(context)->frame()->tree().top(); 87 Frame* mainFrame = toDocument(context)->frame()->tree().top();
52 if (mainFrame && mainFrame->isLocalFrame()) 88 if (mainFrame && mainFrame->isLocalFrame())
53 root = toLocalFrame(mainFrame)->document()->documentElement(); 89 root = toLocalFrame(mainFrame)->document()->documentElement();
54 } 90 }
55 if (!root) { 91 if (!root) {
56 exceptionState.throwDOMException(HierarchyRequestError, "Unable to get r oot element in main frame to track."); 92 exceptionState.throwDOMException(HierarchyRequestError, "Unable to get r oot element in main frame to track.");
57 return nullptr; 93 return nullptr;
58 } 94 }
59 95
96 Vector<Length> rootMargin;
97 if (observerInit.hasRootMargin())
98 parseRootMargin(observerInit.rootMargin(), rootMargin, exceptionState);
99 if (exceptionState.hadException())
100 return nullptr;
101
60 Vector<float> thresholds; 102 Vector<float> thresholds;
61 if (observerInit.hasThreshold()) 103 if (observerInit.hasThreshold())
62 parseThresholds(observerInit.threshold(), thresholds, exceptionState); 104 parseThresholds(observerInit.threshold(), thresholds, exceptionState);
63 else 105 else
64 thresholds.append(0); 106 thresholds.append(0);
65 if (exceptionState.hadException()) 107 if (exceptionState.hadException())
66 return nullptr; 108 return nullptr;
67 109
68 return new IntersectionObserver(callback, *root, thresholds); 110 return new IntersectionObserver(callback, *root, rootMargin, thresholds);
69 } 111 }
70 112
71 IntersectionObserver::IntersectionObserver(IntersectionObserverCallback& callbac k, Element& root, const Vector<float>& thresholds) 113 IntersectionObserver::IntersectionObserver(IntersectionObserverCallback& callbac k, Element& root, const Vector<Length>& rootMargin, const Vector<float>& thresho lds)
72 : m_callback(&callback) 114 : m_callback(&callback)
73 , m_root(root.ensureIntersectionObserverData().createWeakPtr(&root)) 115 , m_root(root.ensureIntersectionObserverData().createWeakPtr(&root))
74 , m_thresholds(thresholds) 116 , m_thresholds(thresholds)
75 { 117 {
118 switch (rootMargin.size()) {
119 case 0:
120 break;
121 case 1:
122 m_topMargin = m_rightMargin = m_bottomMargin = m_leftMargin = rootMargin [0];
123 break;
124 case 2:
125 m_topMargin = m_bottomMargin = rootMargin[0];
126 m_rightMargin = m_leftMargin = rootMargin[1];
127 break;
128 case 3:
129 m_topMargin = rootMargin[0];
130 m_rightMargin = m_leftMargin = rootMargin[1];
131 m_bottomMargin = rootMargin[2];
132 break;
133 case 4:
134 m_topMargin = rootMargin[0];
135 m_rightMargin = rootMargin[1];
136 m_bottomMargin = rootMargin[2];
137 m_leftMargin = rootMargin[3];
138 break;
139 default:
140 ASSERT_NOT_REACHED();
141 break;
142 }
76 root.document().ensureIntersectionObserverController().addTrackedObserver(*t his); 143 root.document().ensureIntersectionObserverController().addTrackedObserver(*t his);
77 } 144 }
78 145
79 LayoutObject* IntersectionObserver::rootLayoutObject() 146 LayoutObject* IntersectionObserver::rootLayoutObject()
80 { 147 {
81 Element* rootElement = root(); 148 Element* rootElement = root();
82 if (rootElement == rootElement->document().documentElement()) 149 if (rootElement == rootElement->document().documentElement())
83 return rootElement->document().layoutView(); 150 return rootElement->document().layoutView();
84 return rootElement->layoutObject(); 151 return rootElement->layoutObject();
85 } 152 }
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
118 if (m_root.get() == target) { 185 if (m_root.get() == target) {
119 exceptionState.throwDOMException(HierarchyRequestError, "Cannot use the same element for root and target."); 186 exceptionState.throwDOMException(HierarchyRequestError, "Cannot use the same element for root and target.");
120 return; 187 return;
121 } 188 }
122 if (!isDescendantOfRoot(target)) { 189 if (!isDescendantOfRoot(target)) {
123 exceptionState.throwDOMException(HierarchyRequestError, "Observed elemen t must be a descendant of the observer's root element."); 190 exceptionState.throwDOMException(HierarchyRequestError, "Observed elemen t must be a descendant of the observer's root element.");
124 return; 191 return;
125 } 192 }
126 193
127 bool shouldReportRootBounds = target->document().frame()->securityContext()- >securityOrigin()->canAccess(root()->document().frame()->securityContext()->secu rityOrigin()); 194 bool shouldReportRootBounds = target->document().frame()->securityContext()- >securityOrigin()->canAccess(root()->document().frame()->securityContext()->secu rityOrigin());
195 if (!shouldReportRootBounds && hasPercentMargin()) {
196 exceptionState.throwDOMException(HierarchyRequestError, "Cannot observe a cross-origin target because the observer has a root margin value specified as a percent.");
ojan 2016/01/12 01:08:49 TODO: I wonder if we should have a TODO or somethi
szager1 2016/01/12 19:24:43 Added a TODO.
197 return;
198 }
128 199
129 if (target->ensureIntersectionObserverData().getObservationFor(*this)) 200 if (target->ensureIntersectionObserverData().getObservationFor(*this))
130 return; 201 return;
131 202
132 IntersectionObservation* observation = new IntersectionObservation(*this, *t arget, shouldReportRootBounds); 203 IntersectionObservation* observation = new IntersectionObservation(*this, *t arget, shouldReportRootBounds);
133 target->ensureIntersectionObserverData().addObservation(*observation); 204 target->ensureIntersectionObserverData().addObservation(*observation);
134 m_observations.add(observation); 205 m_observations.add(observation);
135 } 206 }
136 207
137 void IntersectionObserver::unobserve(Element* target, ExceptionState&) 208 void IntersectionObserver::unobserve(Element* target, ExceptionState&)
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
175 entries.swap(m_entries); 246 entries.swap(m_entries);
176 return entries; 247 return entries;
177 } 248 }
178 249
179 void IntersectionObserver::enqueueIntersectionObserverEntry(IntersectionObserver Entry& entry) 250 void IntersectionObserver::enqueueIntersectionObserverEntry(IntersectionObserver Entry& entry)
180 { 251 {
181 m_entries.append(&entry); 252 m_entries.append(&entry);
182 toDocument(m_callback->executionContext())->ensureIntersectionObserverContro ller().scheduleIntersectionObserverForDelivery(*this); 253 toDocument(m_callback->executionContext())->ensureIntersectionObserverContro ller().scheduleIntersectionObserverForDelivery(*this);
183 } 254 }
184 255
256 static LayoutUnit computeMargin(const Length& length, LayoutUnit referenceLength )
257 {
258 if (length.type() == Percent)
259 return LayoutUnit(static_cast<int>(referenceLength.toFloat() * length.pe rcent() / 100.0));
260 return LayoutUnit(length.intValue());
ojan 2016/01/12 01:08:49 ASSERT(length.type() == Fixed); ?
szager1 2016/01/12 19:24:43 Done.
261 }
262
263 void IntersectionObserver::applyRootMargin(LayoutRect& rect) const
264 {
265 LayoutUnit topMargin = computeMargin(m_topMargin, rect.height());
266 LayoutUnit rightMargin = computeMargin(m_rightMargin, rect.width());
ojan 2016/01/12 01:08:49 TODO: We should make sure the spec is clear that t
szager1 2016/01/12 19:24:43 Added TODO.
267 LayoutUnit bottomMargin = computeMargin(m_bottomMargin, rect.height());
268 LayoutUnit leftMargin = computeMargin(m_leftMargin, rect.width());
269
270 rect.setX(rect.x() - leftMargin);
271 rect.setWidth(rect.width() + leftMargin + rightMargin);
272 rect.setY(rect.y() - topMargin);
273 rect.setHeight(rect.height() + topMargin + bottomMargin);
274 }
275
185 unsigned IntersectionObserver::firstThresholdGreaterThan(float ratio) const 276 unsigned IntersectionObserver::firstThresholdGreaterThan(float ratio) const
186 { 277 {
187 unsigned result = 0; 278 unsigned result = 0;
188 while (result < m_thresholds.size() && m_thresholds[result] < ratio) 279 while (result < m_thresholds.size() && m_thresholds[result] < ratio)
189 ++result; 280 ++result;
190 return result; 281 return result;
191 } 282 }
192 283
193 void IntersectionObserver::deliver() 284 void IntersectionObserver::deliver()
194 { 285 {
195 checkRootAndDetachIfNeeded(); 286 checkRootAndDetachIfNeeded();
196 287
197 if (m_entries.isEmpty()) 288 if (m_entries.isEmpty())
198 return; 289 return;
199 290
200 HeapVector<Member<IntersectionObserverEntry>> entries; 291 HeapVector<Member<IntersectionObserverEntry>> entries;
201 entries.swap(m_entries); 292 entries.swap(m_entries);
202 m_callback->handleEvent(entries, *this); 293 m_callback->handleEvent(entries, *this);
203 } 294 }
204 295
205 void IntersectionObserver::setActive(bool active) 296 void IntersectionObserver::setActive(bool active)
206 { 297 {
207 checkRootAndDetachIfNeeded(); 298 checkRootAndDetachIfNeeded();
208 for (auto& observation : m_observations) 299 for (auto& observation : m_observations)
209 observation->setActive(m_root && active && isDescendantOfRoot(observatio n->target())); 300 observation->setActive(m_root && active && isDescendantOfRoot(observatio n->target()));
210 } 301 }
211 302
303 bool IntersectionObserver::hasPercentMargin() const
304 {
305 return (m_topMargin.type() == Percent
306 || m_rightMargin.type() == Percent
307 || m_bottomMargin.type() == Percent
308 || m_leftMargin.type() == Percent);
309 }
310
212 void IntersectionObserver::checkRootAndDetachIfNeeded() 311 void IntersectionObserver::checkRootAndDetachIfNeeded()
213 { 312 {
214 #if ENABLE(OILPAN) 313 #if ENABLE(OILPAN)
215 // TODO(szager): Pre-oilpan, ElementIntersectionObserverData::dispose() will take 314 // TODO(szager): Pre-oilpan, ElementIntersectionObserverData::dispose() will take
216 // care of this cleanup. When oilpan ships, there will be a potential leak of the 315 // care of this cleanup. When oilpan ships, there will be a potential leak of the
217 // callback's execution context when the root goes away. For a detailed exp lanation: 316 // callback's execution context when the root goes away. For a detailed exp lanation:
218 // 317 //
219 // https://goo.gl/PC2Baj 318 // https://goo.gl/PC2Baj
220 // 319 //
221 // When that happens, this method should catch most potential leaks, but a c omplete 320 // When that happens, this method should catch most potential leaks, but a c omplete
222 // solution will still be needed, along the lines described in the above lin k. 321 // solution will still be needed, along the lines described in the above lin k.
223 if (m_root) 322 if (m_root)
224 return; 323 return;
225 disconnect(); 324 disconnect();
226 #endif 325 #endif
227 } 326 }
228 327
229 DEFINE_TRACE(IntersectionObserver) 328 DEFINE_TRACE(IntersectionObserver)
230 { 329 {
231 visitor->trace(m_callback); 330 visitor->trace(m_callback);
232 visitor->trace(m_root); 331 visitor->trace(m_root);
233 visitor->trace(m_observations); 332 visitor->trace(m_observations);
234 visitor->trace(m_entries); 333 visitor->trace(m_entries);
235 } 334 }
236 335
237 } // namespace blink 336 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698