/* 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; }
/* 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; }