/src/mozilla-central/image/imgRequestProxy.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 "imgRequestProxy.h" |
8 | | |
9 | | #include "ImageLogging.h" |
10 | | #include "imgLoader.h" |
11 | | #include "Image.h" |
12 | | #include "ImageOps.h" |
13 | | #include "nsError.h" |
14 | | #include "nsCRTGlue.h" |
15 | | #include "imgINotificationObserver.h" |
16 | | #include "mozilla/dom/TabGroup.h" // for TabGroup |
17 | | #include "mozilla/dom/DocGroup.h" // for DocGroup |
18 | | #include "mozilla/Move.h" |
19 | | #include "mozilla/Telemetry.h" // for Telemetry |
20 | | |
21 | | using namespace mozilla; |
22 | | using namespace mozilla::image; |
23 | | |
24 | | // The split of imgRequestProxy and imgRequestProxyStatic means that |
25 | | // certain overridden functions need to be usable in the destructor. |
26 | | // Since virtual functions can't be used in that way, this class |
27 | | // provides a behavioural trait for each class to use instead. |
28 | | class ProxyBehaviour |
29 | | { |
30 | | public: |
31 | 0 | virtual ~ProxyBehaviour() = default; |
32 | | |
33 | | virtual already_AddRefed<mozilla::image::Image> GetImage() const = 0; |
34 | | virtual bool HasImage() const = 0; |
35 | | virtual already_AddRefed<ProgressTracker> GetProgressTracker() const = 0; |
36 | | virtual imgRequest* GetOwner() const = 0; |
37 | | virtual void SetOwner(imgRequest* aOwner) = 0; |
38 | | }; |
39 | | |
40 | | class RequestBehaviour : public ProxyBehaviour |
41 | | { |
42 | | public: |
43 | 0 | RequestBehaviour() : mOwner(nullptr), mOwnerHasImage(false) {} |
44 | | |
45 | | already_AddRefed<mozilla::image::Image>GetImage() const override; |
46 | | bool HasImage() const override; |
47 | | already_AddRefed<ProgressTracker> GetProgressTracker() const override; |
48 | | |
49 | 0 | imgRequest* GetOwner() const override { |
50 | 0 | return mOwner; |
51 | 0 | } |
52 | | |
53 | 0 | void SetOwner(imgRequest* aOwner) override { |
54 | 0 | mOwner = aOwner; |
55 | 0 |
|
56 | 0 | if (mOwner) { |
57 | 0 | RefPtr<ProgressTracker> ownerProgressTracker = GetProgressTracker(); |
58 | 0 | mOwnerHasImage = ownerProgressTracker && ownerProgressTracker->HasImage(); |
59 | 0 | } else { |
60 | 0 | mOwnerHasImage = false; |
61 | 0 | } |
62 | 0 | } |
63 | | |
64 | | private: |
65 | | // We maintain the following invariant: |
66 | | // The proxy is registered at most with a single imgRequest as an observer, |
67 | | // and whenever it is, mOwner points to that object. This helps ensure that |
68 | | // imgRequestProxy::~imgRequestProxy unregisters the proxy as an observer |
69 | | // from whatever request it was registered with (if any). This, in turn, |
70 | | // means that imgRequest::mObservers will not have any stale pointers in it. |
71 | | RefPtr<imgRequest> mOwner; |
72 | | |
73 | | bool mOwnerHasImage; |
74 | | }; |
75 | | |
76 | | already_AddRefed<mozilla::image::Image> |
77 | | RequestBehaviour::GetImage() const |
78 | 0 | { |
79 | 0 | if (!mOwnerHasImage) { |
80 | 0 | return nullptr; |
81 | 0 | } |
82 | 0 | RefPtr<ProgressTracker> progressTracker = GetProgressTracker(); |
83 | 0 | return progressTracker->GetImage(); |
84 | 0 | } |
85 | | |
86 | | already_AddRefed<ProgressTracker> |
87 | | RequestBehaviour::GetProgressTracker() const |
88 | 0 | { |
89 | 0 | // NOTE: It's possible that our mOwner has an Image that it didn't notify |
90 | 0 | // us about, if we were Canceled before its Image was constructed. |
91 | 0 | // (Canceling removes us as an observer, so mOwner has no way to notify us). |
92 | 0 | // That's why this method uses mOwner->GetProgressTracker() instead of just |
93 | 0 | // mOwner->mProgressTracker -- we might have a null mImage and yet have an |
94 | 0 | // mOwner with a non-null mImage (and a null mProgressTracker pointer). |
95 | 0 | return mOwner->GetProgressTracker(); |
96 | 0 | } |
97 | | |
98 | | NS_IMPL_ADDREF(imgRequestProxy) |
99 | | NS_IMPL_RELEASE(imgRequestProxy) |
100 | | |
101 | 0 | NS_INTERFACE_MAP_BEGIN(imgRequestProxy) |
102 | 0 | NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, imgIRequest) |
103 | 0 | NS_INTERFACE_MAP_ENTRY(imgIRequest) |
104 | 0 | NS_INTERFACE_MAP_ENTRY(nsIRequest) |
105 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupportsPriority) |
106 | 0 | NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITimedChannel, |
107 | 0 | TimedChannel() != nullptr) |
108 | 0 | NS_INTERFACE_MAP_END |
109 | | |
110 | | imgRequestProxy::imgRequestProxy() : |
111 | | mBehaviour(new RequestBehaviour), |
112 | | mURI(nullptr), |
113 | | mListener(nullptr), |
114 | | mLoadFlags(nsIRequest::LOAD_NORMAL), |
115 | | mLockCount(0), |
116 | | mAnimationConsumers(0), |
117 | | mCanceled(false), |
118 | | mIsInLoadGroup(false), |
119 | | mForceDispatchLoadGroup(false), |
120 | | mListenerIsStrongRef(false), |
121 | | mDecodeRequested(false), |
122 | | mPendingNotify(false), |
123 | | mValidating(false), |
124 | | mHadListener(false), |
125 | | mHadDispatch(false) |
126 | 0 | { |
127 | 0 | /* member initializers and constructor code */ |
128 | 0 | LOG_FUNC(gImgLog, "imgRequestProxy::imgRequestProxy"); |
129 | 0 | } |
130 | | |
131 | | imgRequestProxy::~imgRequestProxy() |
132 | 0 | { |
133 | 0 | /* destructor code */ |
134 | 0 | MOZ_ASSERT(!mListener, |
135 | 0 | "Someone forgot to properly cancel this request!"); |
136 | 0 |
|
137 | 0 | // If we had a listener, that means we would have issued notifications. With |
138 | 0 | // bug 1359833, we added support for main thread scheduler groups. Each |
139 | 0 | // imgRequestProxy may have its own associated listener, document and/or |
140 | 0 | // scheduler group. Typically most imgRequestProxy belong to the same |
141 | 0 | // document, or have no listener, which means we will want to execute all main |
142 | 0 | // thread code in that shared scheduler group. Less frequently, there may be |
143 | 0 | // multiple imgRequests and they have separate documents, which means that |
144 | 0 | // when we issue state notifications, some or all need to be dispatched to the |
145 | 0 | // appropriate scheduler group for each request. This should be rare, so we |
146 | 0 | // want to monitor the frequency of dispatching in the wild. |
147 | 0 | if (mHadListener) { |
148 | 0 | mozilla::Telemetry::Accumulate(mozilla::Telemetry::IMAGE_REQUEST_DISPATCHED, |
149 | 0 | mHadDispatch); |
150 | 0 | } |
151 | 0 |
|
152 | 0 | MOZ_RELEASE_ASSERT(!mLockCount, "Someone forgot to unlock on time?"); |
153 | 0 |
|
154 | 0 | ClearAnimationConsumers(); |
155 | 0 |
|
156 | 0 | // Explicitly set mListener to null to ensure that the RemoveProxy |
157 | 0 | // call below can't send |this| to an arbitrary listener while |this| |
158 | 0 | // is being destroyed. This is all belt-and-suspenders in view of the |
159 | 0 | // above assert. |
160 | 0 | NullOutListener(); |
161 | 0 |
|
162 | 0 | /* Call RemoveProxy with a successful status. This will keep the |
163 | 0 | channel, if still downloading data, from being canceled if 'this' is |
164 | 0 | the last observer. This allows the image to continue to download and |
165 | 0 | be cached even if no one is using it currently. |
166 | 0 | */ |
167 | 0 | mCanceled = true; |
168 | 0 | RemoveFromOwner(NS_OK); |
169 | 0 |
|
170 | 0 | RemoveFromLoadGroup(); |
171 | 0 | LOG_FUNC(gImgLog, "imgRequestProxy::~imgRequestProxy"); |
172 | 0 | } |
173 | | |
174 | | nsresult |
175 | | imgRequestProxy::Init(imgRequest* aOwner, |
176 | | nsILoadGroup* aLoadGroup, |
177 | | nsIDocument* aLoadingDocument, |
178 | | nsIURI* aURI, |
179 | | imgINotificationObserver* aObserver) |
180 | 0 | { |
181 | 0 | MOZ_ASSERT(!GetOwner() && !mListener, |
182 | 0 | "imgRequestProxy is already initialized"); |
183 | 0 |
|
184 | 0 | LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequestProxy::Init", "request", |
185 | 0 | aOwner); |
186 | 0 |
|
187 | 0 | MOZ_ASSERT(mAnimationConsumers == 0, "Cannot have animation before Init"); |
188 | 0 |
|
189 | 0 | mBehaviour->SetOwner(aOwner); |
190 | 0 | mListener = aObserver; |
191 | 0 | // Make sure to addref mListener before the AddToOwner call below, since |
192 | 0 | // that call might well want to release it if the imgRequest has |
193 | 0 | // already seen OnStopRequest. |
194 | 0 | if (mListener) { |
195 | 0 | mHadListener = true; |
196 | 0 | mListenerIsStrongRef = true; |
197 | 0 | NS_ADDREF(mListener); |
198 | 0 | } |
199 | 0 | mLoadGroup = aLoadGroup; |
200 | 0 | mURI = aURI; |
201 | 0 |
|
202 | 0 | // Note: AddToOwner won't send all the On* notifications immediately |
203 | 0 | AddToOwner(aLoadingDocument); |
204 | 0 |
|
205 | 0 | return NS_OK; |
206 | 0 | } |
207 | | |
208 | | nsresult |
209 | | imgRequestProxy::ChangeOwner(imgRequest* aNewOwner) |
210 | 0 | { |
211 | 0 | MOZ_ASSERT(GetOwner(), |
212 | 0 | "Cannot ChangeOwner on a proxy without an owner!"); |
213 | 0 |
|
214 | 0 | if (mCanceled) { |
215 | 0 | // Ensure that this proxy has received all notifications to date |
216 | 0 | // before we clean it up when removing it from the old owner below. |
217 | 0 | SyncNotifyListener(); |
218 | 0 | } |
219 | 0 |
|
220 | 0 | // If we're holding locks, unlock the old image. |
221 | 0 | // Note that UnlockImage decrements mLockCount each time it's called. |
222 | 0 | uint32_t oldLockCount = mLockCount; |
223 | 0 | while (mLockCount) { |
224 | 0 | UnlockImage(); |
225 | 0 | } |
226 | 0 |
|
227 | 0 | // If we're holding animation requests, undo them. |
228 | 0 | uint32_t oldAnimationConsumers = mAnimationConsumers; |
229 | 0 | ClearAnimationConsumers(); |
230 | 0 |
|
231 | 0 | GetOwner()->RemoveProxy(this, NS_OK); |
232 | 0 |
|
233 | 0 | mBehaviour->SetOwner(aNewOwner); |
234 | 0 | MOZ_ASSERT(!GetValidator(), "New owner cannot be validating!"); |
235 | 0 |
|
236 | 0 | // If we were locked, apply the locks here |
237 | 0 | for (uint32_t i = 0; i < oldLockCount; i++) { |
238 | 0 | LockImage(); |
239 | 0 | } |
240 | 0 |
|
241 | 0 | // If we had animation requests, restore them here. Note that we |
242 | 0 | // do this *after* RemoveProxy, which clears out animation consumers |
243 | 0 | // (see bug 601723). |
244 | 0 | for (uint32_t i = 0; i < oldAnimationConsumers; i++) { |
245 | 0 | IncrementAnimationConsumers(); |
246 | 0 | } |
247 | 0 |
|
248 | 0 | AddToOwner(nullptr); |
249 | 0 | return NS_OK; |
250 | 0 | } |
251 | | |
252 | | void |
253 | | imgRequestProxy::MarkValidating() |
254 | 0 | { |
255 | 0 | MOZ_ASSERT(GetValidator()); |
256 | 0 | mValidating = true; |
257 | 0 | } |
258 | | |
259 | | void |
260 | | imgRequestProxy::ClearValidating() |
261 | 0 | { |
262 | 0 | MOZ_ASSERT(mValidating); |
263 | 0 | MOZ_ASSERT(!GetValidator()); |
264 | 0 | mValidating = false; |
265 | 0 |
|
266 | 0 | // If we'd previously requested a synchronous decode, request a decode on the |
267 | 0 | // new image. |
268 | 0 | if (mDecodeRequested) { |
269 | 0 | mDecodeRequested = false; |
270 | 0 | StartDecoding(imgIContainer::FLAG_NONE); |
271 | 0 | } |
272 | 0 | } |
273 | | |
274 | | bool |
275 | | imgRequestProxy::IsOnEventTarget() const |
276 | 0 | { |
277 | 0 | // Ensure we are in some main thread context because the scheduler group |
278 | 0 | // methods are only safe to call on the main thread. |
279 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
280 | 0 |
|
281 | 0 | if (mTabGroup) { |
282 | 0 | MOZ_ASSERT(mEventTarget); |
283 | 0 | return mTabGroup->IsSafeToRun(); |
284 | 0 | } |
285 | 0 |
|
286 | 0 | if (mListener) { |
287 | 0 | // If we have no scheduler group but we do have a listener, then we know |
288 | 0 | // that the listener requires unlabelled dispatch. |
289 | 0 | MOZ_ASSERT(mEventTarget); |
290 | 0 | return mozilla::SchedulerGroup::IsSafeToRunUnlabeled(); |
291 | 0 | } |
292 | 0 |
|
293 | 0 | // No listener means it is always safe, as there is nothing to do. |
294 | 0 | return true; |
295 | 0 | } |
296 | | |
297 | | already_AddRefed<nsIEventTarget> |
298 | | imgRequestProxy::GetEventTarget() const |
299 | 0 | { |
300 | 0 | nsCOMPtr<nsIEventTarget> target(mEventTarget); |
301 | 0 | return target.forget(); |
302 | 0 | } |
303 | | |
304 | | nsresult |
305 | | imgRequestProxy::DispatchWithTargetIfAvailable(already_AddRefed<nsIRunnable> aEvent) |
306 | 0 | { |
307 | 0 | LOG_FUNC(gImgLog, "imgRequestProxy::DispatchWithTargetIfAvailable"); |
308 | 0 |
|
309 | 0 | // This method should only be used when it is *expected* that we are |
310 | 0 | // dispatching an event (e.g. we want to handle an event asynchronously) |
311 | 0 | // rather we need to (e.g. we are in the wrong scheduler group context). |
312 | 0 | // As such, we do not set mHadDispatch for telemetry purposes. |
313 | 0 | if (mEventTarget) { |
314 | 0 | mEventTarget->Dispatch(std::move(aEvent), NS_DISPATCH_NORMAL); |
315 | 0 | return NS_OK; |
316 | 0 | } |
317 | 0 |
|
318 | 0 | return NS_DispatchToMainThread(std::move(aEvent)); |
319 | 0 | } |
320 | | |
321 | | void |
322 | | imgRequestProxy::DispatchWithTarget(already_AddRefed<nsIRunnable> aEvent) |
323 | 0 | { |
324 | 0 | LOG_FUNC(gImgLog, "imgRequestProxy::DispatchWithTarget"); |
325 | 0 |
|
326 | 0 | MOZ_ASSERT(mListener || mTabGroup); |
327 | 0 | MOZ_ASSERT(mEventTarget); |
328 | 0 |
|
329 | 0 | mHadDispatch = true; |
330 | 0 | mEventTarget->Dispatch(std::move(aEvent), NS_DISPATCH_NORMAL); |
331 | 0 | } |
332 | | |
333 | | void |
334 | | imgRequestProxy::AddToOwner(nsIDocument* aLoadingDocument) |
335 | 0 | { |
336 | 0 | // An imgRequestProxy can be initialized with neither a listener nor a |
337 | 0 | // document. The caller could follow up later by cloning the canonical |
338 | 0 | // imgRequestProxy with the actual listener. This is possible because |
339 | 0 | // imgLoader::LoadImage does not require a valid listener to be provided. |
340 | 0 | // |
341 | 0 | // Without a listener, we don't need to set our scheduler group, because |
342 | 0 | // we have nothing to signal. However if we were told what document this |
343 | 0 | // is for, it is likely that future listeners will belong to the same |
344 | 0 | // scheduler group. |
345 | 0 | // |
346 | 0 | // With a listener, we always need to update our scheduler group. A null |
347 | 0 | // scheduler group is valid with or without a document, but that means |
348 | 0 | // we will use the most generic event target possible on dispatch. |
349 | 0 | if (aLoadingDocument) { |
350 | 0 | RefPtr<mozilla::dom::DocGroup> docGroup = aLoadingDocument->GetDocGroup(); |
351 | 0 | if (docGroup) { |
352 | 0 | mTabGroup = docGroup->GetTabGroup(); |
353 | 0 | MOZ_ASSERT(mTabGroup); |
354 | 0 |
|
355 | 0 | mEventTarget = docGroup->EventTargetFor(mozilla::TaskCategory::Other); |
356 | 0 | MOZ_ASSERT(mEventTarget); |
357 | 0 | } |
358 | 0 | } |
359 | 0 |
|
360 | 0 | if (mListener && !mEventTarget) { |
361 | 0 | mEventTarget = do_GetMainThread(); |
362 | 0 | } |
363 | 0 |
|
364 | 0 | imgRequest* owner = GetOwner(); |
365 | 0 | if (!owner) { |
366 | 0 | return; |
367 | 0 | } |
368 | 0 | |
369 | 0 | owner->AddProxy(this); |
370 | 0 | } |
371 | | |
372 | | void |
373 | | imgRequestProxy::RemoveFromOwner(nsresult aStatus) |
374 | 0 | { |
375 | 0 | imgRequest* owner = GetOwner(); |
376 | 0 | if (owner) { |
377 | 0 | if (mValidating) { |
378 | 0 | imgCacheValidator* validator = owner->GetValidator(); |
379 | 0 | MOZ_ASSERT(validator); |
380 | 0 | validator->RemoveProxy(this); |
381 | 0 | mValidating = false; |
382 | 0 | } |
383 | 0 |
|
384 | 0 | owner->RemoveProxy(this, aStatus); |
385 | 0 | } |
386 | 0 | } |
387 | | |
388 | | void |
389 | | imgRequestProxy::AddToLoadGroup() |
390 | 0 | { |
391 | 0 | NS_ASSERTION(!mIsInLoadGroup, "Whaa, we're already in the loadgroup!"); |
392 | 0 | MOZ_ASSERT(!mForceDispatchLoadGroup); |
393 | 0 |
|
394 | 0 | /* While in theory there could be a dispatch outstanding to remove this |
395 | 0 | request from the load group, in practice we only add to the load group |
396 | 0 | (when previously not in a load group) at initialization. */ |
397 | 0 | if (!mIsInLoadGroup && mLoadGroup) { |
398 | 0 | LOG_FUNC(gImgLog, "imgRequestProxy::AddToLoadGroup"); |
399 | 0 | mLoadGroup->AddRequest(this, nullptr); |
400 | 0 | mIsInLoadGroup = true; |
401 | 0 | } |
402 | 0 | } |
403 | | |
404 | | void |
405 | | imgRequestProxy::RemoveFromLoadGroup() |
406 | 0 | { |
407 | 0 | if (!mIsInLoadGroup || !mLoadGroup) { |
408 | 0 | return; |
409 | 0 | } |
410 | 0 | |
411 | 0 | /* Sometimes we may not be able to remove ourselves from the load group in |
412 | 0 | the current context. This is because our listeners are not re-entrant (e.g. |
413 | 0 | we are in the middle of CancelAndForgetObserver or SyncClone). */ |
414 | 0 | if (mForceDispatchLoadGroup) { |
415 | 0 | LOG_FUNC(gImgLog, "imgRequestProxy::RemoveFromLoadGroup -- dispatch"); |
416 | 0 |
|
417 | 0 | /* We take away the load group from the request temporarily; this prevents |
418 | 0 | additional dispatches via RemoveFromLoadGroup occurring, as well as |
419 | 0 | MoveToBackgroundInLoadGroup from removing and readding. This is safe |
420 | 0 | because we know that once we get here, blocking the load group at all is |
421 | 0 | unnecessary. */ |
422 | 0 | mIsInLoadGroup = false; |
423 | 0 | nsCOMPtr<nsILoadGroup> loadGroup = std::move(mLoadGroup); |
424 | 0 | RefPtr<imgRequestProxy> self(this); |
425 | 0 | DispatchWithTargetIfAvailable(NS_NewRunnableFunction( |
426 | 0 | "imgRequestProxy::RemoveFromLoadGroup", |
427 | 0 | [self, loadGroup]() -> void { |
428 | 0 | loadGroup->RemoveRequest(self, nullptr, NS_OK); |
429 | 0 | })); |
430 | 0 | return; |
431 | 0 | } |
432 | 0 |
|
433 | 0 | LOG_FUNC(gImgLog, "imgRequestProxy::RemoveFromLoadGroup"); |
434 | 0 |
|
435 | 0 | /* calling RemoveFromLoadGroup may cause the document to finish |
436 | 0 | loading, which could result in our death. We need to make sure |
437 | 0 | that we stay alive long enough to fight another battle... at |
438 | 0 | least until we exit this function. */ |
439 | 0 | nsCOMPtr<imgIRequest> kungFuDeathGrip(this); |
440 | 0 | mLoadGroup->RemoveRequest(this, nullptr, NS_OK); |
441 | 0 | mLoadGroup = nullptr; |
442 | 0 | mIsInLoadGroup = false; |
443 | 0 | } |
444 | | |
445 | | void |
446 | | imgRequestProxy::MoveToBackgroundInLoadGroup() |
447 | 0 | { |
448 | 0 | /* Even if we are still in the load group, we may have taken away the load |
449 | 0 | group reference itself because we are in the process of leaving the group. |
450 | 0 | In that case, there is no need to background the request. */ |
451 | 0 | if (!mLoadGroup) { |
452 | 0 | return; |
453 | 0 | } |
454 | 0 | |
455 | 0 | /* There is no need to dispatch if we only need to add ourselves to the load |
456 | 0 | group without removal. It is the removal which causes the problematic |
457 | 0 | callbacks (see RemoveFromLoadGroup). */ |
458 | 0 | if (mIsInLoadGroup && mForceDispatchLoadGroup) { |
459 | 0 | LOG_FUNC(gImgLog, "imgRequestProxy::MoveToBackgroundInLoadGroup -- dispatch"); |
460 | 0 |
|
461 | 0 | RefPtr<imgRequestProxy> self(this); |
462 | 0 | DispatchWithTargetIfAvailable(NS_NewRunnableFunction( |
463 | 0 | "imgRequestProxy::MoveToBackgroundInLoadGroup", |
464 | 0 | [self]() -> void { |
465 | 0 | self->MoveToBackgroundInLoadGroup(); |
466 | 0 | })); |
467 | 0 | return; |
468 | 0 | } |
469 | 0 |
|
470 | 0 | LOG_FUNC(gImgLog, "imgRequestProxy::MoveToBackgroundInLoadGroup"); |
471 | 0 | nsCOMPtr<imgIRequest> kungFuDeathGrip(this); |
472 | 0 | if (mIsInLoadGroup) { |
473 | 0 | mLoadGroup->RemoveRequest(this, nullptr, NS_OK); |
474 | 0 | } |
475 | 0 |
|
476 | 0 | mLoadFlags |= nsIRequest::LOAD_BACKGROUND; |
477 | 0 | mLoadGroup->AddRequest(this, nullptr); |
478 | 0 | } |
479 | | |
480 | | /** nsIRequest / imgIRequest methods **/ |
481 | | |
482 | | NS_IMETHODIMP |
483 | | imgRequestProxy::GetName(nsACString& aName) |
484 | 0 | { |
485 | 0 | aName.Truncate(); |
486 | 0 |
|
487 | 0 | if (mURI) { |
488 | 0 | mURI->GetSpec(aName); |
489 | 0 | } |
490 | 0 |
|
491 | 0 | return NS_OK; |
492 | 0 | } |
493 | | |
494 | | NS_IMETHODIMP |
495 | | imgRequestProxy::IsPending(bool* _retval) |
496 | 0 | { |
497 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
498 | 0 | } |
499 | | |
500 | | NS_IMETHODIMP |
501 | | imgRequestProxy::GetStatus(nsresult* aStatus) |
502 | 0 | { |
503 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
504 | 0 | } |
505 | | |
506 | | NS_IMETHODIMP |
507 | | imgRequestProxy::Cancel(nsresult status) |
508 | 0 | { |
509 | 0 | if (mCanceled) { |
510 | 0 | return NS_ERROR_FAILURE; |
511 | 0 | } |
512 | 0 | |
513 | 0 | LOG_SCOPE(gImgLog, "imgRequestProxy::Cancel"); |
514 | 0 |
|
515 | 0 | mCanceled = true; |
516 | 0 |
|
517 | 0 | nsCOMPtr<nsIRunnable> ev = new imgCancelRunnable(this, status); |
518 | 0 | return DispatchWithTargetIfAvailable(ev.forget()); |
519 | 0 | } |
520 | | |
521 | | void |
522 | | imgRequestProxy::DoCancel(nsresult status) |
523 | 0 | { |
524 | 0 | RemoveFromOwner(status); |
525 | 0 | RemoveFromLoadGroup(); |
526 | 0 | NullOutListener(); |
527 | 0 | } |
528 | | |
529 | | NS_IMETHODIMP |
530 | | imgRequestProxy::CancelAndForgetObserver(nsresult aStatus) |
531 | 0 | { |
532 | 0 | // If mCanceled is true but mListener is non-null, that means |
533 | 0 | // someone called Cancel() on us but the imgCancelRunnable is still |
534 | 0 | // pending. We still need to null out mListener before returning |
535 | 0 | // from this function in this case. That means we want to do the |
536 | 0 | // RemoveProxy call right now, because we need to deliver the |
537 | 0 | // onStopRequest. |
538 | 0 | if (mCanceled && !mListener) { |
539 | 0 | return NS_ERROR_FAILURE; |
540 | 0 | } |
541 | 0 | |
542 | 0 | LOG_SCOPE(gImgLog, "imgRequestProxy::CancelAndForgetObserver"); |
543 | 0 |
|
544 | 0 | mCanceled = true; |
545 | 0 | mForceDispatchLoadGroup = true; |
546 | 0 | RemoveFromOwner(aStatus); |
547 | 0 | RemoveFromLoadGroup(); |
548 | 0 | mForceDispatchLoadGroup = false; |
549 | 0 |
|
550 | 0 | NullOutListener(); |
551 | 0 |
|
552 | 0 | return NS_OK; |
553 | 0 | } |
554 | | |
555 | | NS_IMETHODIMP |
556 | | imgRequestProxy::StartDecoding(uint32_t aFlags) |
557 | 0 | { |
558 | 0 | // Flag this, so we know to request after validation if pending. |
559 | 0 | if (IsValidating()) { |
560 | 0 | mDecodeRequested = true; |
561 | 0 | return NS_OK; |
562 | 0 | } |
563 | 0 | |
564 | 0 | RefPtr<Image> image = GetImage(); |
565 | 0 | if (image) { |
566 | 0 | return image->StartDecoding(aFlags); |
567 | 0 | } |
568 | 0 | |
569 | 0 | if (GetOwner()) { |
570 | 0 | GetOwner()->StartDecoding(); |
571 | 0 | } |
572 | 0 |
|
573 | 0 | return NS_OK; |
574 | 0 | } |
575 | | |
576 | | bool |
577 | | imgRequestProxy::StartDecodingWithResult(uint32_t aFlags) |
578 | 0 | { |
579 | 0 | // Flag this, so we know to request after validation if pending. |
580 | 0 | if (IsValidating()) { |
581 | 0 | mDecodeRequested = true; |
582 | 0 | return false; |
583 | 0 | } |
584 | 0 | |
585 | 0 | RefPtr<Image> image = GetImage(); |
586 | 0 | if (image) { |
587 | 0 | return image->StartDecodingWithResult(aFlags); |
588 | 0 | } |
589 | 0 | |
590 | 0 | if (GetOwner()) { |
591 | 0 | GetOwner()->StartDecoding(); |
592 | 0 | } |
593 | 0 |
|
594 | 0 | return false; |
595 | 0 | } |
596 | | |
597 | | NS_IMETHODIMP |
598 | | imgRequestProxy::LockImage() |
599 | 0 | { |
600 | 0 | mLockCount++; |
601 | 0 | RefPtr<Image> image = GetImage(); |
602 | 0 | if (image) { |
603 | 0 | return image->LockImage(); |
604 | 0 | } |
605 | 0 | return NS_OK; |
606 | 0 | } |
607 | | |
608 | | NS_IMETHODIMP |
609 | | imgRequestProxy::UnlockImage() |
610 | 0 | { |
611 | 0 | MOZ_ASSERT(mLockCount > 0, "calling unlock but no locks!"); |
612 | 0 |
|
613 | 0 | mLockCount--; |
614 | 0 | RefPtr<Image> image = GetImage(); |
615 | 0 | if (image) { |
616 | 0 | return image->UnlockImage(); |
617 | 0 | } |
618 | 0 | return NS_OK; |
619 | 0 | } |
620 | | |
621 | | NS_IMETHODIMP |
622 | | imgRequestProxy::RequestDiscard() |
623 | 0 | { |
624 | 0 | RefPtr<Image> image = GetImage(); |
625 | 0 | if (image) { |
626 | 0 | return image->RequestDiscard(); |
627 | 0 | } |
628 | 0 | return NS_OK; |
629 | 0 | } |
630 | | |
631 | | NS_IMETHODIMP |
632 | | imgRequestProxy::IncrementAnimationConsumers() |
633 | 0 | { |
634 | 0 | mAnimationConsumers++; |
635 | 0 | RefPtr<Image> image = GetImage(); |
636 | 0 | if (image) { |
637 | 0 | image->IncrementAnimationConsumers(); |
638 | 0 | } |
639 | 0 | return NS_OK; |
640 | 0 | } |
641 | | |
642 | | NS_IMETHODIMP |
643 | | imgRequestProxy::DecrementAnimationConsumers() |
644 | 0 | { |
645 | 0 | // We may get here if some responsible code called Increment, |
646 | 0 | // then called us, but we have meanwhile called ClearAnimationConsumers |
647 | 0 | // because we needed to get rid of them earlier (see |
648 | 0 | // imgRequest::RemoveProxy), and hence have nothing left to |
649 | 0 | // decrement. (In such a case we got rid of the animation consumers |
650 | 0 | // early, but not the observer.) |
651 | 0 | if (mAnimationConsumers > 0) { |
652 | 0 | mAnimationConsumers--; |
653 | 0 | RefPtr<Image> image = GetImage(); |
654 | 0 | if (image) { |
655 | 0 | image->DecrementAnimationConsumers(); |
656 | 0 | } |
657 | 0 | } |
658 | 0 | return NS_OK; |
659 | 0 | } |
660 | | |
661 | | void |
662 | | imgRequestProxy::ClearAnimationConsumers() |
663 | 0 | { |
664 | 0 | while (mAnimationConsumers > 0) { |
665 | 0 | DecrementAnimationConsumers(); |
666 | 0 | } |
667 | 0 | } |
668 | | |
669 | | NS_IMETHODIMP |
670 | | imgRequestProxy::Suspend() |
671 | 0 | { |
672 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
673 | 0 | } |
674 | | |
675 | | NS_IMETHODIMP |
676 | | imgRequestProxy::Resume() |
677 | 0 | { |
678 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
679 | 0 | } |
680 | | |
681 | | NS_IMETHODIMP |
682 | | imgRequestProxy::GetLoadGroup(nsILoadGroup** loadGroup) |
683 | 0 | { |
684 | 0 | NS_IF_ADDREF(*loadGroup = mLoadGroup.get()); |
685 | 0 | return NS_OK; |
686 | 0 | } |
687 | | NS_IMETHODIMP |
688 | | imgRequestProxy::SetLoadGroup(nsILoadGroup* loadGroup) |
689 | 0 | { |
690 | 0 | if (loadGroup != mLoadGroup) { |
691 | 0 | MOZ_ASSERT_UNREACHABLE("Switching load groups is unsupported!"); |
692 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
693 | 0 | } |
694 | 0 | return NS_OK; |
695 | 0 | } |
696 | | |
697 | | NS_IMETHODIMP |
698 | | imgRequestProxy::GetLoadFlags(nsLoadFlags* flags) |
699 | 0 | { |
700 | 0 | *flags = mLoadFlags; |
701 | 0 | return NS_OK; |
702 | 0 | } |
703 | | NS_IMETHODIMP |
704 | | imgRequestProxy::SetLoadFlags(nsLoadFlags flags) |
705 | 0 | { |
706 | 0 | mLoadFlags = flags; |
707 | 0 | return NS_OK; |
708 | 0 | } |
709 | | |
710 | | /** imgIRequest methods **/ |
711 | | |
712 | | NS_IMETHODIMP |
713 | | imgRequestProxy::GetImage(imgIContainer** aImage) |
714 | 0 | { |
715 | 0 | NS_ENSURE_TRUE(aImage, NS_ERROR_NULL_POINTER); |
716 | 0 | // It's possible that our owner has an image but hasn't notified us of it - |
717 | 0 | // that'll happen if we get Canceled before the owner instantiates its image |
718 | 0 | // (because Canceling unregisters us as a listener on mOwner). If we're |
719 | 0 | // in that situation, just grab the image off of mOwner. |
720 | 0 | RefPtr<Image> image = GetImage(); |
721 | 0 | nsCOMPtr<imgIContainer> imageToReturn; |
722 | 0 | if (image) { |
723 | 0 | imageToReturn = do_QueryInterface(image); |
724 | 0 | } |
725 | 0 | if (!imageToReturn && GetOwner()) { |
726 | 0 | imageToReturn = GetOwner()->GetImage(); |
727 | 0 | } |
728 | 0 | if (!imageToReturn) { |
729 | 0 | return NS_ERROR_FAILURE; |
730 | 0 | } |
731 | 0 | |
732 | 0 | imageToReturn.swap(*aImage); |
733 | 0 |
|
734 | 0 | return NS_OK; |
735 | 0 | } |
736 | | |
737 | | NS_IMETHODIMP |
738 | | imgRequestProxy::GetImageStatus(uint32_t* aStatus) |
739 | 0 | { |
740 | 0 | if (IsValidating()) { |
741 | 0 | // We are currently validating the image, and so our status could revert if |
742 | 0 | // we discard the cache. We should also be deferring notifications, such |
743 | 0 | // that the caller will be notified when validation completes. Rather than |
744 | 0 | // risk misleading the caller, return nothing. |
745 | 0 | *aStatus = imgIRequest::STATUS_NONE; |
746 | 0 | } else { |
747 | 0 | RefPtr<ProgressTracker> progressTracker = GetProgressTracker(); |
748 | 0 | *aStatus = progressTracker->GetImageStatus(); |
749 | 0 | } |
750 | 0 |
|
751 | 0 | return NS_OK; |
752 | 0 | } |
753 | | |
754 | | NS_IMETHODIMP |
755 | | imgRequestProxy::GetImageErrorCode(nsresult* aStatus) |
756 | 0 | { |
757 | 0 | if (!GetOwner()) { |
758 | 0 | return NS_ERROR_FAILURE; |
759 | 0 | } |
760 | 0 | |
761 | 0 | *aStatus = GetOwner()->GetImageErrorCode(); |
762 | 0 |
|
763 | 0 | return NS_OK; |
764 | 0 | } |
765 | | |
766 | | NS_IMETHODIMP |
767 | | imgRequestProxy::GetURI(nsIURI** aURI) |
768 | 0 | { |
769 | 0 | MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread to convert URI"); |
770 | 0 | nsCOMPtr<nsIURI> uri = mURI; |
771 | 0 | uri.forget(aURI); |
772 | 0 | return NS_OK; |
773 | 0 | } |
774 | | |
775 | | nsresult |
776 | | imgRequestProxy::GetFinalURI(nsIURI** aURI) |
777 | 0 | { |
778 | 0 | if (!GetOwner()) { |
779 | 0 | return NS_ERROR_FAILURE; |
780 | 0 | } |
781 | 0 | |
782 | 0 | return GetOwner()->GetFinalURI(aURI); |
783 | 0 | } |
784 | | |
785 | | NS_IMETHODIMP |
786 | | imgRequestProxy::GetNotificationObserver(imgINotificationObserver** aObserver) |
787 | 0 | { |
788 | 0 | *aObserver = mListener; |
789 | 0 | NS_IF_ADDREF(*aObserver); |
790 | 0 | return NS_OK; |
791 | 0 | } |
792 | | |
793 | | NS_IMETHODIMP |
794 | | imgRequestProxy::GetMimeType(char** aMimeType) |
795 | 0 | { |
796 | 0 | if (!GetOwner()) { |
797 | 0 | return NS_ERROR_FAILURE; |
798 | 0 | } |
799 | 0 | |
800 | 0 | const char* type = GetOwner()->GetMimeType(); |
801 | 0 | if (!type) { |
802 | 0 | return NS_ERROR_FAILURE; |
803 | 0 | } |
804 | 0 | |
805 | 0 | *aMimeType = NS_xstrdup(type); |
806 | 0 |
|
807 | 0 | return NS_OK; |
808 | 0 | } |
809 | | |
810 | | imgRequestProxy* imgRequestProxy::NewClonedProxy() |
811 | 0 | { |
812 | 0 | return new imgRequestProxy(); |
813 | 0 | } |
814 | | |
815 | | NS_IMETHODIMP |
816 | | imgRequestProxy::Clone(imgINotificationObserver* aObserver, |
817 | | imgIRequest** aClone) |
818 | 0 | { |
819 | 0 | nsresult result; |
820 | 0 | imgRequestProxy* proxy; |
821 | 0 | result = PerformClone(aObserver, nullptr, /* aSyncNotify */ true, &proxy); |
822 | 0 | *aClone = proxy; |
823 | 0 | return result; |
824 | 0 | } |
825 | | |
826 | | nsresult imgRequestProxy::SyncClone(imgINotificationObserver* aObserver, |
827 | | nsIDocument* aLoadingDocument, |
828 | | imgRequestProxy** aClone) |
829 | 0 | { |
830 | 0 | return PerformClone(aObserver, aLoadingDocument, |
831 | 0 | /* aSyncNotify */ true, aClone); |
832 | 0 | } |
833 | | |
834 | | nsresult imgRequestProxy::Clone(imgINotificationObserver* aObserver, |
835 | | nsIDocument* aLoadingDocument, |
836 | | imgRequestProxy** aClone) |
837 | 0 | { |
838 | 0 | return PerformClone(aObserver, aLoadingDocument, |
839 | 0 | /* aSyncNotify */ false, aClone); |
840 | 0 | } |
841 | | |
842 | | nsresult |
843 | | imgRequestProxy::PerformClone(imgINotificationObserver* aObserver, |
844 | | nsIDocument* aLoadingDocument, |
845 | | bool aSyncNotify, |
846 | | imgRequestProxy** aClone) |
847 | 0 | { |
848 | 0 | MOZ_ASSERT(aClone, "Null out param"); |
849 | 0 |
|
850 | 0 | LOG_SCOPE(gImgLog, "imgRequestProxy::Clone"); |
851 | 0 |
|
852 | 0 | *aClone = nullptr; |
853 | 0 | RefPtr<imgRequestProxy> clone = NewClonedProxy(); |
854 | 0 |
|
855 | 0 | nsCOMPtr<nsILoadGroup> loadGroup; |
856 | 0 | if (aLoadingDocument) { |
857 | 0 | loadGroup = aLoadingDocument->GetDocumentLoadGroup(); |
858 | 0 | } |
859 | 0 |
|
860 | 0 | // It is important to call |SetLoadFlags()| before calling |Init()| because |
861 | 0 | // |Init()| adds the request to the loadgroup. |
862 | 0 | // When a request is added to a loadgroup, its load flags are merged |
863 | 0 | // with the load flags of the loadgroup. |
864 | 0 | // XXXldb That's not true anymore. Stuff from imgLoader adds the |
865 | 0 | // request to the loadgroup. |
866 | 0 | clone->SetLoadFlags(mLoadFlags); |
867 | 0 | nsresult rv = clone->Init(mBehaviour->GetOwner(), loadGroup, |
868 | 0 | aLoadingDocument, mURI, aObserver); |
869 | 0 | if (NS_FAILED(rv)) { |
870 | 0 | return rv; |
871 | 0 | } |
872 | 0 | |
873 | 0 | // Assign to *aClone before calling Notify so that if the caller expects to |
874 | 0 | // only be notified for requests it's already holding pointers to it won't be |
875 | 0 | // surprised. |
876 | 0 | NS_ADDREF(*aClone = clone); |
877 | 0 |
|
878 | 0 | imgCacheValidator* validator = GetValidator(); |
879 | 0 | if (validator) { |
880 | 0 | // Note that if we have a validator, we don't want to issue notifications at |
881 | 0 | // here because we want to defer until that completes. AddProxy will add us |
882 | 0 | // to the load group; we cannot avoid that in this case, because we don't |
883 | 0 | // know when the validation will complete, and if it will cause us to |
884 | 0 | // discard our cached state anyways. We are probably already blocked by the |
885 | 0 | // original LoadImage(WithChannel) request in any event. |
886 | 0 | clone->MarkValidating(); |
887 | 0 | validator->AddProxy(clone); |
888 | 0 | } else { |
889 | 0 | // We only want to add the request to the load group of the owning document |
890 | 0 | // if it is still in progress. Some callers cannot handle a supurious load |
891 | 0 | // group removal (e.g. print preview) so we must be careful. On the other |
892 | 0 | // hand, if after cloning, the original request proxy is cancelled / |
893 | 0 | // destroyed, we need to ensure that any clones still block the load group |
894 | 0 | // if it is incomplete. |
895 | 0 | bool addToLoadGroup = mIsInLoadGroup; |
896 | 0 | if (!addToLoadGroup) { |
897 | 0 | RefPtr<ProgressTracker> tracker = clone->GetProgressTracker(); |
898 | 0 | addToLoadGroup = tracker && !(tracker->GetProgress() & FLAG_LOAD_COMPLETE); |
899 | 0 | } |
900 | 0 |
|
901 | 0 | if (addToLoadGroup) { |
902 | 0 | clone->AddToLoadGroup(); |
903 | 0 | } |
904 | 0 |
|
905 | 0 | if (aSyncNotify) { |
906 | 0 | // This is wrong!!! We need to notify asynchronously, but there's code |
907 | 0 | // that assumes that we don't. This will be fixed in bug 580466. Note that |
908 | 0 | // if we have a validator, we won't issue notifications anyways because |
909 | 0 | // they are deferred, so there is no point in requesting. |
910 | 0 | clone->mForceDispatchLoadGroup = true; |
911 | 0 | clone->SyncNotifyListener(); |
912 | 0 | clone->mForceDispatchLoadGroup = false; |
913 | 0 | } else { |
914 | 0 | // Without a validator, we can request asynchronous notifications |
915 | 0 | // immediately. If there was a validator, this would override the deferral |
916 | 0 | // and that would be incorrect. |
917 | 0 | clone->NotifyListener(); |
918 | 0 | } |
919 | 0 | } |
920 | 0 |
|
921 | 0 | return NS_OK; |
922 | 0 | } |
923 | | |
924 | | NS_IMETHODIMP |
925 | | imgRequestProxy::GetImagePrincipal(nsIPrincipal** aPrincipal) |
926 | 0 | { |
927 | 0 | if (!GetOwner()) { |
928 | 0 | return NS_ERROR_FAILURE; |
929 | 0 | } |
930 | 0 | |
931 | 0 | nsCOMPtr<nsIPrincipal> principal = GetOwner()->GetPrincipal(); |
932 | 0 | principal.forget(aPrincipal); |
933 | 0 | return NS_OK; |
934 | 0 | } |
935 | | |
936 | | NS_IMETHODIMP |
937 | | imgRequestProxy::GetMultipart(bool* aMultipart) |
938 | 0 | { |
939 | 0 | if (!GetOwner()) { |
940 | 0 | return NS_ERROR_FAILURE; |
941 | 0 | } |
942 | 0 | |
943 | 0 | *aMultipart = GetOwner()->GetMultipart(); |
944 | 0 |
|
945 | 0 | return NS_OK; |
946 | 0 | } |
947 | | |
948 | | NS_IMETHODIMP |
949 | | imgRequestProxy::GetCORSMode(int32_t* aCorsMode) |
950 | 0 | { |
951 | 0 | if (!GetOwner()) { |
952 | 0 | return NS_ERROR_FAILURE; |
953 | 0 | } |
954 | 0 | |
955 | 0 | *aCorsMode = GetOwner()->GetCORSMode(); |
956 | 0 |
|
957 | 0 | return NS_OK; |
958 | 0 | } |
959 | | |
960 | | NS_IMETHODIMP |
961 | | imgRequestProxy::BoostPriority(uint32_t aCategory) |
962 | 0 | { |
963 | 0 | NS_ENSURE_STATE(GetOwner() && !mCanceled); |
964 | 0 | GetOwner()->BoostPriority(aCategory); |
965 | 0 | return NS_OK; |
966 | 0 | } |
967 | | |
968 | | /** nsISupportsPriority methods **/ |
969 | | |
970 | | NS_IMETHODIMP |
971 | | imgRequestProxy::GetPriority(int32_t* priority) |
972 | 0 | { |
973 | 0 | NS_ENSURE_STATE(GetOwner()); |
974 | 0 | *priority = GetOwner()->Priority(); |
975 | 0 | return NS_OK; |
976 | 0 | } |
977 | | |
978 | | NS_IMETHODIMP |
979 | | imgRequestProxy::SetPriority(int32_t priority) |
980 | 0 | { |
981 | 0 | NS_ENSURE_STATE(GetOwner() && !mCanceled); |
982 | 0 | GetOwner()->AdjustPriority(this, priority - GetOwner()->Priority()); |
983 | 0 | return NS_OK; |
984 | 0 | } |
985 | | |
986 | | NS_IMETHODIMP |
987 | | imgRequestProxy::AdjustPriority(int32_t priority) |
988 | 0 | { |
989 | 0 | // We don't require |!mCanceled| here. This may be called even if we're |
990 | 0 | // cancelled, because it's invoked as part of the process of removing an image |
991 | 0 | // from the load group. |
992 | 0 | NS_ENSURE_STATE(GetOwner()); |
993 | 0 | GetOwner()->AdjustPriority(this, priority); |
994 | 0 | return NS_OK; |
995 | 0 | } |
996 | | |
997 | | static const char* |
998 | | NotificationTypeToString(int32_t aType) |
999 | 0 | { |
1000 | 0 | switch(aType) |
1001 | 0 | { |
1002 | 0 | case imgINotificationObserver::SIZE_AVAILABLE: return "SIZE_AVAILABLE"; |
1003 | 0 | case imgINotificationObserver::FRAME_UPDATE: return "FRAME_UPDATE"; |
1004 | 0 | case imgINotificationObserver::FRAME_COMPLETE: return "FRAME_COMPLETE"; |
1005 | 0 | case imgINotificationObserver::LOAD_COMPLETE: return "LOAD_COMPLETE"; |
1006 | 0 | case imgINotificationObserver::DECODE_COMPLETE: return "DECODE_COMPLETE"; |
1007 | 0 | case imgINotificationObserver::DISCARD: return "DISCARD"; |
1008 | 0 | case imgINotificationObserver::UNLOCKED_DRAW: return "UNLOCKED_DRAW"; |
1009 | 0 | case imgINotificationObserver::IS_ANIMATED: return "IS_ANIMATED"; |
1010 | 0 | case imgINotificationObserver::HAS_TRANSPARENCY: return "HAS_TRANSPARENCY"; |
1011 | 0 | default: |
1012 | 0 | MOZ_ASSERT_UNREACHABLE("Notification list should be exhaustive"); |
1013 | 0 | return "(unknown notification)"; |
1014 | 0 | } |
1015 | 0 | } |
1016 | | |
1017 | | void |
1018 | | imgRequestProxy::Notify(int32_t aType, const mozilla::gfx::IntRect* aRect) |
1019 | 0 | { |
1020 | 0 | MOZ_ASSERT(aType != imgINotificationObserver::LOAD_COMPLETE, |
1021 | 0 | "Should call OnLoadComplete"); |
1022 | 0 |
|
1023 | 0 | LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::Notify", "type", |
1024 | 0 | NotificationTypeToString(aType)); |
1025 | 0 |
|
1026 | 0 | if (!mListener || mCanceled) { |
1027 | 0 | return; |
1028 | 0 | } |
1029 | 0 | |
1030 | 0 | if (!IsOnEventTarget()) { |
1031 | 0 | RefPtr<imgRequestProxy> self(this); |
1032 | 0 | if (aRect) { |
1033 | 0 | const mozilla::gfx::IntRect rect = *aRect; |
1034 | 0 | DispatchWithTarget(NS_NewRunnableFunction("imgRequestProxy::Notify", |
1035 | 0 | [self, rect, aType]() -> void { |
1036 | 0 | self->Notify(aType, &rect); |
1037 | 0 | })); |
1038 | 0 | } else { |
1039 | 0 | DispatchWithTarget(NS_NewRunnableFunction("imgRequestProxy::Notify", |
1040 | 0 | [self, aType]() -> void { |
1041 | 0 | self->Notify(aType, nullptr); |
1042 | 0 | })); |
1043 | 0 | } |
1044 | 0 | return; |
1045 | 0 | } |
1046 | 0 |
|
1047 | 0 | // Make sure the listener stays alive while we notify. |
1048 | 0 | nsCOMPtr<imgINotificationObserver> listener(mListener); |
1049 | 0 |
|
1050 | 0 | listener->Notify(this, aType, aRect); |
1051 | 0 | } |
1052 | | |
1053 | | void |
1054 | | imgRequestProxy::OnLoadComplete(bool aLastPart) |
1055 | 0 | { |
1056 | 0 | LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::OnLoadComplete", |
1057 | 0 | "uri", mURI); |
1058 | 0 |
|
1059 | 0 | // There's all sorts of stuff here that could kill us (the OnStopRequest call |
1060 | 0 | // on the listener, the removal from the loadgroup, the release of the |
1061 | 0 | // listener, etc). Don't let them do it. |
1062 | 0 | RefPtr<imgRequestProxy> self(this); |
1063 | 0 |
|
1064 | 0 | if (!IsOnEventTarget()) { |
1065 | 0 | DispatchWithTarget(NS_NewRunnableFunction("imgRequestProxy::OnLoadComplete", |
1066 | 0 | [self, aLastPart]() -> void { |
1067 | 0 | self->OnLoadComplete(aLastPart); |
1068 | 0 | })); |
1069 | 0 | return; |
1070 | 0 | } |
1071 | 0 |
|
1072 | 0 | if (mListener && !mCanceled) { |
1073 | 0 | // Hold a ref to the listener while we call it, just in case. |
1074 | 0 | nsCOMPtr<imgINotificationObserver> listener(mListener); |
1075 | 0 | listener->Notify(this, imgINotificationObserver::LOAD_COMPLETE, nullptr); |
1076 | 0 | } |
1077 | 0 |
|
1078 | 0 | // If we're expecting more data from a multipart channel, re-add ourself |
1079 | 0 | // to the loadgroup so that the document doesn't lose track of the load. |
1080 | 0 | // If the request is already a background request and there's more data |
1081 | 0 | // coming, we can just leave the request in the loadgroup as-is. |
1082 | 0 | if (aLastPart || (mLoadFlags & nsIRequest::LOAD_BACKGROUND) == 0) { |
1083 | 0 | if (aLastPart) { |
1084 | 0 | RemoveFromLoadGroup(); |
1085 | 0 | } else { |
1086 | 0 | // More data is coming, so change the request to be a background request |
1087 | 0 | // and put it back in the loadgroup. |
1088 | 0 | MoveToBackgroundInLoadGroup(); |
1089 | 0 | } |
1090 | 0 | } |
1091 | 0 |
|
1092 | 0 | if (mListenerIsStrongRef && aLastPart) { |
1093 | 0 | MOZ_ASSERT(mListener, "How did that happen?"); |
1094 | 0 | // Drop our strong ref to the listener now that we're done with |
1095 | 0 | // everything. Note that this can cancel us and other fun things |
1096 | 0 | // like that. Don't add anything in this method after this point. |
1097 | 0 | imgINotificationObserver* obs = mListener; |
1098 | 0 | mListenerIsStrongRef = false; |
1099 | 0 | NS_RELEASE(obs); |
1100 | 0 | } |
1101 | 0 | } |
1102 | | |
1103 | | void |
1104 | | imgRequestProxy::NullOutListener() |
1105 | 0 | { |
1106 | 0 | // If we have animation consumers, then they don't matter anymore |
1107 | 0 | if (mListener) { |
1108 | 0 | ClearAnimationConsumers(); |
1109 | 0 | } |
1110 | 0 |
|
1111 | 0 | if (mListenerIsStrongRef) { |
1112 | 0 | // Releasing could do weird reentery stuff, so just play it super-safe |
1113 | 0 | nsCOMPtr<imgINotificationObserver> obs; |
1114 | 0 | obs.swap(mListener); |
1115 | 0 | mListenerIsStrongRef = false; |
1116 | 0 | } else { |
1117 | 0 | mListener = nullptr; |
1118 | 0 | } |
1119 | 0 |
|
1120 | 0 | // Note that we don't free the event target. We actually need that to ensure |
1121 | 0 | // we get removed from the ProgressTracker properly. No harm in keeping it |
1122 | 0 | // however. |
1123 | 0 | mTabGroup = nullptr; |
1124 | 0 | } |
1125 | | |
1126 | | NS_IMETHODIMP |
1127 | | imgRequestProxy::GetStaticRequest(imgIRequest** aReturn) |
1128 | 0 | { |
1129 | 0 | imgRequestProxy* proxy; |
1130 | 0 | nsresult result = GetStaticRequest(nullptr, &proxy); |
1131 | 0 | *aReturn = proxy; |
1132 | 0 | return result; |
1133 | 0 | } |
1134 | | |
1135 | | nsresult |
1136 | | imgRequestProxy::GetStaticRequest(nsIDocument* aLoadingDocument, |
1137 | | imgRequestProxy** aReturn) |
1138 | 0 | { |
1139 | 0 | *aReturn = nullptr; |
1140 | 0 | RefPtr<Image> image = GetImage(); |
1141 | 0 |
|
1142 | 0 | bool animated; |
1143 | 0 | if (!image || (NS_SUCCEEDED(image->GetAnimated(&animated)) && !animated)) { |
1144 | 0 | // Early exit - we're not animated, so we don't have to do anything. |
1145 | 0 | NS_ADDREF(*aReturn = this); |
1146 | 0 | return NS_OK; |
1147 | 0 | } |
1148 | 0 |
|
1149 | 0 | // Check for errors in the image. Callers code rely on GetStaticRequest |
1150 | 0 | // failing in this case, though with FrozenImage there's no technical reason |
1151 | 0 | // for it anymore. |
1152 | 0 | if (image->HasError()) { |
1153 | 0 | return NS_ERROR_FAILURE; |
1154 | 0 | } |
1155 | 0 | |
1156 | 0 | // We are animated. We need to create a frozen version of this image. |
1157 | 0 | RefPtr<Image> frozenImage = ImageOps::Freeze(image); |
1158 | 0 |
|
1159 | 0 | // Create a static imgRequestProxy with our new extracted frame. |
1160 | 0 | nsCOMPtr<nsIPrincipal> currentPrincipal; |
1161 | 0 | GetImagePrincipal(getter_AddRefs(currentPrincipal)); |
1162 | 0 | RefPtr<imgRequestProxy> req = new imgRequestProxyStatic(frozenImage, |
1163 | 0 | currentPrincipal); |
1164 | 0 | req->Init(nullptr, nullptr, aLoadingDocument, mURI, nullptr); |
1165 | 0 |
|
1166 | 0 | NS_ADDREF(*aReturn = req); |
1167 | 0 |
|
1168 | 0 | return NS_OK; |
1169 | 0 | } |
1170 | | |
1171 | | void |
1172 | | imgRequestProxy::NotifyListener() |
1173 | 0 | { |
1174 | 0 | // It would be nice to notify the observer directly in the status tracker |
1175 | 0 | // instead of through the proxy, but there are several places we do extra |
1176 | 0 | // processing when we receive notifications (like OnStopRequest()), and we |
1177 | 0 | // need to check mCanceled everywhere too. |
1178 | 0 |
|
1179 | 0 | RefPtr<ProgressTracker> progressTracker = GetProgressTracker(); |
1180 | 0 | if (GetOwner()) { |
1181 | 0 | // Send the notifications to our listener asynchronously. |
1182 | 0 | progressTracker->Notify(this); |
1183 | 0 | } else { |
1184 | 0 | // We don't have an imgRequest, so we can only notify the clone of our |
1185 | 0 | // current state, but we still have to do that asynchronously. |
1186 | 0 | MOZ_ASSERT(HasImage(), |
1187 | 0 | "if we have no imgRequest, we should have an Image"); |
1188 | 0 | progressTracker->NotifyCurrentState(this); |
1189 | 0 | } |
1190 | 0 | } |
1191 | | |
1192 | | void |
1193 | | imgRequestProxy::SyncNotifyListener() |
1194 | 0 | { |
1195 | 0 | // It would be nice to notify the observer directly in the status tracker |
1196 | 0 | // instead of through the proxy, but there are several places we do extra |
1197 | 0 | // processing when we receive notifications (like OnStopRequest()), and we |
1198 | 0 | // need to check mCanceled everywhere too. |
1199 | 0 |
|
1200 | 0 | RefPtr<ProgressTracker> progressTracker = GetProgressTracker(); |
1201 | 0 | progressTracker->SyncNotify(this); |
1202 | 0 | } |
1203 | | |
1204 | | void |
1205 | | imgRequestProxy::SetHasImage() |
1206 | 0 | { |
1207 | 0 | RefPtr<ProgressTracker> progressTracker = GetProgressTracker(); |
1208 | 0 | MOZ_ASSERT(progressTracker); |
1209 | 0 | RefPtr<Image> image = progressTracker->GetImage(); |
1210 | 0 | MOZ_ASSERT(image); |
1211 | 0 |
|
1212 | 0 | // Force any private status related to the owner to reflect |
1213 | 0 | // the presence of an image; |
1214 | 0 | mBehaviour->SetOwner(mBehaviour->GetOwner()); |
1215 | 0 |
|
1216 | 0 | // Apply any locks we have |
1217 | 0 | for (uint32_t i = 0; i < mLockCount; ++i) { |
1218 | 0 | image->LockImage(); |
1219 | 0 | } |
1220 | 0 |
|
1221 | 0 | // Apply any animation consumers we have |
1222 | 0 | for (uint32_t i = 0; i < mAnimationConsumers; i++) { |
1223 | 0 | image->IncrementAnimationConsumers(); |
1224 | 0 | } |
1225 | 0 | } |
1226 | | |
1227 | | already_AddRefed<ProgressTracker> |
1228 | | imgRequestProxy::GetProgressTracker() const |
1229 | 0 | { |
1230 | 0 | return mBehaviour->GetProgressTracker(); |
1231 | 0 | } |
1232 | | |
1233 | | already_AddRefed<mozilla::image::Image> |
1234 | | imgRequestProxy::GetImage() const |
1235 | 0 | { |
1236 | 0 | return mBehaviour->GetImage(); |
1237 | 0 | } |
1238 | | |
1239 | | bool |
1240 | | RequestBehaviour::HasImage() const |
1241 | 0 | { |
1242 | 0 | if (!mOwnerHasImage) { |
1243 | 0 | return false; |
1244 | 0 | } |
1245 | 0 | RefPtr<ProgressTracker> progressTracker = GetProgressTracker(); |
1246 | 0 | return progressTracker ? progressTracker->HasImage() : false; |
1247 | 0 | } |
1248 | | |
1249 | | bool |
1250 | | imgRequestProxy::HasImage() const |
1251 | 0 | { |
1252 | 0 | return mBehaviour->HasImage(); |
1253 | 0 | } |
1254 | | |
1255 | | imgRequest* |
1256 | | imgRequestProxy::GetOwner() const |
1257 | 0 | { |
1258 | 0 | return mBehaviour->GetOwner(); |
1259 | 0 | } |
1260 | | |
1261 | | imgCacheValidator* |
1262 | | imgRequestProxy::GetValidator() const |
1263 | 0 | { |
1264 | 0 | imgRequest* owner = GetOwner(); |
1265 | 0 | if (!owner) { |
1266 | 0 | return nullptr; |
1267 | 0 | } |
1268 | 0 | return owner->GetValidator(); |
1269 | 0 | } |
1270 | | |
1271 | | ////////////////// imgRequestProxyStatic methods |
1272 | | |
1273 | | class StaticBehaviour : public ProxyBehaviour |
1274 | | { |
1275 | | public: |
1276 | 0 | explicit StaticBehaviour(mozilla::image::Image* aImage) : mImage(aImage) {} |
1277 | | |
1278 | | already_AddRefed<mozilla::image::Image> |
1279 | 0 | GetImage() const override { |
1280 | 0 | RefPtr<mozilla::image::Image> image = mImage; |
1281 | 0 | return image.forget(); |
1282 | 0 | } |
1283 | | |
1284 | 0 | bool HasImage() const override { |
1285 | 0 | return mImage; |
1286 | 0 | } |
1287 | | |
1288 | | already_AddRefed<ProgressTracker> GetProgressTracker() |
1289 | 0 | const override { |
1290 | 0 | return mImage->GetProgressTracker(); |
1291 | 0 | } |
1292 | | |
1293 | 0 | imgRequest* GetOwner() const override { |
1294 | 0 | return nullptr; |
1295 | 0 | } |
1296 | | |
1297 | 0 | void SetOwner(imgRequest* aOwner) override { |
1298 | 0 | MOZ_ASSERT(!aOwner, |
1299 | 0 | "We shouldn't be giving static requests a non-null owner."); |
1300 | 0 | } |
1301 | | |
1302 | | private: |
1303 | | // Our image. We have to hold a strong reference here, because that's normally |
1304 | | // the job of the underlying request. |
1305 | | RefPtr<mozilla::image::Image> mImage; |
1306 | | }; |
1307 | | |
1308 | | imgRequestProxyStatic::imgRequestProxyStatic(mozilla::image::Image* aImage, |
1309 | | nsIPrincipal* aPrincipal) |
1310 | | : mPrincipal(aPrincipal) |
1311 | 0 | { |
1312 | 0 | mBehaviour = mozilla::MakeUnique<StaticBehaviour>(aImage); |
1313 | 0 | } |
1314 | | |
1315 | | NS_IMETHODIMP |
1316 | | imgRequestProxyStatic::GetImagePrincipal(nsIPrincipal** aPrincipal) |
1317 | 0 | { |
1318 | 0 | if (!mPrincipal) { |
1319 | 0 | return NS_ERROR_FAILURE; |
1320 | 0 | } |
1321 | 0 | |
1322 | 0 | NS_ADDREF(*aPrincipal = mPrincipal); |
1323 | 0 |
|
1324 | 0 | return NS_OK; |
1325 | 0 | } |
1326 | | |
1327 | | imgRequestProxy* imgRequestProxyStatic::NewClonedProxy() |
1328 | 0 | { |
1329 | 0 | nsCOMPtr<nsIPrincipal> currentPrincipal; |
1330 | 0 | GetImagePrincipal(getter_AddRefs(currentPrincipal)); |
1331 | 0 | RefPtr<mozilla::image::Image> image = GetImage(); |
1332 | 0 | return new imgRequestProxyStatic(image, currentPrincipal); |
1333 | 0 | } |