| 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" |
| (...skipping 10 matching lines...) Expand all Loading... |
| 21 #include "core/html/HTMLFrameOwnerElement.h" | 21 #include "core/html/HTMLFrameOwnerElement.h" |
| 22 #include "core/inspector/ConsoleMessage.h" | 22 #include "core/inspector/ConsoleMessage.h" |
| 23 #include "core/layout/LayoutView.h" | 23 #include "core/layout/LayoutView.h" |
| 24 #include "core/timing/DOMWindowPerformance.h" | 24 #include "core/timing/DOMWindowPerformance.h" |
| 25 #include "core/timing/Performance.h" | 25 #include "core/timing/Performance.h" |
| 26 #include "platform/Timer.h" | 26 #include "platform/Timer.h" |
| 27 #include <algorithm> | 27 #include <algorithm> |
| 28 | 28 |
| 29 namespace blink { | 29 namespace blink { |
| 30 | 30 |
| 31 static void parseRootMargin(String rootMarginParameter, Vector<Length>& rootMarg
in, ExceptionState& exceptionState) | 31 namespace { |
| 32 |
| 33 // Internal implementation of IntersectionObserverCallback when using |
| 34 // IntersectionObserver with an EventCallback. |
| 35 class IntersectionObserverCallbackImpl final : public IntersectionObserverCallba
ck { |
| 36 WTF_MAKE_NONCOPYABLE(IntersectionObserverCallbackImpl); |
| 37 public: |
| 38 IntersectionObserverCallbackImpl(ExecutionContext* context, std::unique_ptr<
IntersectionObserver::EventCallback> callback) |
| 39 : m_context(context) |
| 40 , m_callback(std::move(callback)) |
| 41 {} |
| 42 |
| 43 void handleEvent(const HeapVector<Member<IntersectionObserverEntry>>& entrie
s, IntersectionObserver&) override |
| 44 { |
| 45 (*m_callback.get())(entries); |
| 46 } |
| 47 |
| 48 ExecutionContext* getExecutionContext() const override |
| 49 { |
| 50 return m_context; |
| 51 } |
| 52 |
| 53 DEFINE_INLINE_TRACE() |
| 54 { |
| 55 IntersectionObserverCallback::trace(visitor); |
| 56 visitor->trace(m_context); |
| 57 } |
| 58 |
| 59 private: |
| 60 WeakMember<ExecutionContext> m_context; |
| 61 std::unique_ptr<IntersectionObserver::EventCallback> m_callback; |
| 62 }; |
| 63 |
| 64 void parseRootMargin(String rootMarginParameter, Vector<Length>& rootMargin, Exc
eptionState& exceptionState) |
| 32 { | 65 { |
| 33 // TODO(szager): Make sure this exact syntax and behavior is spec-ed somewhe
re. | 66 // TODO(szager): Make sure this exact syntax and behavior is spec-ed somewhe
re. |
| 34 | 67 |
| 35 // The root margin argument accepts syntax similar to that for CSS margin: | 68 // The root margin argument accepts syntax similar to that for CSS margin: |
| 36 // | 69 // |
| 37 // "1px" = top/right/bottom/left | 70 // "1px" = top/right/bottom/left |
| 38 // "1px 2px" = top/bottom left/right | 71 // "1px 2px" = top/bottom left/right |
| 39 // "1px 2px 3px" = top left/right bottom | 72 // "1px 2px 3px" = top left/right bottom |
| 40 // "1px 2px 3px 4px" = top left right bottom | 73 // "1px 2px 3px 4px" = top left right bottom |
| 41 // | 74 // |
| (...skipping 17 matching lines...) Expand all Loading... |
| 59 default: | 92 default: |
| 60 exceptionState.throwDOMException(SyntaxError, "rootMargin must b
e specified in pixels or percent."); | 93 exceptionState.throwDOMException(SyntaxError, "rootMargin must b
e specified in pixels or percent."); |
| 61 } | 94 } |
| 62 break; | 95 break; |
| 63 default: | 96 default: |
| 64 exceptionState.throwDOMException(SyntaxError, "rootMargin must be sp
ecified in pixels or percent."); | 97 exceptionState.throwDOMException(SyntaxError, "rootMargin must be sp
ecified in pixels or percent."); |
| 65 } | 98 } |
| 66 } | 99 } |
| 67 } | 100 } |
| 68 | 101 |
| 69 static void parseThresholds(const DoubleOrDoubleArray& thresholdParameter, Vecto
r<float>& thresholds, ExceptionState& exceptionState) | 102 void parseThresholds(const DoubleOrDoubleArray& thresholdParameter, Vector<float
>& thresholds, ExceptionState& exceptionState) |
| 70 { | 103 { |
| 71 if (thresholdParameter.isDouble()) { | 104 if (thresholdParameter.isDouble()) { |
| 72 thresholds.append(static_cast<float>(thresholdParameter.getAsDouble())); | 105 thresholds.append(static_cast<float>(thresholdParameter.getAsDouble())); |
| 73 } else { | 106 } else { |
| 74 for (auto thresholdValue : thresholdParameter.getAsDoubleArray()) | 107 for (auto thresholdValue : thresholdParameter.getAsDoubleArray()) |
| 75 thresholds.append(static_cast<float>(thresholdValue)); | 108 thresholds.append(static_cast<float>(thresholdValue)); |
| 76 } | 109 } |
| 77 | 110 |
| 78 for (auto thresholdValue : thresholds) { | 111 for (auto thresholdValue : thresholds) { |
| 79 if (thresholdValue < 0.0 || thresholdValue > 1.0) { | 112 if (thresholdValue < 0.0 || thresholdValue > 1.0) { |
| 80 exceptionState.throwRangeError("Threshold values must be between 0 a
nd 1"); | 113 exceptionState.throwRangeError("Threshold values must be between 0 a
nd 1"); |
| 81 break; | 114 break; |
| 82 } | 115 } |
| 83 } | 116 } |
| 84 | 117 |
| 85 std::sort(thresholds.begin(), thresholds.end()); | 118 std::sort(thresholds.begin(), thresholds.end()); |
| 86 } | 119 } |
| 87 | 120 |
| 121 // Returns the root Node of a given Document to use as the IntersectionObserver |
| 122 // root when no root is given. |
| 123 // TODO(szager): it doesn't support RemoteFrames, see https://crbug.com/615156 |
| 124 Node* getRootNode(Document* document) |
| 125 { |
| 126 Frame* mainFrame = document->frame()->tree().top(); |
| 127 if (mainFrame && mainFrame->isLocalFrame()) |
| 128 return toLocalFrame(mainFrame)->document(); |
| 129 return nullptr; |
| 130 } |
| 131 |
| 132 } // anonymous namespace |
| 133 |
| 88 IntersectionObserver* IntersectionObserver::create(const IntersectionObserverIni
t& observerInit, IntersectionObserverCallback& callback, ExceptionState& excepti
onState) | 134 IntersectionObserver* IntersectionObserver::create(const IntersectionObserverIni
t& observerInit, IntersectionObserverCallback& callback, ExceptionState& excepti
onState) |
| 89 { | 135 { |
| 90 Node* root = observerInit.root(); | 136 Node* root = observerInit.root(); |
| 91 if (!root) { | 137 if (!root) { |
| 92 // TODO(szager): Use Document instead of document element for implicit r
oot. (crbug.com/570538) | |
| 93 ExecutionContext* context = callback.getExecutionContext(); | 138 ExecutionContext* context = callback.getExecutionContext(); |
| 94 DCHECK(context->isDocument()); | 139 DCHECK(context->isDocument()); |
| 95 Frame* mainFrame = toDocument(context)->frame()->tree().top(); | 140 root = getRootNode(toDocument(context)); |
| 96 if (mainFrame && mainFrame->isLocalFrame()) | |
| 97 root = toLocalFrame(mainFrame)->document(); | |
| 98 } | 141 } |
| 99 if (!root) { | 142 if (!root) { |
| 100 exceptionState.throwDOMException(HierarchyRequestError, "Unable to get r
oot node in main frame to track."); | 143 exceptionState.throwDOMException(HierarchyRequestError, "Unable to get r
oot node in main frame to track."); |
| 101 return nullptr; | 144 return nullptr; |
| 102 } | 145 } |
| 103 | 146 |
| 104 Vector<Length> rootMargin; | 147 Vector<Length> rootMargin; |
| 105 if (observerInit.hasRootMargin()) | 148 if (observerInit.hasRootMargin()) |
| 106 parseRootMargin(observerInit.rootMargin(), rootMargin, exceptionState); | 149 parseRootMargin(observerInit.rootMargin(), rootMargin, exceptionState); |
| 107 if (exceptionState.hadException()) | 150 if (exceptionState.hadException()) |
| 108 return nullptr; | 151 return nullptr; |
| 109 | 152 |
| 110 Vector<float> thresholds; | 153 Vector<float> thresholds; |
| 111 if (observerInit.hasThreshold()) | 154 if (observerInit.hasThreshold()) |
| 112 parseThresholds(observerInit.threshold(), thresholds, exceptionState); | 155 parseThresholds(observerInit.threshold(), thresholds, exceptionState); |
| 113 else | 156 else |
| 114 thresholds.append(0); | 157 thresholds.append(0); |
| 115 if (exceptionState.hadException()) | 158 if (exceptionState.hadException()) |
| 116 return nullptr; | 159 return nullptr; |
| 117 | 160 |
| 118 return new IntersectionObserver(callback, *root, rootMargin, thresholds); | 161 return new IntersectionObserver(callback, *root, rootMargin, thresholds); |
| 119 } | 162 } |
| 120 | 163 |
| 164 IntersectionObserver* IntersectionObserver::create(const Vector<Length>& rootMar
gin, const Vector<float>& thresholds, Document* document, std::unique_ptr<EventC
allback> callback) |
| 165 { |
| 166 Node* root = getRootNode(document); |
| 167 if (!root) |
| 168 return nullptr; |
| 169 |
| 170 IntersectionObserverCallbackImpl* intersectionObserverCallback = new Interse
ctionObserverCallbackImpl(document, std::move(callback)); |
| 171 return new IntersectionObserver(*intersectionObserverCallback, *root, rootMa
rgin, thresholds); |
| 172 } |
| 173 |
| 121 IntersectionObserver::IntersectionObserver(IntersectionObserverCallback& callbac
k, Node& root, const Vector<Length>& rootMargin, const Vector<float>& thresholds
) | 174 IntersectionObserver::IntersectionObserver(IntersectionObserverCallback& callbac
k, Node& root, const Vector<Length>& rootMargin, const Vector<float>& thresholds
) |
| 122 : m_callback(&callback) | 175 : m_callback(&callback) |
| 123 , m_root(&root) | 176 , m_root(&root) |
| 124 , m_thresholds(thresholds) | 177 , m_thresholds(thresholds) |
| 125 , m_topMargin(Fixed) | 178 , m_topMargin(Fixed) |
| 126 , m_rightMargin(Fixed) | 179 , m_rightMargin(Fixed) |
| 127 , m_bottomMargin(Fixed) | 180 , m_bottomMargin(Fixed) |
| 128 , m_leftMargin(Fixed) | 181 , m_leftMargin(Fixed) |
| 129 { | 182 { |
| 130 switch (rootMargin.size()) { | 183 switch (rootMargin.size()) { |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 177 if (!m_root) { | 230 if (!m_root) { |
| 178 exceptionState.throwDOMException(InvalidStateError, "observe() called on
an IntersectionObserver with an invalid root."); | 231 exceptionState.throwDOMException(InvalidStateError, "observe() called on
an IntersectionObserver with an invalid root."); |
| 179 return; | 232 return; |
| 180 } | 233 } |
| 181 | 234 |
| 182 if (!target || m_root.get() == target) | 235 if (!target || m_root.get() == target) |
| 183 return; | 236 return; |
| 184 | 237 |
| 185 if (target->ensureIntersectionObserverData().getObservationFor(*this)) | 238 if (target->ensureIntersectionObserverData().getObservationFor(*this)) |
| 186 return; | 239 return; |
| 187 | |
| 188 bool shouldReportRootBounds = false; | 240 bool shouldReportRootBounds = false; |
| 189 bool isDOMDescendant = false; | 241 bool isDOMDescendant = false; |
| 190 LocalFrame* targetFrame = target->document().frame(); | 242 LocalFrame* targetFrame = target->document().frame(); |
| 191 LocalFrame* rootFrame = m_root->document().frame(); | 243 LocalFrame* rootFrame = m_root->document().frame(); |
| 192 | 244 |
| 193 if (target->document() == rootNode()->document()) { | 245 if (target->document() == rootNode()->document()) { |
| 194 shouldReportRootBounds = true; | 246 shouldReportRootBounds = true; |
| 195 isDOMDescendant = target->isDescendantOf(rootNode()); | 247 isDOMDescendant = target->isDescendantOf(rootNode()); |
| 196 } else if (targetFrame && rootFrame) { | 248 } else if (targetFrame && rootFrame) { |
| 197 shouldReportRootBounds = targetFrame->securityContext()->getSecurityOrig
in()->canAccess(rootFrame->securityContext()->getSecurityOrigin()); | 249 shouldReportRootBounds = targetFrame->securityContext()->getSecurityOrig
in()->canAccess(rootFrame->securityContext()->getSecurityOrigin()); |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 333 unsigned IntersectionObserver::firstThresholdGreaterThan(float ratio) const | 385 unsigned IntersectionObserver::firstThresholdGreaterThan(float ratio) const |
| 334 { | 386 { |
| 335 unsigned result = 0; | 387 unsigned result = 0; |
| 336 while (result < m_thresholds.size() && m_thresholds[result] <= ratio) | 388 while (result < m_thresholds.size() && m_thresholds[result] <= ratio) |
| 337 ++result; | 389 ++result; |
| 338 return result; | 390 return result; |
| 339 } | 391 } |
| 340 | 392 |
| 341 void IntersectionObserver::deliver() | 393 void IntersectionObserver::deliver() |
| 342 { | 394 { |
| 343 | |
| 344 if (m_entries.isEmpty()) | 395 if (m_entries.isEmpty()) |
| 345 return; | 396 return; |
| 346 | 397 |
| 347 HeapVector<Member<IntersectionObserverEntry>> entries; | 398 HeapVector<Member<IntersectionObserverEntry>> entries; |
| 348 entries.swap(m_entries); | 399 entries.swap(m_entries); |
| 349 m_callback->handleEvent(entries, *this); | 400 m_callback->handleEvent(entries, *this); |
| 350 } | 401 } |
| 351 | 402 |
| 352 DEFINE_TRACE(IntersectionObserver) | 403 DEFINE_TRACE(IntersectionObserver) |
| 353 { | 404 { |
| 354 visitor->template registerWeakMembers<IntersectionObserver, &IntersectionObs
erver::clearWeakMembers>(this); | 405 visitor->template registerWeakMembers<IntersectionObserver, &IntersectionObs
erver::clearWeakMembers>(this); |
| 355 visitor->trace(m_callback); | 406 visitor->trace(m_callback); |
| 356 visitor->trace(m_observations); | 407 visitor->trace(m_observations); |
| 357 visitor->trace(m_entries); | 408 visitor->trace(m_entries); |
| 358 } | 409 } |
| 359 | 410 |
| 360 } // namespace blink | 411 } // namespace blink |
| OLD | NEW |