OLD | NEW |
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/Element.h" | 10 #include "core/dom/Element.h" |
| 11 #include "core/dom/ElementIntersectionObserverData.h" |
11 #include "core/dom/ExceptionCode.h" | 12 #include "core/dom/ExceptionCode.h" |
12 #include "core/dom/ExecutionContext.h" | 13 #include "core/dom/ExecutionContext.h" |
13 #include "core/dom/IntersectionObserverCallback.h" | 14 #include "core/dom/IntersectionObserverCallback.h" |
14 #include "core/dom/IntersectionObserverController.h" | 15 #include "core/dom/IntersectionObserverController.h" |
15 #include "core/dom/IntersectionObserverEntry.h" | 16 #include "core/dom/IntersectionObserverEntry.h" |
16 #include "core/dom/IntersectionObserverInit.h" | 17 #include "core/dom/IntersectionObserverInit.h" |
17 #include "core/dom/NodeIntersectionObserverData.h" | |
18 #include "core/frame/FrameView.h" | 18 #include "core/frame/FrameView.h" |
19 #include "core/frame/LocalDOMWindow.h" | 19 #include "core/frame/LocalDOMWindow.h" |
20 #include "core/frame/LocalFrame.h" | 20 #include "core/frame/LocalFrame.h" |
21 #include "core/html/HTMLFrameOwnerElement.h" | |
22 #include "core/inspector/ConsoleMessage.h" | 21 #include "core/inspector/ConsoleMessage.h" |
| 22 #include "core/layout/LayoutView.h" |
23 #include "core/timing/DOMWindowPerformance.h" | 23 #include "core/timing/DOMWindowPerformance.h" |
24 #include "core/timing/Performance.h" | 24 #include "core/timing/Performance.h" |
25 #include "platform/Timer.h" | 25 #include "platform/Timer.h" |
26 #include <algorithm> | 26 #include <algorithm> |
27 | 27 |
28 namespace blink { | 28 namespace blink { |
29 | 29 |
30 namespace { | 30 namespace { |
31 | 31 |
32 // Internal implementation of IntersectionObserverCallback when using | 32 // Internal implementation of IntersectionObserverCallback when using |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
121 thresholdValue > 1.0) { | 121 thresholdValue > 1.0) { |
122 exceptionState.throwRangeError( | 122 exceptionState.throwRangeError( |
123 "Threshold values must be numbers between 0 and 1"); | 123 "Threshold values must be numbers between 0 and 1"); |
124 break; | 124 break; |
125 } | 125 } |
126 } | 126 } |
127 | 127 |
128 std::sort(thresholds.begin(), thresholds.end()); | 128 std::sort(thresholds.begin(), thresholds.end()); |
129 } | 129 } |
130 | 130 |
131 // Returns the root Node of a given Document to use as the IntersectionObserver | |
132 // root when no root is given. | |
133 // TODO(szager): it doesn't support RemoteFrames, see https://crbug.com/615156 | |
134 Node* getRootNode(Document* document) { | |
135 Frame* mainFrame = document->frame()->tree().top(); | |
136 if (mainFrame && mainFrame->isLocalFrame()) | |
137 return toLocalFrame(mainFrame)->document(); | |
138 return nullptr; | |
139 } | |
140 | |
141 } // anonymous namespace | 131 } // anonymous namespace |
142 | 132 |
143 IntersectionObserver* IntersectionObserver::create( | 133 IntersectionObserver* IntersectionObserver::create( |
144 const IntersectionObserverInit& observerInit, | 134 const IntersectionObserverInit& observerInit, |
145 IntersectionObserverCallback& callback, | 135 IntersectionObserverCallback& callback, |
146 ExceptionState& exceptionState) { | 136 ExceptionState& exceptionState) { |
147 Node* root = observerInit.root(); | 137 Element* root = observerInit.root(); |
148 if (!root) { | |
149 ExecutionContext* context = callback.getExecutionContext(); | |
150 DCHECK(context->isDocument()); | |
151 root = getRootNode(toDocument(context)); | |
152 } | |
153 if (!root) { | |
154 exceptionState.throwDOMException( | |
155 HierarchyRequestError, | |
156 "Unable to get root node in main frame to track."); | |
157 return nullptr; | |
158 } | |
159 | 138 |
160 Vector<Length> rootMargin; | 139 Vector<Length> rootMargin; |
161 parseRootMargin(observerInit.rootMargin(), rootMargin, exceptionState); | 140 parseRootMargin(observerInit.rootMargin(), rootMargin, exceptionState); |
162 if (exceptionState.hadException()) | 141 if (exceptionState.hadException()) |
163 return nullptr; | 142 return nullptr; |
164 | 143 |
165 Vector<float> thresholds; | 144 Vector<float> thresholds; |
166 parseThresholds(observerInit.threshold(), thresholds, exceptionState); | 145 parseThresholds(observerInit.threshold(), thresholds, exceptionState); |
167 if (exceptionState.hadException()) | 146 if (exceptionState.hadException()) |
168 return nullptr; | 147 return nullptr; |
169 | 148 |
170 return new IntersectionObserver(callback, *root, rootMargin, thresholds); | 149 return new IntersectionObserver(callback, root, rootMargin, thresholds); |
171 } | 150 } |
172 | 151 |
173 IntersectionObserver* IntersectionObserver::create( | 152 IntersectionObserver* IntersectionObserver::create( |
174 const Vector<Length>& rootMargin, | 153 const Vector<Length>& rootMargin, |
175 const Vector<float>& thresholds, | 154 const Vector<float>& thresholds, |
176 Document* document, | 155 Document* document, |
177 std::unique_ptr<EventCallback> callback, | 156 std::unique_ptr<EventCallback> callback, |
178 ExceptionState& exceptionState) { | 157 ExceptionState& exceptionState) { |
179 Node* root = getRootNode(document); | |
180 if (!root) { | |
181 exceptionState.throwDOMException( | |
182 HierarchyRequestError, | |
183 "Unable to get root node in main frame to track."); | |
184 return nullptr; | |
185 } | |
186 | |
187 IntersectionObserverCallbackImpl* intersectionObserverCallback = | 158 IntersectionObserverCallbackImpl* intersectionObserverCallback = |
188 new IntersectionObserverCallbackImpl(document, std::move(callback)); | 159 new IntersectionObserverCallbackImpl(document, std::move(callback)); |
189 return new IntersectionObserver(*intersectionObserverCallback, *root, | 160 return new IntersectionObserver(*intersectionObserverCallback, nullptr, |
190 rootMargin, thresholds); | 161 rootMargin, thresholds); |
191 } | 162 } |
192 | 163 |
193 IntersectionObserver::IntersectionObserver( | 164 IntersectionObserver::IntersectionObserver( |
194 IntersectionObserverCallback& callback, | 165 IntersectionObserverCallback& callback, |
195 Node& root, | 166 Element* root, |
196 const Vector<Length>& rootMargin, | 167 const Vector<Length>& rootMargin, |
197 const Vector<float>& thresholds) | 168 const Vector<float>& thresholds) |
198 : m_callback(&callback), | 169 : m_callback(&callback), |
199 m_root(&root), | 170 m_root(root), |
200 m_thresholds(thresholds), | 171 m_thresholds(thresholds), |
201 m_topMargin(Fixed), | 172 m_topMargin(Fixed), |
202 m_rightMargin(Fixed), | 173 m_rightMargin(Fixed), |
203 m_bottomMargin(Fixed), | 174 m_bottomMargin(Fixed), |
204 m_leftMargin(Fixed), | 175 m_leftMargin(Fixed), |
| 176 m_rootIsImplicit(root ? 0 : 1), |
205 m_initialState(InitialState::kHidden) { | 177 m_initialState(InitialState::kHidden) { |
206 switch (rootMargin.size()) { | 178 switch (rootMargin.size()) { |
207 case 0: | 179 case 0: |
208 break; | 180 break; |
209 case 1: | 181 case 1: |
210 m_topMargin = m_rightMargin = m_bottomMargin = m_leftMargin = | 182 m_topMargin = m_rightMargin = m_bottomMargin = m_leftMargin = |
211 rootMargin[0]; | 183 rootMargin[0]; |
212 break; | 184 break; |
213 case 2: | 185 case 2: |
214 m_topMargin = m_bottomMargin = rootMargin[0]; | 186 m_topMargin = m_bottomMargin = rootMargin[0]; |
215 m_rightMargin = m_leftMargin = rootMargin[1]; | 187 m_rightMargin = m_leftMargin = rootMargin[1]; |
216 break; | 188 break; |
217 case 3: | 189 case 3: |
218 m_topMargin = rootMargin[0]; | 190 m_topMargin = rootMargin[0]; |
219 m_rightMargin = m_leftMargin = rootMargin[1]; | 191 m_rightMargin = m_leftMargin = rootMargin[1]; |
220 m_bottomMargin = rootMargin[2]; | 192 m_bottomMargin = rootMargin[2]; |
221 break; | 193 break; |
222 case 4: | 194 case 4: |
223 m_topMargin = rootMargin[0]; | 195 m_topMargin = rootMargin[0]; |
224 m_rightMargin = rootMargin[1]; | 196 m_rightMargin = rootMargin[1]; |
225 m_bottomMargin = rootMargin[2]; | 197 m_bottomMargin = rootMargin[2]; |
226 m_leftMargin = rootMargin[3]; | 198 m_leftMargin = rootMargin[3]; |
227 break; | 199 break; |
228 default: | 200 default: |
229 NOTREACHED(); | 201 NOTREACHED(); |
230 break; | 202 break; |
231 } | 203 } |
232 root.document().ensureIntersectionObserverController().addTrackedObserver( | 204 if (root) |
| 205 root->ensureIntersectionObserverData().addObserver(*this); |
| 206 trackingDocument().ensureIntersectionObserverController().addTrackedObserver( |
233 *this); | 207 *this); |
234 } | 208 } |
235 | 209 |
236 void IntersectionObserver::clearWeakMembers(Visitor* visitor) { | 210 void IntersectionObserver::clearWeakMembers(Visitor* visitor) { |
237 if (ThreadHeap::isHeapObjectAlive(m_root)) | 211 if (ThreadHeap::isHeapObjectAlive(root())) |
238 return; | 212 return; |
239 IgnorableExceptionState exceptionState; | 213 IgnorableExceptionState exceptionState; |
240 disconnect(exceptionState); | 214 disconnect(exceptionState); |
241 m_root = nullptr; | 215 m_root = nullptr; |
242 } | 216 } |
243 | 217 |
| 218 bool IntersectionObserver::rootIsValid() const { |
| 219 return rootIsImplicit() || root(); |
| 220 } |
| 221 |
| 222 Document& IntersectionObserver::trackingDocument() const { |
| 223 Document* document = nullptr; |
| 224 if (rootIsImplicit()) { |
| 225 DCHECK(m_callback->getExecutionContext()); |
| 226 document = toDocument(m_callback->getExecutionContext()); |
| 227 } else { |
| 228 DCHECK(root()); |
| 229 document = &root()->document(); |
| 230 } |
| 231 DCHECK(document); |
| 232 DCHECK(document->frame()); |
| 233 return *document->frame()->localFrameRoot()->document(); |
| 234 } |
| 235 |
| 236 LayoutObject* IntersectionObserver::rootLayoutObject() const { |
| 237 if (rootIsImplicit()) |
| 238 return trackingDocument().layoutView(); |
| 239 return root() ? root()->layoutObject() : nullptr; |
| 240 } |
| 241 |
244 void IntersectionObserver::observe(Element* target, | 242 void IntersectionObserver::observe(Element* target, |
245 ExceptionState& exceptionState) { | 243 ExceptionState& exceptionState) { |
246 if (!m_root) { | 244 if (!rootIsValid()) { |
247 exceptionState.throwDOMException( | 245 exceptionState.throwDOMException( |
248 InvalidStateError, | 246 InvalidStateError, |
249 "observe() called on an IntersectionObserver with an invalid root."); | 247 "observe() called on an IntersectionObserver with an invalid root."); |
250 return; | 248 return; |
251 } | 249 } |
252 | 250 |
253 if (!target || m_root.get() == target) | 251 if (!target || root() == target) |
| 252 return; |
| 253 |
| 254 LocalFrame* targetFrame = target->document().frame(); |
| 255 if (!targetFrame) |
254 return; | 256 return; |
255 | 257 |
256 if (target->ensureIntersectionObserverData().getObservationFor(*this)) | 258 if (target->ensureIntersectionObserverData().getObservationFor(*this)) |
257 return; | 259 return; |
| 260 |
| 261 bool isDOMDescendant = true; |
258 bool shouldReportRootBounds = false; | 262 bool shouldReportRootBounds = false; |
259 bool isDOMDescendant = false; | 263 if (rootIsImplicit()) { |
260 LocalFrame* targetFrame = target->document().frame(); | 264 Frame* rootFrame = targetFrame->tree().top(); |
261 LocalFrame* rootFrame = m_root->document().frame(); | 265 DCHECK(rootFrame); |
262 | 266 if (rootFrame == targetFrame) { |
263 if (target->document() == rootNode()->document()) { | 267 shouldReportRootBounds = true; |
| 268 } else { |
| 269 shouldReportRootBounds = |
| 270 targetFrame->securityContext()->getSecurityOrigin()->canAccess( |
| 271 rootFrame->securityContext()->getSecurityOrigin()); |
| 272 } |
| 273 } else { |
264 shouldReportRootBounds = true; | 274 shouldReportRootBounds = true; |
265 isDOMDescendant = rootNode()->isShadowIncludingInclusiveAncestorOf(target); | 275 isDOMDescendant = root()->isShadowIncludingInclusiveAncestorOf(target); |
266 } else if (targetFrame && rootFrame) { | |
267 shouldReportRootBounds = | |
268 targetFrame->securityContext()->getSecurityOrigin()->canAccess( | |
269 rootFrame->securityContext()->getSecurityOrigin()); | |
270 isDOMDescendant = (targetFrame->tree().top() == rootFrame); | |
271 } | 276 } |
272 | 277 |
273 IntersectionObservation* observation = | 278 IntersectionObservation* observation = |
274 new IntersectionObservation(*this, *target, shouldReportRootBounds); | 279 new IntersectionObservation(*this, *target, shouldReportRootBounds); |
275 target->ensureIntersectionObserverData().addObservation(*observation); | 280 target->ensureIntersectionObserverData().addObservation(*observation); |
276 m_observations.add(observation); | 281 m_observations.add(observation); |
277 | 282 |
278 if (!isDOMDescendant) { | 283 if (!isDOMDescendant) { |
279 m_root->document().addConsoleMessage( | 284 root()->document().addConsoleMessage( |
280 ConsoleMessage::create(JSMessageSource, WarningMessageLevel, | 285 ConsoleMessage::create(JSMessageSource, WarningMessageLevel, |
281 "IntersectionObserver.observe(target): target " | 286 "IntersectionObserver.observe(target): target " |
282 "element is not a descendant of root.")); | 287 "element is not a descendant of root.")); |
283 return; | |
284 } | 288 } |
285 | 289 |
286 if (m_initialState == InitialState::kAuto) { | 290 if (m_initialState == InitialState::kAuto) { |
287 for (auto& observation : m_observations) | 291 for (auto& observation : m_observations) |
288 observation->setLastThresholdIndex(std::numeric_limits<unsigned>::max()); | 292 observation->setLastThresholdIndex(std::numeric_limits<unsigned>::max()); |
289 } | 293 } |
290 | 294 |
291 if (!rootFrame) | 295 if (FrameView* frameView = targetFrame->view()) |
292 return; | 296 frameView->scheduleAnimation(); |
293 if (FrameView* rootFrameView = rootFrame->view()) | |
294 rootFrameView->scheduleAnimation(); | |
295 } | 297 } |
296 | 298 |
297 void IntersectionObserver::unobserve(Element* target, | 299 void IntersectionObserver::unobserve(Element* target, |
298 ExceptionState& exceptionState) { | 300 ExceptionState& exceptionState) { |
299 if (!m_root) { | 301 if (!rootIsValid()) { |
300 exceptionState.throwDOMException( | 302 exceptionState.throwDOMException( |
301 InvalidStateError, | 303 InvalidStateError, |
302 "unobserve() called on an IntersectionObserver with an invalid root."); | 304 "unobserve() called on an IntersectionObserver with an invalid root."); |
303 return; | 305 return; |
304 } | 306 } |
305 | 307 |
306 if (!target || !target->intersectionObserverData()) | 308 if (!target || !target->intersectionObserverData()) |
307 return; | 309 return; |
308 // TODO(szager): unobserve callback | 310 |
309 if (IntersectionObservation* observation = | 311 if (IntersectionObservation* observation = |
310 target->intersectionObserverData()->getObservationFor(*this)) | 312 target->intersectionObserverData()->getObservationFor(*this)) |
311 observation->disconnect(); | 313 observation->disconnect(); |
312 } | 314 } |
313 | 315 |
314 void IntersectionObserver::computeIntersectionObservations() { | 316 void IntersectionObserver::computeIntersectionObservations() { |
| 317 if (!rootIsValid()) |
| 318 return; |
315 Document* callbackDocument = toDocument(m_callback->getExecutionContext()); | 319 Document* callbackDocument = toDocument(m_callback->getExecutionContext()); |
316 if (!callbackDocument) | 320 if (!callbackDocument) |
317 return; | 321 return; |
318 LocalDOMWindow* callbackDOMWindow = callbackDocument->domWindow(); | 322 LocalDOMWindow* callbackDOMWindow = callbackDocument->domWindow(); |
319 if (!callbackDOMWindow) | 323 if (!callbackDOMWindow) |
320 return; | 324 return; |
321 DOMHighResTimeStamp timestamp = | 325 DOMHighResTimeStamp timestamp = |
322 DOMWindowPerformance::performance(*callbackDOMWindow)->now(); | 326 DOMWindowPerformance::performance(*callbackDOMWindow)->now(); |
323 for (auto& observation : m_observations) | 327 for (auto& observation : m_observations) |
324 observation->computeIntersectionObservations(timestamp); | 328 observation->computeIntersectionObservations(timestamp); |
325 } | 329 } |
326 | 330 |
327 void IntersectionObserver::disconnect(ExceptionState& exceptionState) { | 331 void IntersectionObserver::disconnect(ExceptionState& exceptionState) { |
328 if (!m_root) { | 332 if (!rootIsValid()) { |
329 exceptionState.throwDOMException( | 333 exceptionState.throwDOMException( |
330 InvalidStateError, | 334 InvalidStateError, |
331 "disconnect() called on an IntersectionObserver with an invalid root."); | 335 "disconnect() called on an IntersectionObserver with an invalid root."); |
332 return; | 336 return; |
333 } | 337 } |
334 | 338 |
335 for (auto& observation : m_observations) | 339 for (auto& observation : m_observations) |
336 observation->clearRootAndRemoveFromTarget(); | 340 observation->clearRootAndRemoveFromTarget(); |
337 m_observations.clear(); | 341 m_observations.clear(); |
338 } | 342 } |
339 | 343 |
340 void IntersectionObserver::removeObservation( | 344 void IntersectionObserver::removeObservation( |
341 IntersectionObservation& observation) { | 345 IntersectionObservation& observation) { |
342 m_observations.remove(&observation); | 346 m_observations.remove(&observation); |
343 } | 347 } |
344 | 348 |
345 void IntersectionObserver::setInitialState(InitialState initialState) { | 349 void IntersectionObserver::setInitialState(InitialState initialState) { |
346 DCHECK(m_observations.isEmpty()); | 350 DCHECK(m_observations.isEmpty()); |
347 m_initialState = initialState; | 351 m_initialState = initialState; |
348 } | 352 } |
349 | 353 |
350 HeapVector<Member<IntersectionObserverEntry>> IntersectionObserver::takeRecords( | 354 HeapVector<Member<IntersectionObserverEntry>> IntersectionObserver::takeRecords( |
351 ExceptionState& exceptionState) { | 355 ExceptionState& exceptionState) { |
352 HeapVector<Member<IntersectionObserverEntry>> entries; | 356 HeapVector<Member<IntersectionObserverEntry>> entries; |
353 | 357 |
354 if (!m_root) | 358 if (!rootIsValid()) { |
355 exceptionState.throwDOMException(InvalidStateError, | 359 exceptionState.throwDOMException(InvalidStateError, |
356 "takeRecords() called on an " | 360 "takeRecords() called on an " |
357 "IntersectionObserver with an invalid " | 361 "IntersectionObserver with an invalid " |
358 "root."); | 362 "root."); |
359 else | 363 } else { |
360 entries.swap(m_entries); | 364 entries.swap(m_entries); |
| 365 } |
361 | 366 |
362 return entries; | 367 return entries; |
363 } | 368 } |
364 | 369 |
365 Element* IntersectionObserver::root() const { | |
366 Node* node = rootNode(); | |
367 if (node && !node->isDocumentNode()) | |
368 return toElement(node); | |
369 return nullptr; | |
370 } | |
371 | |
372 static void appendLength(StringBuilder& stringBuilder, const Length& length) { | 370 static void appendLength(StringBuilder& stringBuilder, const Length& length) { |
373 stringBuilder.appendNumber(length.intValue()); | 371 stringBuilder.appendNumber(length.intValue()); |
374 if (length.type() == Percent) | 372 if (length.type() == Percent) |
375 stringBuilder.append('%'); | 373 stringBuilder.append('%'); |
376 else | 374 else |
377 stringBuilder.append("px", 2); | 375 stringBuilder.append("px", 2); |
378 } | 376 } |
379 | 377 |
380 String IntersectionObserver::rootMargin() const { | 378 String IntersectionObserver::rootMargin() const { |
381 StringBuilder stringBuilder; | 379 StringBuilder stringBuilder; |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
415 | 413 |
416 DEFINE_TRACE(IntersectionObserver) { | 414 DEFINE_TRACE(IntersectionObserver) { |
417 visitor->template registerWeakMembers< | 415 visitor->template registerWeakMembers< |
418 IntersectionObserver, &IntersectionObserver::clearWeakMembers>(this); | 416 IntersectionObserver, &IntersectionObserver::clearWeakMembers>(this); |
419 visitor->trace(m_callback); | 417 visitor->trace(m_callback); |
420 visitor->trace(m_observations); | 418 visitor->trace(m_observations); |
421 visitor->trace(m_entries); | 419 visitor->trace(m_entries); |
422 } | 420 } |
423 | 421 |
424 } // namespace blink | 422 } // namespace blink |
OLD | NEW |