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

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: oilpan-ify Created 5 years, 1 month 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/html/HTMLFrameOwnerElement.h"
17 #include "platform/Timer.h"
18 #include "wtf/MainThread.h"
19 #include <algorithm>
20
21 namespace blink {
22
23 IntersectionObserver* IntersectionObserver::create(Document* owningDocument, con st IntersectionObserverInit& observerInit, IntersectionObserverCallback* callbac k, ExceptionState& exceptionState)
24 {
25 ASSERT(isMainThread());
26
27 int rootMargin = 0;
28 if (observerInit.hasRootMargin()) {
29 CSSParserTokenRange tokenRange = CSSTokenizer::Scope(observerInit.rootMa rgin()).tokenRange();
30 const CSSParserToken& token = tokenRange.consumeIncludingWhitespace();
31 switch (token.type()) {
32 case EOFToken:
33 break;
34 case NumberToken:
35 rootMargin = static_cast<int>(floor(token.numericValue()));
36 break;
37 case DimensionToken:
38 switch (token.unitType()) {
39 case CSSPrimitiveValue::UnitType::Number:
40 case CSSPrimitiveValue::UnitType::Pixels:
41 rootMargin = static_cast<int>(token.numericValue());
42 break;
43 default:
44 exceptionState.throwTypeError("rootMargin must be specified in p ixels.");
45 break;
46 }
47 break;
48 default:
49 exceptionState.throwTypeError("rootMargin must be specified in pixel s.");
50 }
51 }
52 if (exceptionState.hadException())
53 return nullptr;
54
55 Vector<float> thresholds;
56 if (observerInit.hasThreshold()) {
57 const DoubleOrDoubleArray& thresholdParam = observerInit.threshold();
58 if (thresholdParam.isDouble()) {
59 thresholds.append(static_cast<float>(thresholdParam.getAsDouble()));
60 } else {
61 for (auto thresholdValue: thresholdParam.getAsDoubleArray())
62 thresholds.append(static_cast<float>(thresholdValue));
63 }
64 } else {
65 thresholds.append(0);
66 }
67 for (auto thresholdValue: thresholds) {
68 if (thresholdValue < 0.0 || thresholdValue > 1.0) {
69 exceptionState.throwTypeError("Threshold values must be between 0 an d 1");
70 break;
71 }
72 }
73 std::sort(thresholds.begin(), thresholds.end());
74 if (exceptionState.hadException())
75 return nullptr;
76
77 return new IntersectionObserver(owningDocument, callback, observerInit.root( ).get(), rootMargin, thresholds);
78 }
79
80 IntersectionObserver::IntersectionObserver(Document* owningDocument, Intersectio nObserverCallback* callback, PassRefPtrWillBeRawPtr<Element> root, int rootMargi n, const Vector<float>& thresholds)
81 : m_callback(callback)
82 , m_owningDocument(owningDocument->createWeakPtr())
83 , m_thresholds(thresholds)
84 , m_rootMargin(rootMargin)
85 , m_hasRoot(root)
86 {
87 if (root) {
88 // This is a phony observation, created just to ensure the root element keeps
89 // a reference to the observer. That reference will be used to disconne ct
90 // the observer when the root element is destroyed.
91 m_root = root->createWeakPtr();
92 m_trackingDocument = root->document().createWeakPtr();
93 createObservation(root.get());
94 }
95 }
96
97 bool IntersectionObserver::isDescendantOfRoot(Element* target)
98 {
99 Element* rootElement = root();
100 if (target == rootElement || !target->inDocument())
101 return false;
102 if (!hasRoot())
103 return true;
104 if (!rootElement || !rootElement->inDocument())
105 return false;
106 LocalFrame* rootFrame = rootElement->document().frame();
107 LocalFrame* targetFrame = target->document().frame();
108 if (!rootFrame || !targetFrame || !targetFrame->tree().isDescendantOf(rootFr ame))
109 return false;
110 while (targetFrame != rootFrame) {
111 FrameOwner* targetOwner = targetFrame->owner();
112 if (!targetOwner || !targetOwner->isLocal())
113 return false;
114 target = toHTMLFrameOwnerElement(targetOwner);
115 ASSERT(target);
116 targetFrame = target->document().frame();
117 if (!targetFrame)
118 return false;
119 }
120 return target->isDescendantOf(rootElement);
121 }
122
123 Document& IntersectionObserver::getTrackingDocumentForTarget(Element* target)
124 {
125 if (root())
126 return root()->document();
127 ASSERT(target);
128 Frame* frame = target->document().frame();
129 ASSERT(frame);
130 frame = frame->tree().top();
131 ASSERT(frame && frame->isLocalFrame() && toLocalFrame(frame)->document());
132 return *toLocalFrame(frame)->document();
133 }
134
135 void IntersectionObserver::observe(Element* target, ExceptionState& exceptionSta te)
136 {
137 if (!target)
138 exceptionState.throwTypeError("Observation target must be an element.");
139 else if ((m_hasRoot && !root()) || (m_observations->size() && !trackingDocum ent()))
140 exceptionState.throwDOMException(HierarchyRequestError, "Invalid observe r: root element or containing document has been deleted.");
141 else if (root() == target)
142 exceptionState.throwDOMException(HierarchyRequestError, "Cannot use the same element for root and target.");
143 if (exceptionState.hadException())
144 return;
145
146 if (IntersectionObservation::contains(*m_observations, this, target))
147 return;
148
149 // If there's no root and this is the first target, then we didn't know unti l
150 // now which document will track the observation and generate notifications.
151 if (!trackingDocument()) {
152 ASSERT(!m_root && !m_observations->size());
153 m_trackingDocument = getTrackingDocumentForTarget(target).createWeakPtr( );
154 } else if (m_trackingDocument.get() != &getTrackingDocumentForTarget(target) ) {
155 exceptionState.throwDOMException(HierarchyRequestError, "All target elem ents must be in the same frame tree.");
156 return;
157 }
158
159 createObservation(target);
160 }
161
162 void IntersectionObserver::unobserve(Element* target, ExceptionState&)
163 {
164 if (!target)
165 return;
166 IntersectionObservation::HashSet::iterator observationIterator = Intersectio nObservation::find(*m_observations, this, target);
167 if (observationIterator == m_observations->end())
168 return;
169 (*observationIterator)->disconnect();
170 }
171
172 void IntersectionObserver::disconnect(IntersectionObservation& observation)
173 {
174 // This will get called during Element destruction, so we must be careful
175 // to check the weak pointers (root, target, trackingDocument).
176 m_observations->remove(&observation);
177 if (observation.target())
178 observation.target()->removeIntersectionObservation(observation);
179 if (root())
180 root()->removeIntersectionObservation(observation);
181 // TODO: This will probably break things if there are unsent observations.
182 if (trackingDocument())
183 trackingDocument()->intersectionObservationRegistry()->removeObservation (observation);
184 }
185
186 void IntersectionObserver::disconnect()
187 {
188 HeapVector<Member<IntersectionObservation>> toDisconnect;
189 for (auto& observation: *m_observations)
190 toDisconnect.append(observation);
191 for (auto& observation: toDisconnect)
192 observation->disconnect();
193 ASSERT(m_observations->size());
194 }
195
196 IntersectionObserverEntryVector IntersectionObserver::takeRecords()
197 {
198 IntersectionObserverEntryVector entries;
199 entries.swap(*m_entries);
200 return entries;
201 }
202
203 void IntersectionObserver::enqueueIntersectionObserverEntry(IntersectionObserver Entry* entry)
204 {
205 ASSERT(isMainThread());
206 m_entries->append(entry);
207 m_trackingDocument->intersectionObservationRegistry()->activateIntersectionO bserver(*this);
208 }
209
210 size_t IntersectionObserver::firstThresholdGreaterThan(float ratio) const
211 {
212 size_t result = 0;
213 size_t max = m_thresholds.size();
214 while (result < max && m_thresholds[result] <= ratio)
215 ++result;
216 return result;
217 }
218
219 bool IntersectionObserver::shouldBeSuspended() const
220 {
221 return m_callback->executionContext() && m_callback->executionContext()->act iveDOMObjectsAreSuspended();
222 }
223
224 void IntersectionObserver::deliver()
225 {
226 ASSERT(!shouldBeSuspended());
227
228 if (m_entries->isEmpty())
229 return;
230
231 IntersectionObserverEntryVector entries;
232 entries.swap(*m_entries);
233 m_callback->handleEvent(entries, this);
234 }
235
236 IntersectionObservation* IntersectionObserver::createObservation(Element* target )
237 {
238 IntersectionObservation* observation = new IntersectionObservation(*this, ta rget->createWeakPtr(), false);
239 m_observations->add(observation);
240 m_trackingDocument->intersectionObservationRegistry()->addObservation(*obser vation);
241 target->addIntersectionObservation(*observation);
242 return observation;
243 }
244
245 DEFINE_TRACE(IntersectionObserver)
246 {
247 visitor->trace(m_callback);
248 visitor->trace(m_root);
249 visitor->trace(m_owningDocument);
250 visitor->trace(m_trackingDocument);
251 visitor->trace(m_observations);
252 visitor->trace(m_entries);
253 }
254
255 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698