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

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: Fix rootMargin parsing 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 CSSTokenizer::Scope tokenizerScope(observerInit.rootMargin());
46 CSSParserTokenRange tokenRange = tokenizerScope.tokenRange();
47 const CSSParserToken& token = tokenRange.consumeIncludingWhitespace();
48 switch (token.type()) {
49 case EOFToken:
50 break;
51 case NumberToken:
52 rootMargin = static_cast<int>(floor(token.numericValue()));
53 break;
54 case DimensionToken:
55 switch (token.unitType()) {
56 case CSSPrimitiveValue::UnitType::Number:
57 case CSSPrimitiveValue::UnitType::Pixels:
58 rootMargin = static_cast<int>(token.numericValue());
59 break;
60 default:
61 exceptionState.throwTypeError("rootMargin must be specified in p ixels.");
62 break;
63 }
64 break;
65 default:
66 exceptionState.throwTypeError("rootMargin must be specified in pixel s.");
67 }
68 }
69 if (exceptionState.hadException())
70 return nullptr;
71
72 Vector<float> thresholds;
73 if (observerInit.hasThreshold()) {
74 const DoubleOrDoubleArray& thresholdParam = observerInit.threshold();
75 if (thresholdParam.isDouble()) {
76 thresholds.append(static_cast<float>(thresholdParam.getAsDouble()));
77 } else {
78 for (auto thresholdValue: thresholdParam.getAsDoubleArray())
79 thresholds.append(static_cast<float>(thresholdValue));
80 }
81 } else {
82 thresholds.append(0);
83 }
84 for (auto thresholdValue: thresholds) {
85 if (thresholdValue < 0.0 || thresholdValue > 1.0) {
86 exceptionState.throwTypeError("Threshold values must be between 0 an d 1");
87 break;
88 }
89 }
90 std::sort(thresholds.begin(), thresholds.end());
91 if (exceptionState.hadException())
92 return nullptr;
93
94 return new IntersectionObserver(callback, root.get(), rootMargin, thresholds );
95 }
96
97 IntersectionObserver::IntersectionObserver(IntersectionObserverCallback* callbac k, Element* root, int rootMargin, const Vector<float>& thresholds)
98 : m_callback(callback)
99 , m_root(root->createWeakPtr())
100 , m_observations(new IntersectionObservation::WeakHashSet())
101 , m_entries(new IntersectionObserverEntryVector())
102 , m_thresholds(thresholds)
103 , m_rootMargin(rootMargin)
104 {
105 root->document().intersectionObserverRegistry()->addTrackedObserver(*this);
106 }
107
108 bool IntersectionObserver::isDescendantOfRoot(Element* target)
109 {
110 checkRootAndDetachIfNecessary();
111 Element* rootElement = m_root.get();
112 if (!rootElement)
113 return false;
114
115 if (target == rootElement || !target->inDocument() || !rootElement->inDocume nt())
116 return false;
117 LocalFrame* rootFrame = rootElement->document().frame();
118 LocalFrame* targetFrame = target->document().frame();
119 if (!rootFrame || !targetFrame || !targetFrame->tree().isDescendantOf(rootFr ame))
120 return false;
121 while (targetFrame != rootFrame) {
122 FrameOwner* targetOwner = targetFrame->owner();
123 if (!targetOwner || !targetOwner->isLocal())
124 return false;
125 target = toHTMLFrameOwnerElement(targetOwner);
126 ASSERT(target);
127 targetFrame = target->document().frame();
128 if (!targetFrame)
129 return false;
130 }
131 return target->isDescendantOf(rootElement);
132 }
133
134 void IntersectionObserver::observe(Element* target, ExceptionState& exceptionSta te)
135 {
136 checkRootAndDetachIfNecessary();
dcheng 2015/12/10 00:37:09 Similarly, I don't think weak pointers is making t
szager1 2015/12/10 22:38:55 The correct place to do this cleanup is when the r
137 if (!m_root)
138 exceptionState.throwDOMException(HierarchyRequestError, "Invalid observe r: root element or containing document has been deleted.");
139 else if (!target)
140 exceptionState.throwTypeError("Observation target must be an element.");
141 else if (m_root.get() == target)
142 exceptionState.throwDOMException(HierarchyRequestError, "Cannot use the same element for root and target.");
143 else if (!isDescendantOfRoot(target))
144 exceptionState.throwDOMException(HierarchyRequestError, "Observed elemen t must be a descendant of the observer's root element.");
145 if (exceptionState.hadException())
146 return;
147
148 if (IntersectionObservation::contains(*m_observations, this, target))
149 return;
150
151 createObservation(target);
152 }
153
154 void IntersectionObserver::unobserve(Element* target, ExceptionState&)
155 {
156 checkRootAndDetachIfNecessary();
157 if (!target)
158 return;
159 IntersectionObservation::WeakHashSet::iterator observationIterator = Interse ctionObservation::find(*m_observations, this, target);
160 // TODO: unobserve callback
161 if (observationIterator != m_observations->end())
162 (*observationIterator)->disconnect();
163 }
164
165 void IntersectionObserver::computeIntersectionObservations(int timestamp)
166 {
167 checkRootAndDetachIfNecessary();
168 if (!m_root)
169 return;
170 for (auto& observation: *m_observations)
171 observation->computeIntersectionObservations(timestamp);
172 }
173
174 void IntersectionObserver::disconnect(IntersectionObservation& observation)
175 {
176 m_observations->remove(&observation);
177 }
178
179 void IntersectionObserver::disconnect()
180 {
181 checkRootAndDetachIfNecessary();
182 HeapVector<Member<IntersectionObservation>> toDisconnect;
183 for (auto& observation: *m_observations)
184 toDisconnect.append(observation);
185 for (auto& observation: toDisconnect)
186 observation->disconnect();
187 ASSERT(!m_observations->size());
188 }
189
190 IntersectionObserverEntryVector IntersectionObserver::takeRecords()
191 {
192 checkRootAndDetachIfNecessary();
193 IntersectionObserverEntryVector entries;
194 entries.swap(*m_entries);
195 return entries;
196 }
197
198 void IntersectionObserver::enqueueIntersectionObserverEntry(IntersectionObserver Entry* entry)
199 {
200 ASSERT(isMainThread());
201 m_entries->append(entry);
202 static_cast<Document*>(m_callback->executionContext())->intersectionObserver Registry()->scheduleIntersectionObserverForDelivery(*this);
203 }
204
205 size_t IntersectionObserver::firstThresholdGreaterThan(float ratio) const
206 {
207 size_t result = 0;
208 size_t max = m_thresholds.size();
209 while (result < max && m_thresholds[result] < ratio)
210 ++result;
211 return result;
212 }
213
214 bool IntersectionObserver::shouldBeSuspended() const
215 {
216 return m_callback->executionContext() && m_callback->executionContext()->act iveDOMObjectsAreSuspended();
217 }
218
219 void IntersectionObserver::deliver()
220 {
221 checkRootAndDetachIfNecessary();
222
223 ASSERT(!shouldBeSuspended());
224
225 if (m_entries->isEmpty())
226 return;
227
228 IntersectionObserverEntryVector entries;
229 entries.swap(*m_entries);
230 m_callback->handleEvent(entries, this);
231 }
232
233 void IntersectionObserver::setActive(bool active)
234 {
235 checkRootAndDetachIfNecessary();
236 for (auto& observation: *m_observations)
237 observation->setActive(m_root && active && isDescendantOfRoot(observatio n->target()));
238 }
239
240 IntersectionObservation* IntersectionObserver::createObservation(Element* target )
241 {
242 IntersectionObservation* observation = new IntersectionObservation(*this, ta rget);
243 m_observations->add(observation);
244 return observation;
245 }
246
247 void IntersectionObserver::checkRootAndDetachIfNecessary()
248 {
249 if (m_root)
250 return;
251 m_callback.clear();
252 HeapVector<Member<IntersectionObservation>> toDisconnect;
253 for (auto& observation: *m_observations)
254 toDisconnect.append(observation);
255 for (auto& observation: toDisconnect)
256 observation->disconnect();
257 ASSERT(!m_observations->size());
258 // TODO: should we deliver pending notifications?
259 m_entries.clear();
260 }
261
262 DEFINE_TRACE(IntersectionObserver)
263 {
264 visitor->trace(m_callback);
265 visitor->trace(m_root);
266 visitor->trace(m_observations);
267 visitor->trace(m_entries);
268 }
269
270 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698