gecko-dev/layout/base/nsPresContext.cpp

472 lines
11 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsPresContext.h"
#include "nsIPresShell.h"
#include "nsILinkHandler.h"
#include "nsIStyleContext.h"
#include "nsIStyleSet.h"
#include "nsIFontMetrics.h"
#include "nsIImageManager.h"
#include "nsIImageGroup.h"
#include "nsIImageRequest.h"
#include "nsIImageObserver.h"
#include "nsIView.h"
#include "nsIViewManager.h"
#include "nsIFrame.h"
#include "nsIRenderingContext.h"
#include "nsTransform2D.h"
#include "nsIDocument.h"
#include "nsIURL.h"
#include "nsString.h"
static NS_DEFINE_IID(kIPresContextIID, NS_IPRESCONTEXT_IID);
#if 0
// XXX this code needs to move far away from here
static nsIImageManager* gImageManager = nsnull;
static void InitImageLibrary()
{
// Initialize image library
if (NS_OK == NS_NewImageManager(&gImageManager)) {
if (NS_OK == gImageManager->Init()) {
gImageManager->SetCacheSize(1024*1024);
return;
}
}
printf("can't initialize image library\n");
}
// XXX end code with no home...
#endif
class ImageLoader : public nsIImageRequestObserver {
public:
ImageLoader(nsPresContext* aCX, nsIImageGroup* aGroup, nsIFrame* aFrame);
~ImageLoader();
NS_DECL_ISUPPORTS
void LoadImage(const nsString& aURL);
const nsString& GetURL() const {
return mURL;
}
nsIImage* GetImage() {
return mImage;
}
PRInt32 FrameCount();
PRBool RemoveFrame(nsIFrame* aFrame);
void AddFrame(nsIFrame* aFrame);
void UpdateFrames();
virtual void Notify(nsIImageRequest *aImageRequest,
nsIImage *aImage,
nsImageNotification aNotificationType,
PRInt32 aParam1, PRInt32 aParam2,
void *aParam3);
virtual void NotifyError(nsIImageRequest *aImageRequest,
nsImageError aErrorType);
nsVoidArray mFrames;
nsString mURL;
nsPresContext* mCX;
nsIImage* mImage;
nsIImageRequest* mImageRequest;
nsIImageGroup* mImageGroup;
PRInt32 mWidth, mHeight; // in pixels
};
ImageLoader::ImageLoader(nsPresContext* aCX, nsIImageGroup* aGroup,
nsIFrame* aFrame)
{
mCX = aCX;
AddFrame(aFrame);
mImage = nsnull;
mImageRequest = nsnull;
mImageGroup = aGroup; NS_ADDREF(aGroup);
mWidth = -1;
mHeight = -1;
}
ImageLoader::~ImageLoader()
{
if (nsnull != mImageRequest) {
mImageRequest->Interrupt();
mImageRequest->RemoveObserver(this);
}
}
void ImageLoader::LoadImage(const nsString& aURL)
{
if (nsnull == mImage) {
// Save url
mURL.SetLength(0);
mURL.Append(aURL);
// Start image load
char* cp = aURL.ToNewCString();
mImageRequest = mImageGroup->GetImage(cp, this, NS_RGB(255,255,255),
0, 0, 0);
delete cp;
}
}
static NS_DEFINE_IID(kIImageRequestObserver, NS_IIMAGEREQUESTOBSERVER_IID);
NS_IMPL_ISUPPORTS(ImageLoader, kIImageRequestObserver);
static PRBool gXXXInstalledColorMap;
void ImageLoader::Notify(nsIImageRequest *aImageRequest,
nsIImage *aImage,
nsImageNotification aNotificationType,
PRInt32 aParam1, PRInt32 aParam2,
void *aParam3)
{
switch (aNotificationType) {
case nsImageNotification_kDimensions:
#ifdef NOISY
printf("Image:%d x %d\n", aParam1, aParam2);
#endif
mWidth = aParam1;
mHeight = aParam2;
break;
case nsImageNotification_kPixmapUpdate:
case nsImageNotification_kImageComplete:
case nsImageNotification_kFrameComplete:
if ((mImage == nsnull) && (nsnull != aImage)) {
mImage = aImage;
NS_ADDREF(aImage);
}
UpdateFrames();
}
}
PRInt32 ImageLoader::FrameCount()
{
return mFrames.Count();
}
PRBool ImageLoader::RemoveFrame(nsIFrame* aFrame)
{
return mFrames.RemoveElement(aFrame);
}
void ImageLoader::AddFrame(nsIFrame* aFrame)
{
PRInt32 i, n = mFrames.Count();
for (i = 0; i < n; i++) {
nsIFrame* frame = (nsIFrame*) mFrames.ElementAt(i);
if (frame == aFrame) {
return;
}
}
mFrames.AppendElement(aFrame);
}
void ImageLoader::UpdateFrames()
{
PRInt32 i, n = mFrames.Count();
for (i = 0; i < n; i++) {
nsIFrame* frame = (nsIFrame*) mFrames.ElementAt(i);
// XXX installed colormap should be presentation-context/window state
nsIWidget* window;
frame->GetWindow(window);
if (!gXXXInstalledColorMap && mImage) {
nsColorMap* cmap = mImage->GetColorMap();
if ((nsnull != cmap) && (cmap->NumColors > 0)) {
window->SetColorMap(cmap);
}
gXXXInstalledColorMap = PR_TRUE;
}
// Determine damaged area and tell view manager to redraw it
nsPoint offset;
nsRect bounds;
frame->GetRect(bounds);
nsIView* view;
frame->GetOffsetFromView(offset, view);
nsIViewManager* vm = view->GetViewManager();
bounds.x = offset.x;
bounds.y = offset.y;
vm->UpdateView(view, &bounds, 0);
NS_RELEASE(vm);
NS_RELEASE(view);
}
}
void ImageLoader::NotifyError(nsIImageRequest *aImageRequest,
nsImageError aErrorType)
{
printf("XXX image error: %d\n", aErrorType);
}
//----------------------------------------------------------------------
nsPresContext::nsPresContext()
: mVisibleArea(0, 0, 0, 0),
mDefaultFont("Times", NS_FONT_STYLE_NORMAL,
NS_FONT_VARIANT_NORMAL,
NS_FONT_WEIGHT_NORMAL,
0,
NS_POINTS_TO_TWIPS_INT(12))
{
NS_INIT_REFCNT();
mShell = nsnull;
mDeviceContext = nsnull;
mImageGroup = nsnull;
mLinkHandler = nsnull;
mContainer = nsnull;
}
nsPresContext::~nsPresContext()
{
mShell = nsnull;
// XXX there is a race between an async notify and this code because
// the presentation shell code deletes the frame tree first and then
// deletes us. We need an "Deactivation" hook for this code too
// Release all the image loaders
PRInt32 n = mImageLoaders.Count();
for (PRInt32 i = 0; i < n; i++) {
ImageLoader* loader = (ImageLoader*) mImageLoaders.ElementAt(i);
delete loader;
}
// XXX Release all the cached images
if (nsnull != mImageGroup) {
mImageGroup->Interrupt();
NS_RELEASE(mImageGroup);
}
NS_IF_RELEASE(mLinkHandler);
NS_IF_RELEASE(mContainer);
}
nsrefcnt nsPresContext::AddRef(void)
{
return ++mRefCnt;
}
nsrefcnt nsPresContext::Release(void)
{
NS_PRECONDITION(0 != mRefCnt, "bad refcnt");
if (--mRefCnt == 0) {
delete this;
return 0;
}
return mRefCnt;
}
NS_IMPL_QUERY_INTERFACE(nsPresContext, kIPresContextIID);
// Note: We don't hold a reference on the shell; it has a reference to
// us
void nsPresContext::SetShell(nsIPresShell* aShell)
{
mShell = aShell;
}
nsIPresShell* nsPresContext::GetShell()
{
if (nsnull != mShell) {
NS_ADDREF(mShell);
}
return mShell;
}
nsIStyleContext*
nsPresContext::ResolveStyleContextFor(nsIContent* aContent,
nsIFrame* aParentFrame)
{
nsIStyleContext* result = nsnull;
nsIStyleSet* set = mShell->GetStyleSet();
if (nsnull != set) {
result = set->ResolveStyleFor(this, aContent, aParentFrame);
NS_RELEASE(set);
}
return result;
}
nsIFontMetrics* nsPresContext::GetMetricsFor(const nsFont& aFont)
{
if (nsnull != mDeviceContext)
return mDeviceContext->GetMetricsFor(aFont);
return nsnull;
}
const nsFont& nsPresContext::GetDefaultFont(void)
{
return mDefaultFont;
}
nsRect nsPresContext::GetVisibleArea()
{
return mVisibleArea;
}
void nsPresContext::SetVisibleArea(const nsRect& r)
{
mVisibleArea = r;
}
float nsPresContext::GetPixelsToTwips() const
{
if (nsnull != mDeviceContext)
return mDeviceContext->GetDevUnitsToAppUnits();
return 1.0f;
}
float nsPresContext::GetTwipsToPixels() const
{
if (nsnull != mDeviceContext)
return mDeviceContext->GetAppUnitsToDevUnits();
return 1.0f;
}
nsIDeviceContext * nsPresContext::GetDeviceContext() const
{
NS_IF_ADDREF(mDeviceContext);
return mDeviceContext;
}
nsIImage* nsPresContext::LoadImage(const nsString& aURL, nsIFrame* aForFrame)
{
if (nsnull == mImageGroup) {
// XXX this is bad; if we allow for subwindows that have different
// rendering context's this won't work
nsIWidget* window;
aForFrame->GetWindow(window);
nsIRenderingContext* drawCtx = window->GetRenderingContext();
drawCtx->Scale(mDeviceContext->GetAppUnitsToDevUnits(),
mDeviceContext->GetAppUnitsToDevUnits());
if ((NS_OK != NS_NewImageGroup(&mImageGroup)) ||
(NS_OK != mImageGroup->Init(drawCtx))) {
// XXX what to do?
printf("XXX: can't get image group created\n");
NS_RELEASE(drawCtx);
return nsnull;
}
NS_RELEASE(drawCtx);
}
// Get absolute version of the url
nsIDocument* doc = mShell->GetDocument();
nsIURL* docURL = doc->GetDocumentURL();
char* spec = aURL.ToNewCString();
nsIURL* url;
nsresult rv = NS_NewURL(&url, docURL, spec);
delete spec;
NS_RELEASE(docURL);
NS_RELEASE(doc);
if (NS_OK != rv) {
// XXX lost error
return nsnull;
}
nsAutoString absURL;
url->ToString(absURL);
NS_RELEASE(url);
PRInt32 n = mImageLoaders.Count();
for (PRInt32 i = 0; i < n; i++) {
ImageLoader* loader = (ImageLoader*) mImageLoaders.ElementAt(i);
if (absURL.Equals(loader->GetURL())) {
loader->AddFrame(aForFrame);
return loader->GetImage();
}
}
ImageLoader* loader = new ImageLoader(this, mImageGroup, aForFrame);
mImageLoaders.AppendElement(loader);
loader->LoadImage(absURL);
return loader->GetImage();
}
void nsPresContext::StopLoadImage(nsIFrame* aForFrame)
{
PRInt32 n = mImageLoaders.Count();
for (PRInt32 i = 0; i < n;) {
ImageLoader* loader = (ImageLoader*) mImageLoaders.ElementAt(i);
if (loader->RemoveFrame(aForFrame)) {
if (0 == loader->FrameCount()) {
delete loader;
mImageLoaders.RemoveElementAt(i);
n--;
continue;
}
}
i++;
}
}
NS_IMETHODIMP nsPresContext::SetLinkHandler(nsILinkHandler* aHandler)
{ // XXX should probably be a WEAK reference
NS_IF_RELEASE(mLinkHandler);
mLinkHandler = aHandler;
NS_IF_ADDREF(aHandler);
return NS_OK;
}
NS_IMETHODIMP nsPresContext::GetLinkHandler(nsILinkHandler** aResult)
{
NS_PRECONDITION(nsnull != aResult, "null ptr");
if (nsnull == aResult) {
return NS_ERROR_NULL_POINTER;
}
*aResult = mLinkHandler;
NS_IF_ADDREF(mLinkHandler);
return NS_OK;
}
NS_IMETHODIMP nsPresContext::SetContainer(nsISupports* aHandler)
{ // XXX should most likely be a WEAK reference
NS_IF_RELEASE(mContainer);
mContainer = aHandler;
NS_IF_ADDREF(aHandler);
return NS_OK;
}
NS_IMETHODIMP nsPresContext::GetContainer(nsISupports** aResult)
{
NS_PRECONDITION(nsnull != aResult, "null ptr");
if (nsnull == aResult) {
return NS_ERROR_NULL_POINTER;
}
*aResult = mContainer;
NS_IF_ADDREF(mContainer);
return NS_OK;
}