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

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

Issue 2553343004: IntersectionObserver: use nullptr for implicit root. (Closed)
Patch Set: rebase Created 4 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
1 // Copyright 2016 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "core/dom/IntersectionObserver.h" 5 #include "core/dom/IntersectionObserver.h"
6 6
7 #include "bindings/core/v8/ExceptionState.h" 7 #include "bindings/core/v8/ExceptionState.h"
8 #include "core/css/parser/CSSParserTokenRange.h" 8 #include "core/css/parser/CSSParserTokenRange.h"
9 #include "core/css/parser/CSSTokenizer.h" 9 #include "core/css/parser/CSSTokenizer.h"
10 #include "core/dom/Element.h" 10 #include "core/dom/Element.h"
11 #include "core/dom/ElementIntersectionObserverData.h"
11 #include "core/dom/ExceptionCode.h" 12 #include "core/dom/ExceptionCode.h"
12 #include "core/dom/ExecutionContext.h" 13 #include "core/dom/ExecutionContext.h"
13 #include "core/dom/IntersectionObserverCallback.h" 14 #include "core/dom/IntersectionObserverCallback.h"
14 #include "core/dom/IntersectionObserverController.h" 15 #include "core/dom/IntersectionObserverController.h"
15 #include "core/dom/IntersectionObserverEntry.h" 16 #include "core/dom/IntersectionObserverEntry.h"
16 #include "core/dom/IntersectionObserverInit.h" 17 #include "core/dom/IntersectionObserverInit.h"
17 #include "core/dom/NodeIntersectionObserverData.h"
18 #include "core/frame/FrameView.h" 18 #include "core/frame/FrameView.h"
19 #include "core/frame/LocalDOMWindow.h" 19 #include "core/frame/LocalDOMWindow.h"
20 #include "core/frame/LocalFrame.h" 20 #include "core/frame/LocalFrame.h"
21 #include "core/html/HTMLFrameOwnerElement.h"
22 #include "core/inspector/ConsoleMessage.h" 21 #include "core/inspector/ConsoleMessage.h"
22 #include "core/layout/LayoutView.h"
23 #include "core/timing/DOMWindowPerformance.h" 23 #include "core/timing/DOMWindowPerformance.h"
24 #include "core/timing/Performance.h" 24 #include "core/timing/Performance.h"
25 #include "platform/Timer.h" 25 #include "platform/Timer.h"
26 #include <algorithm> 26 #include <algorithm>
27 27
28 namespace blink { 28 namespace blink {
29 29
30 namespace { 30 namespace {
31 31
32 // Internal implementation of IntersectionObserverCallback when using 32 // Internal implementation of IntersectionObserverCallback when using
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
121 thresholdValue > 1.0) { 121 thresholdValue > 1.0) {
122 exceptionState.throwRangeError( 122 exceptionState.throwRangeError(
123 "Threshold values must be numbers between 0 and 1"); 123 "Threshold values must be numbers between 0 and 1");
124 break; 124 break;
125 } 125 }
126 } 126 }
127 127
128 std::sort(thresholds.begin(), thresholds.end()); 128 std::sort(thresholds.begin(), thresholds.end());
129 } 129 }
130 130
131 // Returns the root Node of a given Document to use as the IntersectionObserver
132 // root when no root is given.
133 // TODO(szager): it doesn't support RemoteFrames, see https://crbug.com/615156
134 Node* getRootNode(Document* document) {
135 Frame* mainFrame = document->frame()->tree().top();
136 if (mainFrame && mainFrame->isLocalFrame())
137 return toLocalFrame(mainFrame)->document();
138 return nullptr;
139 }
140
141 } // anonymous namespace 131 } // anonymous namespace
142 132
143 IntersectionObserver* IntersectionObserver::create( 133 IntersectionObserver* IntersectionObserver::create(
144 const IntersectionObserverInit& observerInit, 134 const IntersectionObserverInit& observerInit,
145 IntersectionObserverCallback& callback, 135 IntersectionObserverCallback& callback,
146 ExceptionState& exceptionState) { 136 ExceptionState& exceptionState) {
147 Node* root = observerInit.root(); 137 Element* root = observerInit.root();
148 if (!root) {
149 ExecutionContext* context = callback.getExecutionContext();
150 DCHECK(context->isDocument());
151 root = getRootNode(toDocument(context));
152 }
153 if (!root) {
154 exceptionState.throwDOMException(
155 HierarchyRequestError,
156 "Unable to get root node in main frame to track.");
157 return nullptr;
158 }
159 138
160 Vector<Length> rootMargin; 139 Vector<Length> rootMargin;
161 parseRootMargin(observerInit.rootMargin(), rootMargin, exceptionState); 140 parseRootMargin(observerInit.rootMargin(), rootMargin, exceptionState);
162 if (exceptionState.hadException()) 141 if (exceptionState.hadException())
163 return nullptr; 142 return nullptr;
164 143
165 Vector<float> thresholds; 144 Vector<float> thresholds;
166 parseThresholds(observerInit.threshold(), thresholds, exceptionState); 145 parseThresholds(observerInit.threshold(), thresholds, exceptionState);
167 if (exceptionState.hadException()) 146 if (exceptionState.hadException())
168 return nullptr; 147 return nullptr;
169 148
170 return new IntersectionObserver(callback, *root, rootMargin, thresholds); 149 return new IntersectionObserver(callback, root, rootMargin, thresholds);
171 } 150 }
172 151
173 IntersectionObserver* IntersectionObserver::create( 152 IntersectionObserver* IntersectionObserver::create(
174 const Vector<Length>& rootMargin, 153 const Vector<Length>& rootMargin,
175 const Vector<float>& thresholds, 154 const Vector<float>& thresholds,
176 Document* document, 155 Document* document,
177 std::unique_ptr<EventCallback> callback, 156 std::unique_ptr<EventCallback> callback,
178 ExceptionState& exceptionState) { 157 ExceptionState& exceptionState) {
179 Node* root = getRootNode(document);
180 if (!root) {
181 exceptionState.throwDOMException(
182 HierarchyRequestError,
183 "Unable to get root node in main frame to track.");
184 return nullptr;
185 }
186
187 IntersectionObserverCallbackImpl* intersectionObserverCallback = 158 IntersectionObserverCallbackImpl* intersectionObserverCallback =
188 new IntersectionObserverCallbackImpl(document, std::move(callback)); 159 new IntersectionObserverCallbackImpl(document, std::move(callback));
189 return new IntersectionObserver(*intersectionObserverCallback, *root, 160 return new IntersectionObserver(*intersectionObserverCallback, nullptr,
190 rootMargin, thresholds); 161 rootMargin, thresholds);
191 } 162 }
192 163
193 IntersectionObserver::IntersectionObserver( 164 IntersectionObserver::IntersectionObserver(
194 IntersectionObserverCallback& callback, 165 IntersectionObserverCallback& callback,
195 Node& root, 166 Element* root,
196 const Vector<Length>& rootMargin, 167 const Vector<Length>& rootMargin,
197 const Vector<float>& thresholds) 168 const Vector<float>& thresholds)
198 : m_callback(&callback), 169 : m_callback(&callback),
199 m_root(&root), 170 m_root(root),
200 m_thresholds(thresholds), 171 m_thresholds(thresholds),
201 m_topMargin(Fixed), 172 m_topMargin(Fixed),
202 m_rightMargin(Fixed), 173 m_rightMargin(Fixed),
203 m_bottomMargin(Fixed), 174 m_bottomMargin(Fixed),
204 m_leftMargin(Fixed), 175 m_leftMargin(Fixed),
176 m_rootIsImplicit(root ? 0 : 1),
205 m_initialState(InitialState::kHidden) { 177 m_initialState(InitialState::kHidden) {
206 switch (rootMargin.size()) { 178 switch (rootMargin.size()) {
207 case 0: 179 case 0:
208 break; 180 break;
209 case 1: 181 case 1:
210 m_topMargin = m_rightMargin = m_bottomMargin = m_leftMargin = 182 m_topMargin = m_rightMargin = m_bottomMargin = m_leftMargin =
211 rootMargin[0]; 183 rootMargin[0];
212 break; 184 break;
213 case 2: 185 case 2:
214 m_topMargin = m_bottomMargin = rootMargin[0]; 186 m_topMargin = m_bottomMargin = rootMargin[0];
215 m_rightMargin = m_leftMargin = rootMargin[1]; 187 m_rightMargin = m_leftMargin = rootMargin[1];
216 break; 188 break;
217 case 3: 189 case 3:
218 m_topMargin = rootMargin[0]; 190 m_topMargin = rootMargin[0];
219 m_rightMargin = m_leftMargin = rootMargin[1]; 191 m_rightMargin = m_leftMargin = rootMargin[1];
220 m_bottomMargin = rootMargin[2]; 192 m_bottomMargin = rootMargin[2];
221 break; 193 break;
222 case 4: 194 case 4:
223 m_topMargin = rootMargin[0]; 195 m_topMargin = rootMargin[0];
224 m_rightMargin = rootMargin[1]; 196 m_rightMargin = rootMargin[1];
225 m_bottomMargin = rootMargin[2]; 197 m_bottomMargin = rootMargin[2];
226 m_leftMargin = rootMargin[3]; 198 m_leftMargin = rootMargin[3];
227 break; 199 break;
228 default: 200 default:
229 NOTREACHED(); 201 NOTREACHED();
230 break; 202 break;
231 } 203 }
232 root.document().ensureIntersectionObserverController().addTrackedObserver( 204 if (root)
205 root->ensureIntersectionObserverData().addObserver(*this);
206 trackingDocument().ensureIntersectionObserverController().addTrackedObserver(
233 *this); 207 *this);
234 } 208 }
235 209
236 void IntersectionObserver::clearWeakMembers(Visitor* visitor) { 210 void IntersectionObserver::clearWeakMembers(Visitor* visitor) {
237 if (ThreadHeap::isHeapObjectAlive(m_root)) 211 if (ThreadHeap::isHeapObjectAlive(root()))
238 return; 212 return;
239 IgnorableExceptionState exceptionState; 213 IgnorableExceptionState exceptionState;
240 disconnect(exceptionState); 214 disconnect(exceptionState);
241 m_root = nullptr; 215 m_root = nullptr;
242 } 216 }
243 217
218 bool IntersectionObserver::rootIsValid() const {
219 return rootIsImplicit() || root();
220 }
221
222 Document& IntersectionObserver::trackingDocument() const {
223 Document* document = nullptr;
224 if (rootIsImplicit()) {
225 DCHECK(m_callback->getExecutionContext());
226 document = toDocument(m_callback->getExecutionContext());
227 } else {
228 DCHECK(root());
229 document = &root()->document();
230 }
231 DCHECK(document);
232 DCHECK(document->frame());
233 return *document->frame()->localFrameRoot()->document();
234 }
235
244 void IntersectionObserver::observe(Element* target, 236 void IntersectionObserver::observe(Element* target,
245 ExceptionState& exceptionState) { 237 ExceptionState& exceptionState) {
246 if (!m_root) { 238 if (!rootIsValid()) {
247 exceptionState.throwDOMException( 239 exceptionState.throwDOMException(
248 InvalidStateError, 240 InvalidStateError,
249 "observe() called on an IntersectionObserver with an invalid root."); 241 "observe() called on an IntersectionObserver with an invalid root.");
250 return; 242 return;
251 } 243 }
252 244
253 if (!target || m_root.get() == target) 245 if (!target || root() == target)
246 return;
247
248 LocalFrame* targetFrame = target->document().frame();
249 if (!targetFrame)
254 return; 250 return;
255 251
256 if (target->ensureIntersectionObserverData().getObservationFor(*this)) 252 if (target->ensureIntersectionObserverData().getObservationFor(*this))
257 return; 253 return;
254
255 bool isDOMDescendant = true;
258 bool shouldReportRootBounds = false; 256 bool shouldReportRootBounds = false;
259 bool isDOMDescendant = false; 257 if (rootIsImplicit()) {
260 LocalFrame* targetFrame = target->document().frame(); 258 Frame* rootFrame = targetFrame->tree().top();
261 LocalFrame* rootFrame = m_root->document().frame(); 259 DCHECK(rootFrame);
262 260 if (rootFrame == targetFrame) {
263 if (target->document() == rootNode()->document()) { 261 shouldReportRootBounds = true;
262 } else {
263 shouldReportRootBounds =
264 targetFrame->securityContext()->getSecurityOrigin()->canAccess(
265 rootFrame->securityContext()->getSecurityOrigin());
266 }
267 } else {
264 shouldReportRootBounds = true; 268 shouldReportRootBounds = true;
265 isDOMDescendant = rootNode()->isShadowIncludingInclusiveAncestorOf(target); 269 isDOMDescendant = root()->isShadowIncludingInclusiveAncestorOf(target);
266 } else if (targetFrame && rootFrame) {
267 shouldReportRootBounds =
268 targetFrame->securityContext()->getSecurityOrigin()->canAccess(
269 rootFrame->securityContext()->getSecurityOrigin());
270 isDOMDescendant = (targetFrame->tree().top() == rootFrame);
271 } 270 }
272 271
273 IntersectionObservation* observation = 272 IntersectionObservation* observation =
274 new IntersectionObservation(*this, *target, shouldReportRootBounds); 273 new IntersectionObservation(*this, *target, shouldReportRootBounds);
275 target->ensureIntersectionObserverData().addObservation(*observation); 274 target->ensureIntersectionObserverData().addObservation(*observation);
276 m_observations.add(observation); 275 m_observations.add(observation);
277 276
278 if (!isDOMDescendant) { 277 if (!isDOMDescendant) {
279 m_root->document().addConsoleMessage( 278 root()->document().addConsoleMessage(
280 ConsoleMessage::create(JSMessageSource, WarningMessageLevel, 279 ConsoleMessage::create(JSMessageSource, WarningMessageLevel,
281 "IntersectionObserver.observe(target): target " 280 "IntersectionObserver.observe(target): target "
282 "element is not a descendant of root.")); 281 "element is not a descendant of root."));
283 return;
284 } 282 }
285 283
286 if (m_initialState == InitialState::kAuto) { 284 if (m_initialState == InitialState::kAuto) {
287 for (auto& observation : m_observations) 285 for (auto& observation : m_observations)
288 observation->setLastThresholdIndex(std::numeric_limits<unsigned>::max()); 286 observation->setLastThresholdIndex(std::numeric_limits<unsigned>::max());
289 } 287 }
290 288
291 if (!rootFrame) 289 if (FrameView* frameView = targetFrame->view())
292 return; 290 frameView->scheduleAnimation();
293 if (FrameView* rootFrameView = rootFrame->view())
294 rootFrameView->scheduleAnimation();
295 } 291 }
296 292
297 void IntersectionObserver::unobserve(Element* target, 293 void IntersectionObserver::unobserve(Element* target,
298 ExceptionState& exceptionState) { 294 ExceptionState& exceptionState) {
299 if (!m_root) { 295 if (!rootIsValid()) {
300 exceptionState.throwDOMException( 296 exceptionState.throwDOMException(
301 InvalidStateError, 297 InvalidStateError,
302 "unobserve() called on an IntersectionObserver with an invalid root."); 298 "unobserve() called on an IntersectionObserver with an invalid root.");
303 return; 299 return;
304 } 300 }
305 301
306 if (!target || !target->intersectionObserverData()) 302 if (!target || !target->intersectionObserverData())
307 return; 303 return;
308 // TODO(szager): unobserve callback 304
309 if (IntersectionObservation* observation = 305 if (IntersectionObservation* observation =
310 target->intersectionObserverData()->getObservationFor(*this)) 306 target->intersectionObserverData()->getObservationFor(*this))
311 observation->disconnect(); 307 observation->disconnect();
312 } 308 }
313 309
314 void IntersectionObserver::computeIntersectionObservations() { 310 void IntersectionObserver::computeIntersectionObservations() {
311 if (!rootIsValid())
312 return;
315 Document* callbackDocument = toDocument(m_callback->getExecutionContext()); 313 Document* callbackDocument = toDocument(m_callback->getExecutionContext());
316 if (!callbackDocument) 314 if (!callbackDocument)
317 return; 315 return;
318 LocalDOMWindow* callbackDOMWindow = callbackDocument->domWindow(); 316 LocalDOMWindow* callbackDOMWindow = callbackDocument->domWindow();
319 if (!callbackDOMWindow) 317 if (!callbackDOMWindow)
320 return; 318 return;
321 DOMHighResTimeStamp timestamp = 319 DOMHighResTimeStamp timestamp =
322 DOMWindowPerformance::performance(*callbackDOMWindow)->now(); 320 DOMWindowPerformance::performance(*callbackDOMWindow)->now();
323 for (auto& observation : m_observations) 321 for (auto& observation : m_observations)
324 observation->computeIntersectionObservations(timestamp); 322 observation->computeIntersectionObservations(timestamp);
325 } 323 }
326 324
327 void IntersectionObserver::disconnect(ExceptionState& exceptionState) { 325 void IntersectionObserver::disconnect(ExceptionState& exceptionState) {
328 if (!m_root) { 326 if (!rootIsValid()) {
329 exceptionState.throwDOMException( 327 exceptionState.throwDOMException(
330 InvalidStateError, 328 InvalidStateError,
331 "disconnect() called on an IntersectionObserver with an invalid root."); 329 "disconnect() called on an IntersectionObserver with an invalid root.");
332 return; 330 return;
333 } 331 }
334 332
335 for (auto& observation : m_observations) 333 for (auto& observation : m_observations)
336 observation->clearRootAndRemoveFromTarget(); 334 observation->clearRootAndRemoveFromTarget();
337 m_observations.clear(); 335 m_observations.clear();
338 } 336 }
339 337
340 void IntersectionObserver::removeObservation( 338 void IntersectionObserver::removeObservation(
341 IntersectionObservation& observation) { 339 IntersectionObservation& observation) {
342 m_observations.remove(&observation); 340 m_observations.remove(&observation);
343 } 341 }
344 342
345 void IntersectionObserver::setInitialState(InitialState initialState) { 343 void IntersectionObserver::setInitialState(InitialState initialState) {
346 DCHECK(m_observations.isEmpty()); 344 DCHECK(m_observations.isEmpty());
347 m_initialState = initialState; 345 m_initialState = initialState;
348 } 346 }
349 347
350 HeapVector<Member<IntersectionObserverEntry>> IntersectionObserver::takeRecords( 348 HeapVector<Member<IntersectionObserverEntry>> IntersectionObserver::takeRecords(
351 ExceptionState& exceptionState) { 349 ExceptionState& exceptionState) {
352 HeapVector<Member<IntersectionObserverEntry>> entries; 350 HeapVector<Member<IntersectionObserverEntry>> entries;
353 351
354 if (!m_root) 352 if (!rootIsValid()) {
355 exceptionState.throwDOMException(InvalidStateError, 353 exceptionState.throwDOMException(InvalidStateError,
356 "takeRecords() called on an " 354 "takeRecords() called on an "
357 "IntersectionObserver with an invalid " 355 "IntersectionObserver with an invalid "
358 "root."); 356 "root.");
359 else 357 } else {
360 entries.swap(m_entries); 358 entries.swap(m_entries);
359 }
361 360
362 return entries; 361 return entries;
363 } 362 }
364 363
365 Element* IntersectionObserver::root() const {
366 Node* node = rootNode();
367 if (node && !node->isDocumentNode())
368 return toElement(node);
369 return nullptr;
370 }
371
372 static void appendLength(StringBuilder& stringBuilder, const Length& length) { 364 static void appendLength(StringBuilder& stringBuilder, const Length& length) {
373 stringBuilder.appendNumber(length.intValue()); 365 stringBuilder.appendNumber(length.intValue());
374 if (length.type() == Percent) 366 if (length.type() == Percent)
375 stringBuilder.append('%'); 367 stringBuilder.append('%');
376 else 368 else
377 stringBuilder.append("px", 2); 369 stringBuilder.append("px", 2);
378 } 370 }
379 371
380 String IntersectionObserver::rootMargin() const { 372 String IntersectionObserver::rootMargin() const {
381 StringBuilder stringBuilder; 373 StringBuilder stringBuilder;
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
415 407
416 DEFINE_TRACE(IntersectionObserver) { 408 DEFINE_TRACE(IntersectionObserver) {
417 visitor->template registerWeakMembers< 409 visitor->template registerWeakMembers<
418 IntersectionObserver, &IntersectionObserver::clearWeakMembers>(this); 410 IntersectionObserver, &IntersectionObserver::clearWeakMembers>(this);
419 visitor->trace(m_callback); 411 visitor->trace(m_callback);
420 visitor->trace(m_observations); 412 visitor->trace(m_observations);
421 visitor->trace(m_entries); 413 visitor->trace(m_entries);
422 } 414 }
423 415
424 } // namespace blink 416 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698