/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
NS_IMETHODIMP imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
{
  LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "count", count);

  NS_ASSERTION(aRequest, "imgRequest::OnDataAvailable -- no request!");

  nsresult rv;

  PRUint16 imageType;
  if (mGotData) {
    imageType = mImage->GetType();
  } else {
    LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |First time through... finding mimetype|");

    mGotData = PR_TRUE;

    /* look at the first few bytes and see if we can tell what the data is from that
     * since servers tend to lie. :(
     */
    PRUint32 out;
    inStr->ReadSegments(sniff_mimetype_callback, this, count, &out);

#ifdef NS_DEBUG
    /* NS_WARNING if the content type from the channel isn't the same if the sniffing */
#endif

    if (mContentType.IsEmpty()) {
      LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |sniffing of mimetype failed|");

      nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));

      rv = NS_ERROR_FAILURE;
      if (chan) {
        rv = chan->GetContentType(mContentType);
      }

      if (NS_FAILED(rv)) {
        PR_LOG(gImgLog, PR_LOG_ERROR,
               ("[this=%p] imgRequest::OnDataAvailable -- Content type unavailable from the channel\n",
                this));

        this->Cancel(NS_IMAGELIB_ERROR_FAILURE);

        return NS_BINDING_ABORTED;
      }

      LOG_MSG(gImgLog, "imgRequest::OnDataAvailable", "Got content type from the channel");
    }

    /* now we have mimetype, so we can infer the image type that we want */
    if (mContentType.EqualsLiteral(SVG_MIMETYPE)) {
      mImage = new VectorImage(mStatusTracker.forget());
    } else {
      mImage = new RasterImage(mStatusTracker.forget());
    }
    mImage->SetWindowID(mWindowId);
    imageType = mImage->GetType();

    // Notify any imgRequestProxys that are observing us that we have an Image.
    nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
    while (iter.HasMore()) {
      iter.GetNext()->SetImage(mImage);
    }

    /* set our mimetype as a property */
    nsCOMPtr<nsISupportsCString> contentType(do_CreateInstance("@mozilla.org/supports-cstring;1"));
    if (contentType) {
      contentType->SetData(mContentType);
      mProperties->Set("type", contentType);
    }

    /* set our content disposition as a property */
    nsCAutoString disposition;
    nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
    if (httpChannel) {
      httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-disposition"), disposition);
    } else {
      nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aRequest));
      if (multiPartChannel) {
        multiPartChannel->GetContentDisposition(disposition);
      }
    }
    if (!disposition.IsEmpty()) {
      nsCOMPtr<nsISupportsCString> contentDisposition(do_CreateInstance("@mozilla.org/supports-cstring;1"));
      if (contentDisposition) {
        contentDisposition->SetData(disposition);
        mProperties->Set("content-disposition", contentDisposition);
      }
    }

    LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "content type", mContentType.get());

    //
    // Figure out our Image initialization flags
    //

    // We default to the static globals
    PRBool isDiscardable = gDiscardable;
    PRBool doDecodeOnDraw = gDecodeOnDraw;

    // We want UI to be as snappy as possible and not to flicker. Disable discarding
    // and decode-on-draw for chrome URLS
    PRBool isChrome = PR_FALSE;
    rv = mURI->SchemeIs("chrome", &isChrome);
    if (NS_SUCCEEDED(rv) && isChrome)
      isDiscardable = doDecodeOnDraw = PR_FALSE;

    // We don't want resources like the "loading" icon to be discardable or
    // decode-on-draw either.
    PRBool isResource = PR_FALSE;
    rv = mURI->SchemeIs("resource", &isResource);
    if (NS_SUCCEEDED(rv) && isResource)
      isDiscardable = doDecodeOnDraw = PR_FALSE;

    // For multipart/x-mixed-replace, we basically want a direct channel to the
    // decoder. Disable both for this case as well.
    if (mIsMultiPartChannel)
      isDiscardable = doDecodeOnDraw = PR_FALSE;

    // We have all the information we need
    PRUint32 imageFlags = Image::INIT_FLAG_NONE;
    if (isDiscardable)
      imageFlags |= Image::INIT_FLAG_DISCARDABLE;
    if (doDecodeOnDraw)
      imageFlags |= Image::INIT_FLAG_DECODE_ON_DRAW;
    if (mIsMultiPartChannel)
      imageFlags |= Image::INIT_FLAG_MULTIPART;

    // Get our URI string
    nsCAutoString uriString;
    rv = mURI->GetSpec(uriString);
    if (NS_FAILED(rv))
      uriString.Assign("<unknown image URI>");

    // Initialize the image that we created above. For RasterImages, this
    // instantiates a decoder behind the scenes, so if we don't have a decoder
    // for this mimetype we'll find out about it here.
    rv = mImage->Init(this, mContentType.get(), uriString.get(), imageFlags);
    if (NS_FAILED(rv)) { // Probably bad mimetype

      this->Cancel(rv);
      return NS_BINDING_ABORTED;
    }

    if (imageType == imgIContainer::TYPE_RASTER) {
      /* Use content-length as a size hint for http channels. */
      if (httpChannel) {
        nsCAutoString contentLength;
        rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-length"),
                                            contentLength);
        if (NS_SUCCEEDED(rv)) {
          PRInt32 len = contentLength.ToInteger(&rv);

          // Pass anything usable on so that the RasterImage can preallocate
          // its source buffer
          if (len > 0) {
            PRUint32 sizeHint = (PRUint32) len;
            sizeHint = PR_MIN(sizeHint, 20000000); /* Bound by something reasonable */
            RasterImage* rasterImage = static_cast<RasterImage*>(mImage.get());
            rasterImage->SetSourceSizeHint(sizeHint);
          }
        }
      }
    }

    if (imageType == imgIContainer::TYPE_RASTER) {
      // If we were waiting on the image to do something, now's our chance.
      if (mDecodeRequested) {
        mImage->RequestDecode();
      }
    } else { // imageType == imgIContainer::TYPE_VECTOR
      nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
      NS_ABORT_IF_FALSE(imageAsStream,
                        "SVG-typed Image failed QI to nsIStreamListener");
      imageAsStream->OnStartRequest(aRequest, nsnull);
    }
  }

  if (imageType == imgIContainer::TYPE_RASTER) {
    // WriteToRasterImage always consumes everything it gets
    PRUint32 bytesRead;
    rv = inStr->ReadSegments(RasterImage::WriteToRasterImage,
                             static_cast<void*>(mImage),
                             count, &bytesRead);
    NS_ABORT_IF_FALSE(bytesRead == count,
                      "WriteToRasterImage should consume everything!");
  } else { // imageType == imgIContainer::TYPE_VECTOR
    nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
    rv = imageAsStream->OnDataAvailable(aRequest, ctxt, inStr,
                                        sourceOffset, count);
  }
  if (NS_FAILED(rv)) {
    PR_LOG(gImgLog, PR_LOG_WARNING,
           ("[this=%p] imgRequest::OnDataAvailable -- "
            "copy to RasterImage failed\n", this));
    this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
    return NS_BINDING_ABORTED;
  }

  return NS_OK;
}
Beispiel #2
0
/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long long sourceOffset, in unsigned long count); */
NS_IMETHODIMP
imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt,
                            nsIInputStream *inStr, uint64_t sourceOffset,
                            uint32_t count)
{
  LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "count", count);

  NS_ASSERTION(aRequest, "imgRequest::OnDataAvailable -- no request!");

  nsresult rv;

  if (!mGotData || mResniffMimeType) {
    LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |First time through... finding mimetype|");

    mGotData = true;

    mimetype_closure closure;
    nsAutoCString newType;
    closure.request = this;
    closure.newType = &newType;

    /* look at the first few bytes and see if we can tell what the data is from that
     * since servers tend to lie. :(
     */
    uint32_t out;
    inStr->ReadSegments(sniff_mimetype_callback, &closure, count, &out);

#ifdef DEBUG
    /* NS_WARNING if the content type from the channel isn't the same if the sniffing */
#endif

    nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
    if (newType.IsEmpty()) {
      LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |sniffing of mimetype failed|");

      rv = NS_ERROR_FAILURE;
      if (chan) {
        rv = chan->GetContentType(newType);
      }

      if (NS_FAILED(rv)) {
        PR_LOG(gImgLog, PR_LOG_ERROR,
               ("[this=%p] imgRequest::OnDataAvailable -- Content type unavailable from the channel\n",
                this));

        this->Cancel(NS_IMAGELIB_ERROR_FAILURE);

        return NS_BINDING_ABORTED;
      }

      LOG_MSG(gImgLog, "imgRequest::OnDataAvailable", "Got content type from the channel");
    }

    // If we're a regular image and this is the first call to OnDataAvailable,
    // this will always be true. If we've resniffed our MIME type (i.e. we're a
    // multipart/x-mixed-replace image), we have to be able to switch our image
    // type and decoder.
    // We always reinitialize for SVGs, because they have no way of
    // reinitializing themselves.
    if (mContentType != newType || newType.EqualsLiteral(SVG_MIMETYPE)) {
      mContentType = newType;

      // If we've resniffed our MIME type and it changed, we need to create a
      // new status tracker to give to the image, because we don't have one of
      // our own any more.
      if (mResniffMimeType) {
        NS_ABORT_IF_FALSE(mIsMultiPartChannel, "Resniffing a non-multipart image");
        mStatusTracker = new imgStatusTracker(nullptr);
      }

      mResniffMimeType = false;

      /* now we have mimetype, so we can infer the image type that we want */
      if (mContentType.EqualsLiteral(SVG_MIMETYPE)) {
        mImage = new VectorImage(mStatusTracker.forget());
      } else {
        mImage = new RasterImage(mStatusTracker.forget());
      }
      mImage->SetInnerWindowID(mInnerWindowId);

      // Notify any imgRequestProxys that are observing us that we have an Image.
      nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
      while (iter.HasMore()) {
        iter.GetNext()->SetImage(mImage);
      }

      /* set our mimetype as a property */
      nsCOMPtr<nsISupportsCString> contentType(do_CreateInstance("@mozilla.org/supports-cstring;1"));
      if (contentType) {
        contentType->SetData(mContentType);
        mProperties->Set("type", contentType);
      }

      /* set our content disposition as a property */
      nsAutoCString disposition;
      if (chan) {
        chan->GetContentDispositionHeader(disposition);
      }
      if (!disposition.IsEmpty()) {
        nsCOMPtr<nsISupportsCString> contentDisposition(do_CreateInstance("@mozilla.org/supports-cstring;1"));
        if (contentDisposition) {
          contentDisposition->SetData(disposition);
          mProperties->Set("content-disposition", contentDisposition);
        }
      }

      LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "content type", mContentType.get());

      //
      // Figure out our Image initialization flags
      //

      // We default to the static globals
      bool isDiscardable = gDiscardable;
      bool doDecodeOnDraw = gDecodeOnDraw;

      // We want UI to be as snappy as possible and not to flicker. Disable discarding
      // and decode-on-draw for chrome URLS
      bool isChrome = false;
      rv = mURI->SchemeIs("chrome", &isChrome);
      if (NS_SUCCEEDED(rv) && isChrome)
        isDiscardable = doDecodeOnDraw = false;

      // We don't want resources like the "loading" icon to be discardable or
      // decode-on-draw either.
      bool isResource = false;
      rv = mURI->SchemeIs("resource", &isResource);
      if (NS_SUCCEEDED(rv) && isResource)
        isDiscardable = doDecodeOnDraw = false;

      // For multipart/x-mixed-replace, we basically want a direct channel to the
      // decoder. Disable both for this case as well.
      if (mIsMultiPartChannel)
        isDiscardable = doDecodeOnDraw = false;

      // We have all the information we need
      uint32_t imageFlags = Image::INIT_FLAG_NONE;
      if (isDiscardable)
        imageFlags |= Image::INIT_FLAG_DISCARDABLE;
      if (doDecodeOnDraw)
        imageFlags |= Image::INIT_FLAG_DECODE_ON_DRAW;
      if (mIsMultiPartChannel)
        imageFlags |= Image::INIT_FLAG_MULTIPART;

      // Get our URI string
      nsAutoCString uriString;
      rv = mURI->GetSpec(uriString);
      if (NS_FAILED(rv))
        uriString.Assign("<unknown image URI>");

      // Initialize the image that we created above. For RasterImages, this
      // instantiates a decoder behind the scenes, so if we don't have a decoder
      // for this mimetype we'll find out about it here.
      rv = mImage->Init(this, mContentType.get(), uriString.get(), imageFlags);

      // We allow multipart images to fail to initialize without cancelling the
      // load because subsequent images might be fine.
      if (NS_FAILED(rv) && !mIsMultiPartChannel) { // Probably bad mimetype

        this->Cancel(rv);
        return NS_BINDING_ABORTED;
      }

      if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
        /* Use content-length as a size hint for http channels. */
        nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
        if (httpChannel) {
          nsAutoCString contentLength;
          rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-length"),
                                              contentLength);
          if (NS_SUCCEEDED(rv)) {
            int32_t len = contentLength.ToInteger(&rv);

            // Pass anything usable on so that the RasterImage can preallocate
            // its source buffer
            if (len > 0) {
              uint32_t sizeHint = (uint32_t) len;
              sizeHint = NS_MIN<uint32_t>(sizeHint, 20000000); /* Bound by something reasonable */
              RasterImage* rasterImage = static_cast<RasterImage*>(mImage.get());
              rv = rasterImage->SetSourceSizeHint(sizeHint);
              if (NS_FAILED(rv)) {
                // Flush memory, try to get some back, and try again
                rv = nsMemory::HeapMinimize(true);
                nsresult rv2 = rasterImage->SetSourceSizeHint(sizeHint);
                // If we've still failed at this point, things are going downhill
                if (NS_FAILED(rv) || NS_FAILED(rv2)) {
                  NS_WARNING("About to hit OOM in imagelib!");
                }
              }
            }
          }
        }
      }

      if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
        // If we were waiting on the image to do something, now's our chance.
        if (mDecodeRequested) {
          mImage->StartDecoding();
        }
      } else { // mImage->GetType() == imgIContainer::TYPE_VECTOR
        nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
        NS_ABORT_IF_FALSE(imageAsStream,
                          "SVG-typed Image failed QI to nsIStreamListener");
        imageAsStream->OnStartRequest(aRequest, nullptr);
      }
    }
  }

  if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
    // WriteToRasterImage always consumes everything it gets
    // if it doesn't run out of memory
    uint32_t bytesRead;
    rv = inStr->ReadSegments(RasterImage::WriteToRasterImage,
                             static_cast<void*>(mImage),
                             count, &bytesRead);
    NS_ABORT_IF_FALSE(bytesRead == count || mImage->HasError(),
  "WriteToRasterImage should consume everything or the image must be in error!");
  } else { // mImage->GetType() == imgIContainer::TYPE_VECTOR
    nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
    rv = imageAsStream->OnDataAvailable(aRequest, ctxt, inStr,
                                        sourceOffset, count);
  }
  if (NS_FAILED(rv)) {
    PR_LOG(gImgLog, PR_LOG_WARNING,
           ("[this=%p] imgRequest::OnDataAvailable -- "
            "copy to RasterImage failed\n", this));
    this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
    return NS_BINDING_ABORTED;
  }

  return NS_OK;
}