/src/mozilla-central/dom/base/DOMIntersectionObserver.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
5 | | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "DOMIntersectionObserver.h" |
8 | | #include "nsCSSPropertyID.h" |
9 | | #include "nsIFrame.h" |
10 | | #include "nsContentUtils.h" |
11 | | #include "nsLayoutUtils.h" |
12 | | #include "mozilla/ServoBindings.h" |
13 | | |
14 | | namespace mozilla { |
15 | | namespace dom { |
16 | | |
17 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMIntersectionObserverEntry) |
18 | 0 | NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
19 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
20 | 0 | NS_INTERFACE_MAP_END |
21 | | |
22 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMIntersectionObserverEntry) |
23 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMIntersectionObserverEntry) |
24 | | |
25 | | NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMIntersectionObserverEntry, mOwner, |
26 | | mRootBounds, mBoundingClientRect, |
27 | | mIntersectionRect, mTarget) |
28 | | |
29 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMIntersectionObserver) |
30 | 0 | NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
31 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
32 | 0 | NS_INTERFACE_MAP_ENTRY(DOMIntersectionObserver) |
33 | 0 | NS_INTERFACE_MAP_END |
34 | | |
35 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMIntersectionObserver) |
36 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMIntersectionObserver) |
37 | | |
38 | | NS_IMPL_CYCLE_COLLECTION_CLASS(DOMIntersectionObserver) |
39 | | |
40 | 0 | NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMIntersectionObserver) |
41 | 0 | NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER |
42 | 0 | NS_IMPL_CYCLE_COLLECTION_TRACE_END |
43 | | |
44 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMIntersectionObserver) |
45 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER |
46 | 0 | tmp->Disconnect(); |
47 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner) |
48 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument) |
49 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback) |
50 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot) |
51 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mQueuedEntries) |
52 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
53 | | |
54 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMIntersectionObserver) |
55 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner) |
56 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument) |
57 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback) |
58 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot) |
59 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueuedEntries) |
60 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
61 | | |
62 | | already_AddRefed<DOMIntersectionObserver> |
63 | | DOMIntersectionObserver::Constructor(const mozilla::dom::GlobalObject& aGlobal, |
64 | | mozilla::dom::IntersectionCallback& aCb, |
65 | | mozilla::ErrorResult& aRv) |
66 | 0 | { |
67 | 0 | return Constructor(aGlobal, aCb, IntersectionObserverInit(), aRv); |
68 | 0 | } |
69 | | |
70 | | already_AddRefed<DOMIntersectionObserver> |
71 | | DOMIntersectionObserver::Constructor(const mozilla::dom::GlobalObject& aGlobal, |
72 | | mozilla::dom::IntersectionCallback& aCb, |
73 | | const mozilla::dom::IntersectionObserverInit& aOptions, |
74 | | mozilla::ErrorResult& aRv) |
75 | 0 | { |
76 | 0 | nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports()); |
77 | 0 | if (!window) { |
78 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
79 | 0 | return nullptr; |
80 | 0 | } |
81 | 0 | RefPtr<DOMIntersectionObserver> observer = |
82 | 0 | new DOMIntersectionObserver(window.forget(), aCb); |
83 | 0 |
|
84 | 0 | observer->mRoot = aOptions.mRoot; |
85 | 0 |
|
86 | 0 | if (!observer->SetRootMargin(aOptions.mRootMargin)) { |
87 | 0 | aRv.ThrowDOMException(NS_ERROR_DOM_SYNTAX_ERR, |
88 | 0 | NS_LITERAL_CSTRING("rootMargin must be specified in pixels or percent.")); |
89 | 0 | return nullptr; |
90 | 0 | } |
91 | 0 |
|
92 | 0 | if (aOptions.mThreshold.IsDoubleSequence()) { |
93 | 0 | const mozilla::dom::Sequence<double>& thresholds = aOptions.mThreshold.GetAsDoubleSequence(); |
94 | 0 | observer->mThresholds.SetCapacity(thresholds.Length()); |
95 | 0 | for (const auto& thresh : thresholds) { |
96 | 0 | if (thresh < 0.0 || thresh > 1.0) { |
97 | 0 | aRv.ThrowTypeError<dom::MSG_THRESHOLD_RANGE_ERROR>(); |
98 | 0 | return nullptr; |
99 | 0 | } |
100 | 0 | observer->mThresholds.AppendElement(thresh); |
101 | 0 | } |
102 | 0 | observer->mThresholds.Sort(); |
103 | 0 | } else { |
104 | 0 | double thresh = aOptions.mThreshold.GetAsDouble(); |
105 | 0 | if (thresh < 0.0 || thresh > 1.0) { |
106 | 0 | aRv.ThrowTypeError<dom::MSG_THRESHOLD_RANGE_ERROR>(); |
107 | 0 | return nullptr; |
108 | 0 | } |
109 | 0 | observer->mThresholds.AppendElement(thresh); |
110 | 0 | } |
111 | 0 |
|
112 | 0 | return observer.forget(); |
113 | 0 | } |
114 | | |
115 | | bool |
116 | | DOMIntersectionObserver::SetRootMargin(const nsAString& aString) |
117 | 0 | { |
118 | 0 | return Servo_IntersectionObserverRootMargin_Parse(&aString, &mRootMargin); |
119 | 0 | } |
120 | | |
121 | | void |
122 | | DOMIntersectionObserver::GetRootMargin(mozilla::dom::DOMString& aRetVal) |
123 | 0 | { |
124 | 0 | nsString& retVal = aRetVal; |
125 | 0 | Servo_IntersectionObserverRootMargin_ToString(&mRootMargin, &retVal); |
126 | 0 | } |
127 | | |
128 | | void |
129 | | DOMIntersectionObserver::GetThresholds(nsTArray<double>& aRetVal) |
130 | 0 | { |
131 | 0 | aRetVal = mThresholds; |
132 | 0 | } |
133 | | |
134 | | void |
135 | | DOMIntersectionObserver::Observe(Element& aTarget) |
136 | 0 | { |
137 | 0 | if (mObservationTargets.Contains(&aTarget)) { |
138 | 0 | return; |
139 | 0 | } |
140 | 0 | aTarget.RegisterIntersectionObserver(this); |
141 | 0 | mObservationTargets.AppendElement(&aTarget); |
142 | 0 | Connect(); |
143 | 0 | } |
144 | | |
145 | | void |
146 | | DOMIntersectionObserver::Unobserve(Element& aTarget) |
147 | 0 | { |
148 | 0 | if (!mObservationTargets.Contains(&aTarget)) { |
149 | 0 | return; |
150 | 0 | } |
151 | 0 | |
152 | 0 | if (mObservationTargets.Length() == 1) { |
153 | 0 | Disconnect(); |
154 | 0 | return; |
155 | 0 | } |
156 | 0 | |
157 | 0 | mObservationTargets.RemoveElement(&aTarget); |
158 | 0 | aTarget.UnregisterIntersectionObserver(this); |
159 | 0 | } |
160 | | |
161 | | void |
162 | | DOMIntersectionObserver::UnlinkTarget(Element& aTarget) |
163 | 0 | { |
164 | 0 | mObservationTargets.RemoveElement(&aTarget); |
165 | 0 | if (mObservationTargets.Length() == 0) { |
166 | 0 | Disconnect(); |
167 | 0 | } |
168 | 0 | } |
169 | | |
170 | | void |
171 | | DOMIntersectionObserver::Connect() |
172 | 0 | { |
173 | 0 | if (mConnected) { |
174 | 0 | return; |
175 | 0 | } |
176 | 0 | |
177 | 0 | mConnected = true; |
178 | 0 | if (mDocument) { |
179 | 0 | mDocument->AddIntersectionObserver(this); |
180 | 0 | } |
181 | 0 | } |
182 | | |
183 | | void |
184 | | DOMIntersectionObserver::Disconnect() |
185 | 0 | { |
186 | 0 | if (!mConnected) { |
187 | 0 | return; |
188 | 0 | } |
189 | 0 | |
190 | 0 | mConnected = false; |
191 | 0 | for (size_t i = 0; i < mObservationTargets.Length(); ++i) { |
192 | 0 | Element* target = mObservationTargets.ElementAt(i); |
193 | 0 | target->UnregisterIntersectionObserver(this); |
194 | 0 | } |
195 | 0 | mObservationTargets.Clear(); |
196 | 0 | if (mDocument) { |
197 | 0 | mDocument->RemoveIntersectionObserver(this); |
198 | 0 | } |
199 | 0 | } |
200 | | |
201 | | void |
202 | | DOMIntersectionObserver::TakeRecords(nsTArray<RefPtr<DOMIntersectionObserverEntry>>& aRetVal) |
203 | 0 | { |
204 | 0 | aRetVal.SwapElements(mQueuedEntries); |
205 | 0 | mQueuedEntries.Clear(); |
206 | 0 | } |
207 | | |
208 | | static bool |
209 | | CheckSimilarOrigin(nsINode* aNode1, nsINode* aNode2) |
210 | 0 | { |
211 | 0 | nsIPrincipal* principal1 = aNode1->NodePrincipal(); |
212 | 0 | nsIPrincipal* principal2 = aNode2->NodePrincipal(); |
213 | 0 | nsAutoCString baseDomain1; |
214 | 0 | nsAutoCString baseDomain2; |
215 | 0 |
|
216 | 0 | nsresult rv = principal1->GetBaseDomain(baseDomain1); |
217 | 0 | if (NS_FAILED(rv)) { |
218 | 0 | return principal1 == principal2; |
219 | 0 | } |
220 | 0 | |
221 | 0 | rv = principal2->GetBaseDomain(baseDomain2); |
222 | 0 | if (NS_FAILED(rv)) { |
223 | 0 | return principal1 == principal2; |
224 | 0 | } |
225 | 0 | |
226 | 0 | return baseDomain1 == baseDomain2; |
227 | 0 | } |
228 | | |
229 | | static Maybe<nsRect> |
230 | | EdgeInclusiveIntersection(const nsRect& aRect, const nsRect& aOtherRect) |
231 | 0 | { |
232 | 0 | nscoord left = std::max(aRect.x, aOtherRect.x); |
233 | 0 | nscoord top = std::max(aRect.y, aOtherRect.y); |
234 | 0 | nscoord right = std::min(aRect.XMost(), aOtherRect.XMost()); |
235 | 0 | nscoord bottom = std::min(aRect.YMost(), aOtherRect.YMost()); |
236 | 0 | if (left > right || top > bottom) { |
237 | 0 | return Nothing(); |
238 | 0 | } |
239 | 0 | return Some(nsRect(left, top, right - left, bottom - top)); |
240 | 0 | } |
241 | | |
242 | | enum class BrowsingContextInfo { |
243 | | SimilarOriginBrowsingContext, |
244 | | DifferentOriginBrowsingContext, |
245 | | UnknownBrowsingContext |
246 | | }; |
247 | | |
248 | | void |
249 | | DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time) |
250 | 0 | { |
251 | 0 | Element* root = nullptr; |
252 | 0 | nsIFrame* rootFrame = nullptr; |
253 | 0 | nsRect rootRect; |
254 | 0 |
|
255 | 0 | if (mRoot) { |
256 | 0 | root = mRoot; |
257 | 0 | rootFrame = root->GetPrimaryFrame(); |
258 | 0 | if (rootFrame) { |
259 | 0 | nsRect rootRectRelativeToRootFrame; |
260 | 0 | if (rootFrame->IsScrollFrame()) { |
261 | 0 | // rootRectRelativeToRootFrame should be the content rect of rootFrame, not including the scrollbars. |
262 | 0 | nsIScrollableFrame* scrollFrame = do_QueryFrame(rootFrame); |
263 | 0 | rootRectRelativeToRootFrame = scrollFrame->GetScrollPortRect(); |
264 | 0 | } else { |
265 | 0 | // rootRectRelativeToRootFrame should be the border rect of rootFrame. |
266 | 0 | rootRectRelativeToRootFrame = rootFrame->GetRectRelativeToSelf(); |
267 | 0 | } |
268 | 0 | nsIFrame* containingBlock = |
269 | 0 | nsLayoutUtils::GetContainingBlockForClientRect(rootFrame); |
270 | 0 | rootRect = |
271 | 0 | nsLayoutUtils::TransformFrameRectToAncestor(rootFrame, |
272 | 0 | rootRectRelativeToRootFrame, |
273 | 0 | containingBlock); |
274 | 0 | } |
275 | 0 | } else { |
276 | 0 | nsCOMPtr<nsIPresShell> presShell = aDocument->GetShell(); |
277 | 0 | if (presShell) { |
278 | 0 | rootFrame = presShell->GetRootScrollFrame(); |
279 | 0 | if (rootFrame) { |
280 | 0 | nsPresContext* presContext = rootFrame->PresContext(); |
281 | 0 | while (!presContext->IsRootContentDocument()) { |
282 | 0 | presContext = presContext->GetParentPresContext(); |
283 | 0 | if (!presContext) { |
284 | 0 | break; |
285 | 0 | } |
286 | 0 | nsIFrame* rootScrollFrame = presContext->PresShell()->GetRootScrollFrame(); |
287 | 0 | if (rootScrollFrame) { |
288 | 0 | rootFrame = rootScrollFrame; |
289 | 0 | } else { |
290 | 0 | break; |
291 | 0 | } |
292 | 0 | } |
293 | 0 | root = rootFrame->GetContent()->AsElement(); |
294 | 0 | nsIScrollableFrame* scrollFrame = do_QueryFrame(rootFrame); |
295 | 0 | // If we end up with a null root frame for some reason, we'll proceed |
296 | 0 | // with an empty root intersection rect. |
297 | 0 | if (scrollFrame) { |
298 | 0 | rootRect = scrollFrame->GetScrollPortRect(); |
299 | 0 | } |
300 | 0 | } |
301 | 0 | } |
302 | 0 | } |
303 | 0 |
|
304 | 0 | nsMargin rootMargin; |
305 | 0 | NS_FOR_CSS_SIDES(side) { |
306 | 0 | nscoord basis = side == eSideTop || side == eSideBottom ? |
307 | 0 | rootRect.Height() : rootRect.Width(); |
308 | 0 | nsStyleCoord coord = mRootMargin.Get(side); |
309 | 0 | rootMargin.Side(side) = nsLayoutUtils::ComputeCBDependentValue(basis, coord); |
310 | 0 | } |
311 | 0 |
|
312 | 0 | for (size_t i = 0; i < mObservationTargets.Length(); ++i) { |
313 | 0 | Element* target = mObservationTargets.ElementAt(i); |
314 | 0 | nsIFrame* targetFrame = target->GetPrimaryFrame(); |
315 | 0 | nsRect targetRect; |
316 | 0 | Maybe<nsRect> intersectionRect; |
317 | 0 | bool isSameDoc = root && root->GetComposedDoc() == target->GetComposedDoc(); |
318 | 0 |
|
319 | 0 | if (rootFrame && targetFrame) { |
320 | 0 | // If mRoot is set we are testing intersection with a container element |
321 | 0 | // instead of the implicit root. |
322 | 0 | if (mRoot) { |
323 | 0 | // Skip further processing of this target if it is not in the same |
324 | 0 | // Document as the intersection root, e.g. if root is an element of |
325 | 0 | // the main document and target an element from an embedded iframe. |
326 | 0 | if (!isSameDoc) { |
327 | 0 | continue; |
328 | 0 | } |
329 | 0 | // Skip further processing of this target if is not a descendant of the |
330 | 0 | // intersection root in the containing block chain. E.g. this would be |
331 | 0 | // the case if the target is in a position:absolute element whose |
332 | 0 | // containing block is an ancestor of root. |
333 | 0 | if (!nsLayoutUtils::IsAncestorFrameCrossDoc(rootFrame, targetFrame)) { |
334 | 0 | continue; |
335 | 0 | } |
336 | 0 | } |
337 | 0 | |
338 | 0 | targetRect = nsLayoutUtils::GetAllInFlowRectsUnion( |
339 | 0 | targetFrame, |
340 | 0 | nsLayoutUtils::GetContainingBlockForClientRect(targetFrame), |
341 | 0 | nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS |
342 | 0 | ); |
343 | 0 | intersectionRect = Some(targetFrame->GetRectRelativeToSelf()); |
344 | 0 |
|
345 | 0 | nsIFrame* containerFrame = nsLayoutUtils::GetCrossDocParentFrame(targetFrame); |
346 | 0 | while (containerFrame && containerFrame != rootFrame) { |
347 | 0 | if (containerFrame->IsScrollFrame()) { |
348 | 0 | nsIScrollableFrame* scrollFrame = do_QueryFrame(containerFrame); |
349 | 0 | nsRect subFrameRect = scrollFrame->GetScrollPortRect(); |
350 | 0 | nsRect intersectionRectRelativeToContainer = |
351 | 0 | nsLayoutUtils::TransformFrameRectToAncestor(targetFrame, |
352 | 0 | intersectionRect.value(), |
353 | 0 | containerFrame); |
354 | 0 | intersectionRect = EdgeInclusiveIntersection(intersectionRectRelativeToContainer, |
355 | 0 | subFrameRect); |
356 | 0 | if (!intersectionRect) { |
357 | 0 | break; |
358 | 0 | } |
359 | 0 | targetFrame = containerFrame; |
360 | 0 | } |
361 | 0 |
|
362 | 0 | // TODO: Apply clip-path. |
363 | 0 |
|
364 | 0 | containerFrame = nsLayoutUtils::GetCrossDocParentFrame(containerFrame); |
365 | 0 | } |
366 | 0 | } |
367 | 0 |
|
368 | 0 | nsRect rootIntersectionRect; |
369 | 0 | BrowsingContextInfo isInSimilarOriginBrowsingContext = |
370 | 0 | BrowsingContextInfo::UnknownBrowsingContext; |
371 | 0 |
|
372 | 0 | if (rootFrame && targetFrame) { |
373 | 0 | rootIntersectionRect = rootRect; |
374 | 0 | } |
375 | 0 |
|
376 | 0 | if (root && target) { |
377 | 0 | isInSimilarOriginBrowsingContext = CheckSimilarOrigin(root, target) ? |
378 | 0 | BrowsingContextInfo::SimilarOriginBrowsingContext : |
379 | 0 | BrowsingContextInfo::DifferentOriginBrowsingContext; |
380 | 0 | } |
381 | 0 |
|
382 | 0 | if (isInSimilarOriginBrowsingContext == |
383 | 0 | BrowsingContextInfo::SimilarOriginBrowsingContext) { |
384 | 0 | rootIntersectionRect.Inflate(rootMargin); |
385 | 0 | } |
386 | 0 |
|
387 | 0 | if (intersectionRect.isSome()) { |
388 | 0 | nsRect intersectionRectRelativeToRoot = |
389 | 0 | nsLayoutUtils::TransformFrameRectToAncestor( |
390 | 0 | targetFrame, |
391 | 0 | intersectionRect.value(), |
392 | 0 | nsLayoutUtils::GetContainingBlockForClientRect(rootFrame) |
393 | 0 | ); |
394 | 0 | intersectionRect = EdgeInclusiveIntersection( |
395 | 0 | intersectionRectRelativeToRoot, |
396 | 0 | rootIntersectionRect |
397 | 0 | ); |
398 | 0 | if (intersectionRect.isSome() && !isSameDoc) { |
399 | 0 | nsRect rect = intersectionRect.value(); |
400 | 0 | nsPresContext* presContext = targetFrame->PresContext(); |
401 | 0 | nsIFrame* rootScrollFrame = presContext->PresShell()->GetRootScrollFrame(); |
402 | 0 | if (rootScrollFrame) { |
403 | 0 | nsLayoutUtils::TransformRect(rootFrame, rootScrollFrame, rect); |
404 | 0 | } |
405 | 0 | intersectionRect = Some(rect); |
406 | 0 | } |
407 | 0 | } |
408 | 0 |
|
409 | 0 | int64_t targetArea = |
410 | 0 | (int64_t) targetRect.Width() * (int64_t) targetRect.Height(); |
411 | 0 | int64_t intersectionArea = !intersectionRect ? 0 : |
412 | 0 | (int64_t) intersectionRect->Width() * |
413 | 0 | (int64_t) intersectionRect->Height(); |
414 | 0 |
|
415 | 0 | double intersectionRatio; |
416 | 0 | if (targetArea > 0.0) { |
417 | 0 | intersectionRatio = |
418 | 0 | std::min((double) intersectionArea / (double) targetArea, 1.0); |
419 | 0 | } else { |
420 | 0 | intersectionRatio = intersectionRect.isSome() ? 1.0 : 0.0; |
421 | 0 | } |
422 | 0 |
|
423 | 0 | int32_t threshold = -1; |
424 | 0 | if (intersectionRect.isSome()) { |
425 | 0 | // Spec: "Let thresholdIndex be the index of the first entry in |
426 | 0 | // observer.thresholds whose value is greater than intersectionRatio." |
427 | 0 | threshold = mThresholds.IndexOfFirstElementGt(intersectionRatio); |
428 | 0 | if (threshold == 0) { |
429 | 0 | // Per the spec, we should leave threshold at 0 and distinguish between |
430 | 0 | // "less than all thresholds and intersecting" and "not intersecting" |
431 | 0 | // (queuing observer entries as both cases come to pass). However, |
432 | 0 | // neither Chrome nor the WPT tests expect this behavior, so treat these |
433 | 0 | // two cases as one. |
434 | 0 | threshold = -1; |
435 | 0 | } |
436 | 0 | } |
437 | 0 |
|
438 | 0 | if (target->UpdateIntersectionObservation(this, threshold)) { |
439 | 0 | QueueIntersectionObserverEntry( |
440 | 0 | target, time, |
441 | 0 | isInSimilarOriginBrowsingContext == |
442 | 0 | BrowsingContextInfo::DifferentOriginBrowsingContext ? |
443 | 0 | Nothing() : Some(rootIntersectionRect), |
444 | 0 | targetRect, intersectionRect, intersectionRatio |
445 | 0 | ); |
446 | 0 | } |
447 | 0 | } |
448 | 0 | } |
449 | | |
450 | | void |
451 | | DOMIntersectionObserver::QueueIntersectionObserverEntry(Element* aTarget, |
452 | | DOMHighResTimeStamp time, |
453 | | const Maybe<nsRect>& aRootRect, |
454 | | const nsRect& aTargetRect, |
455 | | const Maybe<nsRect>& aIntersectionRect, |
456 | | double aIntersectionRatio) |
457 | 0 | { |
458 | 0 | RefPtr<DOMRect> rootBounds; |
459 | 0 | if (aRootRect.isSome()) { |
460 | 0 | rootBounds = new DOMRect(this); |
461 | 0 | rootBounds->SetLayoutRect(aRootRect.value()); |
462 | 0 | } |
463 | 0 | RefPtr<DOMRect> boundingClientRect = new DOMRect(this); |
464 | 0 | boundingClientRect->SetLayoutRect(aTargetRect); |
465 | 0 | RefPtr<DOMRect> intersectionRect = new DOMRect(this); |
466 | 0 | if (aIntersectionRect.isSome()) { |
467 | 0 | intersectionRect->SetLayoutRect(aIntersectionRect.value()); |
468 | 0 | } |
469 | 0 | RefPtr<DOMIntersectionObserverEntry> entry = new DOMIntersectionObserverEntry( |
470 | 0 | this, |
471 | 0 | time, |
472 | 0 | rootBounds.forget(), |
473 | 0 | boundingClientRect.forget(), |
474 | 0 | intersectionRect.forget(), |
475 | 0 | aIntersectionRect.isSome(), |
476 | 0 | aTarget, aIntersectionRatio); |
477 | 0 | mQueuedEntries.AppendElement(entry.forget()); |
478 | 0 | } |
479 | | |
480 | | void |
481 | | DOMIntersectionObserver::Notify() |
482 | 0 | { |
483 | 0 | if (!mQueuedEntries.Length()) { |
484 | 0 | return; |
485 | 0 | } |
486 | 0 | mozilla::dom::Sequence<mozilla::OwningNonNull<DOMIntersectionObserverEntry>> entries; |
487 | 0 | if (entries.SetCapacity(mQueuedEntries.Length(), mozilla::fallible)) { |
488 | 0 | for (size_t i = 0; i < mQueuedEntries.Length(); ++i) { |
489 | 0 | RefPtr<DOMIntersectionObserverEntry> next = mQueuedEntries[i]; |
490 | 0 | *entries.AppendElement(mozilla::fallible) = next; |
491 | 0 | } |
492 | 0 | } |
493 | 0 | mQueuedEntries.Clear(); |
494 | 0 | mCallback->Call(this, entries, *this); |
495 | 0 | } |
496 | | |
497 | | |
498 | | } // namespace dom |
499 | | } // namespace mozilla |