Bug 1728955: part 3) Add logging to `nsBaseClipboard`. r=masayuki
[gecko.git] / dom / base / ResizeObserverController.cpp
blobe6632802078817e01df0d1768ddd591bde6a58d6
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/. */
7 #include "mozilla/dom/ResizeObserverController.h"
9 #include "mozilla/dom/BrowsingContext.h"
10 #include "mozilla/dom/Document.h"
11 #include "mozilla/dom/ErrorEvent.h"
12 #include "mozilla/dom/RootedDictionary.h"
13 #include "mozilla/PresShell.h"
14 #include "mozilla/Unused.h"
15 #include "nsPresContext.h"
16 #include "nsRefreshDriver.h"
17 #include <limits>
19 namespace mozilla::dom {
21 void ResizeObserverNotificationHelper::WillRefresh(TimeStamp aTime) {
22 MOZ_DIAGNOSTIC_ASSERT(mOwner, "Should've de-registered on-time!");
23 mOwner->Notify();
24 // Note that mOwner may be null / dead here.
27 nsRefreshDriver* ResizeObserverNotificationHelper::GetRefreshDriver() const {
28 PresShell* presShell = mOwner->GetPresShell();
29 if (MOZ_UNLIKELY(!presShell)) {
30 return nullptr;
33 nsPresContext* presContext = presShell->GetPresContext();
34 if (MOZ_UNLIKELY(!presContext)) {
35 return nullptr;
38 return presContext->RefreshDriver();
41 void ResizeObserverNotificationHelper::Register() {
42 if (mRegistered) {
43 return;
46 nsRefreshDriver* refreshDriver = GetRefreshDriver();
47 if (!refreshDriver) {
48 // We maybe navigating away from this page or currently in an iframe with
49 // display: none. Just abort the Register(), no need to do notification.
50 return;
53 refreshDriver->AddRefreshObserver(this, FlushType::Display, "ResizeObserver");
54 mRegistered = true;
57 void ResizeObserverNotificationHelper::Unregister() {
58 if (!mRegistered) {
59 return;
62 nsRefreshDriver* refreshDriver = GetRefreshDriver();
63 MOZ_RELEASE_ASSERT(
64 refreshDriver,
65 "We should not leave a dangling reference to the observer around");
67 bool rv = refreshDriver->RemoveRefreshObserver(this, FlushType::Display);
68 MOZ_DIAGNOSTIC_ASSERT(rv, "Should remove the observer successfully");
69 Unused << rv;
71 mRegistered = false;
74 ResizeObserverNotificationHelper::~ResizeObserverNotificationHelper() {
75 MOZ_RELEASE_ASSERT(!mRegistered, "How can we die when registered?");
76 MOZ_RELEASE_ASSERT(!mOwner, "Forgot to clear weak pointer?");
79 void ResizeObserverController::ShellDetachedFromDocument() {
80 mResizeObserverNotificationHelper->Unregister();
83 static void FlushLayoutForWholeBrowsingContextTree(Document& aDoc) {
84 if (BrowsingContext* bc = aDoc.GetBrowsingContext()) {
85 RefPtr<BrowsingContext> top = bc->Top();
86 top->PreOrderWalk([](BrowsingContext* aCur) {
87 if (Document* doc = aCur->GetExtantDocument()) {
88 doc->FlushPendingNotifications(FlushType::Layout);
90 });
91 } else {
92 // If there is no browsing context, we just flush this document itself.
93 aDoc.FlushPendingNotifications(FlushType::Layout);
97 void ResizeObserverController::Notify() {
98 if (mResizeObservers.IsEmpty()) {
99 return;
102 // We may call BroadcastAllActiveObservations(), which might cause mDocument
103 // to be destroyed (and the ResizeObserverController with it).
104 // e.g. if mDocument is in an iframe, and the observer JS removes it from the
105 // parent document and we trigger an unlucky GC/CC (or maybe if the observer
106 // JS navigates to a different URL). Or the author uses elem.offsetTop API,
107 // which could flush style + layout and make the document destroyed if we're
108 // inside an iframe that has suddenly become |display:none| via the author
109 // doing something in their ResizeObserver callback.
111 // Therefore, we ref-count mDocument here to make sure it and its members
112 // (e.g. mResizeObserverController, which is `this` pointer) are still alive
113 // in the rest of this function because after it goes up, `this` is possible
114 // deleted.
115 RefPtr<Document> doc(mDocument);
117 uint32_t shallowestTargetDepth = 0;
119 GatherAllActiveObservations(shallowestTargetDepth);
121 while (HasAnyActiveObservations()) {
122 DebugOnly<uint32_t> oldShallowestTargetDepth = shallowestTargetDepth;
123 shallowestTargetDepth = BroadcastAllActiveObservations();
124 NS_ASSERTION(oldShallowestTargetDepth < shallowestTargetDepth,
125 "shallowestTargetDepth should be getting strictly deeper");
127 // Flush layout, so that any callback functions' style changes / resizes
128 // get a chance to take effect. The callback functions may do changes in its
129 // sub-documents or ancestors, so flushing layout for the whole browsing
130 // context tree makes sure we don't miss anyone.
131 FlushLayoutForWholeBrowsingContextTree(*doc);
133 // To avoid infinite resize loop, we only gather all active observations
134 // that have the depth of observed target element more than current
135 // shallowestTargetDepth.
136 GatherAllActiveObservations(shallowestTargetDepth);
139 mResizeObserverNotificationHelper->Unregister();
141 if (HasAnySkippedObservations()) {
142 // Per spec, we deliver an error if the document has any skipped
143 // observations. Also, we re-register via ScheduleNotification().
144 RootedDictionary<ErrorEventInit> init(RootingCx());
146 init.mMessage.AssignLiteral(
147 "ResizeObserver loop completed with undelivered notifications.");
148 init.mBubbles = false;
149 init.mCancelable = false;
151 nsEventStatus status = nsEventStatus_eIgnore;
153 if (nsCOMPtr<nsPIDOMWindowInner> window = doc->GetInnerWindow()) {
154 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(window);
155 MOZ_ASSERT(sgo);
157 if (NS_WARN_IF(sgo->HandleScriptError(init, &status))) {
158 status = nsEventStatus_eIgnore;
160 } else {
161 // We don't fire error events at any global for non-window JS on the main
162 // thread.
165 // We need to deliver pending notifications in next cycle.
166 ScheduleNotification();
170 void ResizeObserverController::GatherAllActiveObservations(uint32_t aDepth) {
171 for (ResizeObserver* observer : mResizeObservers) {
172 observer->GatherActiveObservations(aDepth);
176 uint32_t ResizeObserverController::BroadcastAllActiveObservations() {
177 uint32_t shallowestTargetDepth = std::numeric_limits<uint32_t>::max();
179 // Copy the observers as this invokes the callbacks and could register and
180 // unregister observers at will.
181 const auto observers =
182 ToTArray<nsTArray<RefPtr<ResizeObserver>>>(mResizeObservers);
183 for (auto& observer : observers) {
184 // MOZ_KnownLive because 'observers' is guaranteed to keep it
185 // alive.
187 // This can go away once
188 // https://bugzilla.mozilla.org/show_bug.cgi?id=1620312 is fixed.
189 uint32_t targetDepth =
190 MOZ_KnownLive(observer)->BroadcastActiveObservations();
191 if (targetDepth < shallowestTargetDepth) {
192 shallowestTargetDepth = targetDepth;
196 return shallowestTargetDepth;
199 bool ResizeObserverController::HasAnyActiveObservations() const {
200 for (auto& observer : mResizeObservers) {
201 if (observer->HasActiveObservations()) {
202 return true;
205 return false;
208 bool ResizeObserverController::HasAnySkippedObservations() const {
209 for (auto& observer : mResizeObservers) {
210 if (observer->HasSkippedObservations()) {
211 return true;
214 return false;
217 void ResizeObserverController::ScheduleNotification() {
218 mResizeObserverNotificationHelper->Register();
221 ResizeObserverController::~ResizeObserverController() {
222 MOZ_RELEASE_ASSERT(
223 !mResizeObserverNotificationHelper->IsRegistered(),
224 "Nothing else should keep a reference to our helper when we go away");
225 mResizeObserverNotificationHelper->DetachFromOwner();
228 void ResizeObserverController::AddSizeOfIncludingThis(
229 nsWindowSizes& aSizes) const {
230 MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
231 size_t size = mallocSizeOf(this);
232 size += mResizeObservers.ShallowSizeOfExcludingThis(mallocSizeOf);
233 // TODO(emilio): Measure the observers individually or something? They aren't
234 // really owned by us.
235 aSizes.mDOMSizes.mDOMResizeObserverControllerSize += size;
238 } // namespace mozilla::dom