image/src/imgRequest.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/image/src/imgRequest.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1019 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + *
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "imgRequest.h"
    1.11 +#include "ImageLogging.h"
    1.12 +
    1.13 +#include "imgLoader.h"
    1.14 +#include "imgRequestProxy.h"
    1.15 +#include "imgStatusTracker.h"
    1.16 +#include "ImageFactory.h"
    1.17 +#include "Image.h"
    1.18 +#include "RasterImage.h"
    1.19 +
    1.20 +#include "nsIChannel.h"
    1.21 +#include "nsICachingChannel.h"
    1.22 +#include "nsIThreadRetargetableRequest.h"
    1.23 +#include "nsIInputStream.h"
    1.24 +#include "nsIMultiPartChannel.h"
    1.25 +#include "nsIHttpChannel.h"
    1.26 +#include "nsIApplicationCache.h"
    1.27 +#include "nsIApplicationCacheChannel.h"
    1.28 +#include "nsMimeTypes.h"
    1.29 +
    1.30 +#include "nsIInterfaceRequestorUtils.h"
    1.31 +#include "nsISupportsPrimitives.h"
    1.32 +#include "nsIScriptSecurityManager.h"
    1.33 +#include "nsContentUtils.h"
    1.34 +
    1.35 +#include "nsICacheEntry.h"
    1.36 +
    1.37 +#include "plstr.h" // PL_strcasestr(...)
    1.38 +#include "nsNetUtil.h"
    1.39 +#include "nsIProtocolHandler.h"
    1.40 +#include "imgIRequest.h"
    1.41 +
    1.42 +using namespace mozilla;
    1.43 +using namespace mozilla::image;
    1.44 +
    1.45 +#if defined(PR_LOGGING)
    1.46 +PRLogModuleInfo *
    1.47 +GetImgLog()
    1.48 +{
    1.49 +  static PRLogModuleInfo *sImgLog;
    1.50 +  if (!sImgLog)
    1.51 +    sImgLog = PR_NewLogModule("imgRequest");
    1.52 +  return sImgLog;
    1.53 +}
    1.54 +#endif
    1.55 +
    1.56 +NS_IMPL_ISUPPORTS(imgRequest,
    1.57 +                  nsIStreamListener, nsIRequestObserver,
    1.58 +                  nsIThreadRetargetableStreamListener,
    1.59 +                  nsIChannelEventSink,
    1.60 +                  nsIInterfaceRequestor,
    1.61 +                  nsIAsyncVerifyRedirectCallback)
    1.62 +
    1.63 +imgRequest::imgRequest(imgLoader* aLoader)
    1.64 + : mLoader(aLoader)
    1.65 + , mStatusTracker(new imgStatusTracker(nullptr))
    1.66 + , mValidator(nullptr)
    1.67 + , mInnerWindowId(0)
    1.68 + , mCORSMode(imgIRequest::CORS_NONE)
    1.69 + , mDecodeRequested(false)
    1.70 + , mIsMultiPartChannel(false)
    1.71 + , mGotData(false)
    1.72 + , mIsInCache(false)
    1.73 + , mResniffMimeType(false)
    1.74 +{ }
    1.75 +
    1.76 +imgRequest::~imgRequest()
    1.77 +{
    1.78 +  if (mURI) {
    1.79 +    nsAutoCString spec;
    1.80 +    mURI->GetSpec(spec);
    1.81 +    LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequest::~imgRequest()", "keyuri", spec.get());
    1.82 +  } else
    1.83 +    LOG_FUNC(GetImgLog(), "imgRequest::~imgRequest()");
    1.84 +}
    1.85 +
    1.86 +nsresult imgRequest::Init(nsIURI *aURI,
    1.87 +                          nsIURI *aCurrentURI,
    1.88 +                          nsIURI *aFirstPartyIsolationURI,
    1.89 +                          nsIRequest *aRequest,
    1.90 +                          nsIChannel *aChannel,
    1.91 +                          imgCacheEntry *aCacheEntry,
    1.92 +                          void *aLoadId,
    1.93 +                          nsIPrincipal* aLoadingPrincipal,
    1.94 +                          int32_t aCORSMode)
    1.95 +{
    1.96 +  MOZ_ASSERT(NS_IsMainThread(), "Cannot use nsIURI off main thread!");
    1.97 +
    1.98 +  LOG_FUNC(GetImgLog(), "imgRequest::Init");
    1.99 +
   1.100 +  NS_ABORT_IF_FALSE(!mImage, "Multiple calls to init");
   1.101 +  NS_ABORT_IF_FALSE(aURI, "No uri");
   1.102 +  NS_ABORT_IF_FALSE(aCurrentURI, "No current uri");
   1.103 +  NS_ABORT_IF_FALSE(aRequest, "No request");
   1.104 +  NS_ABORT_IF_FALSE(aChannel, "No channel");
   1.105 +
   1.106 +  mProperties = do_CreateInstance("@mozilla.org/properties;1");
   1.107 +
   1.108 +  // Use ImageURL to ensure access to URI data off main thread.
   1.109 +  mURI = new ImageURL(aURI);
   1.110 +  mCurrentURI = aCurrentURI;
   1.111 +  mFirstPartyIsolationURI = aFirstPartyIsolationURI;
   1.112 +  mRequest = aRequest;
   1.113 +  mChannel = aChannel;
   1.114 +  mTimedChannel = do_QueryInterface(mChannel);
   1.115 +
   1.116 +  mLoadingPrincipal = aLoadingPrincipal;
   1.117 +  mCORSMode = aCORSMode;
   1.118 +
   1.119 +  mChannel->GetNotificationCallbacks(getter_AddRefs(mPrevChannelSink));
   1.120 +
   1.121 +  NS_ASSERTION(mPrevChannelSink != this,
   1.122 +               "Initializing with a channel that already calls back to us!");
   1.123 +
   1.124 +  mChannel->SetNotificationCallbacks(this);
   1.125 +
   1.126 +  mCacheEntry = aCacheEntry;
   1.127 +
   1.128 +  SetLoadId(aLoadId);
   1.129 +
   1.130 +  return NS_OK;
   1.131 +}
   1.132 +
   1.133 +already_AddRefed<imgStatusTracker>
   1.134 +imgRequest::GetStatusTracker()
   1.135 +{
   1.136 +  if (mImage && mGotData) {
   1.137 +    NS_ABORT_IF_FALSE(!mStatusTracker,
   1.138 +                      "Should have given mStatusTracker to mImage");
   1.139 +    return mImage->GetStatusTracker();
   1.140 +  } else {
   1.141 +    NS_ABORT_IF_FALSE(mStatusTracker,
   1.142 +                      "Should have mStatusTracker until we create mImage");
   1.143 +    nsRefPtr<imgStatusTracker> statusTracker = mStatusTracker;
   1.144 +    MOZ_ASSERT(statusTracker);
   1.145 +    return statusTracker.forget();
   1.146 +  }
   1.147 +}
   1.148 +
   1.149 +void imgRequest::SetCacheEntry(imgCacheEntry *entry)
   1.150 +{
   1.151 +  mCacheEntry = entry;
   1.152 +}
   1.153 +
   1.154 +bool imgRequest::HasCacheEntry() const
   1.155 +{
   1.156 +  return mCacheEntry != nullptr;
   1.157 +}
   1.158 +
   1.159 +void imgRequest::ResetCacheEntry()
   1.160 +{
   1.161 +  if (HasCacheEntry()) {
   1.162 +    mCacheEntry->SetDataSize(0);
   1.163 +  }
   1.164 +}
   1.165 +
   1.166 +void imgRequest::AddProxy(imgRequestProxy *proxy)
   1.167 +{
   1.168 +  NS_PRECONDITION(proxy, "null imgRequestProxy passed in");
   1.169 +  LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgRequest::AddProxy", "proxy", proxy);
   1.170 +
   1.171 +  // If we're empty before adding, we have to tell the loader we now have
   1.172 +  // proxies.
   1.173 +  nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
   1.174 +  if (statusTracker->ConsumerCount() == 0) {
   1.175 +    NS_ABORT_IF_FALSE(mURI, "Trying to SetHasProxies without key uri.");
   1.176 +    mLoader->SetHasProxies(mFirstPartyIsolationURI, mURI);
   1.177 +  }
   1.178 +
   1.179 +  statusTracker->AddConsumer(proxy);
   1.180 +}
   1.181 +
   1.182 +nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus)
   1.183 +{
   1.184 +  LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgRequest::RemoveProxy", "proxy", proxy);
   1.185 +
   1.186 +  // This will remove our animation consumers, so after removing
   1.187 +  // this proxy, we don't end up without proxies with observers, but still
   1.188 +  // have animation consumers.
   1.189 +  proxy->ClearAnimationConsumers();
   1.190 +
   1.191 +  // Let the status tracker do its thing before we potentially call Cancel()
   1.192 +  // below, because Cancel() may result in OnStopRequest being called back
   1.193 +  // before Cancel() returns, leaving the image in a different state then the
   1.194 +  // one it was in at this point.
   1.195 +  nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
   1.196 +  if (!statusTracker->RemoveConsumer(proxy, aStatus))
   1.197 +    return NS_OK;
   1.198 +
   1.199 +  if (statusTracker->ConsumerCount() == 0) {
   1.200 +    // If we have no observers, there's nothing holding us alive. If we haven't
   1.201 +    // been cancelled and thus removed from the cache, tell the image loader so
   1.202 +    // we can be evicted from the cache.
   1.203 +    if (mCacheEntry) {
   1.204 +      NS_ABORT_IF_FALSE(mURI, "Removing last observer without key uri.");
   1.205 +
   1.206 +      mLoader->SetHasNoProxies(mURI, mCacheEntry);
   1.207 +    }
   1.208 +#if defined(PR_LOGGING)
   1.209 +    else {
   1.210 +      nsAutoCString spec;
   1.211 +      mURI->GetSpec(spec);
   1.212 +      LOG_MSG_WITH_PARAM(GetImgLog(), "imgRequest::RemoveProxy no cache entry", "uri", spec.get());
   1.213 +    }
   1.214 +#endif
   1.215 +
   1.216 +    /* If |aStatus| is a failure code, then cancel the load if it is still in progress.
   1.217 +       Otherwise, let the load continue, keeping 'this' in the cache with no observers.
   1.218 +       This way, if a proxy is destroyed without calling cancel on it, it won't leak
   1.219 +       and won't leave a bad pointer in the observer list.
   1.220 +     */
   1.221 +    if (statusTracker->IsLoading() && NS_FAILED(aStatus)) {
   1.222 +      LOG_MSG(GetImgLog(), "imgRequest::RemoveProxy", "load in progress.  canceling");
   1.223 +
   1.224 +      this->Cancel(NS_BINDING_ABORTED);
   1.225 +    }
   1.226 +
   1.227 +    /* break the cycle from the cache entry. */
   1.228 +    mCacheEntry = nullptr;
   1.229 +  }
   1.230 +
   1.231 +  // If a proxy is removed for a reason other than its owner being
   1.232 +  // changed, remove the proxy from the loadgroup.
   1.233 +  if (aStatus != NS_IMAGELIB_CHANGING_OWNER)
   1.234 +    proxy->RemoveFromLoadGroup(true);
   1.235 +
   1.236 +  return NS_OK;
   1.237 +}
   1.238 +
   1.239 +void imgRequest::CancelAndAbort(nsresult aStatus)
   1.240 +{
   1.241 +  LOG_SCOPE(GetImgLog(), "imgRequest::CancelAndAbort");
   1.242 +
   1.243 +  Cancel(aStatus);
   1.244 +
   1.245 +  // It's possible for the channel to fail to open after we've set our
   1.246 +  // notification callbacks. In that case, make sure to break the cycle between
   1.247 +  // the channel and us, because it won't.
   1.248 +  if (mChannel) {
   1.249 +    mChannel->SetNotificationCallbacks(mPrevChannelSink);
   1.250 +    mPrevChannelSink = nullptr;
   1.251 +  }
   1.252 +}
   1.253 +
   1.254 +class imgRequestMainThreadCancel : public nsRunnable
   1.255 +{
   1.256 +public:
   1.257 +  imgRequestMainThreadCancel(imgRequest *aImgRequest, nsresult aStatus)
   1.258 +    : mImgRequest(aImgRequest)
   1.259 +    , mStatus(aStatus)
   1.260 +  {
   1.261 +    MOZ_ASSERT(!NS_IsMainThread(), "Create me off main thread only!");
   1.262 +    MOZ_ASSERT(aImgRequest);
   1.263 +  }
   1.264 +
   1.265 +  NS_IMETHOD Run()
   1.266 +  {
   1.267 +    MOZ_ASSERT(NS_IsMainThread(), "I should be running on the main thread!");
   1.268 +    mImgRequest->ContinueCancel(mStatus);
   1.269 +    return NS_OK;
   1.270 +  } 
   1.271 +private:
   1.272 +  nsRefPtr<imgRequest> mImgRequest;
   1.273 +  nsresult mStatus;
   1.274 +};
   1.275 +
   1.276 +void imgRequest::Cancel(nsresult aStatus)
   1.277 +{
   1.278 +  /* The Cancel() method here should only be called by this class. */
   1.279 +
   1.280 +  LOG_SCOPE(GetImgLog(), "imgRequest::Cancel");
   1.281 +
   1.282 +  nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
   1.283 +
   1.284 +  statusTracker->MaybeUnblockOnload();
   1.285 +
   1.286 +  statusTracker->RecordCancel();
   1.287 +
   1.288 +  if (NS_IsMainThread()) {
   1.289 +    ContinueCancel(aStatus);
   1.290 +  } else {
   1.291 +    NS_DispatchToMainThread(new imgRequestMainThreadCancel(this, aStatus));
   1.292 +  }
   1.293 +}
   1.294 +
   1.295 +void imgRequest::ContinueCancel(nsresult aStatus)
   1.296 +{
   1.297 +  MOZ_ASSERT(NS_IsMainThread());
   1.298 +
   1.299 +  RemoveFromCache();
   1.300 +
   1.301 +  nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
   1.302 +  if (mRequest && statusTracker->IsLoading()) {
   1.303 +     mRequest->Cancel(aStatus);
   1.304 +  }
   1.305 +}
   1.306 +
   1.307 +nsresult imgRequest::GetURI(ImageURL **aURI)
   1.308 +{
   1.309 +  MOZ_ASSERT(aURI);
   1.310 +
   1.311 +  LOG_FUNC(GetImgLog(), "imgRequest::GetURI");
   1.312 +
   1.313 +  if (mURI) {
   1.314 +    *aURI = mURI;
   1.315 +    NS_ADDREF(*aURI);
   1.316 +    return NS_OK;
   1.317 +  }
   1.318 +
   1.319 +  return NS_ERROR_FAILURE;
   1.320 +}
   1.321 +
   1.322 +nsresult imgRequest::GetSecurityInfo(nsISupports **aSecurityInfo)
   1.323 +{
   1.324 +  LOG_FUNC(GetImgLog(), "imgRequest::GetSecurityInfo");
   1.325 +
   1.326 +  // Missing security info means this is not a security load
   1.327 +  // i.e. it is not an error when security info is missing
   1.328 +  NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
   1.329 +  return NS_OK;
   1.330 +}
   1.331 +
   1.332 +void imgRequest::RemoveFromCache()
   1.333 +{
   1.334 +  LOG_SCOPE(GetImgLog(), "imgRequest::RemoveFromCache");
   1.335 +
   1.336 +  if (mIsInCache) {
   1.337 +    // mCacheEntry is nulled out when we have no more observers.
   1.338 +    if (mCacheEntry)
   1.339 +      mLoader->RemoveFromCache(mCacheEntry);
   1.340 +    else {
   1.341 +      mLoader->RemoveFromCache(mLoader->GetCacheKey(mFirstPartyIsolationURI, mURI, nullptr),
   1.342 +                               mLoader->GetCache(mURI),
   1.343 +                               mLoader->GetCacheQueue(mURI));
   1.344 +    }
   1.345 +  }
   1.346 +
   1.347 +  mCacheEntry = nullptr;
   1.348 +}
   1.349 +
   1.350 +int32_t imgRequest::Priority() const
   1.351 +{
   1.352 +  int32_t priority = nsISupportsPriority::PRIORITY_NORMAL;
   1.353 +  nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mRequest);
   1.354 +  if (p)
   1.355 +    p->GetPriority(&priority);
   1.356 +  return priority;
   1.357 +}
   1.358 +
   1.359 +void imgRequest::AdjustPriority(imgRequestProxy *proxy, int32_t delta)
   1.360 +{
   1.361 +  // only the first proxy is allowed to modify the priority of this image load.
   1.362 +  //
   1.363 +  // XXX(darin): this is probably not the most optimal algorithm as we may want
   1.364 +  // to increase the priority of requests that have a lot of proxies.  the key
   1.365 +  // concern though is that image loads remain lower priority than other pieces
   1.366 +  // of content such as link clicks, CSS, and JS.
   1.367 +  //
   1.368 +  nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
   1.369 +  if (!statusTracker->FirstConsumerIs(proxy))
   1.370 +    return;
   1.371 +
   1.372 +  nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mChannel);
   1.373 +  if (p)
   1.374 +    p->AdjustPriority(delta);
   1.375 +}
   1.376 +
   1.377 +void imgRequest::SetIsInCache(bool incache)
   1.378 +{
   1.379 +  LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequest::SetIsCacheable", "incache", incache);
   1.380 +  mIsInCache = incache;
   1.381 +}
   1.382 +
   1.383 +void imgRequest::UpdateCacheEntrySize()
   1.384 +{
   1.385 +  if (mCacheEntry)
   1.386 +    mCacheEntry->SetDataSize(mImage->SizeOfData());
   1.387 +}
   1.388 +
   1.389 +void imgRequest::SetCacheValidation(imgCacheEntry* aCacheEntry, nsIRequest* aRequest)
   1.390 +{
   1.391 +  /* get the expires info */
   1.392 +  if (aCacheEntry) {
   1.393 +    nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(aRequest));
   1.394 +    if (cacheChannel) {
   1.395 +      nsCOMPtr<nsISupports> cacheToken;
   1.396 +      cacheChannel->GetCacheToken(getter_AddRefs(cacheToken));
   1.397 +      if (cacheToken) {
   1.398 +        nsCOMPtr<nsICacheEntry> entryDesc(do_QueryInterface(cacheToken));
   1.399 +        if (entryDesc) {
   1.400 +          uint32_t expiration;
   1.401 +          /* get the expiration time from the caching channel's token */
   1.402 +          entryDesc->GetExpirationTime(&expiration);
   1.403 +
   1.404 +          // Expiration time defaults to 0. We set the expiration time on our
   1.405 +          // entry if it hasn't been set yet.
   1.406 +          if (aCacheEntry->GetExpiryTime() == 0)
   1.407 +            aCacheEntry->SetExpiryTime(expiration);
   1.408 +        }
   1.409 +      }
   1.410 +    }
   1.411 +
   1.412 +    // Determine whether the cache entry must be revalidated when we try to use it.
   1.413 +    // Currently, only HTTP specifies this information...
   1.414 +    nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
   1.415 +    if (httpChannel) {
   1.416 +      bool bMustRevalidate = false;
   1.417 +
   1.418 +      httpChannel->IsNoStoreResponse(&bMustRevalidate);
   1.419 +
   1.420 +      if (!bMustRevalidate) {
   1.421 +        httpChannel->IsNoCacheResponse(&bMustRevalidate);
   1.422 +      }
   1.423 +
   1.424 +      if (!bMustRevalidate) {
   1.425 +        nsAutoCString cacheHeader;
   1.426 +
   1.427 +        httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Cache-Control"),
   1.428 +                                            cacheHeader);
   1.429 +        if (PL_strcasestr(cacheHeader.get(), "must-revalidate")) {
   1.430 +          bMustRevalidate = true;
   1.431 +        }
   1.432 +      }
   1.433 +
   1.434 +      // Cache entries default to not needing to validate. We ensure that
   1.435 +      // multiple calls to this function don't override an earlier decision to
   1.436 +      // validate by making validation a one-way decision.
   1.437 +      if (bMustRevalidate)
   1.438 +        aCacheEntry->SetMustValidate(bMustRevalidate);
   1.439 +    }
   1.440 +
   1.441 +    // We always need to validate file URIs.
   1.442 +    nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
   1.443 +    if (channel) {
   1.444 +      nsCOMPtr<nsIURI> uri;
   1.445 +      channel->GetURI(getter_AddRefs(uri));
   1.446 +      bool isfile = false;
   1.447 +      uri->SchemeIs("file", &isfile);
   1.448 +      if (isfile)
   1.449 +        aCacheEntry->SetMustValidate(isfile);
   1.450 +    }
   1.451 +  }
   1.452 +}
   1.453 +
   1.454 +namespace { // anon
   1.455 +
   1.456 +already_AddRefed<nsIApplicationCache>
   1.457 +GetApplicationCache(nsIRequest* aRequest)
   1.458 +{
   1.459 +  nsresult rv;
   1.460 +
   1.461 +  nsCOMPtr<nsIApplicationCacheChannel> appCacheChan = do_QueryInterface(aRequest);
   1.462 +  if (!appCacheChan) {
   1.463 +    return nullptr;
   1.464 +  }
   1.465 +
   1.466 +  bool fromAppCache;
   1.467 +  rv = appCacheChan->GetLoadedFromApplicationCache(&fromAppCache);
   1.468 +  NS_ENSURE_SUCCESS(rv, nullptr);
   1.469 +
   1.470 +  if (!fromAppCache) {
   1.471 +    return nullptr;
   1.472 +  }
   1.473 +
   1.474 +  nsCOMPtr<nsIApplicationCache> appCache;
   1.475 +  rv = appCacheChan->GetApplicationCache(getter_AddRefs(appCache));
   1.476 +  NS_ENSURE_SUCCESS(rv, nullptr);
   1.477 +
   1.478 +  return appCache.forget();
   1.479 +}
   1.480 +
   1.481 +} // anon
   1.482 +
   1.483 +bool
   1.484 +imgRequest::CacheChanged(nsIRequest* aNewRequest)
   1.485 +{
   1.486 +  nsCOMPtr<nsIApplicationCache> newAppCache = GetApplicationCache(aNewRequest);
   1.487 +
   1.488 +  // Application cache not involved at all or the same app cache involved
   1.489 +  // in both of the loads (original and new).
   1.490 +  if (newAppCache == mApplicationCache)
   1.491 +    return false;
   1.492 +
   1.493 +  // In a rare case it may happen that two objects still refer
   1.494 +  // the same application cache version.
   1.495 +  if (newAppCache && mApplicationCache) {
   1.496 +    nsresult rv;
   1.497 +
   1.498 +    nsAutoCString oldAppCacheClientId, newAppCacheClientId;
   1.499 +    rv = mApplicationCache->GetClientID(oldAppCacheClientId);
   1.500 +    NS_ENSURE_SUCCESS(rv, true);
   1.501 +    rv = newAppCache->GetClientID(newAppCacheClientId);
   1.502 +    NS_ENSURE_SUCCESS(rv, true);
   1.503 +
   1.504 +    if (oldAppCacheClientId == newAppCacheClientId)
   1.505 +      return false;
   1.506 +  }
   1.507 +
   1.508 +  // When we get here, app caches differ or app cache is involved
   1.509 +  // just in one of the loads what we also consider as a change
   1.510 +  // in a loading cache.
   1.511 +  return true;
   1.512 +}
   1.513 +
   1.514 +nsresult
   1.515 +imgRequest::LockImage()
   1.516 +{
   1.517 +  return mImage->LockImage();
   1.518 +}
   1.519 +
   1.520 +nsresult
   1.521 +imgRequest::UnlockImage()
   1.522 +{
   1.523 +  return mImage->UnlockImage();
   1.524 +}
   1.525 +
   1.526 +nsresult
   1.527 +imgRequest::RequestDecode()
   1.528 +{
   1.529 +  // If we've initialized our image, we can request a decode.
   1.530 +  if (mImage) {
   1.531 +    return mImage->RequestDecode();
   1.532 +  }
   1.533 +
   1.534 +  // Otherwise, flag to do it when we get the image
   1.535 +  mDecodeRequested = true;
   1.536 +
   1.537 +  return NS_OK;
   1.538 +}
   1.539 +
   1.540 +nsresult
   1.541 +imgRequest::StartDecoding()
   1.542 +{
   1.543 +  // If we've initialized our image, we can request a decode.
   1.544 +  if (mImage) {
   1.545 +    return mImage->StartDecoding();
   1.546 +  }
   1.547 +
   1.548 +  // Otherwise, flag to do it when we get the image
   1.549 +  mDecodeRequested = true;
   1.550 +
   1.551 +  return NS_OK;
   1.552 +}
   1.553 +
   1.554 +/** nsIRequestObserver methods **/
   1.555 +
   1.556 +/* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
   1.557 +NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
   1.558 +{
   1.559 +  LOG_SCOPE(GetImgLog(), "imgRequest::OnStartRequest");
   1.560 +
   1.561 +  // Figure out if we're multipart
   1.562 +  nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
   1.563 +  nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
   1.564 +  if (mpchan) {
   1.565 +    mIsMultiPartChannel = true;
   1.566 +    statusTracker->SetIsMultipart();
   1.567 +  }
   1.568 +
   1.569 +  // If we're not multipart, we shouldn't have an image yet
   1.570 +  NS_ABORT_IF_FALSE(mIsMultiPartChannel || !mImage,
   1.571 +                    "Already have an image for non-multipart request");
   1.572 +
   1.573 +  // If we're multipart and about to load another image, signal so we can
   1.574 +  // detect the mime type in OnDataAvailable.
   1.575 +  if (mIsMultiPartChannel && mImage) {
   1.576 +    mResniffMimeType = true;
   1.577 +
   1.578 +    // Tell the image to reinitialize itself. We have to do this in
   1.579 +    // OnStartRequest so that its state machine is always in a consistent
   1.580 +    // state.
   1.581 +    // Note that if our MIME type changes, mImage will be replaced with a
   1.582 +    // new object.
   1.583 +    mImage->OnNewSourceData();
   1.584 +  }
   1.585 +
   1.586 +  /*
   1.587 +   * If mRequest is null here, then we need to set it so that we'll be able to
   1.588 +   * cancel it if our Cancel() method is called.  Note that this can only
   1.589 +   * happen for multipart channels.  We could simply not null out mRequest for
   1.590 +   * non-last parts, if GetIsLastPart() were reliable, but it's not.  See
   1.591 +   * https://bugzilla.mozilla.org/show_bug.cgi?id=339610
   1.592 +   */
   1.593 +  if (!mRequest) {
   1.594 +    NS_ASSERTION(mpchan,
   1.595 +                 "We should have an mRequest here unless we're multipart");
   1.596 +    nsCOMPtr<nsIChannel> chan;
   1.597 +    mpchan->GetBaseChannel(getter_AddRefs(chan));
   1.598 +    mRequest = chan;
   1.599 +  }
   1.600 +
   1.601 +  // Note: refreshing statusTracker in case OnNewSourceData changed it.
   1.602 +  statusTracker = GetStatusTracker();
   1.603 +  statusTracker->OnStartRequest();
   1.604 +
   1.605 +  nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
   1.606 +  if (channel)
   1.607 +    channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
   1.608 +
   1.609 +  /* Get our principal */
   1.610 +  nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
   1.611 +  if (chan) {
   1.612 +    nsCOMPtr<nsIScriptSecurityManager> secMan = nsContentUtils::GetSecurityManager();
   1.613 +    if (secMan) {
   1.614 +      nsresult rv = secMan->GetChannelPrincipal(chan,
   1.615 +                                                getter_AddRefs(mPrincipal));
   1.616 +      if (NS_FAILED(rv)) {
   1.617 +        return rv;
   1.618 +      }
   1.619 +    }
   1.620 +  }
   1.621 +
   1.622 +  SetCacheValidation(mCacheEntry, aRequest);
   1.623 +
   1.624 +  mApplicationCache = GetApplicationCache(aRequest);
   1.625 +
   1.626 +  // Shouldn't we be dead already if this gets hit?  Probably multipart/x-mixed-replace...
   1.627 +  if (statusTracker->ConsumerCount() == 0) {
   1.628 +    this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
   1.629 +  }
   1.630 +
   1.631 +  // Try to retarget OnDataAvailable to a decode thread.
   1.632 +  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
   1.633 +  nsCOMPtr<nsIThreadRetargetableRequest> retargetable =
   1.634 +    do_QueryInterface(aRequest);
   1.635 +  if (httpChannel && retargetable &&
   1.636 +      ImageFactory::CanRetargetOnDataAvailable(mURI, mIsMultiPartChannel)) {
   1.637 +    nsAutoCString mimeType;
   1.638 +    nsresult rv = httpChannel->GetContentType(mimeType);
   1.639 +    if (NS_SUCCEEDED(rv) && !mimeType.EqualsLiteral(IMAGE_SVG_XML)) {
   1.640 +      // Image object not created until OnDataAvailable, so forward to static
   1.641 +      // DecodePool directly.
   1.642 +      nsCOMPtr<nsIEventTarget> target = RasterImage::GetEventTarget();
   1.643 +      rv = retargetable->RetargetDeliveryTo(target);
   1.644 +    }
   1.645 +    PR_LOG(GetImgLog(), PR_LOG_WARNING,
   1.646 +           ("[this=%p] imgRequest::OnStartRequest -- "
   1.647 +            "RetargetDeliveryTo rv %d=%s\n",
   1.648 +            this, rv, NS_SUCCEEDED(rv) ? "succeeded" : "failed"));
   1.649 +  }
   1.650 +
   1.651 +  return NS_OK;
   1.652 +}
   1.653 +
   1.654 +/* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
   1.655 +NS_IMETHODIMP imgRequest::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status)
   1.656 +{
   1.657 +  LOG_FUNC(GetImgLog(), "imgRequest::OnStopRequest");
   1.658 +
   1.659 +  // XXXldb What if this is a non-last part of a multipart request?
   1.660 +  // xxx before we release our reference to mRequest, lets
   1.661 +  // save the last status that we saw so that the
   1.662 +  // imgRequestProxy will have access to it.
   1.663 +  if (mRequest) {
   1.664 +    mRequest = nullptr;  // we no longer need the request
   1.665 +  }
   1.666 +
   1.667 +  // stop holding a ref to the channel, since we don't need it anymore
   1.668 +  if (mChannel) {
   1.669 +    mChannel->SetNotificationCallbacks(mPrevChannelSink);
   1.670 +    mPrevChannelSink = nullptr;
   1.671 +    mChannel = nullptr;
   1.672 +  }
   1.673 +
   1.674 +  bool lastPart = true;
   1.675 +  nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
   1.676 +  if (mpchan)
   1.677 +    mpchan->GetIsLastPart(&lastPart);
   1.678 +
   1.679 +  // Tell the image that it has all of the source data. Note that this can
   1.680 +  // trigger a failure, since the image might be waiting for more non-optional
   1.681 +  // data and this is the point where we break the news that it's not coming.
   1.682 +  if (mImage) {
   1.683 +    nsresult rv = mImage->OnImageDataComplete(aRequest, ctxt, status, lastPart);
   1.684 +
   1.685 +    // If we got an error in the OnImageDataComplete() call, we don't want to
   1.686 +    // proceed as if nothing bad happened. However, we also want to give
   1.687 +    // precedence to failure status codes from necko, since presumably they're
   1.688 +    // more meaningful.
   1.689 +    if (NS_FAILED(rv) && NS_SUCCEEDED(status))
   1.690 +      status = rv;
   1.691 +  }
   1.692 +
   1.693 +  // If the request went through, update the cache entry size. Otherwise,
   1.694 +  // cancel the request, which removes us from the cache.
   1.695 +  if (mImage && NS_SUCCEEDED(status)) {
   1.696 +    // We update the cache entry size here because this is where we finish
   1.697 +    // loading compressed source data, which is part of our size calculus.
   1.698 +    UpdateCacheEntrySize();
   1.699 +  }
   1.700 +  else {
   1.701 +    // stops animations, removes from cache
   1.702 +    this->Cancel(status);
   1.703 +  }
   1.704 +
   1.705 +  if (!mImage) {
   1.706 +    // We have to fire imgStatusTracker::OnStopRequest ourselves because there's
   1.707 +    // no image capable of doing so.
   1.708 +    nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
   1.709 +    statusTracker->OnStopRequest(lastPart, status);
   1.710 +  }
   1.711 +
   1.712 +  mTimedChannel = nullptr;
   1.713 +  return NS_OK;
   1.714 +}
   1.715 +
   1.716 +struct mimetype_closure
   1.717 +{
   1.718 +  nsACString* newType;
   1.719 +};
   1.720 +
   1.721 +/* prototype for these defined below */
   1.722 +static NS_METHOD sniff_mimetype_callback(nsIInputStream* in, void* closure, const char* fromRawSegment,
   1.723 +                                         uint32_t toOffset, uint32_t count, uint32_t *writeCount);
   1.724 +
   1.725 +/** nsThreadRetargetableStreamListener methods **/
   1.726 +NS_IMETHODIMP
   1.727 +imgRequest::CheckListenerChain()
   1.728 +{
   1.729 +  // TODO Might need more checking here.
   1.730 +  NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
   1.731 +  return NS_OK;
   1.732 +}
   1.733 +
   1.734 +/** nsIStreamListener methods **/
   1.735 +
   1.736 +/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long long sourceOffset, in unsigned long count); */
   1.737 +NS_IMETHODIMP
   1.738 +imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt,
   1.739 +                            nsIInputStream *inStr, uint64_t sourceOffset,
   1.740 +                            uint32_t count)
   1.741 +{
   1.742 +  LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgRequest::OnDataAvailable", "count", count);
   1.743 +
   1.744 +  NS_ASSERTION(aRequest, "imgRequest::OnDataAvailable -- no request!");
   1.745 +
   1.746 +  nsresult rv;
   1.747 +
   1.748 +  if (!mGotData || mResniffMimeType) {
   1.749 +    LOG_SCOPE(GetImgLog(), "imgRequest::OnDataAvailable |First time through... finding mimetype|");
   1.750 +
   1.751 +    mGotData = true;
   1.752 +
   1.753 +    // Store and reset this for the invariant that it's always false after
   1.754 +    // calls to OnDataAvailable (see bug 907575)
   1.755 +    bool resniffMimeType = mResniffMimeType;
   1.756 +    mResniffMimeType = false;
   1.757 +
   1.758 +    mimetype_closure closure;
   1.759 +    nsAutoCString newType;
   1.760 +    closure.newType = &newType;
   1.761 +
   1.762 +    /* look at the first few bytes and see if we can tell what the data is from that
   1.763 +     * since servers tend to lie. :(
   1.764 +     */
   1.765 +    uint32_t out;
   1.766 +    inStr->ReadSegments(sniff_mimetype_callback, &closure, count, &out);
   1.767 +
   1.768 +    nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
   1.769 +    if (newType.IsEmpty()) {
   1.770 +      LOG_SCOPE(GetImgLog(), "imgRequest::OnDataAvailable |sniffing of mimetype failed|");
   1.771 +
   1.772 +      rv = NS_ERROR_FAILURE;
   1.773 +      if (chan) {
   1.774 +        rv = chan->GetContentType(newType);
   1.775 +      }
   1.776 +
   1.777 +      if (NS_FAILED(rv)) {
   1.778 +        PR_LOG(GetImgLog(), PR_LOG_ERROR,
   1.779 +               ("[this=%p] imgRequest::OnDataAvailable -- Content type unavailable from the channel\n",
   1.780 +                this));
   1.781 +
   1.782 +        this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
   1.783 +
   1.784 +        return NS_BINDING_ABORTED;
   1.785 +      }
   1.786 +
   1.787 +      LOG_MSG(GetImgLog(), "imgRequest::OnDataAvailable", "Got content type from the channel");
   1.788 +    }
   1.789 +
   1.790 +    // If we're a regular image and this is the first call to OnDataAvailable,
   1.791 +    // this will always be true. If we've resniffed our MIME type (i.e. we're a
   1.792 +    // multipart/x-mixed-replace image), we have to be able to switch our image
   1.793 +    // type and decoder.
   1.794 +    // We always reinitialize for SVGs, because they have no way of
   1.795 +    // reinitializing themselves.
   1.796 +    if (mContentType != newType || newType.EqualsLiteral(IMAGE_SVG_XML)) {
   1.797 +      mContentType = newType;
   1.798 +
   1.799 +      // If we've resniffed our MIME type and it changed, we need to create a
   1.800 +      // new status tracker to give to the image, because we don't have one of
   1.801 +      // our own any more.
   1.802 +      if (resniffMimeType) {
   1.803 +        NS_ABORT_IF_FALSE(mIsMultiPartChannel, "Resniffing a non-multipart image");
   1.804 +
   1.805 +        nsRefPtr<imgStatusTracker> freshTracker = new imgStatusTracker(nullptr);
   1.806 +        nsRefPtr<imgStatusTracker> oldStatusTracker = GetStatusTracker();
   1.807 +        freshTracker->AdoptConsumers(oldStatusTracker);
   1.808 +        mStatusTracker = freshTracker.forget();
   1.809 +      }
   1.810 +
   1.811 +      SetProperties(chan);
   1.812 +
   1.813 +      LOG_MSG_WITH_PARAM(GetImgLog(), "imgRequest::OnDataAvailable", "content type", mContentType.get());
   1.814 +
   1.815 +      // XXX If server lied about mimetype and it's SVG, we may need to copy
   1.816 +      // the data and dispatch back to the main thread, AND tell the channel to
   1.817 +      // dispatch there in the future.
   1.818 +
   1.819 +      // Now we can create a new image to hold the data. If we don't have a decoder
   1.820 +      // for this mimetype we'll find out about it here.
   1.821 +      mImage = ImageFactory::CreateImage(aRequest, mStatusTracker, mContentType,
   1.822 +                                         mURI, mIsMultiPartChannel,
   1.823 +                                         static_cast<uint32_t>(mInnerWindowId));
   1.824 +
   1.825 +      // Release our copy of the status tracker since the image owns it now.
   1.826 +      mStatusTracker = nullptr;
   1.827 +
   1.828 +      // Notify listeners that we have an image.
   1.829 +      // XXX(seth): The name of this notification method is pretty misleading.
   1.830 +      nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
   1.831 +      statusTracker->OnDataAvailable();
   1.832 +
   1.833 +      if (mImage->HasError() && !mIsMultiPartChannel) { // Probably bad mimetype
   1.834 +        // We allow multipart images to fail to initialize without cancelling the
   1.835 +        // load because subsequent images might be fine; thus only single part
   1.836 +        // images end up here.
   1.837 +        this->Cancel(NS_ERROR_FAILURE);
   1.838 +        return NS_BINDING_ABORTED;
   1.839 +      }
   1.840 +
   1.841 +      NS_ABORT_IF_FALSE(statusTracker->HasImage(), "Status tracker should have an image!");
   1.842 +      NS_ABORT_IF_FALSE(mImage, "imgRequest should have an image!");
   1.843 +
   1.844 +      if (mDecodeRequested)
   1.845 +        mImage->StartDecoding();
   1.846 +    }
   1.847 +  }
   1.848 +
   1.849 +  // Notify the image that it has new data.
   1.850 +  rv = mImage->OnImageDataAvailable(aRequest, ctxt, inStr, sourceOffset, count);
   1.851 +
   1.852 +  if (NS_FAILED(rv)) {
   1.853 +    PR_LOG(GetImgLog(), PR_LOG_WARNING,
   1.854 +           ("[this=%p] imgRequest::OnDataAvailable -- "
   1.855 +            "copy to RasterImage failed\n", this));
   1.856 +    this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
   1.857 +    return NS_BINDING_ABORTED;
   1.858 +  }
   1.859 +
   1.860 +  return NS_OK;
   1.861 +}
   1.862 +
   1.863 +class SetPropertiesEvent : public nsRunnable
   1.864 +{
   1.865 +public:
   1.866 +  SetPropertiesEvent(imgRequest* aImgRequest, nsIChannel* aChan)
   1.867 +    : mImgRequest(aImgRequest)
   1.868 +    , mChan(aChan)
   1.869 +  {
   1.870 +    MOZ_ASSERT(!NS_IsMainThread(), "Should be created off the main thread");
   1.871 +    MOZ_ASSERT(aImgRequest, "aImgRequest cannot be null");
   1.872 +  }
   1.873 +  NS_IMETHOD Run()
   1.874 +  {
   1.875 +    MOZ_ASSERT(NS_IsMainThread(), "Should run on the main thread only");
   1.876 +    MOZ_ASSERT(mImgRequest, "mImgRequest cannot be null");
   1.877 +    mImgRequest->SetProperties(mChan);
   1.878 +    return NS_OK;
   1.879 +  }
   1.880 +private:
   1.881 +  nsRefPtr<imgRequest> mImgRequest;
   1.882 +  nsCOMPtr<nsIChannel> mChan;
   1.883 +};
   1.884 +
   1.885 +void
   1.886 +imgRequest::SetProperties(nsIChannel* aChan)
   1.887 +{
   1.888 +  // Force execution on main thread since some property objects are non
   1.889 +  // threadsafe.
   1.890 +  if (!NS_IsMainThread()) {
   1.891 +    NS_DispatchToMainThread(new SetPropertiesEvent(this, aChan));
   1.892 +    return;
   1.893 +  }
   1.894 +  /* set our mimetype as a property */
   1.895 +  nsCOMPtr<nsISupportsCString> contentType(do_CreateInstance("@mozilla.org/supports-cstring;1"));
   1.896 +  if (contentType) {
   1.897 +    contentType->SetData(mContentType);
   1.898 +    mProperties->Set("type", contentType);
   1.899 +  }
   1.900 +
   1.901 +  /* set our content disposition as a property */
   1.902 +  nsAutoCString disposition;
   1.903 +  if (aChan) {
   1.904 +    aChan->GetContentDispositionHeader(disposition);
   1.905 +  }
   1.906 +  if (!disposition.IsEmpty()) {
   1.907 +    nsCOMPtr<nsISupportsCString> contentDisposition(do_CreateInstance("@mozilla.org/supports-cstring;1"));
   1.908 +    if (contentDisposition) {
   1.909 +      contentDisposition->SetData(disposition);
   1.910 +      mProperties->Set("content-disposition", contentDisposition);
   1.911 +    }
   1.912 +  }
   1.913 +}
   1.914 +
   1.915 +static NS_METHOD sniff_mimetype_callback(nsIInputStream* in,
   1.916 +                                         void* data,
   1.917 +                                         const char* fromRawSegment,
   1.918 +                                         uint32_t toOffset,
   1.919 +                                         uint32_t count,
   1.920 +                                         uint32_t *writeCount)
   1.921 +{
   1.922 +  mimetype_closure* closure = static_cast<mimetype_closure*>(data);
   1.923 +
   1.924 +  NS_ASSERTION(closure, "closure is null!");
   1.925 +
   1.926 +  if (count > 0)
   1.927 +    imgLoader::GetMimeTypeFromContent(fromRawSegment, count, *closure->newType);
   1.928 +
   1.929 +  *writeCount = 0;
   1.930 +  return NS_ERROR_FAILURE;
   1.931 +}
   1.932 +
   1.933 +
   1.934 +/** nsIInterfaceRequestor methods **/
   1.935 +
   1.936 +NS_IMETHODIMP
   1.937 +imgRequest::GetInterface(const nsIID & aIID, void **aResult)
   1.938 +{
   1.939 +  if (!mPrevChannelSink || aIID.Equals(NS_GET_IID(nsIChannelEventSink)))
   1.940 +    return QueryInterface(aIID, aResult);
   1.941 +
   1.942 +  NS_ASSERTION(mPrevChannelSink != this,
   1.943 +               "Infinite recursion - don't keep track of channel sinks that are us!");
   1.944 +  return mPrevChannelSink->GetInterface(aIID, aResult);
   1.945 +}
   1.946 +
   1.947 +/** nsIChannelEventSink methods **/
   1.948 +NS_IMETHODIMP
   1.949 +imgRequest::AsyncOnChannelRedirect(nsIChannel *oldChannel,
   1.950 +                                   nsIChannel *newChannel, uint32_t flags,
   1.951 +                                   nsIAsyncVerifyRedirectCallback *callback)
   1.952 +{
   1.953 +  NS_ASSERTION(mRequest && mChannel, "Got a channel redirect after we nulled out mRequest!");
   1.954 +  NS_ASSERTION(mChannel == oldChannel, "Got a channel redirect for an unknown channel!");
   1.955 +  NS_ASSERTION(newChannel, "Got a redirect to a NULL channel!");
   1.956 +
   1.957 +  SetCacheValidation(mCacheEntry, oldChannel);
   1.958 +
   1.959 +  // Prepare for callback
   1.960 +  mRedirectCallback = callback;
   1.961 +  mNewRedirectChannel = newChannel;
   1.962 +
   1.963 +  nsCOMPtr<nsIChannelEventSink> sink(do_GetInterface(mPrevChannelSink));
   1.964 +  if (sink) {
   1.965 +    nsresult rv = sink->AsyncOnChannelRedirect(oldChannel, newChannel, flags,
   1.966 +                                               this);
   1.967 +    if (NS_FAILED(rv)) {
   1.968 +        mRedirectCallback = nullptr;
   1.969 +        mNewRedirectChannel = nullptr;
   1.970 +    }
   1.971 +    return rv;
   1.972 +  }
   1.973 +
   1.974 +  (void) OnRedirectVerifyCallback(NS_OK);
   1.975 +  return NS_OK;
   1.976 +}
   1.977 +
   1.978 +NS_IMETHODIMP
   1.979 +imgRequest::OnRedirectVerifyCallback(nsresult result)
   1.980 +{
   1.981 +  NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
   1.982 +  NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
   1.983 +
   1.984 +  if (NS_FAILED(result)) {
   1.985 +      mRedirectCallback->OnRedirectVerifyCallback(result);
   1.986 +      mRedirectCallback = nullptr;
   1.987 +      mNewRedirectChannel = nullptr;
   1.988 +      return NS_OK;
   1.989 +  }
   1.990 +
   1.991 +  mChannel = mNewRedirectChannel;
   1.992 +  mTimedChannel = do_QueryInterface(mChannel);
   1.993 +  mNewRedirectChannel = nullptr;
   1.994 +
   1.995 +#if defined(PR_LOGGING)
   1.996 +  nsAutoCString oldspec;
   1.997 +  if (mCurrentURI)
   1.998 +    mCurrentURI->GetSpec(oldspec);
   1.999 +  LOG_MSG_WITH_PARAM(GetImgLog(), "imgRequest::OnChannelRedirect", "old", oldspec.get());
  1.1000 +#endif
  1.1001 +
  1.1002 +  // make sure we have a protocol that returns data rather than opens
  1.1003 +  // an external application, e.g. mailto:
  1.1004 +  mChannel->GetURI(getter_AddRefs(mCurrentURI));
  1.1005 +  bool doesNotReturnData = false;
  1.1006 +  nsresult rv =
  1.1007 +    NS_URIChainHasFlags(mCurrentURI, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
  1.1008 +                        &doesNotReturnData);
  1.1009 +
  1.1010 +  if (NS_SUCCEEDED(rv) && doesNotReturnData)
  1.1011 +    rv = NS_ERROR_ABORT;
  1.1012 +
  1.1013 +  if (NS_FAILED(rv)) {
  1.1014 +    mRedirectCallback->OnRedirectVerifyCallback(rv);
  1.1015 +    mRedirectCallback = nullptr;
  1.1016 +    return NS_OK;
  1.1017 +  }
  1.1018 +
  1.1019 +  mRedirectCallback->OnRedirectVerifyCallback(NS_OK);
  1.1020 +  mRedirectCallback = nullptr;
  1.1021 +  return NS_OK;
  1.1022 +}

mercurial