content/base/src/nsImageLoadingContent.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/base/src/nsImageLoadingContent.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1383 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +// vim: ft=cpp tw=78 sw=2 et ts=2
     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 +/*
    1.11 + * A base class which implements nsIImageLoadingContent and can be
    1.12 + * subclassed by various content nodes that want to provide image
    1.13 + * loading functionality (eg <img>, <object>, etc).
    1.14 + */
    1.15 +
    1.16 +#include "nsImageLoadingContent.h"
    1.17 +#include "nsAutoPtr.h"
    1.18 +#include "nsError.h"
    1.19 +#include "nsIContent.h"
    1.20 +#include "nsIDocument.h"
    1.21 +#include "nsIScriptGlobalObject.h"
    1.22 +#include "nsIDOMWindow.h"
    1.23 +#include "nsServiceManagerUtils.h"
    1.24 +#include "nsContentPolicyUtils.h"
    1.25 +#include "nsIURI.h"
    1.26 +#include "nsILoadGroup.h"
    1.27 +#include "imgIContainer.h"
    1.28 +#include "imgLoader.h"
    1.29 +#include "imgRequestProxy.h"
    1.30 +#include "nsThreadUtils.h"
    1.31 +#include "nsNetUtil.h"
    1.32 +#include "nsImageFrame.h"
    1.33 +
    1.34 +#include "nsIPresShell.h"
    1.35 +
    1.36 +#include "nsIChannel.h"
    1.37 +#include "nsIStreamListener.h"
    1.38 +
    1.39 +#include "nsIFrame.h"
    1.40 +#include "nsIDOMNode.h"
    1.41 +
    1.42 +#include "nsContentUtils.h"
    1.43 +#include "nsLayoutUtils.h"
    1.44 +#include "nsIContentPolicy.h"
    1.45 +#include "nsSVGEffects.h"
    1.46 +
    1.47 +#include "mozAutoDocUpdate.h"
    1.48 +#include "mozilla/AsyncEventDispatcher.h"
    1.49 +#include "mozilla/EventStates.h"
    1.50 +#include "mozilla/dom/Element.h"
    1.51 +#include "mozilla/dom/ScriptSettings.h"
    1.52 +
    1.53 +#ifdef LoadImage
    1.54 +// Undefine LoadImage to prevent naming conflict with Windows.
    1.55 +#undef LoadImage
    1.56 +#endif
    1.57 +
    1.58 +using namespace mozilla;
    1.59 +
    1.60 +#ifdef DEBUG_chb
    1.61 +static void PrintReqURL(imgIRequest* req) {
    1.62 +  if (!req) {
    1.63 +    printf("(null req)\n");
    1.64 +    return;
    1.65 +  }
    1.66 +
    1.67 +  nsCOMPtr<nsIURI> uri;
    1.68 +  req->GetURI(getter_AddRefs(uri));
    1.69 +  if (!uri) {
    1.70 +    printf("(null uri)\n");
    1.71 +    return;
    1.72 +  }
    1.73 +
    1.74 +  nsAutoCString spec;
    1.75 +  uri->GetSpec(spec);
    1.76 +  printf("spec='%s'\n", spec.get());
    1.77 +}
    1.78 +#endif /* DEBUG_chb */
    1.79 +
    1.80 +
    1.81 +nsImageLoadingContent::nsImageLoadingContent()
    1.82 +  : mCurrentRequestFlags(0),
    1.83 +    mPendingRequestFlags(0),
    1.84 +    mObserverList(nullptr),
    1.85 +    mImageBlockingStatus(nsIContentPolicy::ACCEPT),
    1.86 +    mLoadingEnabled(true),
    1.87 +    mIsImageStateForced(false),
    1.88 +    mLoading(false),
    1.89 +    // mBroken starts out true, since an image without a URI is broken....
    1.90 +    mBroken(true),
    1.91 +    mUserDisabled(false),
    1.92 +    mSuppressed(false),
    1.93 +    mFireEventsOnDecode(false),
    1.94 +    mNewRequestsWillNeedAnimationReset(false),
    1.95 +    mStateChangerDepth(0),
    1.96 +    mCurrentRequestRegistered(false),
    1.97 +    mPendingRequestRegistered(false),
    1.98 +    mFrameCreateCalled(false),
    1.99 +    mVisibleCount(0)
   1.100 +{
   1.101 +  if (!nsContentUtils::GetImgLoaderForChannel(nullptr)) {
   1.102 +    mLoadingEnabled = false;
   1.103 +  }
   1.104 +}
   1.105 +
   1.106 +void
   1.107 +nsImageLoadingContent::DestroyImageLoadingContent()
   1.108 +{
   1.109 +  // Cancel our requests so they won't hold stale refs to us
   1.110 +  // NB: Don't ask to discard the images here.
   1.111 +  ClearCurrentRequest(NS_BINDING_ABORTED, 0);
   1.112 +  ClearPendingRequest(NS_BINDING_ABORTED, 0);
   1.113 +}
   1.114 +
   1.115 +nsImageLoadingContent::~nsImageLoadingContent()
   1.116 +{
   1.117 +  NS_ASSERTION(!mCurrentRequest && !mPendingRequest,
   1.118 +               "DestroyImageLoadingContent not called");
   1.119 +  NS_ASSERTION(!mObserverList.mObserver && !mObserverList.mNext,
   1.120 +               "Observers still registered?");
   1.121 +}
   1.122 +
   1.123 +/*
   1.124 + * imgINotificationObserver impl
   1.125 + */
   1.126 +NS_IMETHODIMP
   1.127 +nsImageLoadingContent::Notify(imgIRequest* aRequest,
   1.128 +                              int32_t aType,
   1.129 +                              const nsIntRect* aData)
   1.130 +{
   1.131 +  if (aType == imgINotificationObserver::IS_ANIMATED) {
   1.132 +    return OnImageIsAnimated(aRequest);
   1.133 +  }
   1.134 +
   1.135 +  if (aType == imgINotificationObserver::UNLOCKED_DRAW) {
   1.136 +    OnUnlockedDraw();
   1.137 +    return NS_OK;
   1.138 +  }
   1.139 +
   1.140 +  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
   1.141 +    // We should definitely have a request here
   1.142 +    NS_ABORT_IF_FALSE(aRequest, "no request?");
   1.143 +
   1.144 +    NS_PRECONDITION(aRequest == mCurrentRequest || aRequest == mPendingRequest,
   1.145 +                    "Unknown request");
   1.146 +  }
   1.147 +
   1.148 +  {
   1.149 +    nsAutoScriptBlocker scriptBlocker;
   1.150 +
   1.151 +    for (ImageObserver* observer = &mObserverList, *next; observer;
   1.152 +         observer = next) {
   1.153 +      next = observer->mNext;
   1.154 +      if (observer->mObserver) {
   1.155 +        observer->mObserver->Notify(aRequest, aType, aData);
   1.156 +      }
   1.157 +    }
   1.158 +  }
   1.159 +
   1.160 +  if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
   1.161 +    // Have to check for state changes here, since we might have been in
   1.162 +    // the LOADING state before.
   1.163 +    UpdateImageState(true);
   1.164 +  }
   1.165 +
   1.166 +  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
   1.167 +    uint32_t reqStatus;
   1.168 +    aRequest->GetImageStatus(&reqStatus);
   1.169 +    nsresult status =
   1.170 +        reqStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
   1.171 +    return OnStopRequest(aRequest, status);
   1.172 +  }
   1.173 +
   1.174 +  if (aType == imgINotificationObserver::DECODE_COMPLETE && mFireEventsOnDecode) {
   1.175 +    mFireEventsOnDecode = false;
   1.176 +
   1.177 +    uint32_t reqStatus;
   1.178 +    aRequest->GetImageStatus(&reqStatus);
   1.179 +    if (reqStatus & imgIRequest::STATUS_ERROR) {
   1.180 +      FireEvent(NS_LITERAL_STRING("error"));
   1.181 +    } else {
   1.182 +      FireEvent(NS_LITERAL_STRING("load"));
   1.183 +    }
   1.184 +
   1.185 +    UpdateImageState(true);
   1.186 +  }
   1.187 +
   1.188 +  return NS_OK;
   1.189 +}
   1.190 +
   1.191 +nsresult
   1.192 +nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest,
   1.193 +                                     nsresult aStatus)
   1.194 +{
   1.195 +  uint32_t oldStatus;
   1.196 +  aRequest->GetImageStatus(&oldStatus);
   1.197 +
   1.198 +  //XXXjdm This occurs when we have a pending request created, then another
   1.199 +  //       pending request replaces it before the first one is finished.
   1.200 +  //       This begs the question of what the correct behaviour is; we used
   1.201 +  //       to not have to care because we ran this code in OnStopDecode which
   1.202 +  //       wasn't called when the first request was cancelled. For now, I choose
   1.203 +  //       to punt when the given request doesn't appear to have terminated in
   1.204 +  //       an expected state.
   1.205 +  if (!(oldStatus & (imgIRequest::STATUS_ERROR | imgIRequest::STATUS_LOAD_COMPLETE)))
   1.206 +    return NS_OK;
   1.207 +
   1.208 +  // Our state may change. Watch it.
   1.209 +  AutoStateChanger changer(this, true);
   1.210 +
   1.211 +  // If the pending request is loaded, switch to it.
   1.212 +  if (aRequest == mPendingRequest) {
   1.213 +    MakePendingRequestCurrent();
   1.214 +  }
   1.215 +  NS_ABORT_IF_FALSE(aRequest == mCurrentRequest,
   1.216 +                    "One way or another, we should be current by now");
   1.217 +
   1.218 +  // We just loaded all the data we're going to get. If we're visible and
   1.219 +  // haven't done an initial paint (*), we want to make sure the image starts
   1.220 +  // decoding immediately, for two reasons:
   1.221 +  //
   1.222 +  // 1) This image is sitting idle but might need to be decoded as soon as we
   1.223 +  // start painting, in which case we've wasted time.
   1.224 +  //
   1.225 +  // 2) We want to block onload until all visible images are decoded. We do this
   1.226 +  // by blocking onload until all in-progress decodes get at least one frame
   1.227 +  // decoded. However, if all the data comes in while painting is suppressed
   1.228 +  // (ie, before the initial paint delay is finished), we fire onload without
   1.229 +  // doing a paint first. This means that decode-on-draw images don't start
   1.230 +  // decoding, so we can't wait for them to finish. See bug 512435.
   1.231 +  //
   1.232 +  // (*) IsPaintingSuppressed returns false if we haven't gotten the initial
   1.233 +  // reflow yet, so we have to test !DidInitialize || IsPaintingSuppressed.
   1.234 +  // It's possible for painting to be suppressed for reasons other than the
   1.235 +  // initial paint delay (for example, being in the bfcache), but we probably
   1.236 +  // aren't loading images in those situations.
   1.237 +
   1.238 +  // XXXkhuey should this be GetOurCurrentDoc?  Decoding if we're not in
   1.239 +  // the document seems silly.
   1.240 +  bool startedDecoding = false;
   1.241 +  nsIDocument* doc = GetOurOwnerDoc();
   1.242 +  nsIPresShell* shell = doc ? doc->GetShell() : nullptr;
   1.243 +  if (shell && shell->IsVisible() &&
   1.244 +      (!shell->DidInitialize() || shell->IsPaintingSuppressed())) {
   1.245 +
   1.246 +    // If we've gotten a frame and that frame has called FrameCreate and that
   1.247 +    // frame has been reflowed then we know that it checked it's own visibility
   1.248 +    // so we can trust our visible count and we don't start decode if we are not
   1.249 +    // visible.
   1.250 +    nsIFrame* f = GetOurPrimaryFrame();
   1.251 +    if (!mFrameCreateCalled || !f || (f->GetStateBits() & NS_FRAME_FIRST_REFLOW) ||
   1.252 +        mVisibleCount > 0 || shell->AssumeAllImagesVisible()) {
   1.253 +      if (NS_SUCCEEDED(mCurrentRequest->StartDecoding())) {
   1.254 +        startedDecoding = true;
   1.255 +      }
   1.256 +    }
   1.257 +  }
   1.258 +
   1.259 +  // We want to give the decoder a chance to find errors. If we haven't found
   1.260 +  // an error yet and we've started decoding, either from the above
   1.261 +  // StartDecoding or from some other place, we must only fire these events
   1.262 +  // after we finish decoding.
   1.263 +  uint32_t reqStatus;
   1.264 +  aRequest->GetImageStatus(&reqStatus);
   1.265 +  if (NS_SUCCEEDED(aStatus) && !(reqStatus & imgIRequest::STATUS_ERROR) &&
   1.266 +      (reqStatus & imgIRequest::STATUS_DECODE_STARTED ||
   1.267 +       (startedDecoding && !(reqStatus & imgIRequest::STATUS_DECODE_COMPLETE)))) {
   1.268 +    mFireEventsOnDecode = true;
   1.269 +  } else {
   1.270 +    // Fire the appropriate DOM event.
   1.271 +    if (NS_SUCCEEDED(aStatus)) {
   1.272 +      FireEvent(NS_LITERAL_STRING("load"));
   1.273 +    } else {
   1.274 +      FireEvent(NS_LITERAL_STRING("error"));
   1.275 +    }
   1.276 +  }
   1.277 +
   1.278 +  nsCOMPtr<nsINode> thisNode = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   1.279 +  nsSVGEffects::InvalidateDirectRenderingObservers(thisNode->AsElement());
   1.280 +
   1.281 +  return NS_OK;
   1.282 +}
   1.283 +
   1.284 +void
   1.285 +nsImageLoadingContent::OnUnlockedDraw()
   1.286 +{
   1.287 +  if (mVisibleCount > 0) {
   1.288 +    // We should already be marked as visible, there is nothing more we can do.
   1.289 +    return;
   1.290 +  }
   1.291 +
   1.292 +  nsPresContext* presContext = GetFramePresContext();
   1.293 +  if (!presContext)
   1.294 +    return;
   1.295 +
   1.296 +  nsIPresShell* presShell = presContext->PresShell();
   1.297 +  if (!presShell)
   1.298 +    return;
   1.299 +
   1.300 +  presShell->EnsureImageInVisibleList(this);
   1.301 +}
   1.302 +
   1.303 +nsresult
   1.304 +nsImageLoadingContent::OnImageIsAnimated(imgIRequest *aRequest)
   1.305 +{
   1.306 +  bool* requestFlag = GetRegisteredFlagForRequest(aRequest);
   1.307 +  if (requestFlag) {
   1.308 +    nsLayoutUtils::RegisterImageRequest(GetFramePresContext(),
   1.309 +                                        aRequest, requestFlag);
   1.310 +  }
   1.311 +
   1.312 +  return NS_OK;
   1.313 +}
   1.314 +
   1.315 +/*
   1.316 + * nsIImageLoadingContent impl
   1.317 + */
   1.318 +
   1.319 +NS_IMETHODIMP
   1.320 +nsImageLoadingContent::GetLoadingEnabled(bool *aLoadingEnabled)
   1.321 +{
   1.322 +  *aLoadingEnabled = mLoadingEnabled;
   1.323 +  return NS_OK;
   1.324 +}
   1.325 +
   1.326 +NS_IMETHODIMP
   1.327 +nsImageLoadingContent::SetLoadingEnabled(bool aLoadingEnabled)
   1.328 +{
   1.329 +  if (nsContentUtils::GetImgLoaderForChannel(nullptr)) {
   1.330 +    mLoadingEnabled = aLoadingEnabled;
   1.331 +  }
   1.332 +  return NS_OK;
   1.333 +}
   1.334 +
   1.335 +NS_IMETHODIMP
   1.336 +nsImageLoadingContent::GetImageBlockingStatus(int16_t* aStatus)
   1.337 +{
   1.338 +  NS_PRECONDITION(aStatus, "Null out param");
   1.339 +  *aStatus = ImageBlockingStatus();
   1.340 +  return NS_OK;
   1.341 +}
   1.342 +
   1.343 +NS_IMETHODIMP
   1.344 +nsImageLoadingContent::AddObserver(imgINotificationObserver* aObserver)
   1.345 +{
   1.346 +  NS_ENSURE_ARG_POINTER(aObserver);
   1.347 +
   1.348 +  if (!mObserverList.mObserver) {
   1.349 +    mObserverList.mObserver = aObserver;
   1.350 +    // Don't touch the linking of the list!
   1.351 +    return NS_OK;
   1.352 +  }
   1.353 +
   1.354 +  // otherwise we have to create a new entry
   1.355 +
   1.356 +  ImageObserver* observer = &mObserverList;
   1.357 +  while (observer->mNext) {
   1.358 +    observer = observer->mNext;
   1.359 +  }
   1.360 +
   1.361 +  observer->mNext = new ImageObserver(aObserver);
   1.362 +  if (! observer->mNext) {
   1.363 +    return NS_ERROR_OUT_OF_MEMORY;
   1.364 +  }
   1.365 +
   1.366 +  return NS_OK;
   1.367 +}
   1.368 +
   1.369 +NS_IMETHODIMP
   1.370 +nsImageLoadingContent::RemoveObserver(imgINotificationObserver* aObserver)
   1.371 +{
   1.372 +  NS_ENSURE_ARG_POINTER(aObserver);
   1.373 +
   1.374 +  if (mObserverList.mObserver == aObserver) {
   1.375 +    mObserverList.mObserver = nullptr;
   1.376 +    // Don't touch the linking of the list!
   1.377 +    return NS_OK;
   1.378 +  }
   1.379 +
   1.380 +  // otherwise have to find it and splice it out
   1.381 +  ImageObserver* observer = &mObserverList;
   1.382 +  while (observer->mNext && observer->mNext->mObserver != aObserver) {
   1.383 +    observer = observer->mNext;
   1.384 +  }
   1.385 +
   1.386 +  // At this point, we are pointing to the list element whose mNext is
   1.387 +  // the right observer (assuming of course that mNext is not null)
   1.388 +  if (observer->mNext) {
   1.389 +    // splice it out
   1.390 +    ImageObserver* oldObserver = observer->mNext;
   1.391 +    observer->mNext = oldObserver->mNext;
   1.392 +    oldObserver->mNext = nullptr;  // so we don't destroy them all
   1.393 +    delete oldObserver;
   1.394 +  }
   1.395 +#ifdef DEBUG
   1.396 +  else {
   1.397 +    NS_WARNING("Asked to remove nonexistent observer");
   1.398 +  }
   1.399 +#endif
   1.400 +  return NS_OK;
   1.401 +}
   1.402 +
   1.403 +already_AddRefed<imgIRequest>
   1.404 +nsImageLoadingContent::GetRequest(int32_t aRequestType,
   1.405 +                                  ErrorResult& aError)
   1.406 +{
   1.407 +  nsCOMPtr<imgIRequest> request;
   1.408 +  switch(aRequestType) {
   1.409 +  case CURRENT_REQUEST:
   1.410 +    request = mCurrentRequest;
   1.411 +    break;
   1.412 +  case PENDING_REQUEST:
   1.413 +    request = mPendingRequest;
   1.414 +    break;
   1.415 +  default:
   1.416 +    NS_ERROR("Unknown request type");
   1.417 +    aError.Throw(NS_ERROR_UNEXPECTED);
   1.418 +  }
   1.419 +
   1.420 +  return request.forget();
   1.421 +}
   1.422 +
   1.423 +NS_IMETHODIMP
   1.424 +nsImageLoadingContent::GetRequest(int32_t aRequestType,
   1.425 +                                  imgIRequest** aRequest)
   1.426 +{
   1.427 +  NS_ENSURE_ARG_POINTER(aRequest);
   1.428 +
   1.429 +  ErrorResult result;
   1.430 +  *aRequest = GetRequest(aRequestType, result).take();
   1.431 +
   1.432 +  return result.ErrorCode();
   1.433 +}
   1.434 +
   1.435 +NS_IMETHODIMP_(void)
   1.436 +nsImageLoadingContent::FrameCreated(nsIFrame* aFrame)
   1.437 +{
   1.438 +  NS_ASSERTION(aFrame, "aFrame is null");
   1.439 +
   1.440 +  mFrameCreateCalled = true;
   1.441 +
   1.442 +  if (aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
   1.443 +    // Assume all images in popups are visible.
   1.444 +    IncrementVisibleCount();
   1.445 +  }
   1.446 +
   1.447 +  TrackImage(mCurrentRequest);
   1.448 +  TrackImage(mPendingRequest);
   1.449 +
   1.450 +  // We need to make sure that our image request is registered, if it should
   1.451 +  // be registered.
   1.452 +  nsPresContext* presContext = aFrame->PresContext();
   1.453 +  if (mCurrentRequest) {
   1.454 +    nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, mCurrentRequest,
   1.455 +                                                  &mCurrentRequestRegistered);
   1.456 +  }
   1.457 +
   1.458 +  if (mPendingRequest) {
   1.459 +    nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, mPendingRequest,
   1.460 +                                                  &mPendingRequestRegistered);
   1.461 +  }
   1.462 +}
   1.463 +
   1.464 +NS_IMETHODIMP_(void)
   1.465 +nsImageLoadingContent::FrameDestroyed(nsIFrame* aFrame)
   1.466 +{
   1.467 +  NS_ASSERTION(aFrame, "aFrame is null");
   1.468 +
   1.469 +  mFrameCreateCalled = false;
   1.470 +
   1.471 +  // We need to make sure that our image request is deregistered.
   1.472 +  nsPresContext* presContext = GetFramePresContext();
   1.473 +  if (mCurrentRequest) {
   1.474 +    nsLayoutUtils::DeregisterImageRequest(presContext,
   1.475 +                                          mCurrentRequest,
   1.476 +                                          &mCurrentRequestRegistered);
   1.477 +  }
   1.478 +
   1.479 +  if (mPendingRequest) {
   1.480 +    nsLayoutUtils::DeregisterImageRequest(presContext,
   1.481 +                                          mPendingRequest,
   1.482 +                                          &mPendingRequestRegistered);
   1.483 +  }
   1.484 +
   1.485 +  UntrackImage(mCurrentRequest);
   1.486 +  UntrackImage(mPendingRequest);
   1.487 +
   1.488 +  nsIPresShell* presShell = presContext ? presContext->GetPresShell() : nullptr;
   1.489 +  if (presShell) {
   1.490 +    presShell->RemoveImageFromVisibleList(this);
   1.491 +  }
   1.492 +
   1.493 +  if (aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
   1.494 +    // We assume all images in popups are visible, so this decrement balances
   1.495 +    // out the increment in FrameCreated above.
   1.496 +    DecrementVisibleCount();
   1.497 +  }
   1.498 +}
   1.499 +
   1.500 +int32_t
   1.501 +nsImageLoadingContent::GetRequestType(imgIRequest* aRequest,
   1.502 +                                      ErrorResult& aError)
   1.503 +{
   1.504 +  if (aRequest == mCurrentRequest) {
   1.505 +    return CURRENT_REQUEST;
   1.506 +  }
   1.507 +
   1.508 +  if (aRequest == mPendingRequest) {
   1.509 +    return PENDING_REQUEST;
   1.510 +  }
   1.511 +
   1.512 +  NS_ERROR("Unknown request");
   1.513 +  aError.Throw(NS_ERROR_UNEXPECTED);
   1.514 +  return UNKNOWN_REQUEST;
   1.515 +}
   1.516 +
   1.517 +NS_IMETHODIMP
   1.518 +nsImageLoadingContent::GetRequestType(imgIRequest* aRequest,
   1.519 +                                      int32_t* aRequestType)
   1.520 +{
   1.521 +  NS_PRECONDITION(aRequestType, "Null out param");
   1.522 +
   1.523 +  ErrorResult result;
   1.524 +  *aRequestType = GetRequestType(aRequest, result);
   1.525 +  return result.ErrorCode();
   1.526 +}
   1.527 +
   1.528 +already_AddRefed<nsIURI>
   1.529 +nsImageLoadingContent::GetCurrentURI(ErrorResult& aError)
   1.530 +{
   1.531 +  nsCOMPtr<nsIURI> uri;
   1.532 +  if (mCurrentRequest) {
   1.533 +    mCurrentRequest->GetURI(getter_AddRefs(uri));
   1.534 +  } else if (mCurrentURI) {
   1.535 +    nsresult rv = NS_EnsureSafeToReturn(mCurrentURI, getter_AddRefs(uri));
   1.536 +    if (NS_FAILED(rv)) {
   1.537 +      aError.Throw(rv);
   1.538 +    }
   1.539 +  }
   1.540 +
   1.541 +  return uri.forget();
   1.542 +}
   1.543 +
   1.544 +NS_IMETHODIMP
   1.545 +nsImageLoadingContent::GetCurrentURI(nsIURI** aURI)
   1.546 +{
   1.547 +  NS_ENSURE_ARG_POINTER(aURI);
   1.548 +
   1.549 +  ErrorResult result;
   1.550 +  *aURI = GetCurrentURI(result).take();
   1.551 +  return result.ErrorCode();
   1.552 +}
   1.553 +
   1.554 +already_AddRefed<nsIStreamListener>
   1.555 +nsImageLoadingContent::LoadImageWithChannel(nsIChannel* aChannel,
   1.556 +                                            ErrorResult& aError)
   1.557 +{
   1.558 +  if (!nsContentUtils::GetImgLoaderForChannel(aChannel)) {
   1.559 +    aError.Throw(NS_ERROR_NULL_POINTER);
   1.560 +    return nullptr;
   1.561 +  }
   1.562 +
   1.563 +  nsCOMPtr<nsIDocument> doc = GetOurOwnerDoc();
   1.564 +  if (!doc) {
   1.565 +    // Don't bother
   1.566 +    return nullptr;
   1.567 +  }
   1.568 +
   1.569 +  // XXX what should we do with content policies here, if anything?
   1.570 +  // Shouldn't that be done before the start of the load?
   1.571 +  // XXX what about shouldProcess?
   1.572 +
   1.573 +  // Our state might change. Watch it.
   1.574 +  AutoStateChanger changer(this, true);
   1.575 +
   1.576 +  // Do the load.
   1.577 +  nsCOMPtr<nsIStreamListener> listener;
   1.578 +  nsRefPtr<imgRequestProxy>& req = PrepareNextRequest();
   1.579 +  nsresult rv = nsContentUtils::GetImgLoaderForChannel(aChannel)->
   1.580 +    LoadImageWithChannel(aChannel, this, doc,
   1.581 +                         getter_AddRefs(listener),
   1.582 +                         getter_AddRefs(req));
   1.583 +  if (NS_SUCCEEDED(rv)) {
   1.584 +    TrackImage(req);
   1.585 +    ResetAnimationIfNeeded();
   1.586 +  } else {
   1.587 +    MOZ_ASSERT(!req, "Shouldn't have non-null request here");
   1.588 +    // If we don't have a current URI, we might as well store this URI so people
   1.589 +    // know what we tried (and failed) to load.
   1.590 +    if (!mCurrentRequest)
   1.591 +      aChannel->GetURI(getter_AddRefs(mCurrentURI));
   1.592 +    FireEvent(NS_LITERAL_STRING("error"));
   1.593 +    aError.Throw(rv);
   1.594 +  }
   1.595 +  return listener.forget();
   1.596 +}
   1.597 +
   1.598 +NS_IMETHODIMP
   1.599 +nsImageLoadingContent::LoadImageWithChannel(nsIChannel* aChannel,
   1.600 +                                            nsIStreamListener** aListener)
   1.601 +{
   1.602 +  NS_ENSURE_ARG_POINTER(aListener);
   1.603 +
   1.604 +  ErrorResult result;
   1.605 +  *aListener = LoadImageWithChannel(aChannel, result).take();
   1.606 +  return result.ErrorCode();
   1.607 +}
   1.608 +
   1.609 +void
   1.610 +nsImageLoadingContent::ForceReload(ErrorResult& aError)
   1.611 +{
   1.612 +  nsCOMPtr<nsIURI> currentURI;
   1.613 +  GetCurrentURI(getter_AddRefs(currentURI));
   1.614 +  if (!currentURI) {
   1.615 +    aError.Throw(NS_ERROR_NOT_AVAILABLE);
   1.616 +    return;
   1.617 +  }
   1.618 +
   1.619 +  nsresult rv = LoadImage(currentURI, true, true, nullptr, nsIRequest::VALIDATE_ALWAYS);
   1.620 +  if (NS_FAILED(rv)) {
   1.621 +    aError.Throw(rv);
   1.622 +  }
   1.623 +}
   1.624 +
   1.625 +NS_IMETHODIMP nsImageLoadingContent::ForceReload()
   1.626 +{
   1.627 +  ErrorResult result;
   1.628 +  ForceReload(result);
   1.629 +  return result.ErrorCode();
   1.630 +}
   1.631 +
   1.632 +NS_IMETHODIMP
   1.633 +nsImageLoadingContent::BlockOnload(imgIRequest* aRequest)
   1.634 +{
   1.635 +  if (aRequest == mCurrentRequest) {
   1.636 +    NS_ASSERTION(!(mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD),
   1.637 +                 "Double BlockOnload!?");
   1.638 +    mCurrentRequestFlags |= REQUEST_BLOCKS_ONLOAD;
   1.639 +  } else if (aRequest == mPendingRequest) {
   1.640 +    NS_ASSERTION(!(mPendingRequestFlags & REQUEST_BLOCKS_ONLOAD),
   1.641 +                 "Double BlockOnload!?");
   1.642 +    mPendingRequestFlags |= REQUEST_BLOCKS_ONLOAD;
   1.643 +  } else {
   1.644 +    return NS_OK;
   1.645 +  }
   1.646 +
   1.647 +  nsIDocument* doc = GetOurCurrentDoc();
   1.648 +  if (doc) {
   1.649 +    doc->BlockOnload();
   1.650 +  }
   1.651 +
   1.652 +  return NS_OK;
   1.653 +}
   1.654 +
   1.655 +NS_IMETHODIMP
   1.656 +nsImageLoadingContent::UnblockOnload(imgIRequest* aRequest)
   1.657 +{
   1.658 +  if (aRequest == mCurrentRequest) {
   1.659 +    NS_ASSERTION(mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD,
   1.660 +                 "Double UnblockOnload!?");
   1.661 +    mCurrentRequestFlags &= ~REQUEST_BLOCKS_ONLOAD;
   1.662 +  } else if (aRequest == mPendingRequest) {
   1.663 +    NS_ASSERTION(mPendingRequestFlags & REQUEST_BLOCKS_ONLOAD,
   1.664 +                 "Double UnblockOnload!?");
   1.665 +    mPendingRequestFlags &= ~REQUEST_BLOCKS_ONLOAD;
   1.666 +  } else {
   1.667 +    return NS_OK;
   1.668 +  }
   1.669 +
   1.670 +  nsIDocument* doc = GetOurCurrentDoc();
   1.671 +  if (doc) {
   1.672 +    doc->UnblockOnload(false);
   1.673 +  }
   1.674 +
   1.675 +  return NS_OK;
   1.676 +}
   1.677 +
   1.678 +void
   1.679 +nsImageLoadingContent::IncrementVisibleCount()
   1.680 +{
   1.681 +  mVisibleCount++;
   1.682 +  if (mVisibleCount == 1) {
   1.683 +    TrackImage(mCurrentRequest);
   1.684 +    TrackImage(mPendingRequest);
   1.685 +  }
   1.686 +}
   1.687 +
   1.688 +void
   1.689 +nsImageLoadingContent::DecrementVisibleCount()
   1.690 +{
   1.691 +  NS_ASSERTION(mVisibleCount > 0, "visible count should be positive here");
   1.692 +  mVisibleCount--;
   1.693 +
   1.694 +  if (mVisibleCount == 0) {
   1.695 +    UntrackImage(mCurrentRequest);
   1.696 +    UntrackImage(mPendingRequest);
   1.697 +  }
   1.698 +}
   1.699 +
   1.700 +uint32_t
   1.701 +nsImageLoadingContent::GetVisibleCount()
   1.702 +{
   1.703 +  return mVisibleCount;
   1.704 +}
   1.705 +
   1.706 +/*
   1.707 + * Non-interface methods
   1.708 + */
   1.709 +
   1.710 +nsresult
   1.711 +nsImageLoadingContent::LoadImage(const nsAString& aNewURI,
   1.712 +                                 bool aForce,
   1.713 +                                 bool aNotify)
   1.714 +{
   1.715 +  // First, get a document (needed for security checks and the like)
   1.716 +  nsIDocument* doc = GetOurOwnerDoc();
   1.717 +  if (!doc) {
   1.718 +    // No reason to bother, I think...
   1.719 +    return NS_OK;
   1.720 +  }
   1.721 +
   1.722 +  nsCOMPtr<nsIURI> imageURI;
   1.723 +  nsresult rv = StringToURI(aNewURI, doc, getter_AddRefs(imageURI));
   1.724 +  NS_ENSURE_SUCCESS(rv, rv);
   1.725 +  // XXXbiesi fire onerror if that failed?
   1.726 +
   1.727 +  bool equal;
   1.728 +
   1.729 +  if (aNewURI.IsEmpty() &&
   1.730 +      doc->GetDocumentURI() &&
   1.731 +      NS_SUCCEEDED(doc->GetDocumentURI()->EqualsExceptRef(imageURI, &equal)) &&
   1.732 +      equal)  {
   1.733 +
   1.734 +    // Loading an embedded img from the same URI as the document URI will not work
   1.735 +    // as a resource cannot recursively embed itself. Attempting to do so generally
   1.736 +    // results in having to pre-emptively close down an in-flight HTTP transaction 
   1.737 +    // and then incurring the significant cost of establishing a new TCP channel.
   1.738 +    // This is generally triggered from <img src=""> 
   1.739 +    // In light of that, just skip loading it..
   1.740 +    // Do make sure to drop our existing image, if any
   1.741 +    CancelImageRequests(aNotify);
   1.742 +    return NS_OK;
   1.743 +  }
   1.744 +
   1.745 +  NS_TryToSetImmutable(imageURI);
   1.746 +
   1.747 +  return LoadImage(imageURI, aForce, aNotify, doc);
   1.748 +}
   1.749 +
   1.750 +nsresult
   1.751 +nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
   1.752 +                                 bool aForce,
   1.753 +                                 bool aNotify,
   1.754 +                                 nsIDocument* aDocument,
   1.755 +                                 nsLoadFlags aLoadFlags)
   1.756 +{
   1.757 +  if (!mLoadingEnabled) {
   1.758 +    // XXX Why fire an error here? seems like the callers to SetLoadingEnabled
   1.759 +    // don't want/need it.
   1.760 +    FireEvent(NS_LITERAL_STRING("error"));
   1.761 +    return NS_OK;
   1.762 +  }
   1.763 +
   1.764 +  NS_ASSERTION(!aDocument || aDocument == GetOurOwnerDoc(),
   1.765 +               "Bogus document passed in");
   1.766 +  // First, get a document (needed for security checks and the like)
   1.767 +  if (!aDocument) {
   1.768 +    aDocument = GetOurOwnerDoc();
   1.769 +    if (!aDocument) {
   1.770 +      // No reason to bother, I think...
   1.771 +      return NS_OK;
   1.772 +    }
   1.773 +  }
   1.774 +
   1.775 +  // URI equality check.
   1.776 +  //
   1.777 +  // We skip the equality check if our current image was blocked, since in that
   1.778 +  // case we really do want to try loading again.
   1.779 +  if (!aForce && NS_CP_ACCEPTED(mImageBlockingStatus)) {
   1.780 +    nsCOMPtr<nsIURI> currentURI;
   1.781 +    GetCurrentURI(getter_AddRefs(currentURI));
   1.782 +    bool equal;
   1.783 +    if (currentURI &&
   1.784 +        NS_SUCCEEDED(currentURI->Equals(aNewURI, &equal)) &&
   1.785 +        equal) {
   1.786 +      // Nothing to do here.
   1.787 +      return NS_OK;
   1.788 +    }
   1.789 +  }
   1.790 +
   1.791 +  // From this point on, our image state could change. Watch it.
   1.792 +  AutoStateChanger changer(this, aNotify);
   1.793 +
   1.794 +  // Sanity check.
   1.795 +  //
   1.796 +  // We use the principal of aDocument to avoid having to QI |this| an extra
   1.797 +  // time. It should always be the same as the principal of this node.
   1.798 +#ifdef DEBUG
   1.799 +  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   1.800 +  NS_ABORT_IF_FALSE(thisContent &&
   1.801 +                    thisContent->NodePrincipal() == aDocument->NodePrincipal(),
   1.802 +                    "Principal mismatch?");
   1.803 +#endif
   1.804 +
   1.805 +  // Are we blocked?
   1.806 +  int16_t cpDecision = nsIContentPolicy::REJECT_REQUEST;
   1.807 +  nsContentUtils::CanLoadImage(aNewURI,
   1.808 +                               static_cast<nsIImageLoadingContent*>(this),
   1.809 +                               aDocument,
   1.810 +                               aDocument->NodePrincipal(),
   1.811 +                               &cpDecision);
   1.812 +  if (!NS_CP_ACCEPTED(cpDecision)) {
   1.813 +    FireEvent(NS_LITERAL_STRING("error"));
   1.814 +    SetBlockedRequest(aNewURI, cpDecision);
   1.815 +    return NS_OK;
   1.816 +  }
   1.817 +
   1.818 +  nsLoadFlags loadFlags = aLoadFlags;
   1.819 +  int32_t corsmode = GetCORSMode();
   1.820 +  if (corsmode == CORS_ANONYMOUS) {
   1.821 +    loadFlags |= imgILoader::LOAD_CORS_ANONYMOUS;
   1.822 +  } else if (corsmode == CORS_USE_CREDENTIALS) {
   1.823 +    loadFlags |= imgILoader::LOAD_CORS_USE_CREDENTIALS;
   1.824 +  }
   1.825 +
   1.826 +  // Not blocked. Do the load.
   1.827 +  nsRefPtr<imgRequestProxy>& req = PrepareNextRequest();
   1.828 +  nsCOMPtr<nsIContent> content =
   1.829 +      do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   1.830 +  nsresult rv;
   1.831 +  rv = nsContentUtils::LoadImage(aNewURI, aDocument,
   1.832 +                                 aDocument->NodePrincipal(),
   1.833 +                                 aDocument->GetDocumentURI(),
   1.834 +                                 this, loadFlags,
   1.835 +                                 content->LocalName(),
   1.836 +                                 getter_AddRefs(req));
   1.837 +
   1.838 +  if (NS_SUCCEEDED(rv)) {
   1.839 +    TrackImage(req);
   1.840 +    ResetAnimationIfNeeded();
   1.841 +
   1.842 +    // Handle cases when we just ended up with a pending request but it's
   1.843 +    // already done.  In that situation we have to synchronously switch that
   1.844 +    // request to being the current request, because websites depend on that
   1.845 +    // behavior.
   1.846 +    if (req == mPendingRequest) {
   1.847 +      uint32_t pendingLoadStatus;
   1.848 +      rv = req->GetImageStatus(&pendingLoadStatus);
   1.849 +      if (NS_SUCCEEDED(rv) &&
   1.850 +          (pendingLoadStatus & imgIRequest::STATUS_LOAD_COMPLETE)) {
   1.851 +        MakePendingRequestCurrent();
   1.852 +        MOZ_ASSERT(mCurrentRequest,
   1.853 +                   "How could we not have a current request here?");
   1.854 +
   1.855 +        nsImageFrame *f = do_QueryFrame(GetOurPrimaryFrame());
   1.856 +        if (f) {
   1.857 +          f->NotifyNewCurrentRequest(mCurrentRequest, NS_OK);
   1.858 +        }
   1.859 +      }
   1.860 +    }
   1.861 +  } else {
   1.862 +    MOZ_ASSERT(!req, "Shouldn't have non-null request here");
   1.863 +    // If we don't have a current URI, we might as well store this URI so people
   1.864 +    // know what we tried (and failed) to load.
   1.865 +    if (!mCurrentRequest)
   1.866 +      mCurrentURI = aNewURI;
   1.867 +    FireEvent(NS_LITERAL_STRING("error"));
   1.868 +    return NS_OK;
   1.869 +  }
   1.870 +
   1.871 +  return NS_OK;
   1.872 +}
   1.873 +
   1.874 +nsresult
   1.875 +nsImageLoadingContent::ForceImageState(bool aForce,
   1.876 +                                       EventStates::InternalType aState)
   1.877 +{
   1.878 +  mIsImageStateForced = aForce;
   1.879 +  mForcedImageState = EventStates(aState);
   1.880 +  return NS_OK;
   1.881 +}
   1.882 +
   1.883 +EventStates
   1.884 +nsImageLoadingContent::ImageState() const
   1.885 +{
   1.886 +  if (mIsImageStateForced) {
   1.887 +    return mForcedImageState;
   1.888 +  }
   1.889 +
   1.890 +  EventStates states;
   1.891 +
   1.892 +  if (mBroken) {
   1.893 +    states |= NS_EVENT_STATE_BROKEN;
   1.894 +  }
   1.895 +  if (mUserDisabled) {
   1.896 +    states |= NS_EVENT_STATE_USERDISABLED;
   1.897 +  }
   1.898 +  if (mSuppressed) {
   1.899 +    states |= NS_EVENT_STATE_SUPPRESSED;
   1.900 +  }
   1.901 +  if (mLoading) {
   1.902 +    states |= NS_EVENT_STATE_LOADING;
   1.903 +  }
   1.904 +
   1.905 +  return states;
   1.906 +}
   1.907 +
   1.908 +void
   1.909 +nsImageLoadingContent::UpdateImageState(bool aNotify)
   1.910 +{
   1.911 +  if (mStateChangerDepth > 0) {
   1.912 +    // Ignore this call; we'll update our state when the outermost state
   1.913 +    // changer is destroyed. Need this to work around the fact that some libpr0n
   1.914 +    // stuff is actually sync and hence we can get OnStopDecode called while
   1.915 +    // we're still under LoadImage, and OnStopDecode doesn't know anything about
   1.916 +    // aNotify.
   1.917 +    // XXX - This machinery should be removed after bug 521604.
   1.918 +    return;
   1.919 +  }
   1.920 +  
   1.921 +  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   1.922 +  if (!thisContent) {
   1.923 +    return;
   1.924 +  }
   1.925 +
   1.926 +  mLoading = mBroken = mUserDisabled = mSuppressed = false;
   1.927 +  
   1.928 +  // If we were blocked by server-based content policy, we claim to be
   1.929 +  // suppressed.  If we were blocked by type-based content policy, we claim to
   1.930 +  // be user-disabled.  Otherwise, claim to be broken.
   1.931 +  if (mImageBlockingStatus == nsIContentPolicy::REJECT_SERVER) {
   1.932 +    mSuppressed = true;
   1.933 +  } else if (mImageBlockingStatus == nsIContentPolicy::REJECT_TYPE) {
   1.934 +    mUserDisabled = true;
   1.935 +  } else if (!mCurrentRequest) {
   1.936 +    // No current request means error, since we weren't disabled or suppressed
   1.937 +    mBroken = true;
   1.938 +  } else {
   1.939 +    uint32_t currentLoadStatus;
   1.940 +    nsresult rv = mCurrentRequest->GetImageStatus(&currentLoadStatus);
   1.941 +    if (NS_FAILED(rv) || (currentLoadStatus & imgIRequest::STATUS_ERROR)) {
   1.942 +      mBroken = true;
   1.943 +    } else if (!(currentLoadStatus & imgIRequest::STATUS_SIZE_AVAILABLE)) {
   1.944 +      mLoading = true;
   1.945 +    }
   1.946 +  }
   1.947 +
   1.948 +  NS_ASSERTION(thisContent->IsElement(), "Not an element?");
   1.949 +  thisContent->AsElement()->UpdateState(aNotify);
   1.950 +}
   1.951 +
   1.952 +void
   1.953 +nsImageLoadingContent::CancelImageRequests(bool aNotify)
   1.954 +{
   1.955 +  AutoStateChanger changer(this, aNotify);
   1.956 +  ClearPendingRequest(NS_BINDING_ABORTED, REQUEST_DISCARD);
   1.957 +  ClearCurrentRequest(NS_BINDING_ABORTED, REQUEST_DISCARD);
   1.958 +}
   1.959 +
   1.960 +nsresult
   1.961 +nsImageLoadingContent::UseAsPrimaryRequest(imgRequestProxy* aRequest,
   1.962 +                                           bool aNotify)
   1.963 +{
   1.964 +  // Our state will change. Watch it.
   1.965 +  AutoStateChanger changer(this, aNotify);
   1.966 +
   1.967 +  // Get rid if our existing images
   1.968 +  ClearPendingRequest(NS_BINDING_ABORTED, REQUEST_DISCARD);
   1.969 +  ClearCurrentRequest(NS_BINDING_ABORTED, REQUEST_DISCARD);
   1.970 +
   1.971 +  // Clone the request we were given.
   1.972 +  nsRefPtr<imgRequestProxy>& req = PrepareNextRequest();
   1.973 +  nsresult rv = aRequest->Clone(this, getter_AddRefs(req));
   1.974 +  if (NS_SUCCEEDED(rv)) {
   1.975 +    TrackImage(req);
   1.976 +  } else {
   1.977 +    MOZ_ASSERT(!req, "Shouldn't have non-null request here");
   1.978 +    return rv;
   1.979 +  }
   1.980 +
   1.981 +  return NS_OK;
   1.982 +}
   1.983 +
   1.984 +nsIDocument*
   1.985 +nsImageLoadingContent::GetOurOwnerDoc()
   1.986 +{
   1.987 +  nsCOMPtr<nsIContent> thisContent =
   1.988 +    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   1.989 +  NS_ENSURE_TRUE(thisContent, nullptr);
   1.990 +
   1.991 +  return thisContent->OwnerDoc();
   1.992 +}
   1.993 +
   1.994 +nsIDocument*
   1.995 +nsImageLoadingContent::GetOurCurrentDoc()
   1.996 +{
   1.997 +  nsCOMPtr<nsIContent> thisContent =
   1.998 +    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   1.999 +  NS_ENSURE_TRUE(thisContent, nullptr);
  1.1000 +
  1.1001 +  return thisContent->GetCurrentDoc();
  1.1002 +}
  1.1003 +
  1.1004 +nsIFrame*
  1.1005 +nsImageLoadingContent::GetOurPrimaryFrame()
  1.1006 +{
  1.1007 +  nsCOMPtr<nsIContent> thisContent =
  1.1008 +    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
  1.1009 +  return thisContent->GetPrimaryFrame();
  1.1010 +}
  1.1011 +
  1.1012 +nsPresContext* nsImageLoadingContent::GetFramePresContext()
  1.1013 +{
  1.1014 +  nsIFrame* frame = GetOurPrimaryFrame();
  1.1015 +  if (!frame) {
  1.1016 +    return nullptr;
  1.1017 +  }
  1.1018 +
  1.1019 +  return frame->PresContext();
  1.1020 +}
  1.1021 +
  1.1022 +nsresult
  1.1023 +nsImageLoadingContent::StringToURI(const nsAString& aSpec,
  1.1024 +                                   nsIDocument* aDocument,
  1.1025 +                                   nsIURI** aURI)
  1.1026 +{
  1.1027 +  NS_PRECONDITION(aDocument, "Must have a document");
  1.1028 +  NS_PRECONDITION(aURI, "Null out param");
  1.1029 +
  1.1030 +  // (1) Get the base URI
  1.1031 +  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
  1.1032 +  NS_ASSERTION(thisContent, "An image loading content must be an nsIContent");
  1.1033 +  nsCOMPtr<nsIURI> baseURL = thisContent->GetBaseURI();
  1.1034 +
  1.1035 +  // (2) Get the charset
  1.1036 +  const nsAFlatCString &charset = aDocument->GetDocumentCharacterSet();
  1.1037 +
  1.1038 +  // (3) Construct the silly thing
  1.1039 +  return NS_NewURI(aURI,
  1.1040 +                   aSpec,
  1.1041 +                   charset.IsEmpty() ? nullptr : charset.get(),
  1.1042 +                   baseURL,
  1.1043 +                   nsContentUtils::GetIOService());
  1.1044 +}
  1.1045 +
  1.1046 +nsresult
  1.1047 +nsImageLoadingContent::FireEvent(const nsAString& aEventType)
  1.1048 +{
  1.1049 +  // We have to fire the event asynchronously so that we won't go into infinite
  1.1050 +  // loops in cases when onLoad handlers reset the src and the new src is in
  1.1051 +  // cache.
  1.1052 +
  1.1053 +  nsCOMPtr<nsINode> thisNode = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
  1.1054 +
  1.1055 +  nsRefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
  1.1056 +    new LoadBlockingAsyncEventDispatcher(thisNode, aEventType, false, false);
  1.1057 +  loadBlockingAsyncDispatcher->PostDOMEvent();
  1.1058 +
  1.1059 +  return NS_OK;
  1.1060 +}
  1.1061 +
  1.1062 +nsRefPtr<imgRequestProxy>&
  1.1063 +nsImageLoadingContent::PrepareNextRequest()
  1.1064 +{
  1.1065 +  // If we don't have a usable current request, get rid of any half-baked
  1.1066 +  // request that might be sitting there and make this one current.
  1.1067 +  if (!HaveSize(mCurrentRequest))
  1.1068 +    return PrepareCurrentRequest();
  1.1069 +
  1.1070 +  // Otherwise, make it pending.
  1.1071 +  return PreparePendingRequest();
  1.1072 +}
  1.1073 +
  1.1074 +void
  1.1075 +nsImageLoadingContent::SetBlockedRequest(nsIURI* aURI, int16_t aContentDecision)
  1.1076 +{
  1.1077 +  // Sanity
  1.1078 +  NS_ABORT_IF_FALSE(!NS_CP_ACCEPTED(aContentDecision), "Blocked but not?");
  1.1079 +
  1.1080 +  // We do some slightly illogical stuff here to maintain consistency with
  1.1081 +  // old behavior that people probably depend on. Even in the case where the
  1.1082 +  // new image is blocked, the old one should really be canceled with the
  1.1083 +  // reason "image source changed". However, apparently there's some abuse
  1.1084 +  // over in nsImageFrame where the displaying of the "broken" icon for the
  1.1085 +  // next image depends on the cancel reason of the previous image. ugh.
  1.1086 +  ClearPendingRequest(NS_ERROR_IMAGE_BLOCKED, REQUEST_DISCARD);
  1.1087 +
  1.1088 +  // For the blocked case, we only want to cancel the existing current request
  1.1089 +  // if size is not available. bz says the web depends on this behavior.
  1.1090 +  if (!HaveSize(mCurrentRequest)) {
  1.1091 +
  1.1092 +    mImageBlockingStatus = aContentDecision;
  1.1093 +    ClearCurrentRequest(NS_ERROR_IMAGE_BLOCKED, REQUEST_DISCARD);
  1.1094 +
  1.1095 +    // We still want to remember what URI we were despite not having an actual
  1.1096 +    // request.
  1.1097 +    mCurrentURI = aURI;
  1.1098 +  }
  1.1099 +}
  1.1100 +
  1.1101 +nsRefPtr<imgRequestProxy>&
  1.1102 +nsImageLoadingContent::PrepareCurrentRequest()
  1.1103 +{
  1.1104 +  // Blocked images go through SetBlockedRequest, which is a separate path. For
  1.1105 +  // everything else, we're unblocked.
  1.1106 +  mImageBlockingStatus = nsIContentPolicy::ACCEPT;
  1.1107 +
  1.1108 +  // Get rid of anything that was there previously.
  1.1109 +  ClearCurrentRequest(NS_ERROR_IMAGE_SRC_CHANGED, REQUEST_DISCARD);
  1.1110 +
  1.1111 +  if (mNewRequestsWillNeedAnimationReset) {
  1.1112 +    mCurrentRequestFlags |= REQUEST_NEEDS_ANIMATION_RESET;
  1.1113 +  }
  1.1114 +
  1.1115 +  // Return a reference.
  1.1116 +  return mCurrentRequest;
  1.1117 +}
  1.1118 +
  1.1119 +nsRefPtr<imgRequestProxy>&
  1.1120 +nsImageLoadingContent::PreparePendingRequest()
  1.1121 +{
  1.1122 +  // Get rid of anything that was there previously.
  1.1123 +  ClearPendingRequest(NS_ERROR_IMAGE_SRC_CHANGED, REQUEST_DISCARD);
  1.1124 +
  1.1125 +  if (mNewRequestsWillNeedAnimationReset) {
  1.1126 +    mPendingRequestFlags |= REQUEST_NEEDS_ANIMATION_RESET;
  1.1127 +  }
  1.1128 +
  1.1129 +  // Return a reference.
  1.1130 +  return mPendingRequest;
  1.1131 +}
  1.1132 +
  1.1133 +namespace {
  1.1134 +
  1.1135 +class ImageRequestAutoLock
  1.1136 +{
  1.1137 +public:
  1.1138 +  ImageRequestAutoLock(imgIRequest* aRequest)
  1.1139 +    : mRequest(aRequest)
  1.1140 +  {
  1.1141 +    if (mRequest) {
  1.1142 +      mRequest->LockImage();
  1.1143 +    }
  1.1144 +  }
  1.1145 +
  1.1146 +  ~ImageRequestAutoLock()
  1.1147 +  {
  1.1148 +    if (mRequest) {
  1.1149 +      mRequest->UnlockImage();
  1.1150 +    }
  1.1151 +  }
  1.1152 +
  1.1153 +private:
  1.1154 +  nsCOMPtr<imgIRequest> mRequest;
  1.1155 +};
  1.1156 +
  1.1157 +} // anonymous namespace
  1.1158 +
  1.1159 +void
  1.1160 +nsImageLoadingContent::MakePendingRequestCurrent()
  1.1161 +{
  1.1162 +  MOZ_ASSERT(mPendingRequest);
  1.1163 +
  1.1164 +  // Lock mCurrentRequest for the duration of this method.  We do this because
  1.1165 +  // PrepareCurrentRequest() might unlock mCurrentRequest.  If mCurrentRequest
  1.1166 +  // and mPendingRequest are both requests for the same image, unlocking
  1.1167 +  // mCurrentRequest before we lock mPendingRequest can cause the lock count
  1.1168 +  // to go to 0 and the image to be discarded!
  1.1169 +  ImageRequestAutoLock autoLock(mCurrentRequest);
  1.1170 +
  1.1171 +  PrepareCurrentRequest() = mPendingRequest;
  1.1172 +  mPendingRequest = nullptr;
  1.1173 +  mCurrentRequestFlags = mPendingRequestFlags;
  1.1174 +  mPendingRequestFlags = 0;
  1.1175 +  ResetAnimationIfNeeded();
  1.1176 +}
  1.1177 +
  1.1178 +void
  1.1179 +nsImageLoadingContent::ClearCurrentRequest(nsresult aReason,
  1.1180 +                                           uint32_t aFlags)
  1.1181 +{
  1.1182 +  if (!mCurrentRequest) {
  1.1183 +    // Even if we didn't have a current request, we might have been keeping
  1.1184 +    // a URI as a placeholder for a failed load. Clear that now.
  1.1185 +    mCurrentURI = nullptr;
  1.1186 +    return;
  1.1187 +  }
  1.1188 +  NS_ABORT_IF_FALSE(!mCurrentURI,
  1.1189 +                    "Shouldn't have both mCurrentRequest and mCurrentURI!");
  1.1190 +
  1.1191 +  // Deregister this image from the refresh driver so it no longer receives
  1.1192 +  // notifications.
  1.1193 +  nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mCurrentRequest,
  1.1194 +                                        &mCurrentRequestRegistered);
  1.1195 +
  1.1196 +  // Clean up the request.
  1.1197 +  UntrackImage(mCurrentRequest, aFlags);
  1.1198 +  mCurrentRequest->CancelAndForgetObserver(aReason);
  1.1199 +  mCurrentRequest = nullptr;
  1.1200 +  mCurrentRequestFlags = 0;
  1.1201 +}
  1.1202 +
  1.1203 +void
  1.1204 +nsImageLoadingContent::ClearPendingRequest(nsresult aReason,
  1.1205 +                                           uint32_t aFlags)
  1.1206 +{
  1.1207 +  if (!mPendingRequest)
  1.1208 +    return;
  1.1209 +
  1.1210 +  // Deregister this image from the refresh driver so it no longer receives
  1.1211 +  // notifications.
  1.1212 +  nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mPendingRequest,
  1.1213 +                                        &mPendingRequestRegistered);
  1.1214 +
  1.1215 +  UntrackImage(mPendingRequest, aFlags);
  1.1216 +  mPendingRequest->CancelAndForgetObserver(aReason);
  1.1217 +  mPendingRequest = nullptr;
  1.1218 +  mPendingRequestFlags = 0;
  1.1219 +}
  1.1220 +
  1.1221 +bool*
  1.1222 +nsImageLoadingContent::GetRegisteredFlagForRequest(imgIRequest* aRequest)
  1.1223 +{
  1.1224 +  if (aRequest == mCurrentRequest) {
  1.1225 +    return &mCurrentRequestRegistered;
  1.1226 +  } else if (aRequest == mPendingRequest) {
  1.1227 +    return &mPendingRequestRegistered;
  1.1228 +  } else {
  1.1229 +    return nullptr;
  1.1230 +  }
  1.1231 +}
  1.1232 +
  1.1233 +void
  1.1234 +nsImageLoadingContent::ResetAnimationIfNeeded()
  1.1235 +{
  1.1236 +  if (mCurrentRequest &&
  1.1237 +      (mCurrentRequestFlags & REQUEST_NEEDS_ANIMATION_RESET)) {
  1.1238 +    nsCOMPtr<imgIContainer> container;
  1.1239 +    mCurrentRequest->GetImage(getter_AddRefs(container));
  1.1240 +    if (container)
  1.1241 +      container->ResetAnimation();
  1.1242 +    mCurrentRequestFlags &= ~REQUEST_NEEDS_ANIMATION_RESET;
  1.1243 +  }
  1.1244 +}
  1.1245 +
  1.1246 +bool
  1.1247 +nsImageLoadingContent::HaveSize(imgIRequest *aImage)
  1.1248 +{
  1.1249 +  // Handle the null case
  1.1250 +  if (!aImage)
  1.1251 +    return false;
  1.1252 +
  1.1253 +  // Query the image
  1.1254 +  uint32_t status;
  1.1255 +  nsresult rv = aImage->GetImageStatus(&status);
  1.1256 +  return (NS_SUCCEEDED(rv) && (status & imgIRequest::STATUS_SIZE_AVAILABLE));
  1.1257 +}
  1.1258 +
  1.1259 +void
  1.1260 +nsImageLoadingContent::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
  1.1261 +                                  nsIContent* aBindingParent,
  1.1262 +                                  bool aCompileEventHandlers)
  1.1263 +{
  1.1264 +  // We may be entering the document, so if our image should be tracked,
  1.1265 +  // track it.
  1.1266 +  if (!aDocument)
  1.1267 +    return;
  1.1268 +
  1.1269 +  TrackImage(mCurrentRequest);
  1.1270 +  TrackImage(mPendingRequest);
  1.1271 +
  1.1272 +  if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD)
  1.1273 +    aDocument->BlockOnload();
  1.1274 +}
  1.1275 +
  1.1276 +void
  1.1277 +nsImageLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent)
  1.1278 +{
  1.1279 +  // We may be leaving the document, so if our image is tracked, untrack it.
  1.1280 +  nsCOMPtr<nsIDocument> doc = GetOurCurrentDoc();
  1.1281 +  if (!doc)
  1.1282 +    return;
  1.1283 +
  1.1284 +  UntrackImage(mCurrentRequest);
  1.1285 +  UntrackImage(mPendingRequest);
  1.1286 +
  1.1287 +  if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD)
  1.1288 +    doc->UnblockOnload(false);
  1.1289 +}
  1.1290 +
  1.1291 +void
  1.1292 +nsImageLoadingContent::TrackImage(imgIRequest* aImage)
  1.1293 +{
  1.1294 +  if (!aImage)
  1.1295 +    return;
  1.1296 +
  1.1297 +  MOZ_ASSERT(aImage == mCurrentRequest || aImage == mPendingRequest,
  1.1298 +             "Why haven't we heard of this request?");
  1.1299 +
  1.1300 +  nsIDocument* doc = GetOurCurrentDoc();
  1.1301 +  if (doc && (mFrameCreateCalled || GetOurPrimaryFrame()) &&
  1.1302 +      (mVisibleCount > 0)) {
  1.1303 +    if (aImage == mCurrentRequest && !(mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
  1.1304 +      mCurrentRequestFlags |= REQUEST_IS_TRACKED;
  1.1305 +      doc->AddImage(mCurrentRequest);
  1.1306 +    }
  1.1307 +    if (aImage == mPendingRequest && !(mPendingRequestFlags & REQUEST_IS_TRACKED)) {
  1.1308 +      mPendingRequestFlags |= REQUEST_IS_TRACKED;
  1.1309 +      doc->AddImage(mPendingRequest);
  1.1310 +    }
  1.1311 +  }
  1.1312 +}
  1.1313 +
  1.1314 +void
  1.1315 +nsImageLoadingContent::UntrackImage(imgIRequest* aImage, uint32_t aFlags /* = 0 */)
  1.1316 +{
  1.1317 +  if (!aImage)
  1.1318 +    return;
  1.1319 +
  1.1320 +  MOZ_ASSERT(aImage == mCurrentRequest || aImage == mPendingRequest,
  1.1321 +             "Why haven't we heard of this request?");
  1.1322 +
  1.1323 +  // We may not be in the document.  If we outlived our document that's fine,
  1.1324 +  // because the document empties out the tracker and unlocks all locked images
  1.1325 +  // on destruction.  But if we were never in the document we may need to force
  1.1326 +  // discarding the image here, since this is the only chance we have.
  1.1327 +  nsIDocument* doc = GetOurCurrentDoc();
  1.1328 +  if (aImage == mCurrentRequest) {
  1.1329 +    if (doc && (mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
  1.1330 +      mCurrentRequestFlags &= ~REQUEST_IS_TRACKED;
  1.1331 +      doc->RemoveImage(mCurrentRequest,
  1.1332 +                       (aFlags & REQUEST_DISCARD) ? nsIDocument::REQUEST_DISCARD : 0);
  1.1333 +    }
  1.1334 +    else if (aFlags & REQUEST_DISCARD) {
  1.1335 +      // If we're not in the document we may still need to be discarded.
  1.1336 +      aImage->RequestDiscard();
  1.1337 +    }
  1.1338 +  }
  1.1339 +  if (aImage == mPendingRequest) {
  1.1340 +    if (doc && (mPendingRequestFlags & REQUEST_IS_TRACKED)) {
  1.1341 +      mPendingRequestFlags &= ~REQUEST_IS_TRACKED;
  1.1342 +      doc->RemoveImage(mPendingRequest,
  1.1343 +                       (aFlags & REQUEST_DISCARD) ? nsIDocument::REQUEST_DISCARD : 0);
  1.1344 +    }
  1.1345 +    else if (aFlags & REQUEST_DISCARD) {
  1.1346 +      // If we're not in the document we may still need to be discarded.
  1.1347 +      aImage->RequestDiscard();
  1.1348 +    }
  1.1349 +  }
  1.1350 +}
  1.1351 +
  1.1352 +
  1.1353 +void
  1.1354 +nsImageLoadingContent::CreateStaticImageClone(nsImageLoadingContent* aDest) const
  1.1355 +{
  1.1356 +  aDest->mCurrentRequest = nsContentUtils::GetStaticRequest(mCurrentRequest);
  1.1357 +  aDest->TrackImage(aDest->mCurrentRequest);
  1.1358 +  aDest->mForcedImageState = mForcedImageState;
  1.1359 +  aDest->mImageBlockingStatus = mImageBlockingStatus;
  1.1360 +  aDest->mLoadingEnabled = mLoadingEnabled;
  1.1361 +  aDest->mStateChangerDepth = mStateChangerDepth;
  1.1362 +  aDest->mIsImageStateForced = mIsImageStateForced;
  1.1363 +  aDest->mLoading = mLoading;
  1.1364 +  aDest->mBroken = mBroken;
  1.1365 +  aDest->mUserDisabled = mUserDisabled;
  1.1366 +  aDest->mSuppressed = mSuppressed;
  1.1367 +}
  1.1368 +
  1.1369 +CORSMode
  1.1370 +nsImageLoadingContent::GetCORSMode()
  1.1371 +{
  1.1372 +  return CORS_NONE;
  1.1373 +}
  1.1374 +
  1.1375 +nsImageLoadingContent::ImageObserver::ImageObserver(imgINotificationObserver* aObserver)
  1.1376 +  : mObserver(aObserver)
  1.1377 +  , mNext(nullptr)
  1.1378 +{
  1.1379 +  MOZ_COUNT_CTOR(ImageObserver);
  1.1380 +}
  1.1381 +
  1.1382 +nsImageLoadingContent::ImageObserver::~ImageObserver()
  1.1383 +{
  1.1384 +  MOZ_COUNT_DTOR(ImageObserver);
  1.1385 +  NS_CONTENT_DELETE_LIST_MEMBER(ImageObserver, this, mNext);
  1.1386 +}

mercurial