1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/image/src/imgRequestProxy.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1094 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "imgRequestProxy.h" 1.11 +#include "imgIOnloadBlocker.h" 1.12 + 1.13 +#include "Image.h" 1.14 +#include "ImageOps.h" 1.15 +#include "nsError.h" 1.16 +#include "ImageLogging.h" 1.17 +#include "nsCRTGlue.h" 1.18 +#include "imgINotificationObserver.h" 1.19 +#include "nsNetUtil.h" 1.20 + 1.21 +using namespace mozilla::image; 1.22 + 1.23 +// The split of imgRequestProxy and imgRequestProxyStatic means that 1.24 +// certain overridden functions need to be usable in the destructor. 1.25 +// Since virtual functions can't be used in that way, this class 1.26 +// provides a behavioural trait for each class to use instead. 1.27 +class ProxyBehaviour 1.28 +{ 1.29 + public: 1.30 + virtual ~ProxyBehaviour() {} 1.31 + 1.32 + virtual already_AddRefed<mozilla::image::Image> GetImage() const = 0; 1.33 + virtual bool HasImage() const = 0; 1.34 + virtual already_AddRefed<imgStatusTracker> GetStatusTracker() const = 0; 1.35 + virtual imgRequest* GetOwner() const = 0; 1.36 + virtual void SetOwner(imgRequest* aOwner) = 0; 1.37 +}; 1.38 + 1.39 +class RequestBehaviour : public ProxyBehaviour 1.40 +{ 1.41 + public: 1.42 + RequestBehaviour() : mOwner(nullptr), mOwnerHasImage(false) {} 1.43 + 1.44 + virtual already_AddRefed<mozilla::image::Image> GetImage() const MOZ_OVERRIDE; 1.45 + virtual bool HasImage() const MOZ_OVERRIDE; 1.46 + virtual already_AddRefed<imgStatusTracker> GetStatusTracker() const MOZ_OVERRIDE; 1.47 + 1.48 + virtual imgRequest* GetOwner() const MOZ_OVERRIDE { 1.49 + return mOwner; 1.50 + } 1.51 + 1.52 + virtual void SetOwner(imgRequest* aOwner) MOZ_OVERRIDE { 1.53 + mOwner = aOwner; 1.54 + 1.55 + if (mOwner) { 1.56 + nsRefPtr<imgStatusTracker> ownerStatusTracker = GetStatusTracker(); 1.57 + mOwnerHasImage = ownerStatusTracker && ownerStatusTracker->HasImage(); 1.58 + } else { 1.59 + mOwnerHasImage = false; 1.60 + } 1.61 + } 1.62 + 1.63 + private: 1.64 + // We maintain the following invariant: 1.65 + // The proxy is registered at most with a single imgRequest as an observer, 1.66 + // and whenever it is, mOwner points to that object. This helps ensure that 1.67 + // imgRequestProxy::~imgRequestProxy unregisters the proxy as an observer 1.68 + // from whatever request it was registered with (if any). This, in turn, 1.69 + // means that imgRequest::mObservers will not have any stale pointers in it. 1.70 + nsRefPtr<imgRequest> mOwner; 1.71 + 1.72 + bool mOwnerHasImage; 1.73 +}; 1.74 + 1.75 +already_AddRefed<mozilla::image::Image> 1.76 +RequestBehaviour::GetImage() const 1.77 +{ 1.78 + if (!mOwnerHasImage) 1.79 + return nullptr; 1.80 + nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker(); 1.81 + return statusTracker->GetImage(); 1.82 +} 1.83 + 1.84 +already_AddRefed<imgStatusTracker> 1.85 +RequestBehaviour::GetStatusTracker() const 1.86 +{ 1.87 + // NOTE: It's possible that our mOwner has an Image that it didn't notify 1.88 + // us about, if we were Canceled before its Image was constructed. 1.89 + // (Canceling removes us as an observer, so mOwner has no way to notify us). 1.90 + // That's why this method uses mOwner->GetStatusTracker() instead of just 1.91 + // mOwner->mStatusTracker -- we might have a null mImage and yet have an 1.92 + // mOwner with a non-null mImage (and a null mStatusTracker pointer). 1.93 + return mOwner->GetStatusTracker(); 1.94 +} 1.95 + 1.96 +NS_IMPL_ADDREF(imgRequestProxy) 1.97 +NS_IMPL_RELEASE(imgRequestProxy) 1.98 + 1.99 +NS_INTERFACE_MAP_BEGIN(imgRequestProxy) 1.100 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, imgIRequest) 1.101 + NS_INTERFACE_MAP_ENTRY(imgIRequest) 1.102 + NS_INTERFACE_MAP_ENTRY(nsIRequest) 1.103 + NS_INTERFACE_MAP_ENTRY(nsISupportsPriority) 1.104 + NS_INTERFACE_MAP_ENTRY(nsISecurityInfoProvider) 1.105 + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITimedChannel, TimedChannel() != nullptr) 1.106 +NS_INTERFACE_MAP_END 1.107 + 1.108 +imgRequestProxy::imgRequestProxy() : 1.109 + mBehaviour(new RequestBehaviour), 1.110 + mURI(nullptr), 1.111 + mListener(nullptr), 1.112 + mLoadFlags(nsIRequest::LOAD_NORMAL), 1.113 + mLockCount(0), 1.114 + mAnimationConsumers(0), 1.115 + mCanceled(false), 1.116 + mIsInLoadGroup(false), 1.117 + mListenerIsStrongRef(false), 1.118 + mDecodeRequested(false), 1.119 + mDeferNotifications(false), 1.120 + mSentStartContainer(false) 1.121 +{ 1.122 + /* member initializers and constructor code */ 1.123 + 1.124 +} 1.125 + 1.126 +imgRequestProxy::~imgRequestProxy() 1.127 +{ 1.128 + /* destructor code */ 1.129 + NS_PRECONDITION(!mListener, "Someone forgot to properly cancel this request!"); 1.130 + 1.131 + // Unlock the image the proper number of times if we're holding locks on it. 1.132 + // Note that UnlockImage() decrements mLockCount each time it's called. 1.133 + while (mLockCount) 1.134 + UnlockImage(); 1.135 + 1.136 + ClearAnimationConsumers(); 1.137 + 1.138 + // Explicitly set mListener to null to ensure that the RemoveProxy 1.139 + // call below can't send |this| to an arbitrary listener while |this| 1.140 + // is being destroyed. This is all belt-and-suspenders in view of the 1.141 + // above assert. 1.142 + NullOutListener(); 1.143 + 1.144 + if (GetOwner()) { 1.145 + /* Call RemoveProxy with a successful status. This will keep the 1.146 + channel, if still downloading data, from being canceled if 'this' is 1.147 + the last observer. This allows the image to continue to download and 1.148 + be cached even if no one is using it currently. 1.149 + */ 1.150 + mCanceled = true; 1.151 + GetOwner()->RemoveProxy(this, NS_OK); 1.152 + } 1.153 +} 1.154 + 1.155 +nsresult imgRequestProxy::Init(imgRequest* aOwner, 1.156 + nsILoadGroup* aLoadGroup, 1.157 + ImageURL* aURI, 1.158 + imgINotificationObserver* aObserver) 1.159 +{ 1.160 + NS_PRECONDITION(!GetOwner() && !mListener, "imgRequestProxy is already initialized"); 1.161 + 1.162 + LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgRequestProxy::Init", "request", aOwner); 1.163 + 1.164 + NS_ABORT_IF_FALSE(mAnimationConsumers == 0, "Cannot have animation before Init"); 1.165 + 1.166 + mBehaviour->SetOwner(aOwner); 1.167 + mListener = aObserver; 1.168 + // Make sure to addref mListener before the AddProxy call below, since 1.169 + // that call might well want to release it if the imgRequest has 1.170 + // already seen OnStopRequest. 1.171 + if (mListener) { 1.172 + mListenerIsStrongRef = true; 1.173 + NS_ADDREF(mListener); 1.174 + } 1.175 + mLoadGroup = aLoadGroup; 1.176 + mURI = aURI; 1.177 + 1.178 + // Note: AddProxy won't send all the On* notifications immediately 1.179 + if (GetOwner()) 1.180 + GetOwner()->AddProxy(this); 1.181 + 1.182 + return NS_OK; 1.183 +} 1.184 + 1.185 +nsresult imgRequestProxy::ChangeOwner(imgRequest *aNewOwner) 1.186 +{ 1.187 + NS_PRECONDITION(GetOwner(), "Cannot ChangeOwner on a proxy without an owner!"); 1.188 + 1.189 + if (mCanceled) { 1.190 + // Ensure that this proxy has received all notifications to date before 1.191 + // we clean it up when removing it from the old owner below. 1.192 + SyncNotifyListener(); 1.193 + } 1.194 + 1.195 + // If we're holding locks, unlock the old image. 1.196 + // Note that UnlockImage decrements mLockCount each time it's called. 1.197 + uint32_t oldLockCount = mLockCount; 1.198 + while (mLockCount) 1.199 + UnlockImage(); 1.200 + 1.201 + // If we're holding animation requests, undo them. 1.202 + uint32_t oldAnimationConsumers = mAnimationConsumers; 1.203 + ClearAnimationConsumers(); 1.204 + 1.205 + // Were we decoded before? 1.206 + bool wasDecoded = false; 1.207 + nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker(); 1.208 + if (statusTracker->HasImage() && 1.209 + statusTracker->GetImageStatus() & imgIRequest::STATUS_FRAME_COMPLETE) { 1.210 + wasDecoded = true; 1.211 + } 1.212 + 1.213 + GetOwner()->RemoveProxy(this, NS_IMAGELIB_CHANGING_OWNER); 1.214 + 1.215 + mBehaviour->SetOwner(aNewOwner); 1.216 + 1.217 + // If we were locked, apply the locks here 1.218 + for (uint32_t i = 0; i < oldLockCount; i++) 1.219 + LockImage(); 1.220 + 1.221 + // If we had animation requests, restore them here. Note that we 1.222 + // do this *after* RemoveProxy, which clears out animation consumers 1.223 + // (see bug 601723). 1.224 + for (uint32_t i = 0; i < oldAnimationConsumers; i++) 1.225 + IncrementAnimationConsumers(); 1.226 + 1.227 + GetOwner()->AddProxy(this); 1.228 + 1.229 + // If we were decoded, or if we'd previously requested a decode, request a 1.230 + // decode on the new image 1.231 + if (wasDecoded || mDecodeRequested) 1.232 + GetOwner()->StartDecoding(); 1.233 + 1.234 + return NS_OK; 1.235 +} 1.236 + 1.237 +void imgRequestProxy::AddToLoadGroup() 1.238 +{ 1.239 + NS_ASSERTION(!mIsInLoadGroup, "Whaa, we're already in the loadgroup!"); 1.240 + 1.241 + if (!mIsInLoadGroup && mLoadGroup) { 1.242 + mLoadGroup->AddRequest(this, nullptr); 1.243 + mIsInLoadGroup = true; 1.244 + } 1.245 +} 1.246 + 1.247 +void imgRequestProxy::RemoveFromLoadGroup(bool releaseLoadGroup) 1.248 +{ 1.249 + if (!mIsInLoadGroup) 1.250 + return; 1.251 + 1.252 + /* calling RemoveFromLoadGroup may cause the document to finish 1.253 + loading, which could result in our death. We need to make sure 1.254 + that we stay alive long enough to fight another battle... at 1.255 + least until we exit this function. 1.256 + */ 1.257 + nsCOMPtr<imgIRequest> kungFuDeathGrip(this); 1.258 + 1.259 + mLoadGroup->RemoveRequest(this, nullptr, NS_OK); 1.260 + mIsInLoadGroup = false; 1.261 + 1.262 + if (releaseLoadGroup) { 1.263 + // We're done with the loadgroup, release it. 1.264 + mLoadGroup = nullptr; 1.265 + } 1.266 +} 1.267 + 1.268 + 1.269 +/** nsIRequest / imgIRequest methods **/ 1.270 + 1.271 +/* readonly attribute wstring name; */ 1.272 +NS_IMETHODIMP imgRequestProxy::GetName(nsACString &aName) 1.273 +{ 1.274 + aName.Truncate(); 1.275 + 1.276 + if (mURI) 1.277 + mURI->GetSpec(aName); 1.278 + 1.279 + return NS_OK; 1.280 +} 1.281 + 1.282 +/* boolean isPending (); */ 1.283 +NS_IMETHODIMP imgRequestProxy::IsPending(bool *_retval) 1.284 +{ 1.285 + return NS_ERROR_NOT_IMPLEMENTED; 1.286 +} 1.287 + 1.288 +/* readonly attribute nsresult status; */ 1.289 +NS_IMETHODIMP imgRequestProxy::GetStatus(nsresult *aStatus) 1.290 +{ 1.291 + return NS_ERROR_NOT_IMPLEMENTED; 1.292 +} 1.293 + 1.294 +/* void cancel (in nsresult status); */ 1.295 +NS_IMETHODIMP imgRequestProxy::Cancel(nsresult status) 1.296 +{ 1.297 + if (mCanceled) 1.298 + return NS_ERROR_FAILURE; 1.299 + 1.300 + LOG_SCOPE(GetImgLog(), "imgRequestProxy::Cancel"); 1.301 + 1.302 + mCanceled = true; 1.303 + 1.304 + nsCOMPtr<nsIRunnable> ev = new imgCancelRunnable(this, status); 1.305 + return NS_DispatchToCurrentThread(ev); 1.306 +} 1.307 + 1.308 +void 1.309 +imgRequestProxy::DoCancel(nsresult status) 1.310 +{ 1.311 + if (GetOwner()) { 1.312 + GetOwner()->RemoveProxy(this, status); 1.313 + } 1.314 + 1.315 + NullOutListener(); 1.316 +} 1.317 + 1.318 +/* void cancelAndForgetObserver (in nsresult aStatus); */ 1.319 +NS_IMETHODIMP imgRequestProxy::CancelAndForgetObserver(nsresult aStatus) 1.320 +{ 1.321 + // If mCanceled is true but mListener is non-null, that means 1.322 + // someone called Cancel() on us but the imgCancelRunnable is still 1.323 + // pending. We still need to null out mListener before returning 1.324 + // from this function in this case. That means we want to do the 1.325 + // RemoveProxy call right now, because we need to deliver the 1.326 + // onStopRequest. 1.327 + if (mCanceled && !mListener) 1.328 + return NS_ERROR_FAILURE; 1.329 + 1.330 + LOG_SCOPE(GetImgLog(), "imgRequestProxy::CancelAndForgetObserver"); 1.331 + 1.332 + mCanceled = true; 1.333 + 1.334 + // Now cheat and make sure our removal from loadgroup happens async 1.335 + bool oldIsInLoadGroup = mIsInLoadGroup; 1.336 + mIsInLoadGroup = false; 1.337 + 1.338 + if (GetOwner()) { 1.339 + GetOwner()->RemoveProxy(this, aStatus); 1.340 + } 1.341 + 1.342 + mIsInLoadGroup = oldIsInLoadGroup; 1.343 + 1.344 + if (mIsInLoadGroup) { 1.345 + nsCOMPtr<nsIRunnable> ev = 1.346 + NS_NewRunnableMethod(this, &imgRequestProxy::DoRemoveFromLoadGroup); 1.347 + NS_DispatchToCurrentThread(ev); 1.348 + } 1.349 + 1.350 + NullOutListener(); 1.351 + 1.352 + return NS_OK; 1.353 +} 1.354 + 1.355 +/* void startDecode (); */ 1.356 +NS_IMETHODIMP 1.357 +imgRequestProxy::StartDecoding() 1.358 +{ 1.359 + if (!GetOwner()) 1.360 + return NS_ERROR_FAILURE; 1.361 + 1.362 + // Flag this, so we know to transfer the request if our owner changes 1.363 + mDecodeRequested = true; 1.364 + 1.365 + // Forward the request 1.366 + return GetOwner()->StartDecoding(); 1.367 +} 1.368 + 1.369 +/* void requestDecode (); */ 1.370 +NS_IMETHODIMP 1.371 +imgRequestProxy::RequestDecode() 1.372 +{ 1.373 + if (!GetOwner()) 1.374 + return NS_ERROR_FAILURE; 1.375 + 1.376 + // Flag this, so we know to transfer the request if our owner changes 1.377 + mDecodeRequested = true; 1.378 + 1.379 + // Forward the request 1.380 + return GetOwner()->RequestDecode(); 1.381 +} 1.382 + 1.383 + 1.384 +/* void lockImage (); */ 1.385 +NS_IMETHODIMP 1.386 +imgRequestProxy::LockImage() 1.387 +{ 1.388 + mLockCount++; 1.389 + nsRefPtr<Image> image = GetImage(); 1.390 + if (image) 1.391 + return image->LockImage(); 1.392 + return NS_OK; 1.393 +} 1.394 + 1.395 +/* void unlockImage (); */ 1.396 +NS_IMETHODIMP 1.397 +imgRequestProxy::UnlockImage() 1.398 +{ 1.399 + NS_ABORT_IF_FALSE(mLockCount > 0, "calling unlock but no locks!"); 1.400 + 1.401 + mLockCount--; 1.402 + nsRefPtr<Image> image = GetImage(); 1.403 + if (image) 1.404 + return image->UnlockImage(); 1.405 + return NS_OK; 1.406 +} 1.407 + 1.408 +/* void requestDiscard (); */ 1.409 +NS_IMETHODIMP 1.410 +imgRequestProxy::RequestDiscard() 1.411 +{ 1.412 + nsRefPtr<Image> image = GetImage(); 1.413 + if (image) 1.414 + return image->RequestDiscard(); 1.415 + return NS_OK; 1.416 +} 1.417 + 1.418 +NS_IMETHODIMP 1.419 +imgRequestProxy::IncrementAnimationConsumers() 1.420 +{ 1.421 + mAnimationConsumers++; 1.422 + nsRefPtr<Image> image = GetImage(); 1.423 + if (image) 1.424 + image->IncrementAnimationConsumers(); 1.425 + return NS_OK; 1.426 +} 1.427 + 1.428 +NS_IMETHODIMP 1.429 +imgRequestProxy::DecrementAnimationConsumers() 1.430 +{ 1.431 + // We may get here if some responsible code called Increment, 1.432 + // then called us, but we have meanwhile called ClearAnimationConsumers 1.433 + // because we needed to get rid of them earlier (see 1.434 + // imgRequest::RemoveProxy), and hence have nothing left to 1.435 + // decrement. (In such a case we got rid of the animation consumers 1.436 + // early, but not the observer.) 1.437 + if (mAnimationConsumers > 0) { 1.438 + mAnimationConsumers--; 1.439 + nsRefPtr<Image> image = GetImage(); 1.440 + if (image) 1.441 + image->DecrementAnimationConsumers(); 1.442 + } 1.443 + return NS_OK; 1.444 +} 1.445 + 1.446 +void 1.447 +imgRequestProxy::ClearAnimationConsumers() 1.448 +{ 1.449 + while (mAnimationConsumers > 0) 1.450 + DecrementAnimationConsumers(); 1.451 +} 1.452 + 1.453 +/* void suspend (); */ 1.454 +NS_IMETHODIMP imgRequestProxy::Suspend() 1.455 +{ 1.456 + return NS_ERROR_NOT_IMPLEMENTED; 1.457 +} 1.458 + 1.459 +/* void resume (); */ 1.460 +NS_IMETHODIMP imgRequestProxy::Resume() 1.461 +{ 1.462 + return NS_ERROR_NOT_IMPLEMENTED; 1.463 +} 1.464 + 1.465 +/* attribute nsILoadGroup loadGroup */ 1.466 +NS_IMETHODIMP imgRequestProxy::GetLoadGroup(nsILoadGroup **loadGroup) 1.467 +{ 1.468 + NS_IF_ADDREF(*loadGroup = mLoadGroup.get()); 1.469 + return NS_OK; 1.470 +} 1.471 +NS_IMETHODIMP imgRequestProxy::SetLoadGroup(nsILoadGroup *loadGroup) 1.472 +{ 1.473 + mLoadGroup = loadGroup; 1.474 + return NS_OK; 1.475 +} 1.476 + 1.477 +/* attribute nsLoadFlags loadFlags */ 1.478 +NS_IMETHODIMP imgRequestProxy::GetLoadFlags(nsLoadFlags *flags) 1.479 +{ 1.480 + *flags = mLoadFlags; 1.481 + return NS_OK; 1.482 +} 1.483 +NS_IMETHODIMP imgRequestProxy::SetLoadFlags(nsLoadFlags flags) 1.484 +{ 1.485 + mLoadFlags = flags; 1.486 + return NS_OK; 1.487 +} 1.488 + 1.489 +/** imgIRequest methods **/ 1.490 + 1.491 +/* attribute imgIContainer image; */ 1.492 +NS_IMETHODIMP imgRequestProxy::GetImage(imgIContainer **aImage) 1.493 +{ 1.494 + NS_ENSURE_TRUE(aImage, NS_ERROR_NULL_POINTER); 1.495 + // It's possible that our owner has an image but hasn't notified us of it - 1.496 + // that'll happen if we get Canceled before the owner instantiates its image 1.497 + // (because Canceling unregisters us as a listener on mOwner). If we're 1.498 + // in that situation, just grab the image off of mOwner. 1.499 + nsRefPtr<Image> image = GetImage(); 1.500 + nsCOMPtr<imgIContainer> imageToReturn; 1.501 + if (image) 1.502 + imageToReturn = do_QueryInterface(image); 1.503 + if (!imageToReturn && GetOwner()) 1.504 + imageToReturn = GetOwner()->mImage; 1.505 + 1.506 + if (!imageToReturn) 1.507 + return NS_ERROR_FAILURE; 1.508 + 1.509 + imageToReturn.swap(*aImage); 1.510 + 1.511 + return NS_OK; 1.512 +} 1.513 + 1.514 +/* readonly attribute unsigned long imageStatus; */ 1.515 +NS_IMETHODIMP imgRequestProxy::GetImageStatus(uint32_t *aStatus) 1.516 +{ 1.517 + nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker(); 1.518 + *aStatus = statusTracker->GetImageStatus(); 1.519 + 1.520 + return NS_OK; 1.521 +} 1.522 + 1.523 +/* readonly attribute nsIURI URI; */ 1.524 +NS_IMETHODIMP imgRequestProxy::GetURI(nsIURI **aURI) 1.525 +{ 1.526 + MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread to convert URI"); 1.527 + nsCOMPtr<nsIURI> uri = mURI->ToIURI(); 1.528 + uri.forget(aURI); 1.529 + return NS_OK; 1.530 +} 1.531 + 1.532 +nsresult imgRequestProxy::GetURI(ImageURL **aURI) 1.533 +{ 1.534 + if (!mURI) 1.535 + return NS_ERROR_FAILURE; 1.536 + 1.537 + NS_ADDREF(*aURI = mURI); 1.538 + 1.539 + return NS_OK; 1.540 +} 1.541 + 1.542 +/* readonly attribute imgINotificationObserver notificationObserver; */ 1.543 +NS_IMETHODIMP imgRequestProxy::GetNotificationObserver(imgINotificationObserver **aObserver) 1.544 +{ 1.545 + *aObserver = mListener; 1.546 + NS_IF_ADDREF(*aObserver); 1.547 + return NS_OK; 1.548 +} 1.549 + 1.550 +/* readonly attribute string mimeType; */ 1.551 +NS_IMETHODIMP imgRequestProxy::GetMimeType(char **aMimeType) 1.552 +{ 1.553 + if (!GetOwner()) 1.554 + return NS_ERROR_FAILURE; 1.555 + 1.556 + const char *type = GetOwner()->GetMimeType(); 1.557 + if (!type) 1.558 + return NS_ERROR_FAILURE; 1.559 + 1.560 + *aMimeType = NS_strdup(type); 1.561 + 1.562 + return NS_OK; 1.563 +} 1.564 + 1.565 +static imgRequestProxy* NewProxy(imgRequestProxy* /*aThis*/) 1.566 +{ 1.567 + return new imgRequestProxy(); 1.568 +} 1.569 + 1.570 +imgRequestProxy* NewStaticProxy(imgRequestProxy* aThis) 1.571 +{ 1.572 + nsCOMPtr<nsIPrincipal> currentPrincipal; 1.573 + aThis->GetImagePrincipal(getter_AddRefs(currentPrincipal)); 1.574 + nsRefPtr<Image> image = aThis->GetImage(); 1.575 + return new imgRequestProxyStatic(image, currentPrincipal); 1.576 +} 1.577 + 1.578 +NS_IMETHODIMP imgRequestProxy::Clone(imgINotificationObserver* aObserver, 1.579 + imgIRequest** aClone) 1.580 +{ 1.581 + nsresult result; 1.582 + imgRequestProxy* proxy; 1.583 + result = Clone(aObserver, &proxy); 1.584 + *aClone = proxy; 1.585 + return result; 1.586 +} 1.587 + 1.588 +nsresult imgRequestProxy::Clone(imgINotificationObserver* aObserver, 1.589 + imgRequestProxy** aClone) 1.590 +{ 1.591 + return PerformClone(aObserver, NewProxy, aClone); 1.592 +} 1.593 + 1.594 +nsresult imgRequestProxy::PerformClone(imgINotificationObserver* aObserver, 1.595 + imgRequestProxy* (aAllocFn)(imgRequestProxy*), 1.596 + imgRequestProxy** aClone) 1.597 +{ 1.598 + NS_PRECONDITION(aClone, "Null out param"); 1.599 + 1.600 + LOG_SCOPE(GetImgLog(), "imgRequestProxy::Clone"); 1.601 + 1.602 + *aClone = nullptr; 1.603 + nsRefPtr<imgRequestProxy> clone = aAllocFn(this); 1.604 + 1.605 + // It is important to call |SetLoadFlags()| before calling |Init()| because 1.606 + // |Init()| adds the request to the loadgroup. 1.607 + // When a request is added to a loadgroup, its load flags are merged 1.608 + // with the load flags of the loadgroup. 1.609 + // XXXldb That's not true anymore. Stuff from imgLoader adds the 1.610 + // request to the loadgroup. 1.611 + clone->SetLoadFlags(mLoadFlags); 1.612 + nsresult rv = clone->Init(mBehaviour->GetOwner(), mLoadGroup, mURI, aObserver); 1.613 + if (NS_FAILED(rv)) 1.614 + return rv; 1.615 + 1.616 + // Assign to *aClone before calling Notify so that if the caller expects to 1.617 + // only be notified for requests it's already holding pointers to it won't be 1.618 + // surprised. 1.619 + NS_ADDREF(*aClone = clone); 1.620 + 1.621 + // This is wrong!!! We need to notify asynchronously, but there's code that 1.622 + // assumes that we don't. This will be fixed in bug 580466. 1.623 + clone->SyncNotifyListener(); 1.624 + 1.625 + return NS_OK; 1.626 +} 1.627 + 1.628 +/* readonly attribute nsIPrincipal imagePrincipal; */ 1.629 +NS_IMETHODIMP imgRequestProxy::GetImagePrincipal(nsIPrincipal **aPrincipal) 1.630 +{ 1.631 + if (!GetOwner()) 1.632 + return NS_ERROR_FAILURE; 1.633 + 1.634 + NS_ADDREF(*aPrincipal = GetOwner()->GetPrincipal()); 1.635 + return NS_OK; 1.636 +} 1.637 + 1.638 +/* readonly attribute bool multipart; */ 1.639 +NS_IMETHODIMP imgRequestProxy::GetMultipart(bool *aMultipart) 1.640 +{ 1.641 + if (!GetOwner()) 1.642 + return NS_ERROR_FAILURE; 1.643 + 1.644 + *aMultipart = GetOwner()->GetMultipart(); 1.645 + 1.646 + return NS_OK; 1.647 +} 1.648 + 1.649 +/* readonly attribute int32_t CORSMode; */ 1.650 +NS_IMETHODIMP imgRequestProxy::GetCORSMode(int32_t* aCorsMode) 1.651 +{ 1.652 + if (!GetOwner()) 1.653 + return NS_ERROR_FAILURE; 1.654 + 1.655 + *aCorsMode = GetOwner()->GetCORSMode(); 1.656 + 1.657 + return NS_OK; 1.658 +} 1.659 + 1.660 +/** nsISupportsPriority methods **/ 1.661 + 1.662 +NS_IMETHODIMP imgRequestProxy::GetPriority(int32_t *priority) 1.663 +{ 1.664 + NS_ENSURE_STATE(GetOwner()); 1.665 + *priority = GetOwner()->Priority(); 1.666 + return NS_OK; 1.667 +} 1.668 + 1.669 +NS_IMETHODIMP imgRequestProxy::SetPriority(int32_t priority) 1.670 +{ 1.671 + NS_ENSURE_STATE(GetOwner() && !mCanceled); 1.672 + GetOwner()->AdjustPriority(this, priority - GetOwner()->Priority()); 1.673 + return NS_OK; 1.674 +} 1.675 + 1.676 +NS_IMETHODIMP imgRequestProxy::AdjustPriority(int32_t priority) 1.677 +{ 1.678 + // We don't require |!mCanceled| here. This may be called even if we're 1.679 + // cancelled, because it's invoked as part of the process of removing an image 1.680 + // from the load group. 1.681 + NS_ENSURE_STATE(GetOwner()); 1.682 + GetOwner()->AdjustPriority(this, priority); 1.683 + return NS_OK; 1.684 +} 1.685 + 1.686 +/** nsISecurityInfoProvider methods **/ 1.687 + 1.688 +NS_IMETHODIMP imgRequestProxy::GetSecurityInfo(nsISupports** _retval) 1.689 +{ 1.690 + if (GetOwner()) 1.691 + return GetOwner()->GetSecurityInfo(_retval); 1.692 + 1.693 + *_retval = nullptr; 1.694 + return NS_OK; 1.695 +} 1.696 + 1.697 +NS_IMETHODIMP imgRequestProxy::GetHasTransferredData(bool* hasData) 1.698 +{ 1.699 + if (GetOwner()) { 1.700 + *hasData = GetOwner()->HasTransferredData(); 1.701 + } else { 1.702 + // The safe thing to do is to claim we have data 1.703 + *hasData = true; 1.704 + } 1.705 + return NS_OK; 1.706 +} 1.707 + 1.708 +/** imgDecoderObserver methods **/ 1.709 + 1.710 +void imgRequestProxy::OnStartDecode() 1.711 +{ 1.712 + // This notification is deliberately not propagated since there are no 1.713 + // listeners who care about it. 1.714 + if (GetOwner()) { 1.715 + // In the case of streaming jpegs, it is possible to get multiple 1.716 + // OnStartDecodes which indicates the beginning of a new decode. The cache 1.717 + // entry's size therefore needs to be reset to 0 here. If we do not do 1.718 + // this, the code in imgStatusTrackerObserver::OnStopFrame will continue to 1.719 + // increase the data size cumulatively. 1.720 + GetOwner()->ResetCacheEntry(); 1.721 + } 1.722 +} 1.723 + 1.724 +void imgRequestProxy::OnStartContainer() 1.725 +{ 1.726 + LOG_FUNC(GetImgLog(), "imgRequestProxy::OnStartContainer"); 1.727 + 1.728 + if (mListener && !mCanceled && !mSentStartContainer) { 1.729 + // Hold a ref to the listener while we call it, just in case. 1.730 + nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener); 1.731 + mListener->Notify(this, imgINotificationObserver::SIZE_AVAILABLE, nullptr); 1.732 + mSentStartContainer = true; 1.733 + } 1.734 +} 1.735 + 1.736 +void imgRequestProxy::OnFrameUpdate(const nsIntRect * rect) 1.737 +{ 1.738 + LOG_FUNC(GetImgLog(), "imgRequestProxy::OnDataAvailable"); 1.739 + 1.740 + if (mListener && !mCanceled) { 1.741 + // Hold a ref to the listener while we call it, just in case. 1.742 + nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener); 1.743 + mListener->Notify(this, imgINotificationObserver::FRAME_UPDATE, rect); 1.744 + } 1.745 +} 1.746 + 1.747 +void imgRequestProxy::OnStopFrame() 1.748 +{ 1.749 + LOG_FUNC(GetImgLog(), "imgRequestProxy::OnStopFrame"); 1.750 + 1.751 + if (mListener && !mCanceled) { 1.752 + // Hold a ref to the listener while we call it, just in case. 1.753 + nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener); 1.754 + mListener->Notify(this, imgINotificationObserver::FRAME_COMPLETE, nullptr); 1.755 + } 1.756 +} 1.757 + 1.758 +void imgRequestProxy::OnStopDecode() 1.759 +{ 1.760 + LOG_FUNC(GetImgLog(), "imgRequestProxy::OnStopDecode"); 1.761 + 1.762 + if (mListener && !mCanceled) { 1.763 + // Hold a ref to the listener while we call it, just in case. 1.764 + nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener); 1.765 + mListener->Notify(this, imgINotificationObserver::DECODE_COMPLETE, nullptr); 1.766 + } 1.767 + 1.768 + if (GetOwner()) { 1.769 + // We finished the decode, and thus have the decoded frames. Update the cache 1.770 + // entry size to take this into account. 1.771 + GetOwner()->UpdateCacheEntrySize(); 1.772 + 1.773 + // Multipart needs reset for next OnStartContainer. 1.774 + if (GetOwner()->GetMultipart()) 1.775 + mSentStartContainer = false; 1.776 + } 1.777 +} 1.778 + 1.779 +void imgRequestProxy::OnDiscard() 1.780 +{ 1.781 + LOG_FUNC(GetImgLog(), "imgRequestProxy::OnDiscard"); 1.782 + 1.783 + if (mListener && !mCanceled) { 1.784 + // Hold a ref to the listener while we call it, just in case. 1.785 + nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener); 1.786 + mListener->Notify(this, imgINotificationObserver::DISCARD, nullptr); 1.787 + } 1.788 + if (GetOwner()) { 1.789 + // Update the cache entry size, since we just got rid of frame data. 1.790 + GetOwner()->UpdateCacheEntrySize(); 1.791 + } 1.792 +} 1.793 + 1.794 +void imgRequestProxy::OnUnlockedDraw() 1.795 +{ 1.796 + LOG_FUNC(GetImgLog(), "imgRequestProxy::OnUnlockedDraw"); 1.797 + 1.798 + if (mListener && !mCanceled) { 1.799 + // Hold a ref to the listener while we call it, just in case. 1.800 + nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener); 1.801 + mListener->Notify(this, imgINotificationObserver::UNLOCKED_DRAW, nullptr); 1.802 + } 1.803 +} 1.804 + 1.805 +void imgRequestProxy::OnImageIsAnimated() 1.806 +{ 1.807 + LOG_FUNC(GetImgLog(), "imgRequestProxy::OnImageIsAnimated"); 1.808 + if (mListener && !mCanceled) { 1.809 + // Hold a ref to the listener while we call it, just in case. 1.810 + nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener); 1.811 + mListener->Notify(this, imgINotificationObserver::IS_ANIMATED, nullptr); 1.812 + } 1.813 +} 1.814 + 1.815 +void imgRequestProxy::OnStartRequest() 1.816 +{ 1.817 +#ifdef PR_LOGGING 1.818 + nsAutoCString name; 1.819 + GetName(name); 1.820 + LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::OnStartRequest", "name", name.get()); 1.821 +#endif 1.822 +} 1.823 + 1.824 +void imgRequestProxy::OnStopRequest(bool lastPart) 1.825 +{ 1.826 +#ifdef PR_LOGGING 1.827 + nsAutoCString name; 1.828 + GetName(name); 1.829 + LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::OnStopRequest", "name", name.get()); 1.830 +#endif 1.831 + // There's all sorts of stuff here that could kill us (the OnStopRequest call 1.832 + // on the listener, the removal from the loadgroup, the release of the 1.833 + // listener, etc). Don't let them do it. 1.834 + nsCOMPtr<imgIRequest> kungFuDeathGrip(this); 1.835 + 1.836 + if (mListener && !mCanceled) { 1.837 + // Hold a ref to the listener while we call it, just in case. 1.838 + nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener); 1.839 + mListener->Notify(this, imgINotificationObserver::LOAD_COMPLETE, nullptr); 1.840 + } 1.841 + 1.842 + // If we're expecting more data from a multipart channel, re-add ourself 1.843 + // to the loadgroup so that the document doesn't lose track of the load. 1.844 + // If the request is already a background request and there's more data 1.845 + // coming, we can just leave the request in the loadgroup as-is. 1.846 + if (lastPart || (mLoadFlags & nsIRequest::LOAD_BACKGROUND) == 0) { 1.847 + RemoveFromLoadGroup(lastPart); 1.848 + // More data is coming, so change the request to be a background request 1.849 + // and put it back in the loadgroup. 1.850 + if (!lastPart) { 1.851 + mLoadFlags |= nsIRequest::LOAD_BACKGROUND; 1.852 + AddToLoadGroup(); 1.853 + } 1.854 + } 1.855 + 1.856 + if (mListenerIsStrongRef && lastPart) { 1.857 + NS_PRECONDITION(mListener, "How did that happen?"); 1.858 + // Drop our strong ref to the listener now that we're done with 1.859 + // everything. Note that this can cancel us and other fun things 1.860 + // like that. Don't add anything in this method after this point. 1.861 + imgINotificationObserver* obs = mListener; 1.862 + mListenerIsStrongRef = false; 1.863 + NS_RELEASE(obs); 1.864 + } 1.865 +} 1.866 + 1.867 +void imgRequestProxy::BlockOnload() 1.868 +{ 1.869 +#ifdef PR_LOGGING 1.870 + nsAutoCString name; 1.871 + GetName(name); 1.872 + LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::BlockOnload", "name", name.get()); 1.873 +#endif 1.874 + 1.875 + nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener); 1.876 + if (blocker) { 1.877 + blocker->BlockOnload(this); 1.878 + } 1.879 +} 1.880 + 1.881 +void imgRequestProxy::UnblockOnload() 1.882 +{ 1.883 +#ifdef PR_LOGGING 1.884 + nsAutoCString name; 1.885 + GetName(name); 1.886 + LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::UnblockOnload", "name", name.get()); 1.887 +#endif 1.888 + 1.889 + nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener); 1.890 + if (blocker) { 1.891 + blocker->UnblockOnload(this); 1.892 + } 1.893 +} 1.894 + 1.895 +void imgRequestProxy::NullOutListener() 1.896 +{ 1.897 + // If we have animation consumers, then they don't matter anymore 1.898 + if (mListener) 1.899 + ClearAnimationConsumers(); 1.900 + 1.901 + if (mListenerIsStrongRef) { 1.902 + // Releasing could do weird reentery stuff, so just play it super-safe 1.903 + nsCOMPtr<imgINotificationObserver> obs; 1.904 + obs.swap(mListener); 1.905 + mListenerIsStrongRef = false; 1.906 + } else { 1.907 + mListener = nullptr; 1.908 + } 1.909 +} 1.910 + 1.911 +NS_IMETHODIMP 1.912 +imgRequestProxy::GetStaticRequest(imgIRequest** aReturn) 1.913 +{ 1.914 + imgRequestProxy *proxy; 1.915 + nsresult result = GetStaticRequest(&proxy); 1.916 + *aReturn = proxy; 1.917 + return result; 1.918 +} 1.919 + 1.920 +nsresult 1.921 +imgRequestProxy::GetStaticRequest(imgRequestProxy** aReturn) 1.922 +{ 1.923 + *aReturn = nullptr; 1.924 + nsRefPtr<Image> image = GetImage(); 1.925 + 1.926 + bool animated; 1.927 + if (!image || (NS_SUCCEEDED(image->GetAnimated(&animated)) && !animated)) { 1.928 + // Early exit - we're not animated, so we don't have to do anything. 1.929 + NS_ADDREF(*aReturn = this); 1.930 + return NS_OK; 1.931 + } 1.932 + 1.933 + // Check for errors in the image. Callers code rely on GetStaticRequest 1.934 + // failing in this case, though with FrozenImage there's no technical reason 1.935 + // for it anymore. 1.936 + if (image->HasError()) { 1.937 + return NS_ERROR_FAILURE; 1.938 + } 1.939 + 1.940 + // We are animated. We need to create a frozen version of this image. 1.941 + nsRefPtr<Image> frozenImage = ImageOps::Freeze(image); 1.942 + 1.943 + // Create a static imgRequestProxy with our new extracted frame. 1.944 + nsCOMPtr<nsIPrincipal> currentPrincipal; 1.945 + GetImagePrincipal(getter_AddRefs(currentPrincipal)); 1.946 + nsRefPtr<imgRequestProxy> req = new imgRequestProxyStatic(frozenImage, 1.947 + currentPrincipal); 1.948 + req->Init(nullptr, nullptr, mURI, nullptr); 1.949 + 1.950 + NS_ADDREF(*aReturn = req); 1.951 + 1.952 + return NS_OK; 1.953 +} 1.954 + 1.955 +void imgRequestProxy::NotifyListener() 1.956 +{ 1.957 + // It would be nice to notify the observer directly in the status tracker 1.958 + // instead of through the proxy, but there are several places we do extra 1.959 + // processing when we receive notifications (like OnStopRequest()), and we 1.960 + // need to check mCanceled everywhere too. 1.961 + 1.962 + nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker(); 1.963 + if (GetOwner()) { 1.964 + // Send the notifications to our listener asynchronously. 1.965 + statusTracker->Notify(this); 1.966 + } else { 1.967 + // We don't have an imgRequest, so we can only notify the clone of our 1.968 + // current state, but we still have to do that asynchronously. 1.969 + NS_ABORT_IF_FALSE(HasImage(), 1.970 + "if we have no imgRequest, we should have an Image"); 1.971 + statusTracker->NotifyCurrentState(this); 1.972 + } 1.973 +} 1.974 + 1.975 +void imgRequestProxy::SyncNotifyListener() 1.976 +{ 1.977 + // It would be nice to notify the observer directly in the status tracker 1.978 + // instead of through the proxy, but there are several places we do extra 1.979 + // processing when we receive notifications (like OnStopRequest()), and we 1.980 + // need to check mCanceled everywhere too. 1.981 + 1.982 + nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker(); 1.983 + statusTracker->SyncNotify(this); 1.984 +} 1.985 + 1.986 +void 1.987 +imgRequestProxy::SetHasImage() 1.988 +{ 1.989 + nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker(); 1.990 + MOZ_ASSERT(statusTracker); 1.991 + nsRefPtr<Image> image = statusTracker->GetImage(); 1.992 + MOZ_ASSERT(image); 1.993 + 1.994 + // Force any private status related to the owner to reflect 1.995 + // the presence of an image; 1.996 + mBehaviour->SetOwner(mBehaviour->GetOwner()); 1.997 + 1.998 + // Apply any locks we have 1.999 + for (uint32_t i = 0; i < mLockCount; ++i) 1.1000 + image->LockImage(); 1.1001 + 1.1002 + // Apply any animation consumers we have 1.1003 + for (uint32_t i = 0; i < mAnimationConsumers; i++) 1.1004 + image->IncrementAnimationConsumers(); 1.1005 +} 1.1006 + 1.1007 +already_AddRefed<imgStatusTracker> 1.1008 +imgRequestProxy::GetStatusTracker() const 1.1009 +{ 1.1010 + return mBehaviour->GetStatusTracker(); 1.1011 +} 1.1012 + 1.1013 +already_AddRefed<mozilla::image::Image> 1.1014 +imgRequestProxy::GetImage() const 1.1015 +{ 1.1016 + return mBehaviour->GetImage(); 1.1017 +} 1.1018 + 1.1019 +bool 1.1020 +RequestBehaviour::HasImage() const 1.1021 +{ 1.1022 + if (!mOwnerHasImage) 1.1023 + return false; 1.1024 + nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker(); 1.1025 + return statusTracker ? statusTracker->HasImage() : false; 1.1026 +} 1.1027 + 1.1028 +bool 1.1029 +imgRequestProxy::HasImage() const 1.1030 +{ 1.1031 + return mBehaviour->HasImage(); 1.1032 +} 1.1033 + 1.1034 +imgRequest* 1.1035 +imgRequestProxy::GetOwner() const 1.1036 +{ 1.1037 + return mBehaviour->GetOwner(); 1.1038 +} 1.1039 + 1.1040 +////////////////// imgRequestProxyStatic methods 1.1041 + 1.1042 +class StaticBehaviour : public ProxyBehaviour 1.1043 +{ 1.1044 +public: 1.1045 + StaticBehaviour(mozilla::image::Image* aImage) : mImage(aImage) {} 1.1046 + 1.1047 + virtual already_AddRefed<mozilla::image::Image> 1.1048 + GetImage() const MOZ_OVERRIDE { 1.1049 + nsRefPtr<mozilla::image::Image> image = mImage; 1.1050 + return image.forget(); 1.1051 + } 1.1052 + 1.1053 + virtual bool HasImage() const MOZ_OVERRIDE { 1.1054 + return mImage; 1.1055 + } 1.1056 + 1.1057 + virtual already_AddRefed<imgStatusTracker> GetStatusTracker() const MOZ_OVERRIDE { 1.1058 + return mImage->GetStatusTracker(); 1.1059 + } 1.1060 + 1.1061 + virtual imgRequest* GetOwner() const MOZ_OVERRIDE { 1.1062 + return nullptr; 1.1063 + } 1.1064 + 1.1065 + virtual void SetOwner(imgRequest* aOwner) MOZ_OVERRIDE { 1.1066 + MOZ_ASSERT(!aOwner, "We shouldn't be giving static requests a non-null owner."); 1.1067 + } 1.1068 + 1.1069 +private: 1.1070 + // Our image. We have to hold a strong reference here, because that's normally 1.1071 + // the job of the underlying request. 1.1072 + nsRefPtr<mozilla::image::Image> mImage; 1.1073 +}; 1.1074 + 1.1075 +imgRequestProxyStatic::imgRequestProxyStatic(mozilla::image::Image* aImage, 1.1076 + nsIPrincipal* aPrincipal) 1.1077 +: mPrincipal(aPrincipal) 1.1078 +{ 1.1079 + mBehaviour = new StaticBehaviour(aImage); 1.1080 +} 1.1081 + 1.1082 +NS_IMETHODIMP imgRequestProxyStatic::GetImagePrincipal(nsIPrincipal **aPrincipal) 1.1083 +{ 1.1084 + if (!mPrincipal) 1.1085 + return NS_ERROR_FAILURE; 1.1086 + 1.1087 + NS_ADDREF(*aPrincipal = mPrincipal); 1.1088 + 1.1089 + return NS_OK; 1.1090 +} 1.1091 + 1.1092 +nsresult 1.1093 +imgRequestProxyStatic::Clone(imgINotificationObserver* aObserver, 1.1094 + imgRequestProxy** aClone) 1.1095 +{ 1.1096 + return PerformClone(aObserver, NewStaticProxy, aClone); 1.1097 +}