/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 2001 Netscape Communications Corporation. * All Rights Reserved. * * Contributor(s): * Stuart Parmenter */ #include "nsImageLoader.h" #include "imgILoader.h" #include "nsIURI.h" #include "nsILoadGroup.h" #include "nsNetUtil.h" #include "nsIPresContext.h" #include "nsIPresShell.h" #include "nsIFrame.h" #include "nsIContent.h" #include "nsIDocument.h" #include "nsIHTMLContent.h" #include "nsIViewManager.h" #include "nsIStyleContext.h" NS_IMPL_ISUPPORTS2(nsImageLoader, imgIDecoderObserver, imgIContainerObserver) nsImageLoader::nsImageLoader() : mFrame(nsnull), mPresContext(nsnull) { NS_INIT_ISUPPORTS(); } nsImageLoader::~nsImageLoader() { mFrame = nsnull; mPresContext = nsnull; if (mRequest) { mRequest->Cancel(NS_ERROR_FAILURE); } } void nsImageLoader::Init(nsIFrame *aFrame, nsIPresContext *aPresContext) { mFrame = aFrame; mPresContext = aPresContext; } void nsImageLoader::Destroy() { mFrame = nsnull; mPresContext = nsnull; if (mRequest) { mRequest->Cancel(NS_ERROR_FAILURE); } mRequest = nsnull; } nsresult nsImageLoader::Load(nsIURI *aURI) { if (!mFrame) return NS_ERROR_NOT_INITIALIZED; if (!aURI) return NS_ERROR_FAILURE; nsCOMPtr loadGroup; nsCOMPtr uri; nsCOMPtr baseURI; nsCOMPtr shell; nsresult rv = mPresContext->GetShell(getter_AddRefs(shell)); if (NS_FAILED(rv)) return rv; nsCOMPtr doc; rv = shell->GetDocument(getter_AddRefs(doc)); if (NS_FAILED(rv)) return rv; // Get the document's loadgroup doc->GetDocumentLoadGroup(getter_AddRefs(loadGroup)); if (mRequest) { nsCOMPtr oldURI; mRequest->GetURI(getter_AddRefs(oldURI)); PRBool eq = PR_FALSE; aURI->Equals(oldURI, &eq); if (eq) { return NS_OK; } } nsCOMPtr il(do_GetService("@mozilla.org/image/loader;1", &rv)); if (NS_FAILED(rv)) return rv; return il->LoadImage(aURI, nsnull, loadGroup, NS_STATIC_CAST(imgIDecoderObserver *, this), nsnull, nsIRequest::LOAD_BACKGROUND, nsnull, nsnull, getter_AddRefs(mRequest)); } NS_IMETHODIMP nsImageLoader::OnStartDecode(imgIRequest *aRequest, nsISupports *aContext) { return NS_OK; } NS_IMETHODIMP nsImageLoader::OnStartContainer(imgIRequest *aRequest, nsISupports *aContext, imgIContainer *aImage) { return NS_OK; } NS_IMETHODIMP nsImageLoader::OnStartFrame(imgIRequest *aRequest, nsISupports *aContext, gfxIImageFrame *aFrame) { return NS_OK; } NS_IMETHODIMP nsImageLoader::OnDataAvailable(imgIRequest *aRequest, nsISupports *aContext, gfxIImageFrame *aFrame, const nsRect *aRect) { // Background images are not displayed incrementally, they are displayed after the entire // image has been loaded. // Note: Images referenced by the element are displayed incrementally in nsImageFrame.cpp return NS_OK; } NS_IMETHODIMP nsImageLoader::OnStopFrame(imgIRequest *aRequest, nsISupports *aContext, gfxIImageFrame *aFrame) { if (!mFrame) return NS_ERROR_FAILURE; // Draw the background image RedrawDirtyFrame(nsnull); return NS_OK; } NS_IMETHODIMP nsImageLoader::OnStopContainer(imgIRequest *aRequest, nsISupports *aContext, imgIContainer *aImage) { return NS_OK; } NS_IMETHODIMP nsImageLoader::OnStopDecode(imgIRequest *aRequest, nsISupports *aContext, nsresult status, const PRUnichar *statusArg) { return NS_OK; } NS_IMETHODIMP nsImageLoader::FrameChanged(imgIContainer *aContainer, nsISupports *aContext, gfxIImageFrame *newframe, nsRect * dirtyRect) { if (!mFrame) return NS_ERROR_FAILURE; nsRect r(*dirtyRect); float p2t; mPresContext->GetPixelsToTwips(&p2t); r.x = NSIntPixelsToTwips(r.x, p2t); r.y = NSIntPixelsToTwips(r.y, p2t); r.width = NSIntPixelsToTwips(r.width, p2t); r.height = NSIntPixelsToTwips(r.height, p2t); RedrawDirtyFrame(&r); return NS_OK; } void nsImageLoader::RedrawDirtyFrame(const nsRect* aDamageRect) { // Determine damaged area and tell view manager to redraw it nsPoint offset; nsRect bounds; nsIView* view; // NOTE: It is not sufficient to invalidate only the size of the image: // the image may be tiled! // The best option is to call into the frame, however lacking this // we have to at least invalidate the frame's bounds, hence // as long as we have a frame we'll use its size. // // Invalidate the entire frame // XXX We really only need to invalidate the client area of the frame... mFrame->GetRect(bounds); bounds.x = bounds.y = 0; // XXX this should be ok, but there is some crappy ass bug causing it not to work // XXX seems related to the "body fixup rule" dealing with the canvas and body frames... #if 0 // Invalidate the entire frame only if the frame has a tiled background // image, otherwise just invalidate the intersection of the frame's bounds // with the damaged rect. nsCOMPtr styleContext; mFrame->GetStyleContext(getter_AddRefs(styleContext)); const nsStyleBackground* bg = (const nsStyleBackground*)styleContext->GetStyleData(eStyleStruct_Background); if ((bg->mBackgroundFlags & NS_STYLE_BG_IMAGE_NONE) || (bg->mBackgroundRepeat == NS_STYLE_BG_REPEAT_OFF)) { // The frame does not have a background image so we are free // to invalidate only the intersection of the damage rect and // the frame's bounds. if (aDamageRect) { bounds.IntersectRect(*aDamageRect, bounds); } } #endif if ((bounds.width > 0) && (bounds.height > 0)) { // XXX We should tell the frame the damage area and let it invalidate // itself. Add some API calls to nsIFrame to allow a caller to invalidate // parts of the frame... mFrame->GetView(mPresContext, &view); if (!view) { mFrame->GetOffsetFromView(mPresContext, offset, &view); bounds.x += offset.x; bounds.y += offset.y; } nsCOMPtr vm = nsnull; nsresult rv = NS_OK; rv = view->GetViewManager(*getter_AddRefs(vm)); if (NS_SUCCEEDED(rv) && vm) { vm->UpdateView(view, bounds, NS_VMREFRESH_NO_SYNC); } } }