Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | // vim: ft=cpp tw=78 sw=2 et ts=2 |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | /* |
michael@0 | 8 | * A base class which implements nsIImageLoadingContent and can be |
michael@0 | 9 | * subclassed by various content nodes that want to provide image |
michael@0 | 10 | * loading functionality (eg <img>, <object>, etc). |
michael@0 | 11 | */ |
michael@0 | 12 | |
michael@0 | 13 | #include "nsImageLoadingContent.h" |
michael@0 | 14 | #include "nsAutoPtr.h" |
michael@0 | 15 | #include "nsError.h" |
michael@0 | 16 | #include "nsIContent.h" |
michael@0 | 17 | #include "nsIDocument.h" |
michael@0 | 18 | #include "nsIScriptGlobalObject.h" |
michael@0 | 19 | #include "nsIDOMWindow.h" |
michael@0 | 20 | #include "nsServiceManagerUtils.h" |
michael@0 | 21 | #include "nsContentPolicyUtils.h" |
michael@0 | 22 | #include "nsIURI.h" |
michael@0 | 23 | #include "nsILoadGroup.h" |
michael@0 | 24 | #include "imgIContainer.h" |
michael@0 | 25 | #include "imgLoader.h" |
michael@0 | 26 | #include "imgRequestProxy.h" |
michael@0 | 27 | #include "nsThreadUtils.h" |
michael@0 | 28 | #include "nsNetUtil.h" |
michael@0 | 29 | #include "nsImageFrame.h" |
michael@0 | 30 | |
michael@0 | 31 | #include "nsIPresShell.h" |
michael@0 | 32 | |
michael@0 | 33 | #include "nsIChannel.h" |
michael@0 | 34 | #include "nsIStreamListener.h" |
michael@0 | 35 | |
michael@0 | 36 | #include "nsIFrame.h" |
michael@0 | 37 | #include "nsIDOMNode.h" |
michael@0 | 38 | |
michael@0 | 39 | #include "nsContentUtils.h" |
michael@0 | 40 | #include "nsLayoutUtils.h" |
michael@0 | 41 | #include "nsIContentPolicy.h" |
michael@0 | 42 | #include "nsSVGEffects.h" |
michael@0 | 43 | |
michael@0 | 44 | #include "mozAutoDocUpdate.h" |
michael@0 | 45 | #include "mozilla/AsyncEventDispatcher.h" |
michael@0 | 46 | #include "mozilla/EventStates.h" |
michael@0 | 47 | #include "mozilla/dom/Element.h" |
michael@0 | 48 | #include "mozilla/dom/ScriptSettings.h" |
michael@0 | 49 | |
michael@0 | 50 | #ifdef LoadImage |
michael@0 | 51 | // Undefine LoadImage to prevent naming conflict with Windows. |
michael@0 | 52 | #undef LoadImage |
michael@0 | 53 | #endif |
michael@0 | 54 | |
michael@0 | 55 | using namespace mozilla; |
michael@0 | 56 | |
michael@0 | 57 | #ifdef DEBUG_chb |
michael@0 | 58 | static void PrintReqURL(imgIRequest* req) { |
michael@0 | 59 | if (!req) { |
michael@0 | 60 | printf("(null req)\n"); |
michael@0 | 61 | return; |
michael@0 | 62 | } |
michael@0 | 63 | |
michael@0 | 64 | nsCOMPtr<nsIURI> uri; |
michael@0 | 65 | req->GetURI(getter_AddRefs(uri)); |
michael@0 | 66 | if (!uri) { |
michael@0 | 67 | printf("(null uri)\n"); |
michael@0 | 68 | return; |
michael@0 | 69 | } |
michael@0 | 70 | |
michael@0 | 71 | nsAutoCString spec; |
michael@0 | 72 | uri->GetSpec(spec); |
michael@0 | 73 | printf("spec='%s'\n", spec.get()); |
michael@0 | 74 | } |
michael@0 | 75 | #endif /* DEBUG_chb */ |
michael@0 | 76 | |
michael@0 | 77 | |
michael@0 | 78 | nsImageLoadingContent::nsImageLoadingContent() |
michael@0 | 79 | : mCurrentRequestFlags(0), |
michael@0 | 80 | mPendingRequestFlags(0), |
michael@0 | 81 | mObserverList(nullptr), |
michael@0 | 82 | mImageBlockingStatus(nsIContentPolicy::ACCEPT), |
michael@0 | 83 | mLoadingEnabled(true), |
michael@0 | 84 | mIsImageStateForced(false), |
michael@0 | 85 | mLoading(false), |
michael@0 | 86 | // mBroken starts out true, since an image without a URI is broken.... |
michael@0 | 87 | mBroken(true), |
michael@0 | 88 | mUserDisabled(false), |
michael@0 | 89 | mSuppressed(false), |
michael@0 | 90 | mFireEventsOnDecode(false), |
michael@0 | 91 | mNewRequestsWillNeedAnimationReset(false), |
michael@0 | 92 | mStateChangerDepth(0), |
michael@0 | 93 | mCurrentRequestRegistered(false), |
michael@0 | 94 | mPendingRequestRegistered(false), |
michael@0 | 95 | mFrameCreateCalled(false), |
michael@0 | 96 | mVisibleCount(0) |
michael@0 | 97 | { |
michael@0 | 98 | if (!nsContentUtils::GetImgLoaderForChannel(nullptr)) { |
michael@0 | 99 | mLoadingEnabled = false; |
michael@0 | 100 | } |
michael@0 | 101 | } |
michael@0 | 102 | |
michael@0 | 103 | void |
michael@0 | 104 | nsImageLoadingContent::DestroyImageLoadingContent() |
michael@0 | 105 | { |
michael@0 | 106 | // Cancel our requests so they won't hold stale refs to us |
michael@0 | 107 | // NB: Don't ask to discard the images here. |
michael@0 | 108 | ClearCurrentRequest(NS_BINDING_ABORTED, 0); |
michael@0 | 109 | ClearPendingRequest(NS_BINDING_ABORTED, 0); |
michael@0 | 110 | } |
michael@0 | 111 | |
michael@0 | 112 | nsImageLoadingContent::~nsImageLoadingContent() |
michael@0 | 113 | { |
michael@0 | 114 | NS_ASSERTION(!mCurrentRequest && !mPendingRequest, |
michael@0 | 115 | "DestroyImageLoadingContent not called"); |
michael@0 | 116 | NS_ASSERTION(!mObserverList.mObserver && !mObserverList.mNext, |
michael@0 | 117 | "Observers still registered?"); |
michael@0 | 118 | } |
michael@0 | 119 | |
michael@0 | 120 | /* |
michael@0 | 121 | * imgINotificationObserver impl |
michael@0 | 122 | */ |
michael@0 | 123 | NS_IMETHODIMP |
michael@0 | 124 | nsImageLoadingContent::Notify(imgIRequest* aRequest, |
michael@0 | 125 | int32_t aType, |
michael@0 | 126 | const nsIntRect* aData) |
michael@0 | 127 | { |
michael@0 | 128 | if (aType == imgINotificationObserver::IS_ANIMATED) { |
michael@0 | 129 | return OnImageIsAnimated(aRequest); |
michael@0 | 130 | } |
michael@0 | 131 | |
michael@0 | 132 | if (aType == imgINotificationObserver::UNLOCKED_DRAW) { |
michael@0 | 133 | OnUnlockedDraw(); |
michael@0 | 134 | return NS_OK; |
michael@0 | 135 | } |
michael@0 | 136 | |
michael@0 | 137 | if (aType == imgINotificationObserver::LOAD_COMPLETE) { |
michael@0 | 138 | // We should definitely have a request here |
michael@0 | 139 | NS_ABORT_IF_FALSE(aRequest, "no request?"); |
michael@0 | 140 | |
michael@0 | 141 | NS_PRECONDITION(aRequest == mCurrentRequest || aRequest == mPendingRequest, |
michael@0 | 142 | "Unknown request"); |
michael@0 | 143 | } |
michael@0 | 144 | |
michael@0 | 145 | { |
michael@0 | 146 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 147 | |
michael@0 | 148 | for (ImageObserver* observer = &mObserverList, *next; observer; |
michael@0 | 149 | observer = next) { |
michael@0 | 150 | next = observer->mNext; |
michael@0 | 151 | if (observer->mObserver) { |
michael@0 | 152 | observer->mObserver->Notify(aRequest, aType, aData); |
michael@0 | 153 | } |
michael@0 | 154 | } |
michael@0 | 155 | } |
michael@0 | 156 | |
michael@0 | 157 | if (aType == imgINotificationObserver::SIZE_AVAILABLE) { |
michael@0 | 158 | // Have to check for state changes here, since we might have been in |
michael@0 | 159 | // the LOADING state before. |
michael@0 | 160 | UpdateImageState(true); |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | if (aType == imgINotificationObserver::LOAD_COMPLETE) { |
michael@0 | 164 | uint32_t reqStatus; |
michael@0 | 165 | aRequest->GetImageStatus(&reqStatus); |
michael@0 | 166 | nsresult status = |
michael@0 | 167 | reqStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK; |
michael@0 | 168 | return OnStopRequest(aRequest, status); |
michael@0 | 169 | } |
michael@0 | 170 | |
michael@0 | 171 | if (aType == imgINotificationObserver::DECODE_COMPLETE && mFireEventsOnDecode) { |
michael@0 | 172 | mFireEventsOnDecode = false; |
michael@0 | 173 | |
michael@0 | 174 | uint32_t reqStatus; |
michael@0 | 175 | aRequest->GetImageStatus(&reqStatus); |
michael@0 | 176 | if (reqStatus & imgIRequest::STATUS_ERROR) { |
michael@0 | 177 | FireEvent(NS_LITERAL_STRING("error")); |
michael@0 | 178 | } else { |
michael@0 | 179 | FireEvent(NS_LITERAL_STRING("load")); |
michael@0 | 180 | } |
michael@0 | 181 | |
michael@0 | 182 | UpdateImageState(true); |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | return NS_OK; |
michael@0 | 186 | } |
michael@0 | 187 | |
michael@0 | 188 | nsresult |
michael@0 | 189 | nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest, |
michael@0 | 190 | nsresult aStatus) |
michael@0 | 191 | { |
michael@0 | 192 | uint32_t oldStatus; |
michael@0 | 193 | aRequest->GetImageStatus(&oldStatus); |
michael@0 | 194 | |
michael@0 | 195 | //XXXjdm This occurs when we have a pending request created, then another |
michael@0 | 196 | // pending request replaces it before the first one is finished. |
michael@0 | 197 | // This begs the question of what the correct behaviour is; we used |
michael@0 | 198 | // to not have to care because we ran this code in OnStopDecode which |
michael@0 | 199 | // wasn't called when the first request was cancelled. For now, I choose |
michael@0 | 200 | // to punt when the given request doesn't appear to have terminated in |
michael@0 | 201 | // an expected state. |
michael@0 | 202 | if (!(oldStatus & (imgIRequest::STATUS_ERROR | imgIRequest::STATUS_LOAD_COMPLETE))) |
michael@0 | 203 | return NS_OK; |
michael@0 | 204 | |
michael@0 | 205 | // Our state may change. Watch it. |
michael@0 | 206 | AutoStateChanger changer(this, true); |
michael@0 | 207 | |
michael@0 | 208 | // If the pending request is loaded, switch to it. |
michael@0 | 209 | if (aRequest == mPendingRequest) { |
michael@0 | 210 | MakePendingRequestCurrent(); |
michael@0 | 211 | } |
michael@0 | 212 | NS_ABORT_IF_FALSE(aRequest == mCurrentRequest, |
michael@0 | 213 | "One way or another, we should be current by now"); |
michael@0 | 214 | |
michael@0 | 215 | // We just loaded all the data we're going to get. If we're visible and |
michael@0 | 216 | // haven't done an initial paint (*), we want to make sure the image starts |
michael@0 | 217 | // decoding immediately, for two reasons: |
michael@0 | 218 | // |
michael@0 | 219 | // 1) This image is sitting idle but might need to be decoded as soon as we |
michael@0 | 220 | // start painting, in which case we've wasted time. |
michael@0 | 221 | // |
michael@0 | 222 | // 2) We want to block onload until all visible images are decoded. We do this |
michael@0 | 223 | // by blocking onload until all in-progress decodes get at least one frame |
michael@0 | 224 | // decoded. However, if all the data comes in while painting is suppressed |
michael@0 | 225 | // (ie, before the initial paint delay is finished), we fire onload without |
michael@0 | 226 | // doing a paint first. This means that decode-on-draw images don't start |
michael@0 | 227 | // decoding, so we can't wait for them to finish. See bug 512435. |
michael@0 | 228 | // |
michael@0 | 229 | // (*) IsPaintingSuppressed returns false if we haven't gotten the initial |
michael@0 | 230 | // reflow yet, so we have to test !DidInitialize || IsPaintingSuppressed. |
michael@0 | 231 | // It's possible for painting to be suppressed for reasons other than the |
michael@0 | 232 | // initial paint delay (for example, being in the bfcache), but we probably |
michael@0 | 233 | // aren't loading images in those situations. |
michael@0 | 234 | |
michael@0 | 235 | // XXXkhuey should this be GetOurCurrentDoc? Decoding if we're not in |
michael@0 | 236 | // the document seems silly. |
michael@0 | 237 | bool startedDecoding = false; |
michael@0 | 238 | nsIDocument* doc = GetOurOwnerDoc(); |
michael@0 | 239 | nsIPresShell* shell = doc ? doc->GetShell() : nullptr; |
michael@0 | 240 | if (shell && shell->IsVisible() && |
michael@0 | 241 | (!shell->DidInitialize() || shell->IsPaintingSuppressed())) { |
michael@0 | 242 | |
michael@0 | 243 | // If we've gotten a frame and that frame has called FrameCreate and that |
michael@0 | 244 | // frame has been reflowed then we know that it checked it's own visibility |
michael@0 | 245 | // so we can trust our visible count and we don't start decode if we are not |
michael@0 | 246 | // visible. |
michael@0 | 247 | nsIFrame* f = GetOurPrimaryFrame(); |
michael@0 | 248 | if (!mFrameCreateCalled || !f || (f->GetStateBits() & NS_FRAME_FIRST_REFLOW) || |
michael@0 | 249 | mVisibleCount > 0 || shell->AssumeAllImagesVisible()) { |
michael@0 | 250 | if (NS_SUCCEEDED(mCurrentRequest->StartDecoding())) { |
michael@0 | 251 | startedDecoding = true; |
michael@0 | 252 | } |
michael@0 | 253 | } |
michael@0 | 254 | } |
michael@0 | 255 | |
michael@0 | 256 | // We want to give the decoder a chance to find errors. If we haven't found |
michael@0 | 257 | // an error yet and we've started decoding, either from the above |
michael@0 | 258 | // StartDecoding or from some other place, we must only fire these events |
michael@0 | 259 | // after we finish decoding. |
michael@0 | 260 | uint32_t reqStatus; |
michael@0 | 261 | aRequest->GetImageStatus(&reqStatus); |
michael@0 | 262 | if (NS_SUCCEEDED(aStatus) && !(reqStatus & imgIRequest::STATUS_ERROR) && |
michael@0 | 263 | (reqStatus & imgIRequest::STATUS_DECODE_STARTED || |
michael@0 | 264 | (startedDecoding && !(reqStatus & imgIRequest::STATUS_DECODE_COMPLETE)))) { |
michael@0 | 265 | mFireEventsOnDecode = true; |
michael@0 | 266 | } else { |
michael@0 | 267 | // Fire the appropriate DOM event. |
michael@0 | 268 | if (NS_SUCCEEDED(aStatus)) { |
michael@0 | 269 | FireEvent(NS_LITERAL_STRING("load")); |
michael@0 | 270 | } else { |
michael@0 | 271 | FireEvent(NS_LITERAL_STRING("error")); |
michael@0 | 272 | } |
michael@0 | 273 | } |
michael@0 | 274 | |
michael@0 | 275 | nsCOMPtr<nsINode> thisNode = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this)); |
michael@0 | 276 | nsSVGEffects::InvalidateDirectRenderingObservers(thisNode->AsElement()); |
michael@0 | 277 | |
michael@0 | 278 | return NS_OK; |
michael@0 | 279 | } |
michael@0 | 280 | |
michael@0 | 281 | void |
michael@0 | 282 | nsImageLoadingContent::OnUnlockedDraw() |
michael@0 | 283 | { |
michael@0 | 284 | if (mVisibleCount > 0) { |
michael@0 | 285 | // We should already be marked as visible, there is nothing more we can do. |
michael@0 | 286 | return; |
michael@0 | 287 | } |
michael@0 | 288 | |
michael@0 | 289 | nsPresContext* presContext = GetFramePresContext(); |
michael@0 | 290 | if (!presContext) |
michael@0 | 291 | return; |
michael@0 | 292 | |
michael@0 | 293 | nsIPresShell* presShell = presContext->PresShell(); |
michael@0 | 294 | if (!presShell) |
michael@0 | 295 | return; |
michael@0 | 296 | |
michael@0 | 297 | presShell->EnsureImageInVisibleList(this); |
michael@0 | 298 | } |
michael@0 | 299 | |
michael@0 | 300 | nsresult |
michael@0 | 301 | nsImageLoadingContent::OnImageIsAnimated(imgIRequest *aRequest) |
michael@0 | 302 | { |
michael@0 | 303 | bool* requestFlag = GetRegisteredFlagForRequest(aRequest); |
michael@0 | 304 | if (requestFlag) { |
michael@0 | 305 | nsLayoutUtils::RegisterImageRequest(GetFramePresContext(), |
michael@0 | 306 | aRequest, requestFlag); |
michael@0 | 307 | } |
michael@0 | 308 | |
michael@0 | 309 | return NS_OK; |
michael@0 | 310 | } |
michael@0 | 311 | |
michael@0 | 312 | /* |
michael@0 | 313 | * nsIImageLoadingContent impl |
michael@0 | 314 | */ |
michael@0 | 315 | |
michael@0 | 316 | NS_IMETHODIMP |
michael@0 | 317 | nsImageLoadingContent::GetLoadingEnabled(bool *aLoadingEnabled) |
michael@0 | 318 | { |
michael@0 | 319 | *aLoadingEnabled = mLoadingEnabled; |
michael@0 | 320 | return NS_OK; |
michael@0 | 321 | } |
michael@0 | 322 | |
michael@0 | 323 | NS_IMETHODIMP |
michael@0 | 324 | nsImageLoadingContent::SetLoadingEnabled(bool aLoadingEnabled) |
michael@0 | 325 | { |
michael@0 | 326 | if (nsContentUtils::GetImgLoaderForChannel(nullptr)) { |
michael@0 | 327 | mLoadingEnabled = aLoadingEnabled; |
michael@0 | 328 | } |
michael@0 | 329 | return NS_OK; |
michael@0 | 330 | } |
michael@0 | 331 | |
michael@0 | 332 | NS_IMETHODIMP |
michael@0 | 333 | nsImageLoadingContent::GetImageBlockingStatus(int16_t* aStatus) |
michael@0 | 334 | { |
michael@0 | 335 | NS_PRECONDITION(aStatus, "Null out param"); |
michael@0 | 336 | *aStatus = ImageBlockingStatus(); |
michael@0 | 337 | return NS_OK; |
michael@0 | 338 | } |
michael@0 | 339 | |
michael@0 | 340 | NS_IMETHODIMP |
michael@0 | 341 | nsImageLoadingContent::AddObserver(imgINotificationObserver* aObserver) |
michael@0 | 342 | { |
michael@0 | 343 | NS_ENSURE_ARG_POINTER(aObserver); |
michael@0 | 344 | |
michael@0 | 345 | if (!mObserverList.mObserver) { |
michael@0 | 346 | mObserverList.mObserver = aObserver; |
michael@0 | 347 | // Don't touch the linking of the list! |
michael@0 | 348 | return NS_OK; |
michael@0 | 349 | } |
michael@0 | 350 | |
michael@0 | 351 | // otherwise we have to create a new entry |
michael@0 | 352 | |
michael@0 | 353 | ImageObserver* observer = &mObserverList; |
michael@0 | 354 | while (observer->mNext) { |
michael@0 | 355 | observer = observer->mNext; |
michael@0 | 356 | } |
michael@0 | 357 | |
michael@0 | 358 | observer->mNext = new ImageObserver(aObserver); |
michael@0 | 359 | if (! observer->mNext) { |
michael@0 | 360 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 361 | } |
michael@0 | 362 | |
michael@0 | 363 | return NS_OK; |
michael@0 | 364 | } |
michael@0 | 365 | |
michael@0 | 366 | NS_IMETHODIMP |
michael@0 | 367 | nsImageLoadingContent::RemoveObserver(imgINotificationObserver* aObserver) |
michael@0 | 368 | { |
michael@0 | 369 | NS_ENSURE_ARG_POINTER(aObserver); |
michael@0 | 370 | |
michael@0 | 371 | if (mObserverList.mObserver == aObserver) { |
michael@0 | 372 | mObserverList.mObserver = nullptr; |
michael@0 | 373 | // Don't touch the linking of the list! |
michael@0 | 374 | return NS_OK; |
michael@0 | 375 | } |
michael@0 | 376 | |
michael@0 | 377 | // otherwise have to find it and splice it out |
michael@0 | 378 | ImageObserver* observer = &mObserverList; |
michael@0 | 379 | while (observer->mNext && observer->mNext->mObserver != aObserver) { |
michael@0 | 380 | observer = observer->mNext; |
michael@0 | 381 | } |
michael@0 | 382 | |
michael@0 | 383 | // At this point, we are pointing to the list element whose mNext is |
michael@0 | 384 | // the right observer (assuming of course that mNext is not null) |
michael@0 | 385 | if (observer->mNext) { |
michael@0 | 386 | // splice it out |
michael@0 | 387 | ImageObserver* oldObserver = observer->mNext; |
michael@0 | 388 | observer->mNext = oldObserver->mNext; |
michael@0 | 389 | oldObserver->mNext = nullptr; // so we don't destroy them all |
michael@0 | 390 | delete oldObserver; |
michael@0 | 391 | } |
michael@0 | 392 | #ifdef DEBUG |
michael@0 | 393 | else { |
michael@0 | 394 | NS_WARNING("Asked to remove nonexistent observer"); |
michael@0 | 395 | } |
michael@0 | 396 | #endif |
michael@0 | 397 | return NS_OK; |
michael@0 | 398 | } |
michael@0 | 399 | |
michael@0 | 400 | already_AddRefed<imgIRequest> |
michael@0 | 401 | nsImageLoadingContent::GetRequest(int32_t aRequestType, |
michael@0 | 402 | ErrorResult& aError) |
michael@0 | 403 | { |
michael@0 | 404 | nsCOMPtr<imgIRequest> request; |
michael@0 | 405 | switch(aRequestType) { |
michael@0 | 406 | case CURRENT_REQUEST: |
michael@0 | 407 | request = mCurrentRequest; |
michael@0 | 408 | break; |
michael@0 | 409 | case PENDING_REQUEST: |
michael@0 | 410 | request = mPendingRequest; |
michael@0 | 411 | break; |
michael@0 | 412 | default: |
michael@0 | 413 | NS_ERROR("Unknown request type"); |
michael@0 | 414 | aError.Throw(NS_ERROR_UNEXPECTED); |
michael@0 | 415 | } |
michael@0 | 416 | |
michael@0 | 417 | return request.forget(); |
michael@0 | 418 | } |
michael@0 | 419 | |
michael@0 | 420 | NS_IMETHODIMP |
michael@0 | 421 | nsImageLoadingContent::GetRequest(int32_t aRequestType, |
michael@0 | 422 | imgIRequest** aRequest) |
michael@0 | 423 | { |
michael@0 | 424 | NS_ENSURE_ARG_POINTER(aRequest); |
michael@0 | 425 | |
michael@0 | 426 | ErrorResult result; |
michael@0 | 427 | *aRequest = GetRequest(aRequestType, result).take(); |
michael@0 | 428 | |
michael@0 | 429 | return result.ErrorCode(); |
michael@0 | 430 | } |
michael@0 | 431 | |
michael@0 | 432 | NS_IMETHODIMP_(void) |
michael@0 | 433 | nsImageLoadingContent::FrameCreated(nsIFrame* aFrame) |
michael@0 | 434 | { |
michael@0 | 435 | NS_ASSERTION(aFrame, "aFrame is null"); |
michael@0 | 436 | |
michael@0 | 437 | mFrameCreateCalled = true; |
michael@0 | 438 | |
michael@0 | 439 | if (aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP)) { |
michael@0 | 440 | // Assume all images in popups are visible. |
michael@0 | 441 | IncrementVisibleCount(); |
michael@0 | 442 | } |
michael@0 | 443 | |
michael@0 | 444 | TrackImage(mCurrentRequest); |
michael@0 | 445 | TrackImage(mPendingRequest); |
michael@0 | 446 | |
michael@0 | 447 | // We need to make sure that our image request is registered, if it should |
michael@0 | 448 | // be registered. |
michael@0 | 449 | nsPresContext* presContext = aFrame->PresContext(); |
michael@0 | 450 | if (mCurrentRequest) { |
michael@0 | 451 | nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, mCurrentRequest, |
michael@0 | 452 | &mCurrentRequestRegistered); |
michael@0 | 453 | } |
michael@0 | 454 | |
michael@0 | 455 | if (mPendingRequest) { |
michael@0 | 456 | nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, mPendingRequest, |
michael@0 | 457 | &mPendingRequestRegistered); |
michael@0 | 458 | } |
michael@0 | 459 | } |
michael@0 | 460 | |
michael@0 | 461 | NS_IMETHODIMP_(void) |
michael@0 | 462 | nsImageLoadingContent::FrameDestroyed(nsIFrame* aFrame) |
michael@0 | 463 | { |
michael@0 | 464 | NS_ASSERTION(aFrame, "aFrame is null"); |
michael@0 | 465 | |
michael@0 | 466 | mFrameCreateCalled = false; |
michael@0 | 467 | |
michael@0 | 468 | // We need to make sure that our image request is deregistered. |
michael@0 | 469 | nsPresContext* presContext = GetFramePresContext(); |
michael@0 | 470 | if (mCurrentRequest) { |
michael@0 | 471 | nsLayoutUtils::DeregisterImageRequest(presContext, |
michael@0 | 472 | mCurrentRequest, |
michael@0 | 473 | &mCurrentRequestRegistered); |
michael@0 | 474 | } |
michael@0 | 475 | |
michael@0 | 476 | if (mPendingRequest) { |
michael@0 | 477 | nsLayoutUtils::DeregisterImageRequest(presContext, |
michael@0 | 478 | mPendingRequest, |
michael@0 | 479 | &mPendingRequestRegistered); |
michael@0 | 480 | } |
michael@0 | 481 | |
michael@0 | 482 | UntrackImage(mCurrentRequest); |
michael@0 | 483 | UntrackImage(mPendingRequest); |
michael@0 | 484 | |
michael@0 | 485 | nsIPresShell* presShell = presContext ? presContext->GetPresShell() : nullptr; |
michael@0 | 486 | if (presShell) { |
michael@0 | 487 | presShell->RemoveImageFromVisibleList(this); |
michael@0 | 488 | } |
michael@0 | 489 | |
michael@0 | 490 | if (aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP)) { |
michael@0 | 491 | // We assume all images in popups are visible, so this decrement balances |
michael@0 | 492 | // out the increment in FrameCreated above. |
michael@0 | 493 | DecrementVisibleCount(); |
michael@0 | 494 | } |
michael@0 | 495 | } |
michael@0 | 496 | |
michael@0 | 497 | int32_t |
michael@0 | 498 | nsImageLoadingContent::GetRequestType(imgIRequest* aRequest, |
michael@0 | 499 | ErrorResult& aError) |
michael@0 | 500 | { |
michael@0 | 501 | if (aRequest == mCurrentRequest) { |
michael@0 | 502 | return CURRENT_REQUEST; |
michael@0 | 503 | } |
michael@0 | 504 | |
michael@0 | 505 | if (aRequest == mPendingRequest) { |
michael@0 | 506 | return PENDING_REQUEST; |
michael@0 | 507 | } |
michael@0 | 508 | |
michael@0 | 509 | NS_ERROR("Unknown request"); |
michael@0 | 510 | aError.Throw(NS_ERROR_UNEXPECTED); |
michael@0 | 511 | return UNKNOWN_REQUEST; |
michael@0 | 512 | } |
michael@0 | 513 | |
michael@0 | 514 | NS_IMETHODIMP |
michael@0 | 515 | nsImageLoadingContent::GetRequestType(imgIRequest* aRequest, |
michael@0 | 516 | int32_t* aRequestType) |
michael@0 | 517 | { |
michael@0 | 518 | NS_PRECONDITION(aRequestType, "Null out param"); |
michael@0 | 519 | |
michael@0 | 520 | ErrorResult result; |
michael@0 | 521 | *aRequestType = GetRequestType(aRequest, result); |
michael@0 | 522 | return result.ErrorCode(); |
michael@0 | 523 | } |
michael@0 | 524 | |
michael@0 | 525 | already_AddRefed<nsIURI> |
michael@0 | 526 | nsImageLoadingContent::GetCurrentURI(ErrorResult& aError) |
michael@0 | 527 | { |
michael@0 | 528 | nsCOMPtr<nsIURI> uri; |
michael@0 | 529 | if (mCurrentRequest) { |
michael@0 | 530 | mCurrentRequest->GetURI(getter_AddRefs(uri)); |
michael@0 | 531 | } else if (mCurrentURI) { |
michael@0 | 532 | nsresult rv = NS_EnsureSafeToReturn(mCurrentURI, getter_AddRefs(uri)); |
michael@0 | 533 | if (NS_FAILED(rv)) { |
michael@0 | 534 | aError.Throw(rv); |
michael@0 | 535 | } |
michael@0 | 536 | } |
michael@0 | 537 | |
michael@0 | 538 | return uri.forget(); |
michael@0 | 539 | } |
michael@0 | 540 | |
michael@0 | 541 | NS_IMETHODIMP |
michael@0 | 542 | nsImageLoadingContent::GetCurrentURI(nsIURI** aURI) |
michael@0 | 543 | { |
michael@0 | 544 | NS_ENSURE_ARG_POINTER(aURI); |
michael@0 | 545 | |
michael@0 | 546 | ErrorResult result; |
michael@0 | 547 | *aURI = GetCurrentURI(result).take(); |
michael@0 | 548 | return result.ErrorCode(); |
michael@0 | 549 | } |
michael@0 | 550 | |
michael@0 | 551 | already_AddRefed<nsIStreamListener> |
michael@0 | 552 | nsImageLoadingContent::LoadImageWithChannel(nsIChannel* aChannel, |
michael@0 | 553 | ErrorResult& aError) |
michael@0 | 554 | { |
michael@0 | 555 | if (!nsContentUtils::GetImgLoaderForChannel(aChannel)) { |
michael@0 | 556 | aError.Throw(NS_ERROR_NULL_POINTER); |
michael@0 | 557 | return nullptr; |
michael@0 | 558 | } |
michael@0 | 559 | |
michael@0 | 560 | nsCOMPtr<nsIDocument> doc = GetOurOwnerDoc(); |
michael@0 | 561 | if (!doc) { |
michael@0 | 562 | // Don't bother |
michael@0 | 563 | return nullptr; |
michael@0 | 564 | } |
michael@0 | 565 | |
michael@0 | 566 | // XXX what should we do with content policies here, if anything? |
michael@0 | 567 | // Shouldn't that be done before the start of the load? |
michael@0 | 568 | // XXX what about shouldProcess? |
michael@0 | 569 | |
michael@0 | 570 | // Our state might change. Watch it. |
michael@0 | 571 | AutoStateChanger changer(this, true); |
michael@0 | 572 | |
michael@0 | 573 | // Do the load. |
michael@0 | 574 | nsCOMPtr<nsIStreamListener> listener; |
michael@0 | 575 | nsRefPtr<imgRequestProxy>& req = PrepareNextRequest(); |
michael@0 | 576 | nsresult rv = nsContentUtils::GetImgLoaderForChannel(aChannel)-> |
michael@0 | 577 | LoadImageWithChannel(aChannel, this, doc, |
michael@0 | 578 | getter_AddRefs(listener), |
michael@0 | 579 | getter_AddRefs(req)); |
michael@0 | 580 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 581 | TrackImage(req); |
michael@0 | 582 | ResetAnimationIfNeeded(); |
michael@0 | 583 | } else { |
michael@0 | 584 | MOZ_ASSERT(!req, "Shouldn't have non-null request here"); |
michael@0 | 585 | // If we don't have a current URI, we might as well store this URI so people |
michael@0 | 586 | // know what we tried (and failed) to load. |
michael@0 | 587 | if (!mCurrentRequest) |
michael@0 | 588 | aChannel->GetURI(getter_AddRefs(mCurrentURI)); |
michael@0 | 589 | FireEvent(NS_LITERAL_STRING("error")); |
michael@0 | 590 | aError.Throw(rv); |
michael@0 | 591 | } |
michael@0 | 592 | return listener.forget(); |
michael@0 | 593 | } |
michael@0 | 594 | |
michael@0 | 595 | NS_IMETHODIMP |
michael@0 | 596 | nsImageLoadingContent::LoadImageWithChannel(nsIChannel* aChannel, |
michael@0 | 597 | nsIStreamListener** aListener) |
michael@0 | 598 | { |
michael@0 | 599 | NS_ENSURE_ARG_POINTER(aListener); |
michael@0 | 600 | |
michael@0 | 601 | ErrorResult result; |
michael@0 | 602 | *aListener = LoadImageWithChannel(aChannel, result).take(); |
michael@0 | 603 | return result.ErrorCode(); |
michael@0 | 604 | } |
michael@0 | 605 | |
michael@0 | 606 | void |
michael@0 | 607 | nsImageLoadingContent::ForceReload(ErrorResult& aError) |
michael@0 | 608 | { |
michael@0 | 609 | nsCOMPtr<nsIURI> currentURI; |
michael@0 | 610 | GetCurrentURI(getter_AddRefs(currentURI)); |
michael@0 | 611 | if (!currentURI) { |
michael@0 | 612 | aError.Throw(NS_ERROR_NOT_AVAILABLE); |
michael@0 | 613 | return; |
michael@0 | 614 | } |
michael@0 | 615 | |
michael@0 | 616 | nsresult rv = LoadImage(currentURI, true, true, nullptr, nsIRequest::VALIDATE_ALWAYS); |
michael@0 | 617 | if (NS_FAILED(rv)) { |
michael@0 | 618 | aError.Throw(rv); |
michael@0 | 619 | } |
michael@0 | 620 | } |
michael@0 | 621 | |
michael@0 | 622 | NS_IMETHODIMP nsImageLoadingContent::ForceReload() |
michael@0 | 623 | { |
michael@0 | 624 | ErrorResult result; |
michael@0 | 625 | ForceReload(result); |
michael@0 | 626 | return result.ErrorCode(); |
michael@0 | 627 | } |
michael@0 | 628 | |
michael@0 | 629 | NS_IMETHODIMP |
michael@0 | 630 | nsImageLoadingContent::BlockOnload(imgIRequest* aRequest) |
michael@0 | 631 | { |
michael@0 | 632 | if (aRequest == mCurrentRequest) { |
michael@0 | 633 | NS_ASSERTION(!(mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD), |
michael@0 | 634 | "Double BlockOnload!?"); |
michael@0 | 635 | mCurrentRequestFlags |= REQUEST_BLOCKS_ONLOAD; |
michael@0 | 636 | } else if (aRequest == mPendingRequest) { |
michael@0 | 637 | NS_ASSERTION(!(mPendingRequestFlags & REQUEST_BLOCKS_ONLOAD), |
michael@0 | 638 | "Double BlockOnload!?"); |
michael@0 | 639 | mPendingRequestFlags |= REQUEST_BLOCKS_ONLOAD; |
michael@0 | 640 | } else { |
michael@0 | 641 | return NS_OK; |
michael@0 | 642 | } |
michael@0 | 643 | |
michael@0 | 644 | nsIDocument* doc = GetOurCurrentDoc(); |
michael@0 | 645 | if (doc) { |
michael@0 | 646 | doc->BlockOnload(); |
michael@0 | 647 | } |
michael@0 | 648 | |
michael@0 | 649 | return NS_OK; |
michael@0 | 650 | } |
michael@0 | 651 | |
michael@0 | 652 | NS_IMETHODIMP |
michael@0 | 653 | nsImageLoadingContent::UnblockOnload(imgIRequest* aRequest) |
michael@0 | 654 | { |
michael@0 | 655 | if (aRequest == mCurrentRequest) { |
michael@0 | 656 | NS_ASSERTION(mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD, |
michael@0 | 657 | "Double UnblockOnload!?"); |
michael@0 | 658 | mCurrentRequestFlags &= ~REQUEST_BLOCKS_ONLOAD; |
michael@0 | 659 | } else if (aRequest == mPendingRequest) { |
michael@0 | 660 | NS_ASSERTION(mPendingRequestFlags & REQUEST_BLOCKS_ONLOAD, |
michael@0 | 661 | "Double UnblockOnload!?"); |
michael@0 | 662 | mPendingRequestFlags &= ~REQUEST_BLOCKS_ONLOAD; |
michael@0 | 663 | } else { |
michael@0 | 664 | return NS_OK; |
michael@0 | 665 | } |
michael@0 | 666 | |
michael@0 | 667 | nsIDocument* doc = GetOurCurrentDoc(); |
michael@0 | 668 | if (doc) { |
michael@0 | 669 | doc->UnblockOnload(false); |
michael@0 | 670 | } |
michael@0 | 671 | |
michael@0 | 672 | return NS_OK; |
michael@0 | 673 | } |
michael@0 | 674 | |
michael@0 | 675 | void |
michael@0 | 676 | nsImageLoadingContent::IncrementVisibleCount() |
michael@0 | 677 | { |
michael@0 | 678 | mVisibleCount++; |
michael@0 | 679 | if (mVisibleCount == 1) { |
michael@0 | 680 | TrackImage(mCurrentRequest); |
michael@0 | 681 | TrackImage(mPendingRequest); |
michael@0 | 682 | } |
michael@0 | 683 | } |
michael@0 | 684 | |
michael@0 | 685 | void |
michael@0 | 686 | nsImageLoadingContent::DecrementVisibleCount() |
michael@0 | 687 | { |
michael@0 | 688 | NS_ASSERTION(mVisibleCount > 0, "visible count should be positive here"); |
michael@0 | 689 | mVisibleCount--; |
michael@0 | 690 | |
michael@0 | 691 | if (mVisibleCount == 0) { |
michael@0 | 692 | UntrackImage(mCurrentRequest); |
michael@0 | 693 | UntrackImage(mPendingRequest); |
michael@0 | 694 | } |
michael@0 | 695 | } |
michael@0 | 696 | |
michael@0 | 697 | uint32_t |
michael@0 | 698 | nsImageLoadingContent::GetVisibleCount() |
michael@0 | 699 | { |
michael@0 | 700 | return mVisibleCount; |
michael@0 | 701 | } |
michael@0 | 702 | |
michael@0 | 703 | /* |
michael@0 | 704 | * Non-interface methods |
michael@0 | 705 | */ |
michael@0 | 706 | |
michael@0 | 707 | nsresult |
michael@0 | 708 | nsImageLoadingContent::LoadImage(const nsAString& aNewURI, |
michael@0 | 709 | bool aForce, |
michael@0 | 710 | bool aNotify) |
michael@0 | 711 | { |
michael@0 | 712 | // First, get a document (needed for security checks and the like) |
michael@0 | 713 | nsIDocument* doc = GetOurOwnerDoc(); |
michael@0 | 714 | if (!doc) { |
michael@0 | 715 | // No reason to bother, I think... |
michael@0 | 716 | return NS_OK; |
michael@0 | 717 | } |
michael@0 | 718 | |
michael@0 | 719 | nsCOMPtr<nsIURI> imageURI; |
michael@0 | 720 | nsresult rv = StringToURI(aNewURI, doc, getter_AddRefs(imageURI)); |
michael@0 | 721 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 722 | // XXXbiesi fire onerror if that failed? |
michael@0 | 723 | |
michael@0 | 724 | bool equal; |
michael@0 | 725 | |
michael@0 | 726 | if (aNewURI.IsEmpty() && |
michael@0 | 727 | doc->GetDocumentURI() && |
michael@0 | 728 | NS_SUCCEEDED(doc->GetDocumentURI()->EqualsExceptRef(imageURI, &equal)) && |
michael@0 | 729 | equal) { |
michael@0 | 730 | |
michael@0 | 731 | // Loading an embedded img from the same URI as the document URI will not work |
michael@0 | 732 | // as a resource cannot recursively embed itself. Attempting to do so generally |
michael@0 | 733 | // results in having to pre-emptively close down an in-flight HTTP transaction |
michael@0 | 734 | // and then incurring the significant cost of establishing a new TCP channel. |
michael@0 | 735 | // This is generally triggered from <img src=""> |
michael@0 | 736 | // In light of that, just skip loading it.. |
michael@0 | 737 | // Do make sure to drop our existing image, if any |
michael@0 | 738 | CancelImageRequests(aNotify); |
michael@0 | 739 | return NS_OK; |
michael@0 | 740 | } |
michael@0 | 741 | |
michael@0 | 742 | NS_TryToSetImmutable(imageURI); |
michael@0 | 743 | |
michael@0 | 744 | return LoadImage(imageURI, aForce, aNotify, doc); |
michael@0 | 745 | } |
michael@0 | 746 | |
michael@0 | 747 | nsresult |
michael@0 | 748 | nsImageLoadingContent::LoadImage(nsIURI* aNewURI, |
michael@0 | 749 | bool aForce, |
michael@0 | 750 | bool aNotify, |
michael@0 | 751 | nsIDocument* aDocument, |
michael@0 | 752 | nsLoadFlags aLoadFlags) |
michael@0 | 753 | { |
michael@0 | 754 | if (!mLoadingEnabled) { |
michael@0 | 755 | // XXX Why fire an error here? seems like the callers to SetLoadingEnabled |
michael@0 | 756 | // don't want/need it. |
michael@0 | 757 | FireEvent(NS_LITERAL_STRING("error")); |
michael@0 | 758 | return NS_OK; |
michael@0 | 759 | } |
michael@0 | 760 | |
michael@0 | 761 | NS_ASSERTION(!aDocument || aDocument == GetOurOwnerDoc(), |
michael@0 | 762 | "Bogus document passed in"); |
michael@0 | 763 | // First, get a document (needed for security checks and the like) |
michael@0 | 764 | if (!aDocument) { |
michael@0 | 765 | aDocument = GetOurOwnerDoc(); |
michael@0 | 766 | if (!aDocument) { |
michael@0 | 767 | // No reason to bother, I think... |
michael@0 | 768 | return NS_OK; |
michael@0 | 769 | } |
michael@0 | 770 | } |
michael@0 | 771 | |
michael@0 | 772 | // URI equality check. |
michael@0 | 773 | // |
michael@0 | 774 | // We skip the equality check if our current image was blocked, since in that |
michael@0 | 775 | // case we really do want to try loading again. |
michael@0 | 776 | if (!aForce && NS_CP_ACCEPTED(mImageBlockingStatus)) { |
michael@0 | 777 | nsCOMPtr<nsIURI> currentURI; |
michael@0 | 778 | GetCurrentURI(getter_AddRefs(currentURI)); |
michael@0 | 779 | bool equal; |
michael@0 | 780 | if (currentURI && |
michael@0 | 781 | NS_SUCCEEDED(currentURI->Equals(aNewURI, &equal)) && |
michael@0 | 782 | equal) { |
michael@0 | 783 | // Nothing to do here. |
michael@0 | 784 | return NS_OK; |
michael@0 | 785 | } |
michael@0 | 786 | } |
michael@0 | 787 | |
michael@0 | 788 | // From this point on, our image state could change. Watch it. |
michael@0 | 789 | AutoStateChanger changer(this, aNotify); |
michael@0 | 790 | |
michael@0 | 791 | // Sanity check. |
michael@0 | 792 | // |
michael@0 | 793 | // We use the principal of aDocument to avoid having to QI |this| an extra |
michael@0 | 794 | // time. It should always be the same as the principal of this node. |
michael@0 | 795 | #ifdef DEBUG |
michael@0 | 796 | nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this)); |
michael@0 | 797 | NS_ABORT_IF_FALSE(thisContent && |
michael@0 | 798 | thisContent->NodePrincipal() == aDocument->NodePrincipal(), |
michael@0 | 799 | "Principal mismatch?"); |
michael@0 | 800 | #endif |
michael@0 | 801 | |
michael@0 | 802 | // Are we blocked? |
michael@0 | 803 | int16_t cpDecision = nsIContentPolicy::REJECT_REQUEST; |
michael@0 | 804 | nsContentUtils::CanLoadImage(aNewURI, |
michael@0 | 805 | static_cast<nsIImageLoadingContent*>(this), |
michael@0 | 806 | aDocument, |
michael@0 | 807 | aDocument->NodePrincipal(), |
michael@0 | 808 | &cpDecision); |
michael@0 | 809 | if (!NS_CP_ACCEPTED(cpDecision)) { |
michael@0 | 810 | FireEvent(NS_LITERAL_STRING("error")); |
michael@0 | 811 | SetBlockedRequest(aNewURI, cpDecision); |
michael@0 | 812 | return NS_OK; |
michael@0 | 813 | } |
michael@0 | 814 | |
michael@0 | 815 | nsLoadFlags loadFlags = aLoadFlags; |
michael@0 | 816 | int32_t corsmode = GetCORSMode(); |
michael@0 | 817 | if (corsmode == CORS_ANONYMOUS) { |
michael@0 | 818 | loadFlags |= imgILoader::LOAD_CORS_ANONYMOUS; |
michael@0 | 819 | } else if (corsmode == CORS_USE_CREDENTIALS) { |
michael@0 | 820 | loadFlags |= imgILoader::LOAD_CORS_USE_CREDENTIALS; |
michael@0 | 821 | } |
michael@0 | 822 | |
michael@0 | 823 | // Not blocked. Do the load. |
michael@0 | 824 | nsRefPtr<imgRequestProxy>& req = PrepareNextRequest(); |
michael@0 | 825 | nsCOMPtr<nsIContent> content = |
michael@0 | 826 | do_QueryInterface(static_cast<nsIImageLoadingContent*>(this)); |
michael@0 | 827 | nsresult rv; |
michael@0 | 828 | rv = nsContentUtils::LoadImage(aNewURI, aDocument, |
michael@0 | 829 | aDocument->NodePrincipal(), |
michael@0 | 830 | aDocument->GetDocumentURI(), |
michael@0 | 831 | this, loadFlags, |
michael@0 | 832 | content->LocalName(), |
michael@0 | 833 | getter_AddRefs(req)); |
michael@0 | 834 | |
michael@0 | 835 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 836 | TrackImage(req); |
michael@0 | 837 | ResetAnimationIfNeeded(); |
michael@0 | 838 | |
michael@0 | 839 | // Handle cases when we just ended up with a pending request but it's |
michael@0 | 840 | // already done. In that situation we have to synchronously switch that |
michael@0 | 841 | // request to being the current request, because websites depend on that |
michael@0 | 842 | // behavior. |
michael@0 | 843 | if (req == mPendingRequest) { |
michael@0 | 844 | uint32_t pendingLoadStatus; |
michael@0 | 845 | rv = req->GetImageStatus(&pendingLoadStatus); |
michael@0 | 846 | if (NS_SUCCEEDED(rv) && |
michael@0 | 847 | (pendingLoadStatus & imgIRequest::STATUS_LOAD_COMPLETE)) { |
michael@0 | 848 | MakePendingRequestCurrent(); |
michael@0 | 849 | MOZ_ASSERT(mCurrentRequest, |
michael@0 | 850 | "How could we not have a current request here?"); |
michael@0 | 851 | |
michael@0 | 852 | nsImageFrame *f = do_QueryFrame(GetOurPrimaryFrame()); |
michael@0 | 853 | if (f) { |
michael@0 | 854 | f->NotifyNewCurrentRequest(mCurrentRequest, NS_OK); |
michael@0 | 855 | } |
michael@0 | 856 | } |
michael@0 | 857 | } |
michael@0 | 858 | } else { |
michael@0 | 859 | MOZ_ASSERT(!req, "Shouldn't have non-null request here"); |
michael@0 | 860 | // If we don't have a current URI, we might as well store this URI so people |
michael@0 | 861 | // know what we tried (and failed) to load. |
michael@0 | 862 | if (!mCurrentRequest) |
michael@0 | 863 | mCurrentURI = aNewURI; |
michael@0 | 864 | FireEvent(NS_LITERAL_STRING("error")); |
michael@0 | 865 | return NS_OK; |
michael@0 | 866 | } |
michael@0 | 867 | |
michael@0 | 868 | return NS_OK; |
michael@0 | 869 | } |
michael@0 | 870 | |
michael@0 | 871 | nsresult |
michael@0 | 872 | nsImageLoadingContent::ForceImageState(bool aForce, |
michael@0 | 873 | EventStates::InternalType aState) |
michael@0 | 874 | { |
michael@0 | 875 | mIsImageStateForced = aForce; |
michael@0 | 876 | mForcedImageState = EventStates(aState); |
michael@0 | 877 | return NS_OK; |
michael@0 | 878 | } |
michael@0 | 879 | |
michael@0 | 880 | EventStates |
michael@0 | 881 | nsImageLoadingContent::ImageState() const |
michael@0 | 882 | { |
michael@0 | 883 | if (mIsImageStateForced) { |
michael@0 | 884 | return mForcedImageState; |
michael@0 | 885 | } |
michael@0 | 886 | |
michael@0 | 887 | EventStates states; |
michael@0 | 888 | |
michael@0 | 889 | if (mBroken) { |
michael@0 | 890 | states |= NS_EVENT_STATE_BROKEN; |
michael@0 | 891 | } |
michael@0 | 892 | if (mUserDisabled) { |
michael@0 | 893 | states |= NS_EVENT_STATE_USERDISABLED; |
michael@0 | 894 | } |
michael@0 | 895 | if (mSuppressed) { |
michael@0 | 896 | states |= NS_EVENT_STATE_SUPPRESSED; |
michael@0 | 897 | } |
michael@0 | 898 | if (mLoading) { |
michael@0 | 899 | states |= NS_EVENT_STATE_LOADING; |
michael@0 | 900 | } |
michael@0 | 901 | |
michael@0 | 902 | return states; |
michael@0 | 903 | } |
michael@0 | 904 | |
michael@0 | 905 | void |
michael@0 | 906 | nsImageLoadingContent::UpdateImageState(bool aNotify) |
michael@0 | 907 | { |
michael@0 | 908 | if (mStateChangerDepth > 0) { |
michael@0 | 909 | // Ignore this call; we'll update our state when the outermost state |
michael@0 | 910 | // changer is destroyed. Need this to work around the fact that some libpr0n |
michael@0 | 911 | // stuff is actually sync and hence we can get OnStopDecode called while |
michael@0 | 912 | // we're still under LoadImage, and OnStopDecode doesn't know anything about |
michael@0 | 913 | // aNotify. |
michael@0 | 914 | // XXX - This machinery should be removed after bug 521604. |
michael@0 | 915 | return; |
michael@0 | 916 | } |
michael@0 | 917 | |
michael@0 | 918 | nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this)); |
michael@0 | 919 | if (!thisContent) { |
michael@0 | 920 | return; |
michael@0 | 921 | } |
michael@0 | 922 | |
michael@0 | 923 | mLoading = mBroken = mUserDisabled = mSuppressed = false; |
michael@0 | 924 | |
michael@0 | 925 | // If we were blocked by server-based content policy, we claim to be |
michael@0 | 926 | // suppressed. If we were blocked by type-based content policy, we claim to |
michael@0 | 927 | // be user-disabled. Otherwise, claim to be broken. |
michael@0 | 928 | if (mImageBlockingStatus == nsIContentPolicy::REJECT_SERVER) { |
michael@0 | 929 | mSuppressed = true; |
michael@0 | 930 | } else if (mImageBlockingStatus == nsIContentPolicy::REJECT_TYPE) { |
michael@0 | 931 | mUserDisabled = true; |
michael@0 | 932 | } else if (!mCurrentRequest) { |
michael@0 | 933 | // No current request means error, since we weren't disabled or suppressed |
michael@0 | 934 | mBroken = true; |
michael@0 | 935 | } else { |
michael@0 | 936 | uint32_t currentLoadStatus; |
michael@0 | 937 | nsresult rv = mCurrentRequest->GetImageStatus(¤tLoadStatus); |
michael@0 | 938 | if (NS_FAILED(rv) || (currentLoadStatus & imgIRequest::STATUS_ERROR)) { |
michael@0 | 939 | mBroken = true; |
michael@0 | 940 | } else if (!(currentLoadStatus & imgIRequest::STATUS_SIZE_AVAILABLE)) { |
michael@0 | 941 | mLoading = true; |
michael@0 | 942 | } |
michael@0 | 943 | } |
michael@0 | 944 | |
michael@0 | 945 | NS_ASSERTION(thisContent->IsElement(), "Not an element?"); |
michael@0 | 946 | thisContent->AsElement()->UpdateState(aNotify); |
michael@0 | 947 | } |
michael@0 | 948 | |
michael@0 | 949 | void |
michael@0 | 950 | nsImageLoadingContent::CancelImageRequests(bool aNotify) |
michael@0 | 951 | { |
michael@0 | 952 | AutoStateChanger changer(this, aNotify); |
michael@0 | 953 | ClearPendingRequest(NS_BINDING_ABORTED, REQUEST_DISCARD); |
michael@0 | 954 | ClearCurrentRequest(NS_BINDING_ABORTED, REQUEST_DISCARD); |
michael@0 | 955 | } |
michael@0 | 956 | |
michael@0 | 957 | nsresult |
michael@0 | 958 | nsImageLoadingContent::UseAsPrimaryRequest(imgRequestProxy* aRequest, |
michael@0 | 959 | bool aNotify) |
michael@0 | 960 | { |
michael@0 | 961 | // Our state will change. Watch it. |
michael@0 | 962 | AutoStateChanger changer(this, aNotify); |
michael@0 | 963 | |
michael@0 | 964 | // Get rid if our existing images |
michael@0 | 965 | ClearPendingRequest(NS_BINDING_ABORTED, REQUEST_DISCARD); |
michael@0 | 966 | ClearCurrentRequest(NS_BINDING_ABORTED, REQUEST_DISCARD); |
michael@0 | 967 | |
michael@0 | 968 | // Clone the request we were given. |
michael@0 | 969 | nsRefPtr<imgRequestProxy>& req = PrepareNextRequest(); |
michael@0 | 970 | nsresult rv = aRequest->Clone(this, getter_AddRefs(req)); |
michael@0 | 971 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 972 | TrackImage(req); |
michael@0 | 973 | } else { |
michael@0 | 974 | MOZ_ASSERT(!req, "Shouldn't have non-null request here"); |
michael@0 | 975 | return rv; |
michael@0 | 976 | } |
michael@0 | 977 | |
michael@0 | 978 | return NS_OK; |
michael@0 | 979 | } |
michael@0 | 980 | |
michael@0 | 981 | nsIDocument* |
michael@0 | 982 | nsImageLoadingContent::GetOurOwnerDoc() |
michael@0 | 983 | { |
michael@0 | 984 | nsCOMPtr<nsIContent> thisContent = |
michael@0 | 985 | do_QueryInterface(static_cast<nsIImageLoadingContent*>(this)); |
michael@0 | 986 | NS_ENSURE_TRUE(thisContent, nullptr); |
michael@0 | 987 | |
michael@0 | 988 | return thisContent->OwnerDoc(); |
michael@0 | 989 | } |
michael@0 | 990 | |
michael@0 | 991 | nsIDocument* |
michael@0 | 992 | nsImageLoadingContent::GetOurCurrentDoc() |
michael@0 | 993 | { |
michael@0 | 994 | nsCOMPtr<nsIContent> thisContent = |
michael@0 | 995 | do_QueryInterface(static_cast<nsIImageLoadingContent*>(this)); |
michael@0 | 996 | NS_ENSURE_TRUE(thisContent, nullptr); |
michael@0 | 997 | |
michael@0 | 998 | return thisContent->GetCurrentDoc(); |
michael@0 | 999 | } |
michael@0 | 1000 | |
michael@0 | 1001 | nsIFrame* |
michael@0 | 1002 | nsImageLoadingContent::GetOurPrimaryFrame() |
michael@0 | 1003 | { |
michael@0 | 1004 | nsCOMPtr<nsIContent> thisContent = |
michael@0 | 1005 | do_QueryInterface(static_cast<nsIImageLoadingContent*>(this)); |
michael@0 | 1006 | return thisContent->GetPrimaryFrame(); |
michael@0 | 1007 | } |
michael@0 | 1008 | |
michael@0 | 1009 | nsPresContext* nsImageLoadingContent::GetFramePresContext() |
michael@0 | 1010 | { |
michael@0 | 1011 | nsIFrame* frame = GetOurPrimaryFrame(); |
michael@0 | 1012 | if (!frame) { |
michael@0 | 1013 | return nullptr; |
michael@0 | 1014 | } |
michael@0 | 1015 | |
michael@0 | 1016 | return frame->PresContext(); |
michael@0 | 1017 | } |
michael@0 | 1018 | |
michael@0 | 1019 | nsresult |
michael@0 | 1020 | nsImageLoadingContent::StringToURI(const nsAString& aSpec, |
michael@0 | 1021 | nsIDocument* aDocument, |
michael@0 | 1022 | nsIURI** aURI) |
michael@0 | 1023 | { |
michael@0 | 1024 | NS_PRECONDITION(aDocument, "Must have a document"); |
michael@0 | 1025 | NS_PRECONDITION(aURI, "Null out param"); |
michael@0 | 1026 | |
michael@0 | 1027 | // (1) Get the base URI |
michael@0 | 1028 | nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this)); |
michael@0 | 1029 | NS_ASSERTION(thisContent, "An image loading content must be an nsIContent"); |
michael@0 | 1030 | nsCOMPtr<nsIURI> baseURL = thisContent->GetBaseURI(); |
michael@0 | 1031 | |
michael@0 | 1032 | // (2) Get the charset |
michael@0 | 1033 | const nsAFlatCString &charset = aDocument->GetDocumentCharacterSet(); |
michael@0 | 1034 | |
michael@0 | 1035 | // (3) Construct the silly thing |
michael@0 | 1036 | return NS_NewURI(aURI, |
michael@0 | 1037 | aSpec, |
michael@0 | 1038 | charset.IsEmpty() ? nullptr : charset.get(), |
michael@0 | 1039 | baseURL, |
michael@0 | 1040 | nsContentUtils::GetIOService()); |
michael@0 | 1041 | } |
michael@0 | 1042 | |
michael@0 | 1043 | nsresult |
michael@0 | 1044 | nsImageLoadingContent::FireEvent(const nsAString& aEventType) |
michael@0 | 1045 | { |
michael@0 | 1046 | // We have to fire the event asynchronously so that we won't go into infinite |
michael@0 | 1047 | // loops in cases when onLoad handlers reset the src and the new src is in |
michael@0 | 1048 | // cache. |
michael@0 | 1049 | |
michael@0 | 1050 | nsCOMPtr<nsINode> thisNode = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this)); |
michael@0 | 1051 | |
michael@0 | 1052 | nsRefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher = |
michael@0 | 1053 | new LoadBlockingAsyncEventDispatcher(thisNode, aEventType, false, false); |
michael@0 | 1054 | loadBlockingAsyncDispatcher->PostDOMEvent(); |
michael@0 | 1055 | |
michael@0 | 1056 | return NS_OK; |
michael@0 | 1057 | } |
michael@0 | 1058 | |
michael@0 | 1059 | nsRefPtr<imgRequestProxy>& |
michael@0 | 1060 | nsImageLoadingContent::PrepareNextRequest() |
michael@0 | 1061 | { |
michael@0 | 1062 | // If we don't have a usable current request, get rid of any half-baked |
michael@0 | 1063 | // request that might be sitting there and make this one current. |
michael@0 | 1064 | if (!HaveSize(mCurrentRequest)) |
michael@0 | 1065 | return PrepareCurrentRequest(); |
michael@0 | 1066 | |
michael@0 | 1067 | // Otherwise, make it pending. |
michael@0 | 1068 | return PreparePendingRequest(); |
michael@0 | 1069 | } |
michael@0 | 1070 | |
michael@0 | 1071 | void |
michael@0 | 1072 | nsImageLoadingContent::SetBlockedRequest(nsIURI* aURI, int16_t aContentDecision) |
michael@0 | 1073 | { |
michael@0 | 1074 | // Sanity |
michael@0 | 1075 | NS_ABORT_IF_FALSE(!NS_CP_ACCEPTED(aContentDecision), "Blocked but not?"); |
michael@0 | 1076 | |
michael@0 | 1077 | // We do some slightly illogical stuff here to maintain consistency with |
michael@0 | 1078 | // old behavior that people probably depend on. Even in the case where the |
michael@0 | 1079 | // new image is blocked, the old one should really be canceled with the |
michael@0 | 1080 | // reason "image source changed". However, apparently there's some abuse |
michael@0 | 1081 | // over in nsImageFrame where the displaying of the "broken" icon for the |
michael@0 | 1082 | // next image depends on the cancel reason of the previous image. ugh. |
michael@0 | 1083 | ClearPendingRequest(NS_ERROR_IMAGE_BLOCKED, REQUEST_DISCARD); |
michael@0 | 1084 | |
michael@0 | 1085 | // For the blocked case, we only want to cancel the existing current request |
michael@0 | 1086 | // if size is not available. bz says the web depends on this behavior. |
michael@0 | 1087 | if (!HaveSize(mCurrentRequest)) { |
michael@0 | 1088 | |
michael@0 | 1089 | mImageBlockingStatus = aContentDecision; |
michael@0 | 1090 | ClearCurrentRequest(NS_ERROR_IMAGE_BLOCKED, REQUEST_DISCARD); |
michael@0 | 1091 | |
michael@0 | 1092 | // We still want to remember what URI we were despite not having an actual |
michael@0 | 1093 | // request. |
michael@0 | 1094 | mCurrentURI = aURI; |
michael@0 | 1095 | } |
michael@0 | 1096 | } |
michael@0 | 1097 | |
michael@0 | 1098 | nsRefPtr<imgRequestProxy>& |
michael@0 | 1099 | nsImageLoadingContent::PrepareCurrentRequest() |
michael@0 | 1100 | { |
michael@0 | 1101 | // Blocked images go through SetBlockedRequest, which is a separate path. For |
michael@0 | 1102 | // everything else, we're unblocked. |
michael@0 | 1103 | mImageBlockingStatus = nsIContentPolicy::ACCEPT; |
michael@0 | 1104 | |
michael@0 | 1105 | // Get rid of anything that was there previously. |
michael@0 | 1106 | ClearCurrentRequest(NS_ERROR_IMAGE_SRC_CHANGED, REQUEST_DISCARD); |
michael@0 | 1107 | |
michael@0 | 1108 | if (mNewRequestsWillNeedAnimationReset) { |
michael@0 | 1109 | mCurrentRequestFlags |= REQUEST_NEEDS_ANIMATION_RESET; |
michael@0 | 1110 | } |
michael@0 | 1111 | |
michael@0 | 1112 | // Return a reference. |
michael@0 | 1113 | return mCurrentRequest; |
michael@0 | 1114 | } |
michael@0 | 1115 | |
michael@0 | 1116 | nsRefPtr<imgRequestProxy>& |
michael@0 | 1117 | nsImageLoadingContent::PreparePendingRequest() |
michael@0 | 1118 | { |
michael@0 | 1119 | // Get rid of anything that was there previously. |
michael@0 | 1120 | ClearPendingRequest(NS_ERROR_IMAGE_SRC_CHANGED, REQUEST_DISCARD); |
michael@0 | 1121 | |
michael@0 | 1122 | if (mNewRequestsWillNeedAnimationReset) { |
michael@0 | 1123 | mPendingRequestFlags |= REQUEST_NEEDS_ANIMATION_RESET; |
michael@0 | 1124 | } |
michael@0 | 1125 | |
michael@0 | 1126 | // Return a reference. |
michael@0 | 1127 | return mPendingRequest; |
michael@0 | 1128 | } |
michael@0 | 1129 | |
michael@0 | 1130 | namespace { |
michael@0 | 1131 | |
michael@0 | 1132 | class ImageRequestAutoLock |
michael@0 | 1133 | { |
michael@0 | 1134 | public: |
michael@0 | 1135 | ImageRequestAutoLock(imgIRequest* aRequest) |
michael@0 | 1136 | : mRequest(aRequest) |
michael@0 | 1137 | { |
michael@0 | 1138 | if (mRequest) { |
michael@0 | 1139 | mRequest->LockImage(); |
michael@0 | 1140 | } |
michael@0 | 1141 | } |
michael@0 | 1142 | |
michael@0 | 1143 | ~ImageRequestAutoLock() |
michael@0 | 1144 | { |
michael@0 | 1145 | if (mRequest) { |
michael@0 | 1146 | mRequest->UnlockImage(); |
michael@0 | 1147 | } |
michael@0 | 1148 | } |
michael@0 | 1149 | |
michael@0 | 1150 | private: |
michael@0 | 1151 | nsCOMPtr<imgIRequest> mRequest; |
michael@0 | 1152 | }; |
michael@0 | 1153 | |
michael@0 | 1154 | } // anonymous namespace |
michael@0 | 1155 | |
michael@0 | 1156 | void |
michael@0 | 1157 | nsImageLoadingContent::MakePendingRequestCurrent() |
michael@0 | 1158 | { |
michael@0 | 1159 | MOZ_ASSERT(mPendingRequest); |
michael@0 | 1160 | |
michael@0 | 1161 | // Lock mCurrentRequest for the duration of this method. We do this because |
michael@0 | 1162 | // PrepareCurrentRequest() might unlock mCurrentRequest. If mCurrentRequest |
michael@0 | 1163 | // and mPendingRequest are both requests for the same image, unlocking |
michael@0 | 1164 | // mCurrentRequest before we lock mPendingRequest can cause the lock count |
michael@0 | 1165 | // to go to 0 and the image to be discarded! |
michael@0 | 1166 | ImageRequestAutoLock autoLock(mCurrentRequest); |
michael@0 | 1167 | |
michael@0 | 1168 | PrepareCurrentRequest() = mPendingRequest; |
michael@0 | 1169 | mPendingRequest = nullptr; |
michael@0 | 1170 | mCurrentRequestFlags = mPendingRequestFlags; |
michael@0 | 1171 | mPendingRequestFlags = 0; |
michael@0 | 1172 | ResetAnimationIfNeeded(); |
michael@0 | 1173 | } |
michael@0 | 1174 | |
michael@0 | 1175 | void |
michael@0 | 1176 | nsImageLoadingContent::ClearCurrentRequest(nsresult aReason, |
michael@0 | 1177 | uint32_t aFlags) |
michael@0 | 1178 | { |
michael@0 | 1179 | if (!mCurrentRequest) { |
michael@0 | 1180 | // Even if we didn't have a current request, we might have been keeping |
michael@0 | 1181 | // a URI as a placeholder for a failed load. Clear that now. |
michael@0 | 1182 | mCurrentURI = nullptr; |
michael@0 | 1183 | return; |
michael@0 | 1184 | } |
michael@0 | 1185 | NS_ABORT_IF_FALSE(!mCurrentURI, |
michael@0 | 1186 | "Shouldn't have both mCurrentRequest and mCurrentURI!"); |
michael@0 | 1187 | |
michael@0 | 1188 | // Deregister this image from the refresh driver so it no longer receives |
michael@0 | 1189 | // notifications. |
michael@0 | 1190 | nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mCurrentRequest, |
michael@0 | 1191 | &mCurrentRequestRegistered); |
michael@0 | 1192 | |
michael@0 | 1193 | // Clean up the request. |
michael@0 | 1194 | UntrackImage(mCurrentRequest, aFlags); |
michael@0 | 1195 | mCurrentRequest->CancelAndForgetObserver(aReason); |
michael@0 | 1196 | mCurrentRequest = nullptr; |
michael@0 | 1197 | mCurrentRequestFlags = 0; |
michael@0 | 1198 | } |
michael@0 | 1199 | |
michael@0 | 1200 | void |
michael@0 | 1201 | nsImageLoadingContent::ClearPendingRequest(nsresult aReason, |
michael@0 | 1202 | uint32_t aFlags) |
michael@0 | 1203 | { |
michael@0 | 1204 | if (!mPendingRequest) |
michael@0 | 1205 | return; |
michael@0 | 1206 | |
michael@0 | 1207 | // Deregister this image from the refresh driver so it no longer receives |
michael@0 | 1208 | // notifications. |
michael@0 | 1209 | nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mPendingRequest, |
michael@0 | 1210 | &mPendingRequestRegistered); |
michael@0 | 1211 | |
michael@0 | 1212 | UntrackImage(mPendingRequest, aFlags); |
michael@0 | 1213 | mPendingRequest->CancelAndForgetObserver(aReason); |
michael@0 | 1214 | mPendingRequest = nullptr; |
michael@0 | 1215 | mPendingRequestFlags = 0; |
michael@0 | 1216 | } |
michael@0 | 1217 | |
michael@0 | 1218 | bool* |
michael@0 | 1219 | nsImageLoadingContent::GetRegisteredFlagForRequest(imgIRequest* aRequest) |
michael@0 | 1220 | { |
michael@0 | 1221 | if (aRequest == mCurrentRequest) { |
michael@0 | 1222 | return &mCurrentRequestRegistered; |
michael@0 | 1223 | } else if (aRequest == mPendingRequest) { |
michael@0 | 1224 | return &mPendingRequestRegistered; |
michael@0 | 1225 | } else { |
michael@0 | 1226 | return nullptr; |
michael@0 | 1227 | } |
michael@0 | 1228 | } |
michael@0 | 1229 | |
michael@0 | 1230 | void |
michael@0 | 1231 | nsImageLoadingContent::ResetAnimationIfNeeded() |
michael@0 | 1232 | { |
michael@0 | 1233 | if (mCurrentRequest && |
michael@0 | 1234 | (mCurrentRequestFlags & REQUEST_NEEDS_ANIMATION_RESET)) { |
michael@0 | 1235 | nsCOMPtr<imgIContainer> container; |
michael@0 | 1236 | mCurrentRequest->GetImage(getter_AddRefs(container)); |
michael@0 | 1237 | if (container) |
michael@0 | 1238 | container->ResetAnimation(); |
michael@0 | 1239 | mCurrentRequestFlags &= ~REQUEST_NEEDS_ANIMATION_RESET; |
michael@0 | 1240 | } |
michael@0 | 1241 | } |
michael@0 | 1242 | |
michael@0 | 1243 | bool |
michael@0 | 1244 | nsImageLoadingContent::HaveSize(imgIRequest *aImage) |
michael@0 | 1245 | { |
michael@0 | 1246 | // Handle the null case |
michael@0 | 1247 | if (!aImage) |
michael@0 | 1248 | return false; |
michael@0 | 1249 | |
michael@0 | 1250 | // Query the image |
michael@0 | 1251 | uint32_t status; |
michael@0 | 1252 | nsresult rv = aImage->GetImageStatus(&status); |
michael@0 | 1253 | return (NS_SUCCEEDED(rv) && (status & imgIRequest::STATUS_SIZE_AVAILABLE)); |
michael@0 | 1254 | } |
michael@0 | 1255 | |
michael@0 | 1256 | void |
michael@0 | 1257 | nsImageLoadingContent::BindToTree(nsIDocument* aDocument, nsIContent* aParent, |
michael@0 | 1258 | nsIContent* aBindingParent, |
michael@0 | 1259 | bool aCompileEventHandlers) |
michael@0 | 1260 | { |
michael@0 | 1261 | // We may be entering the document, so if our image should be tracked, |
michael@0 | 1262 | // track it. |
michael@0 | 1263 | if (!aDocument) |
michael@0 | 1264 | return; |
michael@0 | 1265 | |
michael@0 | 1266 | TrackImage(mCurrentRequest); |
michael@0 | 1267 | TrackImage(mPendingRequest); |
michael@0 | 1268 | |
michael@0 | 1269 | if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD) |
michael@0 | 1270 | aDocument->BlockOnload(); |
michael@0 | 1271 | } |
michael@0 | 1272 | |
michael@0 | 1273 | void |
michael@0 | 1274 | nsImageLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent) |
michael@0 | 1275 | { |
michael@0 | 1276 | // We may be leaving the document, so if our image is tracked, untrack it. |
michael@0 | 1277 | nsCOMPtr<nsIDocument> doc = GetOurCurrentDoc(); |
michael@0 | 1278 | if (!doc) |
michael@0 | 1279 | return; |
michael@0 | 1280 | |
michael@0 | 1281 | UntrackImage(mCurrentRequest); |
michael@0 | 1282 | UntrackImage(mPendingRequest); |
michael@0 | 1283 | |
michael@0 | 1284 | if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD) |
michael@0 | 1285 | doc->UnblockOnload(false); |
michael@0 | 1286 | } |
michael@0 | 1287 | |
michael@0 | 1288 | void |
michael@0 | 1289 | nsImageLoadingContent::TrackImage(imgIRequest* aImage) |
michael@0 | 1290 | { |
michael@0 | 1291 | if (!aImage) |
michael@0 | 1292 | return; |
michael@0 | 1293 | |
michael@0 | 1294 | MOZ_ASSERT(aImage == mCurrentRequest || aImage == mPendingRequest, |
michael@0 | 1295 | "Why haven't we heard of this request?"); |
michael@0 | 1296 | |
michael@0 | 1297 | nsIDocument* doc = GetOurCurrentDoc(); |
michael@0 | 1298 | if (doc && (mFrameCreateCalled || GetOurPrimaryFrame()) && |
michael@0 | 1299 | (mVisibleCount > 0)) { |
michael@0 | 1300 | if (aImage == mCurrentRequest && !(mCurrentRequestFlags & REQUEST_IS_TRACKED)) { |
michael@0 | 1301 | mCurrentRequestFlags |= REQUEST_IS_TRACKED; |
michael@0 | 1302 | doc->AddImage(mCurrentRequest); |
michael@0 | 1303 | } |
michael@0 | 1304 | if (aImage == mPendingRequest && !(mPendingRequestFlags & REQUEST_IS_TRACKED)) { |
michael@0 | 1305 | mPendingRequestFlags |= REQUEST_IS_TRACKED; |
michael@0 | 1306 | doc->AddImage(mPendingRequest); |
michael@0 | 1307 | } |
michael@0 | 1308 | } |
michael@0 | 1309 | } |
michael@0 | 1310 | |
michael@0 | 1311 | void |
michael@0 | 1312 | nsImageLoadingContent::UntrackImage(imgIRequest* aImage, uint32_t aFlags /* = 0 */) |
michael@0 | 1313 | { |
michael@0 | 1314 | if (!aImage) |
michael@0 | 1315 | return; |
michael@0 | 1316 | |
michael@0 | 1317 | MOZ_ASSERT(aImage == mCurrentRequest || aImage == mPendingRequest, |
michael@0 | 1318 | "Why haven't we heard of this request?"); |
michael@0 | 1319 | |
michael@0 | 1320 | // We may not be in the document. If we outlived our document that's fine, |
michael@0 | 1321 | // because the document empties out the tracker and unlocks all locked images |
michael@0 | 1322 | // on destruction. But if we were never in the document we may need to force |
michael@0 | 1323 | // discarding the image here, since this is the only chance we have. |
michael@0 | 1324 | nsIDocument* doc = GetOurCurrentDoc(); |
michael@0 | 1325 | if (aImage == mCurrentRequest) { |
michael@0 | 1326 | if (doc && (mCurrentRequestFlags & REQUEST_IS_TRACKED)) { |
michael@0 | 1327 | mCurrentRequestFlags &= ~REQUEST_IS_TRACKED; |
michael@0 | 1328 | doc->RemoveImage(mCurrentRequest, |
michael@0 | 1329 | (aFlags & REQUEST_DISCARD) ? nsIDocument::REQUEST_DISCARD : 0); |
michael@0 | 1330 | } |
michael@0 | 1331 | else if (aFlags & REQUEST_DISCARD) { |
michael@0 | 1332 | // If we're not in the document we may still need to be discarded. |
michael@0 | 1333 | aImage->RequestDiscard(); |
michael@0 | 1334 | } |
michael@0 | 1335 | } |
michael@0 | 1336 | if (aImage == mPendingRequest) { |
michael@0 | 1337 | if (doc && (mPendingRequestFlags & REQUEST_IS_TRACKED)) { |
michael@0 | 1338 | mPendingRequestFlags &= ~REQUEST_IS_TRACKED; |
michael@0 | 1339 | doc->RemoveImage(mPendingRequest, |
michael@0 | 1340 | (aFlags & REQUEST_DISCARD) ? nsIDocument::REQUEST_DISCARD : 0); |
michael@0 | 1341 | } |
michael@0 | 1342 | else if (aFlags & REQUEST_DISCARD) { |
michael@0 | 1343 | // If we're not in the document we may still need to be discarded. |
michael@0 | 1344 | aImage->RequestDiscard(); |
michael@0 | 1345 | } |
michael@0 | 1346 | } |
michael@0 | 1347 | } |
michael@0 | 1348 | |
michael@0 | 1349 | |
michael@0 | 1350 | void |
michael@0 | 1351 | nsImageLoadingContent::CreateStaticImageClone(nsImageLoadingContent* aDest) const |
michael@0 | 1352 | { |
michael@0 | 1353 | aDest->mCurrentRequest = nsContentUtils::GetStaticRequest(mCurrentRequest); |
michael@0 | 1354 | aDest->TrackImage(aDest->mCurrentRequest); |
michael@0 | 1355 | aDest->mForcedImageState = mForcedImageState; |
michael@0 | 1356 | aDest->mImageBlockingStatus = mImageBlockingStatus; |
michael@0 | 1357 | aDest->mLoadingEnabled = mLoadingEnabled; |
michael@0 | 1358 | aDest->mStateChangerDepth = mStateChangerDepth; |
michael@0 | 1359 | aDest->mIsImageStateForced = mIsImageStateForced; |
michael@0 | 1360 | aDest->mLoading = mLoading; |
michael@0 | 1361 | aDest->mBroken = mBroken; |
michael@0 | 1362 | aDest->mUserDisabled = mUserDisabled; |
michael@0 | 1363 | aDest->mSuppressed = mSuppressed; |
michael@0 | 1364 | } |
michael@0 | 1365 | |
michael@0 | 1366 | CORSMode |
michael@0 | 1367 | nsImageLoadingContent::GetCORSMode() |
michael@0 | 1368 | { |
michael@0 | 1369 | return CORS_NONE; |
michael@0 | 1370 | } |
michael@0 | 1371 | |
michael@0 | 1372 | nsImageLoadingContent::ImageObserver::ImageObserver(imgINotificationObserver* aObserver) |
michael@0 | 1373 | : mObserver(aObserver) |
michael@0 | 1374 | , mNext(nullptr) |
michael@0 | 1375 | { |
michael@0 | 1376 | MOZ_COUNT_CTOR(ImageObserver); |
michael@0 | 1377 | } |
michael@0 | 1378 | |
michael@0 | 1379 | nsImageLoadingContent::ImageObserver::~ImageObserver() |
michael@0 | 1380 | { |
michael@0 | 1381 | MOZ_COUNT_DTOR(ImageObserver); |
michael@0 | 1382 | NS_CONTENT_DELETE_LIST_MEMBER(ImageObserver, this, mNext); |
michael@0 | 1383 | } |