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

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: clean up teardown logic Created 4 years, 11 months 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 2016 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 "core/dom/IntersectionObserver.h"
6
7 #include "bindings/core/v8/ExceptionState.h"
8 #include "core/css/parser/CSSParserTokenRange.h"
9 #include "core/css/parser/CSSTokenizer.h"
10 #include "core/dom/ElementIntersectionObserverData.h"
11 #include "core/dom/ExceptionCode.h"
12 #include "core/dom/ExecutionContext.h"
13 #include "core/dom/IntersectionObserverCallback.h"
14 #include "core/dom/IntersectionObserverController.h"
15 #include "core/dom/IntersectionObserverEntry.h"
16 #include "core/dom/IntersectionObserverInit.h"
17 #include "core/html/HTMLFrameOwnerElement.h"
18 #include "core/layout/LayoutView.h"
19 #include "platform/Timer.h"
20 #include "wtf/MainThread.h"
21 #include <algorithm>
22
23 namespace blink {
24
25 static void parseThresholds(const DoubleOrDoubleArray& thresholdParameter, Vecto r<float>& thresholds, ExceptionState& exceptionState)
26 {
27 if (thresholdParameter.isDouble()) {
28 thresholds.append(static_cast<float>(thresholdParameter.getAsDouble()));
29 } else {
30 for (auto thresholdValue : thresholdParameter.getAsDoubleArray())
31 thresholds.append(static_cast<float>(thresholdValue));
32 }
33
34 for (auto thresholdValue : thresholds) {
35 if (thresholdValue < 0.0 || thresholdValue > 1.0) {
36 exceptionState.throwTypeError("Threshold values must be between 0 an d 1");
37 break;
38 }
39 }
40
41 std::sort(thresholds.begin(), thresholds.end());
42 }
43
44 IntersectionObserver* IntersectionObserver::create(const IntersectionObserverIni t& observerInit, IntersectionObserverCallback& callback, ExceptionState& excepti onState)
45 {
46 RefPtrWillBeRawPtr<Element> root = observerInit.root();
47 if (!root) {
48 // TODO(szager): Use Document instead of document element for implicit r oot. (crbug.com/570538)
49 ExecutionContext* context = callback.executionContext();
50 ASSERT(context->isDocument());
51 Frame* mainFrame = toDocument(context)->frame()->tree().top();
52 if (mainFrame && mainFrame->isLocalFrame())
53 root = toLocalFrame(mainFrame)->document()->documentElement();
54 }
55 if (!root) {
56 exceptionState.throwDOMException(HierarchyRequestError, "Unable to get r oot element in main frame to track.");
57 return nullptr;
58 }
59
60 Vector<float> thresholds;
61 if (observerInit.hasThreshold())
62 parseThresholds(observerInit.threshold(), thresholds, exceptionState);
63 else
64 thresholds.append(0);
65 if (exceptionState.hadException())
66 return nullptr;
67
68 return new IntersectionObserver(callback, *root, thresholds);
69 }
70
71 IntersectionObserver::IntersectionObserver(IntersectionObserverCallback& callbac k, Element& root, const Vector<float>& thresholds)
72 : m_callback(&callback)
73 , m_root(root.ensureIntersectionObserverData().createWeakPtr(&root))
74 , m_thresholds(thresholds)
75 {
76 root.document().ensureIntersectionObserverController().addTrackedObserver(*t his);
77 }
78
79 LayoutObject* IntersectionObserver::rootLayoutObject()
80 {
81 Element* rootElement = root();
82 if (rootElement == rootElement->document().documentElement())
83 return rootElement->document().layoutView();
84 return rootElement->layoutObject();
85 }
86
87 bool IntersectionObserver::isDescendantOfRoot(const Element* target) const
88 {
89 // Is m_root an ancestor, through the DOM and frame trees, of target?
90 Element* rootElement = m_root.get();
91 if (!rootElement || !target || target == rootElement)
92 return false;
93 if (!target->inDocument() || !rootElement->inDocument())
94 return false;
95
96 Document* rootDocument = &rootElement->document();
97 Document* targetDocument = &target->document();
98 while (targetDocument != rootDocument) {
99 target = targetDocument->ownerElement();
100 if (!target)
101 return false;
102 targetDocument = &target->document();
103 }
104 return target->isDescendantOf(rootElement);
105 }
106
107 void IntersectionObserver::observe(Element* target, ExceptionState& exceptionSta te)
108 {
109 checkRootAndDetachIfNeeded();
110 if (!m_root) {
111 exceptionState.throwDOMException(HierarchyRequestError, "Invalid observe r: root element or containing document has been deleted.");
112 return;
113 }
114 if (!target) {
115 exceptionState.throwTypeError("Observation target must be an element.");
116 return;
117 }
118 if (m_root.get() == target) {
119 exceptionState.throwDOMException(HierarchyRequestError, "Cannot use the same element for root and target.");
120 return;
121 }
122 if (!isDescendantOfRoot(target)) {
123 exceptionState.throwDOMException(HierarchyRequestError, "Observed elemen t must be a descendant of the observer's root element.");
124 return;
125 }
126
127 bool shouldReportRootBounds = target->document().frame()->securityContext()- >securityOrigin()->canAccess(root()->document().frame()->securityContext()->secu rityOrigin());
128
129 if (target->ensureIntersectionObserverData().getObservationFor(*this))
130 return;
131
132 IntersectionObservation* observation = new IntersectionObservation(*this, *t arget, shouldReportRootBounds);
133 target->ensureIntersectionObserverData().addObservation(*observation);
134 m_observations.add(observation);
135 }
136
137 void IntersectionObserver::unobserve(Element* target, ExceptionState&)
138 {
139 checkRootAndDetachIfNeeded();
140 if (!target || !target->intersectionObserverData())
141 return;
142 // TODO(szager): unobserve callback
143 if (IntersectionObservation* observation = target->intersectionObserverData( )->getObservationFor(*this))
144 observation->disconnect();
145 }
146
147 void IntersectionObserver::computeIntersectionObservations(double timestamp)
148 {
149 checkRootAndDetachIfNeeded();
150 if (!m_root)
151 return;
152 for (auto& observation : m_observations)
153 observation->computeIntersectionObservations(timestamp);
154 }
155
156 void IntersectionObserver::disconnect()
157 {
158 HeapVector<Member<IntersectionObservation>> observationsToDisconnect;
159 copyToVector(m_observations, observationsToDisconnect);
160 for (auto& observation : observationsToDisconnect)
161 observation->disconnect();
162 ASSERT(m_observations.isEmpty());
163 m_root.clear();
164 }
165
166 HeapVector<Member<IntersectionObserverEntry>> IntersectionObserver::takeRecords( )
167 {
168 checkRootAndDetachIfNeeded();
169 HeapVector<Member<IntersectionObserverEntry>> entries;
170 entries.swap(m_entries);
171 return entries;
172 }
173
174 void IntersectionObserver::enqueueIntersectionObserverEntry(IntersectionObserver Entry& entry)
175 {
176 m_entries.append(&entry);
177 toDocument(m_callback->executionContext())->ensureIntersectionObserverContro ller().scheduleIntersectionObserverForDelivery(*this);
178 }
179
180 unsigned IntersectionObserver::firstThresholdGreaterThan(float ratio) const
181 {
182 unsigned result = 0;
183 while (result < m_thresholds.size() && m_thresholds[result] < ratio)
184 ++result;
185 return result;
186 }
187
188 bool IntersectionObserver::shouldBeSuspended() const
haraken 2016/01/07 00:48:17 Factor out this method to a follow-up CL.
szager1 2016/01/07 07:12:22 Done.
189 {
190 return m_callback->executionContext() && m_callback->executionContext()->act iveDOMObjectsAreSuspended();
191 }
192
193 void IntersectionObserver::deliver()
194 {
195 checkRootAndDetachIfNeeded();
196
197 ASSERT(!shouldBeSuspended());
198
199 if (m_entries.isEmpty())
200 return;
201
202 HeapVector<Member<IntersectionObserverEntry>> entries;
203 entries.swap(m_entries);
204 m_callback->handleEvent(entries, *this);
205 }
206
207 void IntersectionObserver::setActive(bool active)
208 {
209 checkRootAndDetachIfNeeded();
210 for (auto& observation : m_observations)
211 observation->setActive(m_root && active && isDescendantOfRoot(observatio n->target()));
212 }
213
214 void IntersectionObserver::checkRootAndDetachIfNeeded()
haraken 2016/01/07 00:48:17 This implementation is clearly wrong in oilpan. We
szager1 2016/01/07 07:12:22 Acknowledged.
215 {
216 #if ENABLE(OILPAN)
217 // TODO(szager): Pre-oilpan, ElementIntersectionObserverData::dispose() will take
218 // care of this cleanup. When oilpan ships, there will be a potential leak of the
219 // callback's execution context when the root goes away. For a detailed exp lanation:
220 //
221 // https://goo.gl/PC2Baj
222 //
223 // When that happens, this method should catch most potential leaks, but a c omplete
224 // solution will still be needed, along the lines described in the above lin k.
225
226 // TODO(szager): As a performance optimization, clear the hash tables before disconnecting
haraken 2016/01/07 00:48:17 Remove this TODO.
szager1 2016/01/07 07:12:22 Done.
227 // the observations, and avoid hash lookups in IntersectionObservation::deta ch.
228 // That would make the ASSERT superfluous, but let's leave it as is for a wh ile
229 // to see if the ASSERT ever fails.
230 if (m_root)
231 return;
232 disconnect();
233 #endif
234 }
235
236 DEFINE_TRACE(IntersectionObserver)
237 {
238 visitor->trace(m_callback);
239 visitor->trace(m_root);
240 visitor->trace(m_observations);
241 visitor->trace(m_entries);
242 }
243
244 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698