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

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: Clarify the tear-down path for observers and observations. 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 PassRefPtrWillBeRawPtr<IntersectionObserver> IntersectionObserver::create(const IntersectionObserverInit& observerInit, PassOwnPtrWillBeRawPtr<IntersectionObser verCallback> callback, 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>(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());
eae 2015/11/16 23:34:44 Is flooring the number the desired result? If so c
szager1 2015/11/19 19:15:38 Done in next patch.
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 PassRefPtrWillBeRawPtr<IntersectionObserver>();
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 PassRefPtrWillBeRawPtr<IntersectionObserver>();
76
77 return adoptRefWillBeNoop(new IntersectionObserver(callback, observerInit.ro ot(), rootMargin, thresholds));
78 }
79
80 IntersectionObserver::IntersectionObserver(PassOwnPtrWillBeRawPtr<IntersectionOb serverCallback> callback, RefPtrWillBeRawPtr<Element> root, int rootMargin, cons t Vector<float>& thresholds)
81 : m_callback(callback)
82 , m_thresholds(thresholds)
83 , m_rootMargin(rootMargin)
84 , m_hasRoot(root)
85 {
86 if (root) {
87 // This is a phony observation, created just to ensure the root element keeps
88 // a reference to the observer. That reference will be used to disconne ct
89 // the observer when the root element is destroyed.
90 m_root = root->createWeakPtr();
91 m_trackingDocument = root->document().createWeakPtr();
92 createObservation(root.get());
93 }
94 }
95
96 IntersectionObserver::~IntersectionObserver()
97 {
98 ASSERT(!m_observations.size());
99 ASSERT(!m_entries.size());
100 }
101
102 bool IntersectionObserver::checkTargetHierarchy(Element* target)
eae 2015/11/16 23:34:45 hasSameRoot or sharesAncestor might be better name
szager1 2015/11/19 19:15:38 Maybe isValidTarget? Or isDescendantOfRoot? I'll
103 {
104 if (!target->inDocument())
105 return false;
106 if (!hasRoot())
107 return true;
108 Element* rootElement = root();
109 if (!rootElement || !rootElement->inDocument())
110 return false;
111 LocalFrame* rootFrame = rootElement->document().frame();
112 LocalFrame* targetFrame = target->document().frame();
113 if (!rootFrame || !targetFrame || !targetFrame->tree().isDescendantOf(rootFr ame))
114 return false;
115 while (targetFrame != rootFrame) {
116 FrameOwner* targetOwner = targetFrame->owner();
117 if (!targetOwner || !targetOwner->isLocal())
118 return false;
119 target = toHTMLFrameOwnerElement(targetOwner);
120 ASSERT(target);
121 targetFrame = target->document().frame();
122 if (!targetFrame)
123 return false;
124 }
125 return target->isDescendantOf(rootElement);
126 }
127
128 Document& IntersectionObserver::getTrackingDocumentForTarget(Element* target)
129 {
130 if (root())
131 return root()->document();
132 ASSERT(target);
133 Frame* frame = target->document().frame();
134 ASSERT(frame);
135 frame = frame->tree().top();
136 ASSERT(frame && frame->isLocalFrame() && toLocalFrame(frame)->document());
137 return *toLocalFrame(frame)->document();
138 }
139
140 void IntersectionObserver::observe(Element* target, ExceptionState& exceptionSta te)
141 {
142 if (!target)
143 exceptionState.throwTypeError("Observation target must be an element.");
144 else if ((m_hasRoot && !root()) || (m_observations.size() && !trackingDocume nt()))
145 exceptionState.throwDOMException(HierarchyRequestError, "Invalid observe r: root element or containing document has been deleted.");
146 else if (root() == target)
147 exceptionState.throwDOMException(HierarchyRequestError, "Cannot use the same element for root and target.");
148 if (exceptionState.hadException())
149 return;
150
151 if (IntersectionObservation::contains(m_observations, this, target))
152 return;
153
154 // If there's no root and this is the first target, then we didn't know unti l
155 // now which document will track the observation and generate notifications.
156 if (!trackingDocument()) {
157 ASSERT(!m_root && !m_observations.size());
158 m_trackingDocument = getTrackingDocumentForTarget(target).createWeakPtr( );
159 } else if (m_trackingDocument.get() != &getTrackingDocumentForTarget(target) ) {
160 exceptionState.throwDOMException(HierarchyRequestError, "All target elem ents must be in the same frame tree.");
161 return;
162 }
163
164 createObservation(target);
165 }
166
167 void IntersectionObserver::unobserve(Element* target, ExceptionState&)
168 {
169 if (!target)
170 return;
171 IntersectionObservation::HashSet::iterator observationIterator = Intersectio nObservation::find(m_observations, this, target);
172 if (observationIterator == m_observations.end())
173 return;
174 (*observationIterator)->disconnect();
175 }
176
177 void IntersectionObserver::disconnect(IntersectionObservation& observation)
178 {
179 // This will get called during Element destruction, so we must be careful
180 // to check the weak pointers (root, target, trackingDocument).
181 m_observations.remove(adoptRef(&observation));
182 if (observation.target())
183 observation.target()->removeIntersectionObservation(observation);
184 if (root())
185 root()->removeIntersectionObservation(observation);
186 // TODO: This will probably break things if there are unsent observations.
187 if (trackingDocument())
188 trackingDocument()->intersectionObservationRegistry()->removeObservation (observation);
189 }
190
191 void IntersectionObserver::disconnect()
192 {
193 WillBeHeapVector<RefPtr<IntersectionObservation>> toDisconnect;
194 for (auto& observation: m_observations)
195 toDisconnect.append(observation);
196 for (auto& observation: toDisconnect)
197 observation->disconnect();
198 ASSERT(!m_observations.size());
199 }
200
201 IntersectionObserverEntryVector IntersectionObserver::takeRecords()
202 {
203 IntersectionObserverEntryVector entries;
204 entries.swap(m_entries);
205 return entries;
206 }
207
208 void IntersectionObserver::enqueueIntersectionObserverEntry(PassRefPtrWillBeRawP tr<IntersectionObserverEntry> entry)
209 {
210 ASSERT(isMainThread());
211 m_entries.append(entry);
212 m_trackingDocument->intersectionObservationRegistry()->activateIntersectionO bserver(*this);
213 }
214
215 size_t IntersectionObserver::firstThresholdGreaterThan(float ratio) const
216 {
217 size_t result = 0;
218 size_t max = m_thresholds.size();
219 while (result < max && m_thresholds[result] <= ratio)
220 ++result;
221 return result;
222 }
223
224 bool IntersectionObserver::shouldBeSuspended() const
225 {
226 return m_callback->executionContext() && m_callback->executionContext()->act iveDOMObjectsAreSuspended();
227 }
228
229 void IntersectionObserver::deliver()
230 {
231 ASSERT(!shouldBeSuspended());
232
233 if (m_entries.isEmpty())
234 return;
235
236 IntersectionObserverEntryVector entries;
237 entries.swap(m_entries);
238 m_callback->handleEvent(entries, this);
239 }
240
241 PassRefPtr<IntersectionObservation> IntersectionObserver::createObservation(Elem ent* target)
242 {
243 RefPtrWillBeRawPtr<IntersectionObservation> observation = IntersectionObserv ation::create(*this, target->createWeakPtr(), false);
244 m_observations.add(observation);
245 m_trackingDocument->intersectionObservationRegistry()->addObservation(*obser vation);
246 target->addIntersectionObservation(*observation);
247 return observation;
248 }
249
250 DEFINE_TRACE(IntersectionObserver)
251 {
252 visitor->trace(m_callback);
253 visitor->trace(m_root);
254 // TODO: Uncommenting the next line causes a compilation failure. Fix it.
255 // visitor->trace(m_observations);
haraken 2015/11/16 00:41:34 You need to use: #if ENABLE(OILPAN) visitor->tr
szager1 2015/11/19 19:15:38 Working on it.
256 visitor->trace(m_entries);
257 }
258
259 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698