| 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 |
| 244 void IntersectionObserver::observe(Element* target, | 236 void IntersectionObserver::observe(Element* target, |
| 245 ExceptionState& exceptionState) { | 237 ExceptionState& exceptionState) { |
| 246 if (!m_root) { | 238 if (!rootIsValid()) { |
| 247 exceptionState.throwDOMException( | 239 exceptionState.throwDOMException( |
| 248 InvalidStateError, | 240 InvalidStateError, |
| 249 "observe() called on an IntersectionObserver with an invalid root."); | 241 "observe() called on an IntersectionObserver with an invalid root."); |
| 250 return; | 242 return; |
| 251 } | 243 } |
| 252 | 244 |
| 253 if (!target || m_root.get() == target) | 245 if (!target || root() == target) |
| 246 return; |
| 247 |
| 248 LocalFrame* targetFrame = target->document().frame(); |
| 249 if (!targetFrame) |
| 254 return; | 250 return; |
| 255 | 251 |
| 256 if (target->ensureIntersectionObserverData().getObservationFor(*this)) | 252 if (target->ensureIntersectionObserverData().getObservationFor(*this)) |
| 257 return; | 253 return; |
| 254 |
| 255 bool isDOMDescendant = true; |
| 258 bool shouldReportRootBounds = false; | 256 bool shouldReportRootBounds = false; |
| 259 bool isDOMDescendant = false; | 257 if (rootIsImplicit()) { |
| 260 LocalFrame* targetFrame = target->document().frame(); | 258 Frame* rootFrame = targetFrame->tree().top(); |
| 261 LocalFrame* rootFrame = m_root->document().frame(); | 259 DCHECK(rootFrame); |
| 262 | 260 if (rootFrame == targetFrame) { |
| 263 if (target->document() == rootNode()->document()) { | 261 shouldReportRootBounds = true; |
| 262 } else { |
| 263 shouldReportRootBounds = |
| 264 targetFrame->securityContext()->getSecurityOrigin()->canAccess( |
| 265 rootFrame->securityContext()->getSecurityOrigin()); |
| 266 } |
| 267 } else { |
| 264 shouldReportRootBounds = true; | 268 shouldReportRootBounds = true; |
| 265 isDOMDescendant = rootNode()->isShadowIncludingInclusiveAncestorOf(target); | 269 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 } | 270 } |
| 272 | 271 |
| 273 IntersectionObservation* observation = | 272 IntersectionObservation* observation = |
| 274 new IntersectionObservation(*this, *target, shouldReportRootBounds); | 273 new IntersectionObservation(*this, *target, shouldReportRootBounds); |
| 275 target->ensureIntersectionObserverData().addObservation(*observation); | 274 target->ensureIntersectionObserverData().addObservation(*observation); |
| 276 m_observations.add(observation); | 275 m_observations.add(observation); |
| 277 | 276 |
| 278 if (!isDOMDescendant) { | 277 if (!isDOMDescendant) { |
| 279 m_root->document().addConsoleMessage( | 278 root()->document().addConsoleMessage( |
| 280 ConsoleMessage::create(JSMessageSource, WarningMessageLevel, | 279 ConsoleMessage::create(JSMessageSource, WarningMessageLevel, |
| 281 "IntersectionObserver.observe(target): target " | 280 "IntersectionObserver.observe(target): target " |
| 282 "element is not a descendant of root.")); | 281 "element is not a descendant of root.")); |
| 283 return; | |
| 284 } | 282 } |
| 285 | 283 |
| 286 if (m_initialState == InitialState::kAuto) { | 284 if (m_initialState == InitialState::kAuto) { |
| 287 for (auto& observation : m_observations) | 285 for (auto& observation : m_observations) |
| 288 observation->setLastThresholdIndex(std::numeric_limits<unsigned>::max()); | 286 observation->setLastThresholdIndex(std::numeric_limits<unsigned>::max()); |
| 289 } | 287 } |
| 290 | 288 |
| 291 if (!rootFrame) | 289 if (FrameView* frameView = targetFrame->view()) |
| 292 return; | 290 frameView->scheduleAnimation(); |
| 293 if (FrameView* rootFrameView = rootFrame->view()) | |
| 294 rootFrameView->scheduleAnimation(); | |
| 295 } | 291 } |
| 296 | 292 |
| 297 void IntersectionObserver::unobserve(Element* target, | 293 void IntersectionObserver::unobserve(Element* target, |
| 298 ExceptionState& exceptionState) { | 294 ExceptionState& exceptionState) { |
| 299 if (!m_root) { | 295 if (!rootIsValid()) { |
| 300 exceptionState.throwDOMException( | 296 exceptionState.throwDOMException( |
| 301 InvalidStateError, | 297 InvalidStateError, |
| 302 "unobserve() called on an IntersectionObserver with an invalid root."); | 298 "unobserve() called on an IntersectionObserver with an invalid root."); |
| 303 return; | 299 return; |
| 304 } | 300 } |
| 305 | 301 |
| 306 if (!target || !target->intersectionObserverData()) | 302 if (!target || !target->intersectionObserverData()) |
| 307 return; | 303 return; |
| 308 // TODO(szager): unobserve callback | 304 |
| 309 if (IntersectionObservation* observation = | 305 if (IntersectionObservation* observation = |
| 310 target->intersectionObserverData()->getObservationFor(*this)) | 306 target->intersectionObserverData()->getObservationFor(*this)) |
| 311 observation->disconnect(); | 307 observation->disconnect(); |
| 312 } | 308 } |
| 313 | 309 |
| 314 void IntersectionObserver::computeIntersectionObservations() { | 310 void IntersectionObserver::computeIntersectionObservations() { |
| 311 if (!rootIsValid()) |
| 312 return; |
| 315 Document* callbackDocument = toDocument(m_callback->getExecutionContext()); | 313 Document* callbackDocument = toDocument(m_callback->getExecutionContext()); |
| 316 if (!callbackDocument) | 314 if (!callbackDocument) |
| 317 return; | 315 return; |
| 318 LocalDOMWindow* callbackDOMWindow = callbackDocument->domWindow(); | 316 LocalDOMWindow* callbackDOMWindow = callbackDocument->domWindow(); |
| 319 if (!callbackDOMWindow) | 317 if (!callbackDOMWindow) |
| 320 return; | 318 return; |
| 321 DOMHighResTimeStamp timestamp = | 319 DOMHighResTimeStamp timestamp = |
| 322 DOMWindowPerformance::performance(*callbackDOMWindow)->now(); | 320 DOMWindowPerformance::performance(*callbackDOMWindow)->now(); |
| 323 for (auto& observation : m_observations) | 321 for (auto& observation : m_observations) |
| 324 observation->computeIntersectionObservations(timestamp); | 322 observation->computeIntersectionObservations(timestamp); |
| 325 } | 323 } |
| 326 | 324 |
| 327 void IntersectionObserver::disconnect(ExceptionState& exceptionState) { | 325 void IntersectionObserver::disconnect(ExceptionState& exceptionState) { |
| 328 if (!m_root) { | 326 if (!rootIsValid()) { |
| 329 exceptionState.throwDOMException( | 327 exceptionState.throwDOMException( |
| 330 InvalidStateError, | 328 InvalidStateError, |
| 331 "disconnect() called on an IntersectionObserver with an invalid root."); | 329 "disconnect() called on an IntersectionObserver with an invalid root."); |
| 332 return; | 330 return; |
| 333 } | 331 } |
| 334 | 332 |
| 335 for (auto& observation : m_observations) | 333 for (auto& observation : m_observations) |
| 336 observation->clearRootAndRemoveFromTarget(); | 334 observation->clearRootAndRemoveFromTarget(); |
| 337 m_observations.clear(); | 335 m_observations.clear(); |
| 338 } | 336 } |
| 339 | 337 |
| 340 void IntersectionObserver::removeObservation( | 338 void IntersectionObserver::removeObservation( |
| 341 IntersectionObservation& observation) { | 339 IntersectionObservation& observation) { |
| 342 m_observations.remove(&observation); | 340 m_observations.remove(&observation); |
| 343 } | 341 } |
| 344 | 342 |
| 345 void IntersectionObserver::setInitialState(InitialState initialState) { | 343 void IntersectionObserver::setInitialState(InitialState initialState) { |
| 346 DCHECK(m_observations.isEmpty()); | 344 DCHECK(m_observations.isEmpty()); |
| 347 m_initialState = initialState; | 345 m_initialState = initialState; |
| 348 } | 346 } |
| 349 | 347 |
| 350 HeapVector<Member<IntersectionObserverEntry>> IntersectionObserver::takeRecords( | 348 HeapVector<Member<IntersectionObserverEntry>> IntersectionObserver::takeRecords( |
| 351 ExceptionState& exceptionState) { | 349 ExceptionState& exceptionState) { |
| 352 HeapVector<Member<IntersectionObserverEntry>> entries; | 350 HeapVector<Member<IntersectionObserverEntry>> entries; |
| 353 | 351 |
| 354 if (!m_root) | 352 if (!rootIsValid()) { |
| 355 exceptionState.throwDOMException(InvalidStateError, | 353 exceptionState.throwDOMException(InvalidStateError, |
| 356 "takeRecords() called on an " | 354 "takeRecords() called on an " |
| 357 "IntersectionObserver with an invalid " | 355 "IntersectionObserver with an invalid " |
| 358 "root."); | 356 "root."); |
| 359 else | 357 } else { |
| 360 entries.swap(m_entries); | 358 entries.swap(m_entries); |
| 359 } |
| 361 | 360 |
| 362 return entries; | 361 return entries; |
| 363 } | 362 } |
| 364 | 363 |
| 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) { | 364 static void appendLength(StringBuilder& stringBuilder, const Length& length) { |
| 373 stringBuilder.appendNumber(length.intValue()); | 365 stringBuilder.appendNumber(length.intValue()); |
| 374 if (length.type() == Percent) | 366 if (length.type() == Percent) |
| 375 stringBuilder.append('%'); | 367 stringBuilder.append('%'); |
| 376 else | 368 else |
| 377 stringBuilder.append("px", 2); | 369 stringBuilder.append("px", 2); |
| 378 } | 370 } |
| 379 | 371 |
| 380 String IntersectionObserver::rootMargin() const { | 372 String IntersectionObserver::rootMargin() const { |
| 381 StringBuilder stringBuilder; | 373 StringBuilder stringBuilder; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 415 | 407 |
| 416 DEFINE_TRACE(IntersectionObserver) { | 408 DEFINE_TRACE(IntersectionObserver) { |
| 417 visitor->template registerWeakMembers< | 409 visitor->template registerWeakMembers< |
| 418 IntersectionObserver, &IntersectionObserver::clearWeakMembers>(this); | 410 IntersectionObserver, &IntersectionObserver::clearWeakMembers>(this); |
| 419 visitor->trace(m_callback); | 411 visitor->trace(m_callback); |
| 420 visitor->trace(m_observations); | 412 visitor->trace(m_observations); |
| 421 visitor->trace(m_entries); | 413 visitor->trace(m_entries); |
| 422 } | 414 } |
| 423 | 415 |
| 424 } // namespace blink | 416 } // namespace blink |
| OLD | NEW |