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

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

Issue 1449623002: IntersectionObserver: second cut. (Closed) Base URL: https://chromium.googlesource.com/chromium/src@master
Patch Set: Refactor clipping code Created 5 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
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "config.h"
6 #include "core/dom/IntersectionObserver.h"
7
8 #include "bindings/core/v8/ExceptionState.h"
9 #include "core/css/parser/CSSParserTokenRange.h"
10 #include "core/css/parser/CSSTokenizer.h"
11 #include "core/dom/ExceptionCode.h"
12 #include "core/dom/ExecutionContext.h"
13 #include "core/dom/IntersectionObserverCallback.h"
14 #include "core/dom/IntersectionObserverEntry.h"
15 #include "core/dom/IntersectionObserverInit.h"
16 #include "core/dom/IntersectionObserverRegistry.h"
17 #include "core/html/HTMLFrameOwnerElement.h"
18 #include "platform/Timer.h"
19 #include "wtf/MainThread.h"
20 #include <algorithm>
21
22 namespace blink {
23
24 IntersectionObserver* IntersectionObserver::create(const IntersectionObserverIni t& observerInit, IntersectionObserverCallback* callback, ExceptionState& excepti onState)
25 {
26 ASSERT(isMainThread());
27
28 RefPtrWillBeRawPtr<Element> root = observerInit.root();
29 if (!root) {
30 ASSERT(callback);
31 ExecutionContext* context = callback->executionContext();
32 if (context->isDocument()) {
33 Frame* mainFrame = static_cast<Document*>(context)->frame()->tree(). top();
34 if (mainFrame && mainFrame->isLocalFrame())
35 root = toLocalFrame(mainFrame)->document()->documentElement();
36 }
37 }
38 if (!root) {
39 exceptionState.throwDOMException(HierarchyRequestError, "Unable to get r oot element in main frame to track.");
40 return nullptr;
41 }
42
43 int rootMargin = 0;
44 if (observerInit.hasRootMargin()) {
45 CSSParserTokenRange tokenRange = CSSTokenizer::Scope(observerInit.rootMa rgin()).tokenRange();
46 const CSSParserToken& token = tokenRange.consumeIncludingWhitespace();
47 switch (token.type()) {
48 case EOFToken:
49 break;
50 case NumberToken:
51 rootMargin = static_cast<int>(floor(token.numericValue()));
52 break;
53 case DimensionToken:
54 switch (token.unitType()) {
55 case CSSPrimitiveValue::UnitType::Number:
56 case CSSPrimitiveValue::UnitType::Pixels:
57 rootMargin = static_cast<int>(token.numericValue());
58 break;
59 default:
60 exceptionState.throwTypeError("rootMargin must be specified in p ixels.");
61 break;
62 }
63 break;
64 default:
65 exceptionState.throwTypeError("rootMargin must be specified in pixel s.");
66 }
67 }
68 if (exceptionState.hadException())
69 return nullptr;
70
71 Vector<float> thresholds;
72 if (observerInit.hasThreshold()) {
73 const DoubleOrDoubleArray& thresholdParam = observerInit.threshold();
74 if (thresholdParam.isDouble()) {
75 thresholds.append(static_cast<float>(thresholdParam.getAsDouble()));
76 } else {
77 for (auto thresholdValue: thresholdParam.getAsDoubleArray())
78 thresholds.append(static_cast<float>(thresholdValue));
79 }
80 } else {
81 thresholds.append(0);
82 }
83 for (auto thresholdValue: thresholds) {
84 if (thresholdValue < 0.0 || thresholdValue > 1.0) {
85 exceptionState.throwTypeError("Threshold values must be between 0 an d 1");
86 break;
87 }
88 }
89 std::sort(thresholds.begin(), thresholds.end());
90 if (exceptionState.hadException())
91 return nullptr;
92
93 return new IntersectionObserver(callback, root.get(), rootMargin, thresholds );
94 }
95
96 IntersectionObserver::IntersectionObserver(IntersectionObserverCallback* callbac k, Element* root, int rootMargin, const Vector<float>& thresholds)
97 : m_callback(callback)
98 , m_root(root->createWeakPtr())
99 , m_observations(new IntersectionObservation::WeakHashSet())
100 , m_entries(new IntersectionObserverEntryVector())
101 , m_thresholds(thresholds)
102 , m_rootMargin(rootMargin)
103 {
104 root->document().intersectionObserverRegistry()->addTrackedObserver(*this);
105 }
106
107 bool IntersectionObserver::isDescendantOfRoot(Element* target)
108 {
109 checkRootAndDetachIfNecessary();
110 Element* rootElement = m_root.get();
111 if (!rootElement)
112 return false;
113
114 if (target == rootElement || !target->inDocument() || !rootElement->inDocume nt())
115 return false;
116 LocalFrame* rootFrame = rootElement->document().frame();
117 LocalFrame* targetFrame = target->document().frame();
118 if (!rootFrame || !targetFrame || !targetFrame->tree().isDescendantOf(rootFr ame))
119 return false;
120 while (targetFrame != rootFrame) {
121 FrameOwner* targetOwner = targetFrame->owner();
122 if (!targetOwner || !targetOwner->isLocal())
123 return false;
124 target = toHTMLFrameOwnerElement(targetOwner);
125 ASSERT(target);
126 targetFrame = target->document().frame();
127 if (!targetFrame)
128 return false;
129 }
130 return target->isDescendantOf(rootElement);
131 }
132
133 void IntersectionObserver::observe(Element* target, ExceptionState& exceptionSta te)
134 {
135 checkRootAndDetachIfNecessary();
136 if (!m_root)
137 exceptionState.throwDOMException(HierarchyRequestError, "Invalid observe r: root element or containing document has been deleted.");
138 else if (!target)
139 exceptionState.throwTypeError("Observation target must be an element.");
140 else if (m_root.get() == target)
141 exceptionState.throwDOMException(HierarchyRequestError, "Cannot use the same element for root and target.");
142 if (exceptionState.hadException())
143 return;
144
145 if (IntersectionObservation::contains(*m_observations, this, target))
146 return;
147
148 createObservation(target);
149 }
150
151 void IntersectionObserver::unobserve(Element* target, ExceptionState&)
152 {
153 checkRootAndDetachIfNecessary();
154 if (!target)
155 return;
156 IntersectionObservation::WeakHashSet::iterator observationIterator = Interse ctionObservation::find(*m_observations, this, target);
157 // TODO: unobserve callback
158 if (observationIterator != m_observations->end())
159 (*observationIterator)->disconnect();
160 }
161
162 void IntersectionObserver::computeIntersectionObservations(int timestamp)
163 {
164 checkRootAndDetachIfNecessary();
165 if (!m_root)
166 return;
167 for (auto& observation: *m_observations)
168 observation->computeIntersectionObservations(timestamp);
169 }
170
171 void IntersectionObserver::disconnect(IntersectionObservation& observation)
172 {
173 m_observations->remove(&observation);
174 }
175
176 void IntersectionObserver::disconnect()
177 {
178 checkRootAndDetachIfNecessary();
179 HeapVector<Member<IntersectionObservation>> toDisconnect;
180 for (auto& observation: *m_observations)
181 toDisconnect.append(observation);
182 for (auto& observation: toDisconnect)
183 observation->disconnect();
184 ASSERT(!m_observations->size());
185 }
186
187 IntersectionObserverEntryVector IntersectionObserver::takeRecords()
188 {
189 checkRootAndDetachIfNecessary();
190 IntersectionObserverEntryVector entries;
191 entries.swap(*m_entries);
192 return entries;
193 }
194
195 void IntersectionObserver::enqueueIntersectionObserverEntry(IntersectionObserver Entry* entry)
196 {
197 ASSERT(isMainThread());
198 m_entries->append(entry);
199 static_cast<Document*>(m_callback->executionContext())->intersectionObserver Registry()->scheduleIntersectionObserverForDelivery(*this);
200 }
201
202 size_t IntersectionObserver::firstThresholdGreaterThan(float ratio) const
203 {
204 size_t result = 0;
205 size_t max = m_thresholds.size();
206 while (result < max && m_thresholds[result] < ratio)
207 ++result;
208 return result;
209 }
210
211 bool IntersectionObserver::shouldBeSuspended() const
212 {
213 return m_callback->executionContext() && m_callback->executionContext()->act iveDOMObjectsAreSuspended();
214 }
215
216 void IntersectionObserver::deliver()
217 {
218 checkRootAndDetachIfNecessary();
219
220 ASSERT(!shouldBeSuspended());
221
222 if (m_entries->isEmpty())
223 return;
224
225 IntersectionObserverEntryVector entries;
226 entries.swap(*m_entries);
227 m_callback->handleEvent(entries, this);
228 }
229
230 void IntersectionObserver::setActive(bool active)
231 {
232 checkRootAndDetachIfNecessary();
233 for (auto& observation: *m_observations)
234 observation->setActive(m_root && active && isDescendantOfRoot(observatio n->target()));
235 }
236
237 IntersectionObservation* IntersectionObserver::createObservation(Element* target )
238 {
239 IntersectionObservation* observation = new IntersectionObservation(*this, ta rget);
240 m_observations->add(observation);
241 return observation;
242 }
243
244 void IntersectionObserver::checkRootAndDetachIfNecessary()
245 {
246 if (m_root)
247 return;
248 m_callback.clear();
249 HeapVector<Member<IntersectionObservation>> toDisconnect;
250 for (auto& observation: *m_observations)
251 toDisconnect.append(observation);
252 for (auto& observation: toDisconnect)
253 observation->disconnect();
254 ASSERT(!m_observations->size());
255 // TODO: should we deliver pending notifications?
256 m_entries.clear();
257 }
258
259 DEFINE_TRACE(IntersectionObserver)
260 {
261 visitor->trace(m_callback);
262 visitor->trace(m_root);
263 visitor->trace(m_observations);
264 visitor->trace(m_entries);
265 }
266
267 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698