/src/mozilla-central/image/ProgressTracker.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
2 | | * |
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 |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "ImageLogging.h" |
8 | | #include "ProgressTracker.h" |
9 | | |
10 | | #include "imgIContainer.h" |
11 | | #include "imgINotificationObserver.h" |
12 | | #include "imgIRequest.h" |
13 | | #include "Image.h" |
14 | | #include "nsNetUtil.h" |
15 | | #include "nsIObserverService.h" |
16 | | |
17 | | #include "mozilla/Assertions.h" |
18 | | #include "mozilla/Services.h" |
19 | | #include "mozilla/SystemGroup.h" |
20 | | |
21 | | using mozilla::WeakPtr; |
22 | | |
23 | | namespace mozilla { |
24 | | namespace image { |
25 | | |
26 | | static void |
27 | | CheckProgressConsistency(Progress aOldProgress, Progress aNewProgress, bool aIsMultipart) |
28 | 0 | { |
29 | 0 | // Check preconditions for every progress bit. |
30 | 0 |
|
31 | 0 | // Error's do not get propagated from the tracker for each image part to the |
32 | 0 | // tracker for the multipart image because we don't want one bad part to |
33 | 0 | // prevent the remaining parts from showing. So we need to consider whether |
34 | 0 | // this is a tracker for a multipart image for these assertions to work. |
35 | 0 |
|
36 | 0 | if (aNewProgress & FLAG_SIZE_AVAILABLE) { |
37 | 0 | // No preconditions. |
38 | 0 | } |
39 | 0 | if (aNewProgress & FLAG_DECODE_COMPLETE) { |
40 | 0 | MOZ_ASSERT(aNewProgress & FLAG_SIZE_AVAILABLE); |
41 | 0 | MOZ_ASSERT(aIsMultipart || aNewProgress & (FLAG_FRAME_COMPLETE | FLAG_HAS_ERROR)); |
42 | 0 | } |
43 | 0 | if (aNewProgress & FLAG_FRAME_COMPLETE) { |
44 | 0 | MOZ_ASSERT(aNewProgress & FLAG_SIZE_AVAILABLE); |
45 | 0 | } |
46 | 0 | if (aNewProgress & FLAG_LOAD_COMPLETE) { |
47 | 0 | MOZ_ASSERT(aIsMultipart || aNewProgress & (FLAG_SIZE_AVAILABLE | FLAG_HAS_ERROR)); |
48 | 0 | } |
49 | 0 | if (aNewProgress & FLAG_IS_ANIMATED) { |
50 | 0 | // No preconditions; like FLAG_HAS_TRANSPARENCY, we should normally never |
51 | 0 | // discover this *after* FLAG_SIZE_AVAILABLE, but unfortunately some corrupt |
52 | 0 | // GIFs may fool us. |
53 | 0 | } |
54 | 0 | if (aNewProgress & FLAG_HAS_TRANSPARENCY) { |
55 | 0 | // XXX We'd like to assert that transparency is only set during metadata |
56 | 0 | // decode but we don't have any way to assert that until bug 1254892 is fixed. |
57 | 0 | } |
58 | 0 | if (aNewProgress & FLAG_LAST_PART_COMPLETE) { |
59 | 0 | MOZ_ASSERT(aNewProgress & FLAG_LOAD_COMPLETE); |
60 | 0 | } |
61 | 0 | if (aNewProgress & FLAG_HAS_ERROR) { |
62 | 0 | // No preconditions. |
63 | 0 | } |
64 | 0 | } |
65 | | |
66 | | ProgressTracker::ProgressTracker() |
67 | | : mMutex("ProgressTracker::mMutex") |
68 | | , mImage(nullptr) |
69 | | , mEventTarget(WrapNotNull(nsCOMPtr<nsIEventTarget>(SystemGroup::EventTargetFor(TaskCategory::Other)))) |
70 | | , mObserversWithTargets(0) |
71 | | , mObservers(new ObserverTable) |
72 | | , mProgress(NoProgress) |
73 | | , mIsMultipart(false) |
74 | 0 | { } |
75 | | |
76 | | void |
77 | | ProgressTracker::SetImage(Image* aImage) |
78 | 0 | { |
79 | 0 | MutexAutoLock lock(mMutex); |
80 | 0 | MOZ_ASSERT(aImage, "Setting null image"); |
81 | 0 | MOZ_ASSERT(!mImage, "Setting image when we already have one"); |
82 | 0 | mImage = aImage; |
83 | 0 | } |
84 | | |
85 | | void |
86 | | ProgressTracker::ResetImage() |
87 | 0 | { |
88 | 0 | MutexAutoLock lock(mMutex); |
89 | 0 | MOZ_ASSERT(mImage, "Resetting image when it's already null!"); |
90 | 0 | mImage = nullptr; |
91 | 0 | } |
92 | | |
93 | | uint32_t |
94 | | ProgressTracker::GetImageStatus() const |
95 | 0 | { |
96 | 0 | uint32_t status = imgIRequest::STATUS_NONE; |
97 | 0 |
|
98 | 0 | // Translate our current state to a set of imgIRequest::STATE_* flags. |
99 | 0 | if (mProgress & FLAG_SIZE_AVAILABLE) { |
100 | 0 | status |= imgIRequest::STATUS_SIZE_AVAILABLE; |
101 | 0 | } |
102 | 0 | if (mProgress & FLAG_DECODE_COMPLETE) { |
103 | 0 | status |= imgIRequest::STATUS_DECODE_COMPLETE; |
104 | 0 | } |
105 | 0 | if (mProgress & FLAG_FRAME_COMPLETE) { |
106 | 0 | status |= imgIRequest::STATUS_FRAME_COMPLETE; |
107 | 0 | } |
108 | 0 | if (mProgress & FLAG_LOAD_COMPLETE) { |
109 | 0 | status |= imgIRequest::STATUS_LOAD_COMPLETE; |
110 | 0 | } |
111 | 0 | if (mProgress & FLAG_IS_ANIMATED) { |
112 | 0 | status |= imgIRequest::STATUS_IS_ANIMATED; |
113 | 0 | } |
114 | 0 | if (mProgress & FLAG_HAS_TRANSPARENCY) { |
115 | 0 | status |= imgIRequest::STATUS_HAS_TRANSPARENCY; |
116 | 0 | } |
117 | 0 | if (mProgress & FLAG_HAS_ERROR) { |
118 | 0 | status |= imgIRequest::STATUS_ERROR; |
119 | 0 | } |
120 | 0 |
|
121 | 0 | return status; |
122 | 0 | } |
123 | | |
124 | | // A helper class to allow us to call SyncNotify asynchronously. |
125 | | class AsyncNotifyRunnable : public Runnable |
126 | | { |
127 | | public: |
128 | | AsyncNotifyRunnable(ProgressTracker* aTracker, |
129 | | IProgressObserver* aObserver) |
130 | | : Runnable("ProgressTracker::AsyncNotifyRunnable") |
131 | | , mTracker(aTracker) |
132 | 0 | { |
133 | 0 | MOZ_ASSERT(NS_IsMainThread(), "Should be created on the main thread"); |
134 | 0 | MOZ_ASSERT(aTracker, "aTracker should not be null"); |
135 | 0 | MOZ_ASSERT(aObserver, "aObserver should not be null"); |
136 | 0 | mObservers.AppendElement(aObserver); |
137 | 0 | } |
138 | | |
139 | | NS_IMETHOD Run() override |
140 | 0 | { |
141 | 0 | MOZ_ASSERT(NS_IsMainThread(), "Should be running on the main thread"); |
142 | 0 | MOZ_ASSERT(mTracker, "mTracker should not be null"); |
143 | 0 | for (uint32_t i = 0; i < mObservers.Length(); ++i) { |
144 | 0 | mObservers[i]->ClearPendingNotify(); |
145 | 0 | mTracker->SyncNotify(mObservers[i]); |
146 | 0 | } |
147 | 0 |
|
148 | 0 | mTracker->mRunnable = nullptr; |
149 | 0 | return NS_OK; |
150 | 0 | } |
151 | | |
152 | | void AddObserver(IProgressObserver* aObserver) |
153 | 0 | { |
154 | 0 | mObservers.AppendElement(aObserver); |
155 | 0 | } |
156 | | |
157 | | void RemoveObserver(IProgressObserver* aObserver) |
158 | 0 | { |
159 | 0 | mObservers.RemoveElement(aObserver); |
160 | 0 | } |
161 | | |
162 | | private: |
163 | | friend class ProgressTracker; |
164 | | |
165 | | RefPtr<ProgressTracker> mTracker; |
166 | | nsTArray<RefPtr<IProgressObserver>> mObservers; |
167 | | }; |
168 | | |
169 | | void |
170 | | ProgressTracker::Notify(IProgressObserver* aObserver) |
171 | 0 | { |
172 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
173 | 0 |
|
174 | 0 | if (aObserver->NotificationsDeferred()) { |
175 | 0 | // There is a pending notification, or the observer isn't ready yet. |
176 | 0 | return; |
177 | 0 | } |
178 | 0 | |
179 | 0 | if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) { |
180 | 0 | RefPtr<Image> image = GetImage(); |
181 | 0 | LOG_FUNC_WITH_PARAM(gImgLog, |
182 | 0 | "ProgressTracker::Notify async", "uri", image); |
183 | 0 | } |
184 | 0 |
|
185 | 0 | aObserver->MarkPendingNotify(); |
186 | 0 |
|
187 | 0 | // If we have an existing runnable that we can use, we just append this |
188 | 0 | // observer to its list of observers to be notified. This ensures we don't |
189 | 0 | // unnecessarily delay onload. |
190 | 0 | AsyncNotifyRunnable* runnable = |
191 | 0 | static_cast<AsyncNotifyRunnable*>(mRunnable.get()); |
192 | 0 |
|
193 | 0 | if (runnable) { |
194 | 0 | runnable->AddObserver(aObserver); |
195 | 0 | } else { |
196 | 0 | mRunnable = new AsyncNotifyRunnable(this, aObserver); |
197 | 0 | mEventTarget->Dispatch(mRunnable, NS_DISPATCH_NORMAL); |
198 | 0 | } |
199 | 0 | } |
200 | | |
201 | | // A helper class to allow us to call SyncNotify asynchronously for a given, |
202 | | // fixed, state. |
203 | | class AsyncNotifyCurrentStateRunnable : public Runnable |
204 | | { |
205 | | public: |
206 | | AsyncNotifyCurrentStateRunnable(ProgressTracker* aProgressTracker, |
207 | | IProgressObserver* aObserver) |
208 | | : Runnable("image::AsyncNotifyCurrentStateRunnable") |
209 | | , mProgressTracker(aProgressTracker) |
210 | | , mObserver(aObserver) |
211 | 0 | { |
212 | 0 | MOZ_ASSERT(NS_IsMainThread(), "Should be created on the main thread"); |
213 | 0 | MOZ_ASSERT(mProgressTracker, "mProgressTracker should not be null"); |
214 | 0 | MOZ_ASSERT(mObserver, "mObserver should not be null"); |
215 | 0 | mImage = mProgressTracker->GetImage(); |
216 | 0 | } |
217 | | |
218 | | NS_IMETHOD Run() override |
219 | 0 | { |
220 | 0 | MOZ_ASSERT(NS_IsMainThread(), "Should be running on the main thread"); |
221 | 0 | mObserver->ClearPendingNotify(); |
222 | 0 |
|
223 | 0 | mProgressTracker->SyncNotify(mObserver); |
224 | 0 | return NS_OK; |
225 | 0 | } |
226 | | |
227 | | private: |
228 | | RefPtr<ProgressTracker> mProgressTracker; |
229 | | RefPtr<IProgressObserver> mObserver; |
230 | | |
231 | | // We have to hold on to a reference to the tracker's image, just in case |
232 | | // it goes away while we're in the event queue. |
233 | | RefPtr<Image> mImage; |
234 | | }; |
235 | | |
236 | | void |
237 | | ProgressTracker::NotifyCurrentState(IProgressObserver* aObserver) |
238 | 0 | { |
239 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
240 | 0 |
|
241 | 0 | if (aObserver->NotificationsDeferred()) { |
242 | 0 | // There is a pending notification, or the observer isn't ready yet. |
243 | 0 | return; |
244 | 0 | } |
245 | 0 | |
246 | 0 | if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) { |
247 | 0 | RefPtr<Image> image = GetImage(); |
248 | 0 | LOG_FUNC_WITH_PARAM(gImgLog, |
249 | 0 | "ProgressTracker::NotifyCurrentState", "uri", image); |
250 | 0 | } |
251 | 0 |
|
252 | 0 | aObserver->MarkPendingNotify(); |
253 | 0 |
|
254 | 0 | nsCOMPtr<nsIRunnable> ev = new AsyncNotifyCurrentStateRunnable(this, |
255 | 0 | aObserver); |
256 | 0 | mEventTarget->Dispatch(ev.forget(), NS_DISPATCH_NORMAL); |
257 | 0 | } |
258 | | |
259 | | /** |
260 | | * ImageObserverNotifier is a helper type that abstracts over the difference |
261 | | * between sending notifications to all of the observers in an ObserverTable, |
262 | | * and sending them to a single observer. This allows the same notification code |
263 | | * to be used for both cases. |
264 | | */ |
265 | | template <typename T> struct ImageObserverNotifier; |
266 | | |
267 | | template <> |
268 | | struct MOZ_STACK_CLASS ImageObserverNotifier<const ObserverTable*> |
269 | | { |
270 | | explicit ImageObserverNotifier(const ObserverTable* aObservers, |
271 | | bool aIgnoreDeferral = false) |
272 | | : mObservers(aObservers) |
273 | | , mIgnoreDeferral(aIgnoreDeferral) |
274 | 0 | { } |
275 | | |
276 | | template <typename Lambda> |
277 | | void operator()(Lambda aFunc) |
278 | 0 | { |
279 | 0 | for (auto iter = mObservers->ConstIter(); !iter.Done(); iter.Next()) { |
280 | 0 | RefPtr<IProgressObserver> observer = iter.Data().get(); |
281 | 0 | if (observer && |
282 | 0 | (mIgnoreDeferral || !observer->NotificationsDeferred())) { |
283 | 0 | aFunc(observer); |
284 | 0 | } |
285 | 0 | } |
286 | 0 | } Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#1}>(void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#1}) Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#2}>(void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#2}) Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#3}>(void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#3}) Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#4}>(void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#4}) Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#5}>(void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#5}) Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#6}>(void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#6}) Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#7}>(void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#7}) Unexecuted instantiation: Unified_cpp_image1.cpp:void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<mozilla::image::ProgressTracker::OnUnlockedDraw()::$_5::operator()(mozilla::image::ObserverTable const*) const::{lambda(mozilla::image::IProgressObserver*)#1}>(mozilla::image::ProgressTracker::OnUnlockedDraw()::$_5::operator()(mozilla::image::ObserverTable const*) const::{lambda(mozilla::image::IProgressObserver*)#1}) Unexecuted instantiation: Unified_cpp_image1.cpp:void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<mozilla::image::ProgressTracker::OnDiscard()::$_6::operator()(mozilla::image::ObserverTable const*) const::{lambda(mozilla::image::IProgressObserver*)#1}>(mozilla::image::ProgressTracker::OnDiscard()::$_6::operator()(mozilla::image::ObserverTable const*) const::{lambda(mozilla::image::IProgressObserver*)#1}) Unexecuted instantiation: Unified_cpp_image1.cpp:void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<mozilla::image::ProgressTracker::OnImageAvailable()::$_7::operator()(mozilla::image::ObserverTable const*) const::{lambda(mozilla::image::IProgressObserver*)#1}>(mozilla::image::ProgressTracker::OnImageAvailable()::$_7::operator()(mozilla::image::ObserverTable const*) const::{lambda(mozilla::image::IProgressObserver*)#1}) |
287 | | |
288 | | private: |
289 | | const ObserverTable* mObservers; |
290 | | const bool mIgnoreDeferral; |
291 | | }; |
292 | | |
293 | | template <> |
294 | | struct MOZ_STACK_CLASS ImageObserverNotifier<IProgressObserver*> |
295 | | { |
296 | | explicit ImageObserverNotifier(IProgressObserver* aObserver) |
297 | | : mObserver(aObserver) |
298 | 0 | { } |
299 | | |
300 | | template <typename Lambda> |
301 | | void operator()(Lambda aFunc) |
302 | 0 | { |
303 | 0 | if (mObserver && !mObserver->NotificationsDeferred()) { |
304 | 0 | aFunc(mObserver); |
305 | 0 | } |
306 | 0 | } Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::IProgressObserver*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#1}>(void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#1}) Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::IProgressObserver*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#2}>(void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#2}) Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::IProgressObserver*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#3}>(void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#3}) Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::IProgressObserver*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#4}>(void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#4}) Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::IProgressObserver*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#5}>(void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#5}) Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::IProgressObserver*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#6}>(void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#6}) Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::IProgressObserver*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#7}>(void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#7}) |
307 | | |
308 | | private: |
309 | | IProgressObserver* mObserver; |
310 | | }; |
311 | | |
312 | | template <typename T> void |
313 | | SyncNotifyInternal(const T& aObservers, |
314 | | bool aHasImage, |
315 | | Progress aProgress, |
316 | | const nsIntRect& aDirtyRect) |
317 | 0 | { |
318 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
319 | 0 |
|
320 | 0 | typedef imgINotificationObserver I; |
321 | 0 | ImageObserverNotifier<T> notify(aObservers); |
322 | 0 |
|
323 | 0 | if (aProgress & FLAG_SIZE_AVAILABLE) { |
324 | 0 | notify([](IProgressObserver* aObs) { aObs->Notify(I::SIZE_AVAILABLE); }); Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#1}::operator()(mozilla::image::IProgressObserver*) const Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#1}::operator()(mozilla::image::IProgressObserver*) const |
325 | 0 | } |
326 | 0 |
|
327 | 0 | if (aHasImage) { |
328 | 0 | // OnFrameUpdate |
329 | 0 | // If there's any content in this frame at all (always true for |
330 | 0 | // vector images, true for raster images that have decoded at |
331 | 0 | // least one frame) then send OnFrameUpdate. |
332 | 0 | if (!aDirtyRect.IsEmpty()) { |
333 | 0 | notify([&](IProgressObserver* aObs) { |
334 | 0 | aObs->Notify(I::FRAME_UPDATE, &aDirtyRect); |
335 | 0 | }); Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#2}::operator()(mozilla::image::IProgressObserver*) const Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#2}::operator()(mozilla::image::IProgressObserver*) const |
336 | 0 | } |
337 | 0 |
|
338 | 0 | if (aProgress & FLAG_FRAME_COMPLETE) { |
339 | 0 | notify([](IProgressObserver* aObs) { aObs->Notify(I::FRAME_COMPLETE); }); Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#3}::operator()(mozilla::image::IProgressObserver*) const Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#3}::operator()(mozilla::image::IProgressObserver*) const |
340 | 0 | } |
341 | 0 |
|
342 | 0 | if (aProgress & FLAG_HAS_TRANSPARENCY) { |
343 | 0 | notify([](IProgressObserver* aObs) { aObs->Notify(I::HAS_TRANSPARENCY); }); Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#4}::operator()(mozilla::image::IProgressObserver*) const Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#4}::operator()(mozilla::image::IProgressObserver*) const |
344 | 0 | } |
345 | 0 |
|
346 | 0 | if (aProgress & FLAG_IS_ANIMATED) { |
347 | 0 | notify([](IProgressObserver* aObs) { aObs->Notify(I::IS_ANIMATED); }); Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#5}::operator()(mozilla::image::IProgressObserver*) const Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#5}::operator()(mozilla::image::IProgressObserver*) const |
348 | 0 | } |
349 | 0 | } |
350 | 0 |
|
351 | 0 | if (aProgress & FLAG_DECODE_COMPLETE) { |
352 | 0 | MOZ_ASSERT(aHasImage, "Stopped decoding without ever having an image?"); |
353 | 0 | notify([](IProgressObserver* aObs) { aObs->Notify(I::DECODE_COMPLETE); }); Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#6}::operator()(mozilla::image::IProgressObserver*) const Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#6}::operator()(mozilla::image::IProgressObserver*) const |
354 | 0 | } |
355 | 0 |
|
356 | 0 | if (aProgress & FLAG_LOAD_COMPLETE) { |
357 | 0 | notify([=](IProgressObserver* aObs) { |
358 | 0 | aObs->OnLoadComplete(aProgress & FLAG_LAST_PART_COMPLETE); |
359 | 0 | }); Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#7}::operator()(mozilla::image::IProgressObserver*) const Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#7}::operator()(mozilla::image::IProgressObserver*) const |
360 | 0 | } |
361 | 0 | } Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&) Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&) |
362 | | |
363 | | void |
364 | | ProgressTracker::SyncNotifyProgress(Progress aProgress, |
365 | | const nsIntRect& aInvalidRect |
366 | | /* = nsIntRect() */) |
367 | 0 | { |
368 | 0 | MOZ_ASSERT(NS_IsMainThread(), "Use mObservers on main thread only"); |
369 | 0 |
|
370 | 0 | Progress progress = Difference(aProgress); |
371 | 0 | CheckProgressConsistency(mProgress, mProgress | progress, mIsMultipart); |
372 | 0 |
|
373 | 0 | // Apply the changes. |
374 | 0 | mProgress |= progress; |
375 | 0 |
|
376 | 0 | // Send notifications. |
377 | 0 | mObservers.Read([&](const ObserverTable* aTable) { |
378 | 0 | SyncNotifyInternal(aTable, HasImage(), progress, aInvalidRect); |
379 | 0 | }); |
380 | 0 |
|
381 | 0 | if (progress & FLAG_HAS_ERROR) { |
382 | 0 | FireFailureNotification(); |
383 | 0 | } |
384 | 0 | } |
385 | | |
386 | | void |
387 | | ProgressTracker::SyncNotify(IProgressObserver* aObserver) |
388 | 0 | { |
389 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
390 | 0 |
|
391 | 0 | RefPtr<Image> image = GetImage(); |
392 | 0 | LOG_SCOPE_WITH_PARAM(gImgLog, |
393 | 0 | "ProgressTracker::SyncNotify", "uri", image); |
394 | 0 |
|
395 | 0 | nsIntRect rect; |
396 | 0 | if (image) { |
397 | 0 | int32_t width, height; |
398 | 0 | if (NS_FAILED(image->GetWidth(&width)) || |
399 | 0 | NS_FAILED(image->GetHeight(&height))) { |
400 | 0 | // Either the image has no intrinsic size, or it has an error. |
401 | 0 | rect = GetMaxSizedIntRect(); |
402 | 0 | } else { |
403 | 0 | rect.SizeTo(width, height); |
404 | 0 | } |
405 | 0 | } |
406 | 0 |
|
407 | 0 | SyncNotifyInternal(aObserver, !!image, mProgress, rect); |
408 | 0 | } |
409 | | |
410 | | void |
411 | | ProgressTracker::EmulateRequestFinished(IProgressObserver* aObserver) |
412 | 0 | { |
413 | 0 | MOZ_ASSERT(NS_IsMainThread(), |
414 | 0 | "SyncNotifyState and mObservers are not threadsafe"); |
415 | 0 | RefPtr<IProgressObserver> kungFuDeathGrip(aObserver); |
416 | 0 |
|
417 | 0 | if (!(mProgress & FLAG_LOAD_COMPLETE)) { |
418 | 0 | aObserver->OnLoadComplete(true); |
419 | 0 | } |
420 | 0 | } |
421 | | |
422 | | already_AddRefed<nsIEventTarget> |
423 | | ProgressTracker::GetEventTarget() const |
424 | 0 | { |
425 | 0 | MutexAutoLock lock(mMutex); |
426 | 0 | nsCOMPtr<nsIEventTarget> target = mEventTarget; |
427 | 0 | return target.forget(); |
428 | 0 | } |
429 | | |
430 | | void |
431 | | ProgressTracker::AddObserver(IProgressObserver* aObserver) |
432 | 0 | { |
433 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
434 | 0 | RefPtr<IProgressObserver> observer = aObserver; |
435 | 0 |
|
436 | 0 | nsCOMPtr<nsIEventTarget> target = observer->GetEventTarget(); |
437 | 0 | if (target) { |
438 | 0 | if (mObserversWithTargets == 0) { |
439 | 0 | // On the first observer with a target (i.e. listener), always accept its |
440 | 0 | // event target; this may be for a specific DocGroup, or it may be the |
441 | 0 | // unlabelled main thread target. |
442 | 0 | MutexAutoLock lock(mMutex); |
443 | 0 | mEventTarget = WrapNotNull(target); |
444 | 0 | } else if (mEventTarget.get() != target.get()) { |
445 | 0 | // If a subsequent observer comes in with a different target, we need to |
446 | 0 | // switch to use the unlabelled main thread target, if we haven't already. |
447 | 0 | MutexAutoLock lock(mMutex); |
448 | 0 | nsCOMPtr<nsIEventTarget> mainTarget(do_GetMainThread()); |
449 | 0 | mEventTarget = WrapNotNull(mainTarget); |
450 | 0 | } |
451 | 0 | ++mObserversWithTargets; |
452 | 0 | } |
453 | 0 |
|
454 | 0 | mObservers.Write([=](ObserverTable* aTable) { |
455 | 0 | MOZ_ASSERT(!aTable->Get(observer, nullptr), |
456 | 0 | "Adding duplicate entry for image observer"); |
457 | 0 |
|
458 | 0 | WeakPtr<IProgressObserver> weakPtr = observer.get(); |
459 | 0 | aTable->Put(observer, weakPtr); |
460 | 0 | }); |
461 | 0 |
|
462 | 0 | MOZ_ASSERT(mObserversWithTargets <= ObserverCount()); |
463 | 0 | } |
464 | | |
465 | | bool |
466 | | ProgressTracker::RemoveObserver(IProgressObserver* aObserver) |
467 | 0 | { |
468 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
469 | 0 | RefPtr<IProgressObserver> observer = aObserver; |
470 | 0 |
|
471 | 0 | // Remove the observer from the list. |
472 | 0 | bool removed = mObservers.Write([observer](ObserverTable* aTable) { |
473 | 0 | return aTable->Remove(observer); |
474 | 0 | }); |
475 | 0 |
|
476 | 0 | // Sometimes once an image is decoded, and all of its observers removed, a new |
477 | 0 | // document may request the same image. Thus we need to clear our event target |
478 | 0 | // state when the last observer is removed, so that we select the most |
479 | 0 | // appropriate event target when a new observer is added. Since the event |
480 | 0 | // target may have changed (e.g. due to the scheduler group going away before |
481 | 0 | // we were removed), so we should be cautious comparing this target against |
482 | 0 | // anything at this stage. |
483 | 0 | if (removed) { |
484 | 0 | nsCOMPtr<nsIEventTarget> target = observer->GetEventTarget(); |
485 | 0 | if (target) { |
486 | 0 | MOZ_ASSERT(mObserversWithTargets > 0); |
487 | 0 | --mObserversWithTargets; |
488 | 0 |
|
489 | 0 | if (mObserversWithTargets == 0) { |
490 | 0 | MutexAutoLock lock(mMutex); |
491 | 0 | nsCOMPtr<nsIEventTarget> target(SystemGroup::EventTargetFor(TaskCategory::Other)); |
492 | 0 | mEventTarget = WrapNotNull(target); |
493 | 0 | } |
494 | 0 | } |
495 | 0 |
|
496 | 0 | MOZ_ASSERT(mObserversWithTargets <= ObserverCount()); |
497 | 0 | } |
498 | 0 |
|
499 | 0 | // Observers can get confused if they don't get all the proper teardown |
500 | 0 | // notifications. Part ways on good terms. |
501 | 0 | if (removed && !aObserver->NotificationsDeferred()) { |
502 | 0 | EmulateRequestFinished(aObserver); |
503 | 0 | } |
504 | 0 |
|
505 | 0 | // Make sure we don't give callbacks to an observer that isn't interested in |
506 | 0 | // them any more. |
507 | 0 | AsyncNotifyRunnable* runnable = |
508 | 0 | static_cast<AsyncNotifyRunnable*>(mRunnable.get()); |
509 | 0 |
|
510 | 0 | if (aObserver->NotificationsDeferred() && runnable) { |
511 | 0 | runnable->RemoveObserver(aObserver); |
512 | 0 | aObserver->ClearPendingNotify(); |
513 | 0 | } |
514 | 0 |
|
515 | 0 | return removed; |
516 | 0 | } |
517 | | |
518 | | uint32_t |
519 | | ProgressTracker::ObserverCount() const |
520 | 0 | { |
521 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
522 | 0 | return mObservers.Read([](const ObserverTable* aTable) { |
523 | 0 | return aTable->Count(); |
524 | 0 | }); |
525 | 0 | } |
526 | | |
527 | | void |
528 | | ProgressTracker::OnUnlockedDraw() |
529 | 0 | { |
530 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
531 | 0 | mObservers.Read([](const ObserverTable* aTable) { |
532 | 0 | ImageObserverNotifier<const ObserverTable*> notify(aTable); |
533 | 0 | notify([](IProgressObserver* aObs) { |
534 | 0 | aObs->Notify(imgINotificationObserver::UNLOCKED_DRAW); |
535 | 0 | }); |
536 | 0 | }); |
537 | 0 | } |
538 | | |
539 | | void |
540 | | ProgressTracker::ResetForNewRequest() |
541 | 0 | { |
542 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
543 | 0 | mProgress = NoProgress; |
544 | 0 | } |
545 | | |
546 | | void |
547 | | ProgressTracker::OnDiscard() |
548 | 0 | { |
549 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
550 | 0 | mObservers.Read([](const ObserverTable* aTable) { |
551 | 0 | ImageObserverNotifier<const ObserverTable*> notify(aTable); |
552 | 0 | notify([](IProgressObserver* aObs) { |
553 | 0 | aObs->Notify(imgINotificationObserver::DISCARD); |
554 | 0 | }); |
555 | 0 | }); |
556 | 0 | } |
557 | | |
558 | | void |
559 | | ProgressTracker::OnImageAvailable() |
560 | 0 | { |
561 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
562 | 0 | // Notify any imgRequestProxys that are observing us that we have an Image. |
563 | 0 | mObservers.Read([](const ObserverTable* aTable) { |
564 | 0 | ImageObserverNotifier<const ObserverTable*> |
565 | 0 | notify(aTable, /* aIgnoreDeferral = */ true); |
566 | 0 | notify([](IProgressObserver* aObs) { |
567 | 0 | aObs->SetHasImage(); |
568 | 0 | }); |
569 | 0 | }); |
570 | 0 | } |
571 | | |
572 | | void |
573 | | ProgressTracker::FireFailureNotification() |
574 | 0 | { |
575 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
576 | 0 |
|
577 | 0 | // Some kind of problem has happened with image decoding. |
578 | 0 | // Report the URI to net:failed-to-process-uri-conent observers. |
579 | 0 | RefPtr<Image> image = GetImage(); |
580 | 0 | if (image) { |
581 | 0 | // Should be on main thread, so ok to create a new nsIURI. |
582 | 0 | nsCOMPtr<nsIURI> uri = image->GetURI(); |
583 | 0 | if (uri) { |
584 | 0 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
585 | 0 | if (os) { |
586 | 0 | os->NotifyObservers(uri, "net:failed-to-process-uri-content", nullptr); |
587 | 0 | } |
588 | 0 | } |
589 | 0 | } |
590 | 0 | } |
591 | | |
592 | | } // namespace image |
593 | | } // namespace mozilla |